From 7dc53ea99fbeee2b4b0286875dbda4bde8f3d7d7 Mon Sep 17 00:00:00 2001 From: m Date: Mon, 13 Jul 2026 00:15:39 +0200 Subject: [PATCH] add tags --- .../internal/db/migrations/003_tags.down.sql | 3 + .../internal/db/migrations/003_tags.up.sql | 19 ++ backend/internal/db/models.go | 16 ++ backend/internal/db/queries/tags.sql | 41 ++++ backend/internal/db/tags.sql.go | 229 ++++++++++++++++++ 5 files changed, 308 insertions(+) create mode 100644 backend/internal/db/migrations/003_tags.down.sql create mode 100644 backend/internal/db/migrations/003_tags.up.sql create mode 100644 backend/internal/db/queries/tags.sql create mode 100644 backend/internal/db/tags.sql.go diff --git a/backend/internal/db/migrations/003_tags.down.sql b/backend/internal/db/migrations/003_tags.down.sql new file mode 100644 index 0000000..e5f21c4 --- /dev/null +++ b/backend/internal/db/migrations/003_tags.down.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS file_tags; + +DROP TABLE IF EXISTS tags; \ No newline at end of file diff --git a/backend/internal/db/migrations/003_tags.up.sql b/backend/internal/db/migrations/003_tags.up.sql new file mode 100644 index 0000000..bd60026 --- /dev/null +++ b/backend/internal/db/migrations/003_tags.up.sql @@ -0,0 +1,19 @@ +-- VaultDrop 003: tags table + +CREATE TABLE tags ( + id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), + parent_tag_id TEXT, + tag_name TEXT NOT NULL, -- exemple: Brice, vélo, facture ... + tag_type TEXT NOT NULL DEFAULT 'none', -- exemple: entity, none, folder + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_parent_tag FOREIGN KEY(parent_tag_id) REFERENCES tags(id) +); + +CREATE TABLE file_tags ( + id TEXT PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), + tag_id TEXT, + file_id TEXT, + CONSTRAINT fk_tag FOREIGN KEY(tag_id) REFERENCES tags(id), + CONSTRAINT fk_file FOREIGN KEY(file_id) REFERENCES files(id) +) diff --git a/backend/internal/db/models.go b/backend/internal/db/models.go index e42014d..a8520c4 100644 --- a/backend/internal/db/models.go +++ b/backend/internal/db/models.go @@ -5,6 +5,7 @@ package db import ( + "database/sql" "time" ) @@ -19,3 +20,18 @@ type File struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } + +type FileTag struct { + ID string `json:"id"` + TagID sql.NullString `json:"tag_id"` + FileID sql.NullString `json:"file_id"` +} + +type Tag struct { + ID string `json:"id"` + ParentTagID sql.NullString `json:"parent_tag_id"` + TagName string `json:"tag_name"` + TagType string `json:"tag_type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/backend/internal/db/queries/tags.sql b/backend/internal/db/queries/tags.sql new file mode 100644 index 0000000..28581a6 --- /dev/null +++ b/backend/internal/db/queries/tags.sql @@ -0,0 +1,41 @@ +-- name: CreateTag :one +INSERT INTO tags (id, tag_name, tag_type) +VALUES ($1, $2, $3) +RETURNING *; + +-- name: GetTag :one +SELECT * FROM tags +WHERE id = $1; + +-- name: GetTagByName :one +SELECT * FROM tags +WHERE tag_name = $1; + +-- name: ListTags :many +SELECT * FROM tags +ORDER BY tag_name ASC; + +-- name: DeleteTag :exec +DELETE FROM tags +WHERE id = $1; + +-- name: AddTagToFile :exec +INSERT INTO file_tags (id, tag_id, file_id) +VALUES ($1, $2, $3) +ON CONFLICT DO NOTHING; + +-- name: RemoveTagFromFile :exec +DELETE FROM file_tags +WHERE tag_id = $1 AND file_id = $2; + +-- name: GetTagsByFileID :many +SELECT t.* FROM tags t +JOIN file_tags ft ON t.id = ft.tag_id +WHERE ft.file_id = $1 +ORDER BY t.tag_name ASC; + +-- name: GetFilesByTagID :many +SELECT f.* FROM files f +JOIN file_tags ft ON f.id = ft.file_id +WHERE ft.tag_id = $1 +ORDER BY f.created_at DESC; diff --git a/backend/internal/db/tags.sql.go b/backend/internal/db/tags.sql.go new file mode 100644 index 0000000..2bb0b86 --- /dev/null +++ b/backend/internal/db/tags.sql.go @@ -0,0 +1,229 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: tags.sql + +package db + +import ( + "context" + "database/sql" +) + +const addTagToFile = `-- name: AddTagToFile :exec +INSERT INTO file_tags (id, tag_id, file_id) +VALUES ($1, $2, $3) +ON CONFLICT DO NOTHING +` + +type AddTagToFileParams struct { + ID string `json:"id"` + TagID sql.NullString `json:"tag_id"` + FileID sql.NullString `json:"file_id"` +} + +func (q *Queries) AddTagToFile(ctx context.Context, arg AddTagToFileParams) error { + _, err := q.db.ExecContext(ctx, addTagToFile, arg.ID, arg.TagID, arg.FileID) + return err +} + +const createTag = `-- name: CreateTag :one +INSERT INTO tags (id, tag_name, tag_type) +VALUES ($1, $2, $3) +RETURNING id, parent_tag_id, tag_name, tag_type, created_at, updated_at +` + +type CreateTagParams struct { + ID string `json:"id"` + TagName string `json:"tag_name"` + TagType string `json:"tag_type"` +} + +func (q *Queries) CreateTag(ctx context.Context, arg CreateTagParams) (Tag, error) { + row := q.db.QueryRowContext(ctx, createTag, arg.ID, arg.TagName, arg.TagType) + var i Tag + err := row.Scan( + &i.ID, + &i.ParentTagID, + &i.TagName, + &i.TagType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const deleteTag = `-- name: DeleteTag :exec +DELETE FROM tags +WHERE id = $1 +` + +func (q *Queries) DeleteTag(ctx context.Context, id string) error { + _, err := q.db.ExecContext(ctx, deleteTag, id) + return err +} + +const getFilesByTagID = `-- name: GetFilesByTagID :many +SELECT f.id, f.name, f.mime_type, f.size, f.storage_key, f.checksum, f.ocr_text, f.created_at, f.updated_at FROM files f +JOIN file_tags ft ON f.id = ft.file_id +WHERE ft.tag_id = $1 +ORDER BY f.created_at DESC +` + +func (q *Queries) GetFilesByTagID(ctx context.Context, tagID sql.NullString) ([]File, error) { + rows, err := q.db.QueryContext(ctx, getFilesByTagID, tagID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []File + for rows.Next() { + var i File + if err := rows.Scan( + &i.ID, + &i.Name, + &i.MimeType, + &i.Size, + &i.StorageKey, + &i.Checksum, + &i.OcrText, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getTag = `-- name: GetTag :one +SELECT id, parent_tag_id, tag_name, tag_type, created_at, updated_at FROM tags +WHERE id = $1 +` + +func (q *Queries) GetTag(ctx context.Context, id string) (Tag, error) { + row := q.db.QueryRowContext(ctx, getTag, id) + var i Tag + err := row.Scan( + &i.ID, + &i.ParentTagID, + &i.TagName, + &i.TagType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getTagByName = `-- name: GetTagByName :one +SELECT id, parent_tag_id, tag_name, tag_type, created_at, updated_at FROM tags +WHERE tag_name = $1 +` + +func (q *Queries) GetTagByName(ctx context.Context, tagName string) (Tag, error) { + row := q.db.QueryRowContext(ctx, getTagByName, tagName) + var i Tag + err := row.Scan( + &i.ID, + &i.ParentTagID, + &i.TagName, + &i.TagType, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getTagsByFileID = `-- name: GetTagsByFileID :many +SELECT t.id, t.parent_tag_id, t.tag_name, t.tag_type, t.created_at, t.updated_at FROM tags t +JOIN file_tags ft ON t.id = ft.tag_id +WHERE ft.file_id = $1 +ORDER BY t.tag_name ASC +` + +func (q *Queries) GetTagsByFileID(ctx context.Context, fileID sql.NullString) ([]Tag, error) { + rows, err := q.db.QueryContext(ctx, getTagsByFileID, fileID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Tag + for rows.Next() { + var i Tag + if err := rows.Scan( + &i.ID, + &i.ParentTagID, + &i.TagName, + &i.TagType, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTags = `-- name: ListTags :many +SELECT id, parent_tag_id, tag_name, tag_type, created_at, updated_at FROM tags +ORDER BY tag_name ASC +` + +func (q *Queries) ListTags(ctx context.Context) ([]Tag, error) { + rows, err := q.db.QueryContext(ctx, listTags) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Tag + for rows.Next() { + var i Tag + if err := rows.Scan( + &i.ID, + &i.ParentTagID, + &i.TagName, + &i.TagType, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const removeTagFromFile = `-- name: RemoveTagFromFile :exec +DELETE FROM file_tags +WHERE tag_id = $1 AND file_id = $2 +` + +type RemoveTagFromFileParams struct { + TagID sql.NullString `json:"tag_id"` + FileID sql.NullString `json:"file_id"` +} + +func (q *Queries) RemoveTagFromFile(ctx context.Context, arg RemoveTagFromFileParams) error { + _, err := q.db.ExecContext(ctx, removeTagFromFile, arg.TagID, arg.FileID) + return err +}