diff --git a/db/migrations/000008_files.down.sql b/db/migrations/000004_files.down.sql similarity index 100% rename from db/migrations/000008_files.down.sql rename to db/migrations/000004_files.down.sql diff --git a/db/migrations/000008_files.up.sql b/db/migrations/000004_files.up.sql similarity index 69% rename from db/migrations/000008_files.up.sql rename to db/migrations/000004_files.up.sql index 2c54e00..a216c90 100644 --- a/db/migrations/000008_files.up.sql +++ b/db/migrations/000004_files.up.sql @@ -7,14 +7,10 @@ CREATE TABLE medias ( original_name VARCHAR(255) NOT NULL, mime_type VARCHAR(100) NOT NULL, size BIGINT NOT NULL, - target_type VARCHAR(50) NOT NULL, - target_id UUID NOT NULL, file_metadata JSONB DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); -CREATE INDEX idx_medias_target ON medias (target_type, target_id); CREATE INDEX idx_medias_user_created ON medias (user_id, created_at DESC); -CREATE INDEX idx_medias_original_name_trgm ON medias USING GIN (original_name gin_trgm_ops); -CREATE INDEX idx_medias_storage_key_trgm ON medias USING GIN (storage_key gin_trgm_ops); \ No newline at end of file +CREATE INDEX idx_medias_original_name_trgm ON medias USING GIN (original_name gin_trgm_ops); \ No newline at end of file diff --git a/db/migrations/000004_verifications.down.sql b/db/migrations/000005_verifications.down.sql similarity index 100% rename from db/migrations/000004_verifications.down.sql rename to db/migrations/000005_verifications.down.sql diff --git a/db/migrations/000004_verifications.up.sql b/db/migrations/000005_verifications.up.sql similarity index 58% rename from db/migrations/000004_verifications.up.sql rename to db/migrations/000005_verifications.up.sql index b9e17dc..dc6f6e8 100644 --- a/db/migrations/000004_verifications.up.sql +++ b/db/migrations/000005_verifications.up.sql @@ -1,28 +1,27 @@ CREATE TABLE IF NOT EXISTS user_verifications ( id UUID PRIMARY KEY DEFAULT uuidv7(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, - verify_type SMALLINT NOT NULL, -- 1 = ID_CARD, 2 = EDUCATION, 3 = EXPERT - document_url TEXT NOT NULL, + verify_type SMALLINT NOT NULL, is_deleted BOOLEAN NOT NULL DEFAULT false, - status SMALLINT NOT NULL DEFAULT 1, -- 1 pending, 2 approved, 3 rejected + status SMALLINT NOT NULL DEFAULT 1, reviewed_by UUID REFERENCES users(id), reviewed_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT now() ); - -CREATE INDEX idx_user_verifications_user_id -ON user_verifications(user_id) -WHERE is_deleted = false; +CREATE TABLE IF NOT EXISTS verification_medias ( + verification_id UUID REFERENCES user_verifications(id) ON DELETE CASCADE, + media_id UUID REFERENCES medias(id) ON DELETE CASCADE, + PRIMARY KEY (verification_id, media_id) +); CREATE INDEX idx_user_verifications_user_type ON user_verifications(user_id, verify_type) WHERE is_deleted = false; -CREATE INDEX idx_user_verifications_status -ON user_verifications(status) -WHERE is_deleted = false; - CREATE INDEX idx_user_verifications_status_created ON user_verifications(status, created_at DESC) -WHERE is_deleted = false; \ No newline at end of file +WHERE is_deleted = false; + +CREATE INDEX idx_verification_medias_media_id +ON verification_medias(media_id); \ No newline at end of file diff --git a/db/migrations/000005_entities.down.sql b/db/migrations/000006_entities.down.sql similarity index 100% rename from db/migrations/000005_entities.down.sql rename to db/migrations/000006_entities.down.sql diff --git a/db/migrations/000005_entities.up.sql b/db/migrations/000006_entities.up.sql similarity index 100% rename from db/migrations/000005_entities.up.sql rename to db/migrations/000006_entities.up.sql diff --git a/db/migrations/000006_wiki.down.sql b/db/migrations/000007_wiki.down.sql similarity index 100% rename from db/migrations/000006_wiki.down.sql rename to db/migrations/000007_wiki.down.sql diff --git a/db/migrations/000006_wiki.up.sql b/db/migrations/000007_wiki.up.sql similarity index 100% rename from db/migrations/000006_wiki.up.sql rename to db/migrations/000007_wiki.up.sql diff --git a/db/migrations/000007_geometries.down.sql b/db/migrations/000008_geometries.down.sql similarity index 100% rename from db/migrations/000007_geometries.down.sql rename to db/migrations/000008_geometries.down.sql diff --git a/db/migrations/000007_geometries.up.sql b/db/migrations/000008_geometries.up.sql similarity index 100% rename from db/migrations/000007_geometries.up.sql rename to db/migrations/000008_geometries.up.sql diff --git a/db/query/files.sql b/db/query/files.sql index ef99b9f..1dbfebd 100644 --- a/db/query/files.sql +++ b/db/query/files.sql @@ -1,16 +1,11 @@ -- name: CreateMedia :one INSERT INTO medias ( - user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata + user_id, storage_key, original_name, mime_type, size, file_metadata ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8 + $1, $2, $3, $4, $5, $6 ) RETURNING *; --- name: GetMediasByTarget :many -SELECT * FROM medias -WHERE target_type = $1 AND target_id = $2 -ORDER BY created_at DESC; - -- name: DeleteMedia :exec DELETE FROM medias WHERE id = $1; @@ -20,7 +15,6 @@ SELECT * FROM medias WHERE (sqlc.narg('cursor')::uuid IS NULL OR id > sqlc.narg('cursor')::uuid) - AND (sqlc.narg('target_types')::varchar[] IS NULL OR target_type = ANY(sqlc.narg('target_types')::varchar[])) AND ( sqlc.narg('search_text')::text IS NULL OR original_name ILIKE '%' || sqlc.narg('search_text')::text || '%' OR diff --git a/db/query/verification.sql b/db/query/verification.sql new file mode 100644 index 0000000..9a89a5e --- /dev/null +++ b/db/query/verification.sql @@ -0,0 +1,96 @@ +-- name: CreateUserVerification :one +INSERT INTO user_verifications ( + user_id, verify_type +) VALUES ( + $1, $2 +) +RETURNING *; + +-- name: CreateVerificationMedia :exec +INSERT INTO verification_medias ( + verification_id, media_id +) VALUES ( + $1, $2 +); + +-- name: GetUserVerificationByID :one +SELECT + uv.id, + uv.user_id, + uv.verify_type, + uv.is_deleted, + uv.status, + uv.reviewed_by, + uv.reviewed_at, + uv.created_at, + ( + SELECT COALESCE( + json_agg( + json_build_object( + 'id', m.id, + 'storage_key', m.storage_key, + 'original_name', m.original_name, + 'mime_type', m.mime_type, + 'size', m.size, + 'file_metadata', m.file_metadata, + 'created_at', m.created_at + ) + ), + '[]' + )::json + FROM verification_medias vm + JOIN medias m ON vm.media_id = m.id + WHERE vm.verification_id = uv.id + ) AS medias +FROM user_verifications uv +WHERE uv.id = $1 AND uv.is_deleted = false; + +-- name: GetUserVerifications :many +SELECT + uv.id, + uv.user_id, + uv.verify_type, + uv.is_deleted, + uv.status, + uv.reviewed_by, + uv.reviewed_at, + uv.created_at, + ( + SELECT COALESCE( + json_agg( + json_build_object( + 'id', m.id, + 'storage_key', m.storage_key, + 'original_name', m.original_name, + 'mime_type', m.mime_type, + 'size', m.size, + 'file_metadata', m.file_metadata, + 'created_at', m.created_at + ) + ), + '[]' + )::json + FROM verification_medias vm + JOIN medias m ON vm.media_id = m.id + WHERE vm.verification_id = uv.id + ) AS medias +FROM user_verifications uv +WHERE uv.user_id = $1 AND uv.is_deleted = false +ORDER BY uv.created_at DESC; + +-- name: UpdateUserVerificationStatus :exec +UPDATE user_verifications +SET + status = $2, + reviewed_by = $3, + reviewed_at = now() +WHERE id = $1 AND is_deleted = false; + +-- name: DeleteUserVerification :exec +UPDATE user_verifications +SET is_deleted = true +WHERE id = $1; + +-- name: DeleteVerificationMedia :exec +DELETE FROM verification_medias +WHERE verification_id = $1 AND media_id = $2; \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index d0cf1c5..fd4f032 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -39,18 +39,6 @@ CREATE TABLE IF NOT EXISTS user_roles ( PRIMARY KEY (user_id, role_id) ); -CREATE TABLE IF NOT EXISTS user_verifications ( - id UUID PRIMARY KEY DEFAULT uuidv7(), - user_id UUID REFERENCES users(id) ON DELETE CASCADE, - verify_type SMALLINT NOT NULL, -- 1 = ID_CARD, 2 = EDUCATION, 3 = EXPERT - document_url TEXT NOT NULL, - status SMALLINT NOT NULL DEFAULT 1, -- 1 pending, 2 approved, 3 rejected - reviewed_by UUID REFERENCES users(id), - reviewed_at TIMESTAMPTZ, - created_at TIMESTAMPTZ DEFAULT now() -); - - CREATE TABLE medias ( id UUID PRIMARY KEY DEFAULT uuidv7(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, @@ -58,9 +46,24 @@ CREATE TABLE medias ( original_name VARCHAR(255) NOT NULL, mime_type VARCHAR(100) NOT NULL, size BIGINT NOT NULL, - target_type VARCHAR(50) NOT NULL, - target_id UUID NOT NULL, file_metadata JSONB DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); + +CREATE TABLE IF NOT EXISTS user_verifications ( + id UUID PRIMARY KEY DEFAULT uuidv7(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + verify_type SMALLINT NOT NULL, + is_deleted BOOLEAN NOT NULL DEFAULT false, + status SMALLINT NOT NULL DEFAULT 1, + reviewed_by UUID REFERENCES users(id), + reviewed_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS verification_medias ( + verification_id UUID REFERENCES user_verifications(id) ON DELETE CASCADE, + media_id UUID REFERENCES medias(id) ON DELETE CASCADE, + PRIMARY KEY (verification_id, media_id) +); \ No newline at end of file diff --git a/internal/controllers/authController.go b/internal/controllers/authController.go index 65f4ffa..356fe08 100644 --- a/internal/controllers/authController.go +++ b/internal/controllers/authController.go @@ -60,7 +60,7 @@ func (h *AuthController) Signin(c fiber.Ctx) error { Value: res.AccessToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) c.Cookie(&fiber.Cookie{ @@ -68,7 +68,7 @@ func (h *AuthController) Signin(c fiber.Ctx) error { Value: res.RefreshToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -113,7 +113,7 @@ func (h *AuthController) Signup(c fiber.Ctx) error { Value: res.AccessToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) c.Cookie(&fiber.Cookie{ @@ -121,7 +121,7 @@ func (h *AuthController) Signup(c fiber.Ctx) error { Value: res.RefreshToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -158,7 +158,7 @@ func (h *AuthController) RefreshToken(c fiber.Ctx) error { Value: res.AccessToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) c.Cookie(&fiber.Cookie{ @@ -166,7 +166,7 @@ func (h *AuthController) RefreshToken(c fiber.Ctx) error { Value: res.RefreshToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ @@ -306,7 +306,7 @@ func (h *AuthController) GoogleLogin(c fiber.Ctx) error { Expires: time.Now().Add(15 * time.Minute), HTTPOnly: true, Secure: secure, - SameSite: "Lax", + SameSite: "None", }) url := h.oauth.AuthCodeURL(state) @@ -375,7 +375,7 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error { Value: res.AccessToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) c.Cookie(&fiber.Cookie{ @@ -383,7 +383,7 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error { Value: res.RefreshToken, HTTPOnly: true, Secure: c.Protocol() == "https", - SameSite: "Lax", + SameSite: "None", }) return c.Redirect().To("http://localhost:5500") diff --git a/internal/dtos/response/media.go b/internal/dtos/response/media.go index 49b1092..46d76c5 100644 --- a/internal/dtos/response/media.go +++ b/internal/dtos/response/media.go @@ -17,8 +17,6 @@ type MediaResponse struct { OriginalName string `json:"original_name"` MimeType string `json:"mime_type"` Size int64 `json:"size"` - TargetType string `json:"target_type"` - TargetID string `json:"target_id"` FileMetadata []byte `json:"file_metadata"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` diff --git a/internal/gen/sqlc/files.sql.go b/internal/gen/sqlc/files.sql.go index 3c0a22c..c569fbb 100644 --- a/internal/gen/sqlc/files.sql.go +++ b/internal/gen/sqlc/files.sql.go @@ -13,11 +13,11 @@ import ( const createMedia = `-- name: CreateMedia :one INSERT INTO medias ( - user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata + user_id, storage_key, original_name, mime_type, size, file_metadata ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8 + $1, $2, $3, $4, $5, $6 ) -RETURNING id, user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata, created_at, updated_at +RETURNING id, user_id, storage_key, original_name, mime_type, size, file_metadata, created_at, updated_at ` type CreateMediaParams struct { @@ -26,8 +26,6 @@ type CreateMediaParams struct { OriginalName string `json:"original_name"` MimeType string `json:"mime_type"` Size int64 `json:"size"` - TargetType string `json:"target_type"` - TargetID pgtype.UUID `json:"target_id"` FileMetadata []byte `json:"file_metadata"` } @@ -38,8 +36,6 @@ func (q *Queries) CreateMedia(ctx context.Context, arg CreateMediaParams) (Media arg.OriginalName, arg.MimeType, arg.Size, - arg.TargetType, - arg.TargetID, arg.FileMetadata, ) var i Media @@ -50,8 +46,6 @@ func (q *Queries) CreateMedia(ctx context.Context, arg CreateMediaParams) (Media &i.OriginalName, &i.MimeType, &i.Size, - &i.TargetType, - &i.TargetID, &i.FileMetadata, &i.CreatedAt, &i.UpdatedAt, @@ -70,7 +64,7 @@ func (q *Queries) DeleteMedia(ctx context.Context, id pgtype.UUID) error { } const getMediaByID = `-- name: GetMediaByID :one -SELECT id, user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata, created_at, updated_at FROM medias +SELECT id, user_id, storage_key, original_name, mime_type, size, file_metadata, created_at, updated_at FROM medias WHERE id = $1 ` @@ -84,8 +78,6 @@ func (q *Queries) GetMediaByID(ctx context.Context, id pgtype.UUID) (Media, erro &i.OriginalName, &i.MimeType, &i.Size, - &i.TargetType, - &i.TargetID, &i.FileMetadata, &i.CreatedAt, &i.UpdatedAt, @@ -93,51 +85,8 @@ func (q *Queries) GetMediaByID(ctx context.Context, id pgtype.UUID) (Media, erro return i, err } -const getMediasByTarget = `-- name: GetMediasByTarget :many -SELECT id, user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata, created_at, updated_at FROM medias -WHERE target_type = $1 AND target_id = $2 -ORDER BY created_at DESC -` - -type GetMediasByTargetParams struct { - TargetType string `json:"target_type"` - TargetID pgtype.UUID `json:"target_id"` -} - -func (q *Queries) GetMediasByTarget(ctx context.Context, arg GetMediasByTargetParams) ([]Media, error) { - rows, err := q.db.Query(ctx, getMediasByTarget, arg.TargetType, arg.TargetID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []Media{} - for rows.Next() { - var i Media - if err := rows.Scan( - &i.ID, - &i.UserID, - &i.StorageKey, - &i.OriginalName, - &i.MimeType, - &i.Size, - &i.TargetType, - &i.TargetID, - &i.FileMetadata, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getMediasByUserID = `-- name: GetMediasByUserID :many -SELECT id, user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata, created_at, updated_at FROM medias +SELECT id, user_id, storage_key, original_name, mime_type, size, file_metadata, created_at, updated_at FROM medias WHERE user_id = $1 ORDER BY created_at DESC ` @@ -158,8 +107,6 @@ func (q *Queries) GetMediasByUserID(ctx context.Context, userID pgtype.UUID) ([] &i.OriginalName, &i.MimeType, &i.Size, - &i.TargetType, - &i.TargetID, &i.FileMetadata, &i.CreatedAt, &i.UpdatedAt, @@ -175,34 +122,27 @@ func (q *Queries) GetMediasByUserID(ctx context.Context, userID pgtype.UUID) ([] } const searchMedias = `-- name: SearchMedias :many -SELECT id, user_id, storage_key, original_name, mime_type, size, target_type, target_id, file_metadata, created_at, updated_at +SELECT id, user_id, storage_key, original_name, mime_type, size, file_metadata, created_at, updated_at FROM medias WHERE ($1::uuid IS NULL OR id > $1::uuid) - AND ($2::varchar[] IS NULL OR target_type = ANY($2::varchar[])) AND ( - $3::text IS NULL OR - original_name ILIKE '%' || $3::text || '%' OR - storage_key ILIKE '%' || $3::text || '%' + $2::text IS NULL OR + original_name ILIKE '%' || $2::text || '%' OR + storage_key ILIKE '%' || $2::text || '%' ) ORDER BY id ASC -LIMIT $4 +LIMIT $3 ` type SearchMediasParams struct { - Cursor pgtype.UUID `json:"cursor"` - TargetTypes []string `json:"target_types"` - SearchText pgtype.Text `json:"search_text"` - Limit int32 `json:"limit"` + Cursor pgtype.UUID `json:"cursor"` + SearchText pgtype.Text `json:"search_text"` + Limit int32 `json:"limit"` } func (q *Queries) SearchMedias(ctx context.Context, arg SearchMediasParams) ([]Media, error) { - rows, err := q.db.Query(ctx, searchMedias, - arg.Cursor, - arg.TargetTypes, - arg.SearchText, - arg.Limit, - ) + rows, err := q.db.Query(ctx, searchMedias, arg.Cursor, arg.SearchText, arg.Limit) if err != nil { return nil, err } @@ -217,8 +157,6 @@ func (q *Queries) SearchMedias(ctx context.Context, arg SearchMediasParams) ([]M &i.OriginalName, &i.MimeType, &i.Size, - &i.TargetType, - &i.TargetID, &i.FileMetadata, &i.CreatedAt, &i.UpdatedAt, diff --git a/internal/gen/sqlc/models.go b/internal/gen/sqlc/models.go index c6efbaa..2255adc 100644 --- a/internal/gen/sqlc/models.go +++ b/internal/gen/sqlc/models.go @@ -15,8 +15,6 @@ type Media struct { OriginalName string `json:"original_name"` MimeType string `json:"mime_type"` Size int64 `json:"size"` - TargetType string `json:"target_type"` - TargetID pgtype.UUID `json:"target_id"` FileMetadata []byte `json:"file_metadata"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` @@ -63,12 +61,17 @@ type UserRole struct { } type UserVerification struct { - ID pgtype.UUID `json:"id"` - UserID pgtype.UUID `json:"user_id"` - VerifyType int16 `json:"verify_type"` - DocumentUrl string `json:"document_url"` - Status int16 `json:"status"` - ReviewedBy pgtype.UUID `json:"reviewed_by"` - ReviewedAt pgtype.Timestamptz `json:"reviewed_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + VerifyType int16 `json:"verify_type"` + IsDeleted bool `json:"is_deleted"` + Status int16 `json:"status"` + ReviewedBy pgtype.UUID `json:"reviewed_by"` + ReviewedAt pgtype.Timestamptz `json:"reviewed_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type VerificationMedia struct { + VerificationID pgtype.UUID `json:"verification_id"` + MediaID pgtype.UUID `json:"media_id"` } diff --git a/internal/gen/sqlc/verification.sql.go b/internal/gen/sqlc/verification.sql.go new file mode 100644 index 0000000..9c2f3b6 --- /dev/null +++ b/internal/gen/sqlc/verification.sql.go @@ -0,0 +1,244 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: verification.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createUserVerification = `-- name: CreateUserVerification :one +INSERT INTO user_verifications ( + user_id, verify_type +) VALUES ( + $1, $2 +) +RETURNING id, user_id, verify_type, is_deleted, status, reviewed_by, reviewed_at, created_at +` + +type CreateUserVerificationParams struct { + UserID pgtype.UUID `json:"user_id"` + VerifyType int16 `json:"verify_type"` +} + +func (q *Queries) CreateUserVerification(ctx context.Context, arg CreateUserVerificationParams) (UserVerification, error) { + row := q.db.QueryRow(ctx, createUserVerification, arg.UserID, arg.VerifyType) + var i UserVerification + err := row.Scan( + &i.ID, + &i.UserID, + &i.VerifyType, + &i.IsDeleted, + &i.Status, + &i.ReviewedBy, + &i.ReviewedAt, + &i.CreatedAt, + ) + return i, err +} + +const createVerificationMedia = `-- name: CreateVerificationMedia :exec +INSERT INTO verification_medias ( + verification_id, media_id +) VALUES ( + $1, $2 +) +` + +type CreateVerificationMediaParams struct { + VerificationID pgtype.UUID `json:"verification_id"` + MediaID pgtype.UUID `json:"media_id"` +} + +func (q *Queries) CreateVerificationMedia(ctx context.Context, arg CreateVerificationMediaParams) error { + _, err := q.db.Exec(ctx, createVerificationMedia, arg.VerificationID, arg.MediaID) + return err +} + +const deleteUserVerification = `-- name: DeleteUserVerification :exec +UPDATE user_verifications +SET is_deleted = true +WHERE id = $1 +` + +func (q *Queries) DeleteUserVerification(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteUserVerification, id) + return err +} + +const deleteVerificationMedia = `-- name: DeleteVerificationMedia :exec +DELETE FROM verification_medias +WHERE verification_id = $1 AND media_id = $2 +` + +type DeleteVerificationMediaParams struct { + VerificationID pgtype.UUID `json:"verification_id"` + MediaID pgtype.UUID `json:"media_id"` +} + +func (q *Queries) DeleteVerificationMedia(ctx context.Context, arg DeleteVerificationMediaParams) error { + _, err := q.db.Exec(ctx, deleteVerificationMedia, arg.VerificationID, arg.MediaID) + return err +} + +const getUserVerificationByID = `-- name: GetUserVerificationByID :one +SELECT + uv.id, + uv.user_id, + uv.verify_type, + uv.is_deleted, + uv.status, + uv.reviewed_by, + uv.reviewed_at, + uv.created_at, + ( + SELECT COALESCE( + json_agg( + json_build_object( + 'id', m.id, + 'storage_key', m.storage_key, + 'original_name', m.original_name, + 'mime_type', m.mime_type, + 'size', m.size, + 'file_metadata', m.file_metadata, + 'created_at', m.created_at + ) + ), + '[]' + )::json + FROM verification_medias vm + JOIN medias m ON vm.media_id = m.id + WHERE vm.verification_id = uv.id + ) AS medias +FROM user_verifications uv +WHERE uv.id = $1 AND uv.is_deleted = false +` + +type GetUserVerificationByIDRow struct { + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + VerifyType int16 `json:"verify_type"` + IsDeleted bool `json:"is_deleted"` + Status int16 `json:"status"` + ReviewedBy pgtype.UUID `json:"reviewed_by"` + ReviewedAt pgtype.Timestamptz `json:"reviewed_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + Medias []byte `json:"medias"` +} + +func (q *Queries) GetUserVerificationByID(ctx context.Context, id pgtype.UUID) (GetUserVerificationByIDRow, error) { + row := q.db.QueryRow(ctx, getUserVerificationByID, id) + var i GetUserVerificationByIDRow + err := row.Scan( + &i.ID, + &i.UserID, + &i.VerifyType, + &i.IsDeleted, + &i.Status, + &i.ReviewedBy, + &i.ReviewedAt, + &i.CreatedAt, + &i.Medias, + ) + return i, err +} + +const getUserVerifications = `-- name: GetUserVerifications :many +SELECT + uv.id, + uv.user_id, + uv.verify_type, + uv.is_deleted, + uv.status, + uv.reviewed_by, + uv.reviewed_at, + uv.created_at, + ( + SELECT COALESCE( + json_agg( + json_build_object( + 'id', m.id, + 'storage_key', m.storage_key, + 'original_name', m.original_name, + 'mime_type', m.mime_type, + 'size', m.size, + 'file_metadata', m.file_metadata, + 'created_at', m.created_at + ) + ), + '[]' + )::json + FROM verification_medias vm + JOIN medias m ON vm.media_id = m.id + WHERE vm.verification_id = uv.id + ) AS medias +FROM user_verifications uv +WHERE uv.user_id = $1 AND uv.is_deleted = false +ORDER BY uv.created_at DESC +` + +type GetUserVerificationsRow struct { + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + VerifyType int16 `json:"verify_type"` + IsDeleted bool `json:"is_deleted"` + Status int16 `json:"status"` + ReviewedBy pgtype.UUID `json:"reviewed_by"` + ReviewedAt pgtype.Timestamptz `json:"reviewed_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + Medias []byte `json:"medias"` +} + +func (q *Queries) GetUserVerifications(ctx context.Context, userID pgtype.UUID) ([]GetUserVerificationsRow, error) { + rows, err := q.db.Query(ctx, getUserVerifications, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetUserVerificationsRow{} + for rows.Next() { + var i GetUserVerificationsRow + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.VerifyType, + &i.IsDeleted, + &i.Status, + &i.ReviewedBy, + &i.ReviewedAt, + &i.CreatedAt, + &i.Medias, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateUserVerificationStatus = `-- name: UpdateUserVerificationStatus :exec +UPDATE user_verifications +SET + status = $2, + reviewed_by = $3, + reviewed_at = now() +WHERE id = $1 AND is_deleted = false +` + +type UpdateUserVerificationStatusParams struct { + ID pgtype.UUID `json:"id"` + Status int16 `json:"status"` + ReviewedBy pgtype.UUID `json:"reviewed_by"` +} + +func (q *Queries) UpdateUserVerificationStatus(ctx context.Context, arg UpdateUserVerificationStatusParams) error { + _, err := q.db.Exec(ctx, updateUserVerificationStatus, arg.ID, arg.Status, arg.ReviewedBy) + return err +} diff --git a/internal/models/media.go b/internal/models/media.go index c1dedc1..2b16e61 100644 --- a/internal/models/media.go +++ b/internal/models/media.go @@ -12,8 +12,6 @@ type MediaEntity struct { OriginalName string `json:"original_name"` MimeType string `json:"mime_type"` Size int64 `json:"size"` - TargetType string `json:"target_type"` - TargetID string `json:"target_id"` FileMetadata []byte `json:"file_metadata"` CreatedAt *time.Time `json:"created_at"` UpdatedAt *time.Time `json:"updated_at"` @@ -27,8 +25,6 @@ func (e *MediaEntity) ToResponse() *response.MediaResponse { OriginalName: e.OriginalName, MimeType: e.MimeType, Size: e.Size, - TargetType: e.TargetType, - TargetID: e.TargetID, FileMetadata: e.FileMetadata, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, diff --git a/internal/repositories/mediaRepository.go b/internal/repositories/mediaRepository.go index 3c9ad39..832e41c 100644 --- a/internal/repositories/mediaRepository.go +++ b/internal/repositories/mediaRepository.go @@ -19,7 +19,6 @@ type MediaRepository interface { GetByUserID(ctx context.Context, userId pgtype.UUID) ([]*models.MediaEntity, error) Search(ctx context.Context, params sqlc.SearchMediasParams) ([]*models.MediaEntity, error) Delete(ctx context.Context, id pgtype.UUID) error - GetByTarget(ctx context.Context, params sqlc.GetMediasByTargetParams) ([]*models.MediaEntity, error) Create(ctx context.Context, params sqlc.CreateMediaParams) (*models.MediaEntity, error) } @@ -101,8 +100,6 @@ func (r *mediaRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models. OriginalName: row.OriginalName, MimeType: row.MimeType, Size: row.Size, - TargetType: row.TargetType, - TargetID: convert.UUIDToString(row.TargetID), FileMetadata: row.FileMetadata, CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), @@ -133,8 +130,6 @@ func (r *mediaRepository) Create(ctx context.Context, params sqlc.CreateMediaPar OriginalName: row.OriginalName, MimeType: row.MimeType, Size: row.Size, - TargetType: row.TargetType, - TargetID: convert.UUIDToString(row.TargetID), FileMetadata: row.FileMetadata, CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), @@ -156,52 +151,6 @@ func (r *mediaRepository) Delete(ctx context.Context, id pgtype.UUID) error { return nil } -func (r *mediaRepository) GetByTarget(ctx context.Context, params sqlc.GetMediasByTargetParams) ([]*models.MediaEntity, error) { - queryKey := r.generateQueryKey("media:target", params) - var cachedIDs []string - if err := r.c.Get(ctx, queryKey, &cachedIDs); err == nil && len(cachedIDs) > 0 { - return r.getByIDsWithFallback(ctx, cachedIDs) - } - - rows, err := r.q.GetMediasByTarget(ctx, params) - if err != nil { - return nil, err - } - var medias []*models.MediaEntity - var ids []string - mediasToCache := make(map[string]any) - - for _, row := range rows { - media := &models.MediaEntity{ - ID: convert.UUIDToString(row.ID), - UserID: convert.UUIDToString(row.UserID), - StorageKey: row.StorageKey, - OriginalName: row.OriginalName, - MimeType: row.MimeType, - Size: row.Size, - TargetType: row.TargetType, - TargetID: convert.UUIDToString(row.TargetID), - FileMetadata: row.FileMetadata, - CreatedAt: convert.TimeToPtr(row.CreatedAt), - UpdatedAt: convert.TimeToPtr(row.UpdatedAt), - } - ids = append(ids, media.ID) - medias = append(medias, media) - - mediasToCache[fmt.Sprintf("media:id:%s", media.ID)] = media - } - - if len(mediasToCache) > 0 { - _ = r.c.MSet(ctx, mediasToCache, constants.NormalCacheDuration) - } - - if len(ids) > 0 { - _ = r.c.Set(ctx, queryKey, ids, constants.ListCacheDuration) - } - - return medias, nil -} - func (r *mediaRepository) Search(ctx context.Context, params sqlc.SearchMediasParams) ([]*models.MediaEntity, error) { queryKey := r.generateQueryKey("media:search", params) var cachedIDs []string @@ -225,8 +174,6 @@ func (r *mediaRepository) Search(ctx context.Context, params sqlc.SearchMediasPa OriginalName: row.OriginalName, MimeType: row.MimeType, Size: row.Size, - TargetType: row.TargetType, - TargetID: convert.UUIDToString(row.TargetID), FileMetadata: row.FileMetadata, CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt), @@ -271,8 +218,6 @@ func (r *mediaRepository) GetByUserID(ctx context.Context, userId pgtype.UUID) ( OriginalName: row.OriginalName, MimeType: row.MimeType, Size: row.Size, - TargetType: row.TargetType, - TargetID: convert.UUIDToString(row.TargetID), FileMetadata: row.FileMetadata, CreatedAt: convert.TimeToPtr(row.CreatedAt), UpdatedAt: convert.TimeToPtr(row.UpdatedAt),