From bc8348b19908d2495c85c771c7b2acd2c2dcd79f Mon Sep 17 00:00:00 2001 From: pptx704 Date: Thu, 16 Apr 2026 03:24:42 +0600 Subject: [PATCH] Add DB queries for account self-service New queries: UpdateUserPassword, SoftDeleteUser, HardDeleteExpiredUsers, CountUserOwnedTeamsWithOtherMembers, GetOAuthProvidersByUserID, DeleteOAuthProvider. --- db/queries/oauth.sql | 6 ++++++ db/queries/users.sql | 19 +++++++++++++++++ pkg/db/oauth.sql.go | 44 ++++++++++++++++++++++++++++++++++++++ pkg/db/users.sql.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/db/queries/oauth.sql b/db/queries/oauth.sql index 31b1ff8..9dc7929 100644 --- a/db/queries/oauth.sql +++ b/db/queries/oauth.sql @@ -5,3 +5,9 @@ VALUES ($1, $2, $3, $4); -- name: GetOAuthProvider :one SELECT * FROM oauth_providers WHERE provider = $1 AND provider_id = $2; + +-- name: GetOAuthProvidersByUserID :many +SELECT * FROM oauth_providers WHERE user_id = $1; + +-- name: DeleteOAuthProvider :exec +DELETE FROM oauth_providers WHERE user_id = $1 AND provider = $2; diff --git a/db/queries/users.sql b/db/queries/users.sql index bd7d85a..30efe78 100644 --- a/db/queries/users.sql +++ b/db/queries/users.sql @@ -66,3 +66,22 @@ WHERE deleted_at IS NULL; -- name: SetUserActive :exec UPDATE users SET is_active = $2, updated_at = NOW() WHERE id = $1; + +-- name: UpdateUserPassword :exec +UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1; + +-- name: SoftDeleteUser :exec +UPDATE users SET deleted_at = NOW(), is_active = false, updated_at = NOW() WHERE id = $1; + +-- name: CountUserOwnedTeamsWithOtherMembers :one +SELECT COUNT(DISTINCT ut.team_id)::int +FROM users_teams ut +WHERE ut.user_id = $1 + AND ut.role = 'owner' + AND EXISTS ( + SELECT 1 FROM users_teams ut2 + WHERE ut2.team_id = ut.team_id AND ut2.user_id <> $1 + ); + +-- name: HardDeleteExpiredUsers :exec +DELETE FROM users WHERE deleted_at IS NOT NULL AND deleted_at < NOW() - INTERVAL '15 days'; diff --git a/pkg/db/oauth.sql.go b/pkg/db/oauth.sql.go index 0270def..724277e 100644 --- a/pkg/db/oauth.sql.go +++ b/pkg/db/oauth.sql.go @@ -11,6 +11,20 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +const deleteOAuthProvider = `-- name: DeleteOAuthProvider :exec +DELETE FROM oauth_providers WHERE user_id = $1 AND provider = $2 +` + +type DeleteOAuthProviderParams struct { + UserID pgtype.UUID `json:"user_id"` + Provider string `json:"provider"` +} + +func (q *Queries) DeleteOAuthProvider(ctx context.Context, arg DeleteOAuthProviderParams) error { + _, err := q.db.Exec(ctx, deleteOAuthProvider, arg.UserID, arg.Provider) + return err +} + const getOAuthProvider = `-- name: GetOAuthProvider :one SELECT provider, provider_id, user_id, email, created_at FROM oauth_providers WHERE provider = $1 AND provider_id = $2 @@ -34,6 +48,36 @@ func (q *Queries) GetOAuthProvider(ctx context.Context, arg GetOAuthProviderPara return i, err } +const getOAuthProvidersByUserID = `-- name: GetOAuthProvidersByUserID :many +SELECT provider, provider_id, user_id, email, created_at FROM oauth_providers WHERE user_id = $1 +` + +func (q *Queries) GetOAuthProvidersByUserID(ctx context.Context, userID pgtype.UUID) ([]OauthProvider, error) { + rows, err := q.db.Query(ctx, getOAuthProvidersByUserID, userID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []OauthProvider + for rows.Next() { + var i OauthProvider + if err := rows.Scan( + &i.Provider, + &i.ProviderID, + &i.UserID, + &i.Email, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const insertOAuthProvider = `-- name: InsertOAuthProvider :exec INSERT INTO oauth_providers (provider, provider_id, user_id, email) VALUES ($1, $2, $3, $4) diff --git a/pkg/db/users.sql.go b/pkg/db/users.sql.go index 73ebe52..9b6e9f9 100644 --- a/pkg/db/users.sql.go +++ b/pkg/db/users.sql.go @@ -11,6 +11,24 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +const countUserOwnedTeamsWithOtherMembers = `-- name: CountUserOwnedTeamsWithOtherMembers :one +SELECT COUNT(DISTINCT ut.team_id)::int +FROM users_teams ut +WHERE ut.user_id = $1 + AND ut.role = 'owner' + AND EXISTS ( + SELECT 1 FROM users_teams ut2 + WHERE ut2.team_id = ut.team_id AND ut2.user_id <> $1 + ) +` + +func (q *Queries) CountUserOwnedTeamsWithOtherMembers(ctx context.Context, userID pgtype.UUID) (int32, error) { + row := q.db.QueryRow(ctx, countUserOwnedTeamsWithOtherMembers, userID) + var column_1 int32 + err := row.Scan(&column_1) + return column_1, err +} + const countUsers = `-- name: CountUsers :one SELECT COUNT(*) FROM users ` @@ -154,6 +172,15 @@ func (q *Queries) GetUserByID(ctx context.Context, id pgtype.UUID) (User, error) return i, err } +const hardDeleteExpiredUsers = `-- name: HardDeleteExpiredUsers :exec +DELETE FROM users WHERE deleted_at IS NOT NULL AND deleted_at < NOW() - INTERVAL '15 days' +` + +func (q *Queries) HardDeleteExpiredUsers(ctx context.Context) error { + _, err := q.db.Exec(ctx, hardDeleteExpiredUsers) + return err +} + const hasAdminPermission = `-- name: HasAdminPermission :one SELECT EXISTS( SELECT 1 FROM admin_permissions WHERE user_id = $1 AND permission = $2 @@ -370,6 +397,15 @@ func (q *Queries) SetUserAdmin(ctx context.Context, arg SetUserAdminParams) erro return err } +const softDeleteUser = `-- name: SoftDeleteUser :exec +UPDATE users SET deleted_at = NOW(), is_active = false, updated_at = NOW() WHERE id = $1 +` + +func (q *Queries) SoftDeleteUser(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, softDeleteUser, id) + return err +} + const updateUserName = `-- name: UpdateUserName :exec UPDATE users SET name = $2, updated_at = NOW() WHERE id = $1 ` @@ -383,3 +419,17 @@ func (q *Queries) UpdateUserName(ctx context.Context, arg UpdateUserNameParams) _, err := q.db.Exec(ctx, updateUserName, arg.ID, arg.Name) return err } + +const updateUserPassword = `-- name: UpdateUserPassword :exec +UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1 +` + +type UpdateUserPasswordParams struct { + ID pgtype.UUID `json:"id"` + PasswordHash pgtype.Text `json:"password_hash"` +} + +func (q *Queries) UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error { + _, err := q.db.Exec(ctx, updateUserPassword, arg.ID, arg.PasswordHash) + return err +}