diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 4a72ead..04548b4 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -23,7 +23,7 @@ func main() { } defer database.Close() - if err := db.RunMigrations(database, migrationsPath); err != nil { + if err := db.RunMigrations(migrationsPath); err != nil { log.Fatalf("Failed to run migrations: %v", err) } diff --git a/backend/internal/db/files.sql.go b/backend/internal/db/files.sql.go new file mode 100644 index 0000000..24a44c5 --- /dev/null +++ b/backend/internal/db/files.sql.go @@ -0,0 +1,142 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: files.sql + +package db + +import ( + "context" +) + +const createFile = `-- name: CreateFile :one +INSERT INTO files (id, name, mime_type, size, storage_key, checksum, created_at, updated_at) +VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +RETURNING id, name, mime_type, size, storage_key, checksum, ocr_text, created_at, updated_at +` + +type CreateFileParams struct { + ID string `json:"id"` + Name string `json:"name"` + MimeType string `json:"mime_type"` + Size int64 `json:"size"` + StorageKey string `json:"storage_key"` + Checksum string `json:"checksum"` +} + +func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (File, error) { + row := q.db.QueryRowContext(ctx, createFile, + arg.ID, + arg.Name, + arg.MimeType, + arg.Size, + arg.StorageKey, + arg.Checksum, + ) + var i File + err := row.Scan( + &i.ID, + &i.Name, + &i.MimeType, + &i.Size, + &i.StorageKey, + &i.Checksum, + &i.OcrText, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const deleteFile = `-- name: DeleteFile :exec +DELETE FROM files +WHERE id = ? +` + +func (q *Queries) DeleteFile(ctx context.Context, id string) error { + _, err := q.db.ExecContext(ctx, deleteFile, id) + return err +} + +const getFile = `-- name: GetFile :one +SELECT id, name, mime_type, size, storage_key, checksum, ocr_text, created_at, updated_at FROM files +WHERE id = ? LIMIT 1 +` + +func (q *Queries) GetFile(ctx context.Context, id string) (File, error) { + row := q.db.QueryRowContext(ctx, getFile, id) + var i File + err := row.Scan( + &i.ID, + &i.Name, + &i.MimeType, + &i.Size, + &i.StorageKey, + &i.Checksum, + &i.OcrText, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const listFiles = `-- name: ListFiles :many +SELECT id, name, mime_type, size, storage_key, checksum, ocr_text, created_at, updated_at FROM files +ORDER BY created_at DESC +` + +func (q *Queries) ListFiles(ctx context.Context) ([]File, error) { + rows, err := q.db.QueryContext(ctx, listFiles) + 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 updateFile = `-- name: UpdateFile :exec +UPDATE files +SET name = ?, mime_type = ?, ocr_text = ?, updated_at = CURRENT_TIMESTAMP +WHERE id = ? +` + +type UpdateFileParams struct { + Name string `json:"name"` + MimeType string `json:"mime_type"` + OcrText string `json:"ocr_text"` + ID string `json:"id"` +} + +func (q *Queries) UpdateFile(ctx context.Context, arg UpdateFileParams) error { + _, err := q.db.ExecContext(ctx, updateFile, + arg.Name, + arg.MimeType, + arg.OcrText, + arg.ID, + ) + return err +} diff --git a/backend/internal/db/migrate.go b/backend/internal/db/migrate.go index f00469c..1d56c83 100644 --- a/backend/internal/db/migrate.go +++ b/backend/internal/db/migrate.go @@ -1,7 +1,6 @@ package db import ( - "database/sql" "fmt" "log" @@ -10,13 +9,19 @@ import ( "github.com/golang-migrate/migrate/v4/source/file" ) -func RunMigrations(database *sql.DB, migrationsURL string) error { +func RunMigrations(migrationsURL string) error { + migDB, err := Connect() + if err != nil { + return fmt.Errorf("open migration db: %w", err) + } + defer migDB.Close() + sourceDriver, err := (&file.File{}).Open(migrationsURL) if err != nil { return fmt.Errorf("open migrations source: %w", err) } - dbDriver, err := sqlite.WithInstance(database, &sqlite.Config{}) + dbDriver, err := sqlite.WithInstance(migDB, &sqlite.Config{}) if err != nil { return fmt.Errorf("create sqlite driver: %w", err) } diff --git a/backend/internal/db/migrations/002_files.down.sql b/backend/internal/db/migrations/002_files.down.sql new file mode 100644 index 0000000..1e0a59b --- /dev/null +++ b/backend/internal/db/migrations/002_files.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS files; diff --git a/backend/internal/db/migrations/002_files.up.sql b/backend/internal/db/migrations/002_files.up.sql new file mode 100644 index 0000000..080384a --- /dev/null +++ b/backend/internal/db/migrations/002_files.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE files ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + mime_type TEXT NOT NULL DEFAULT '', + size INTEGER NOT NULL DEFAULT 0, + storage_key TEXT NOT NULL, + checksum TEXT NOT NULL DEFAULT '', + ocr_text TEXT NOT NULL DEFAULT '', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/backend/internal/db/models.go b/backend/internal/db/models.go index 21f493e..e42014d 100644 --- a/backend/internal/db/models.go +++ b/backend/internal/db/models.go @@ -3,3 +3,19 @@ // sqlc v1.31.1 package db + +import ( + "time" +) + +type File struct { + ID string `json:"id"` + Name string `json:"name"` + MimeType string `json:"mime_type"` + Size int64 `json:"size"` + StorageKey string `json:"storage_key"` + Checksum string `json:"checksum"` + OcrText string `json:"ocr_text"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/backend/internal/db/queries/files.sql b/backend/internal/db/queries/files.sql new file mode 100644 index 0000000..bd64651 --- /dev/null +++ b/backend/internal/db/queries/files.sql @@ -0,0 +1,21 @@ +-- name: GetFile :one +SELECT * FROM files +WHERE id = ? LIMIT 1; + +-- name: ListFiles :many +SELECT * FROM files +ORDER BY created_at DESC; + +-- name: CreateFile :one +INSERT INTO files (id, name, mime_type, size, storage_key, checksum, created_at, updated_at) +VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +RETURNING *; + +-- name: UpdateFile :exec +UPDATE files +SET name = ?, mime_type = ?, ocr_text = ?, updated_at = CURRENT_TIMESTAMP +WHERE id = ?; + +-- name: DeleteFile :exec +DELETE FROM files +WHERE id = ?; diff --git a/backend/internal/handlers/files.go b/backend/internal/handlers/files.go index 09cda66..1babe06 100644 --- a/backend/internal/handlers/files.go +++ b/backend/internal/handlers/files.go @@ -1,6 +1,10 @@ package handlers import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" "net/http" "os" "path" @@ -8,6 +12,7 @@ import ( "strconv" "github.com/gin-gonic/gin" + "github.com/vaultdrop/backend/internal/db" "github.com/vaultdrop/backend/internal/service" ) @@ -215,19 +220,38 @@ func (h *Handlers) UploadFile(c *gin.Context) { } checksum := service.CreateSHA256Hash(fileByte) - // TODO: re-enable once files table + query are added - // createFileParams := db.CreateFileParams{ - // Name: file.Filename, - // StorageKey: dst, - // Checksum: string(checksum[:]), - // } - // dbFile, err := h.queries.CreateFile(ctx, createFileParams) - _ = checksum + + ctx := context.Background() + + id := make([]byte, 16) + rand.Read(id) + + createFileParams := db.CreateFileParams{ + ID: hex.EncodeToString(id), + Name: file.Filename, + Size: file.Size, + StorageKey: dst, + Checksum: hex.EncodeToString(checksum), + } + + dbFile, err := h.queries.CreateFile(ctx, createFileParams) + if err != nil { + + fmt.Println(err) + + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "DB_ERROR", + "message": "Failed to save file metadata", + }, + }) + return + } c.JSON(http.StatusOK, gin.H{ - "error": gin.H{ - "code": "SUCCESS", - "message": "Uploaded", + "data": gin.H{ + "id": dbFile.ID, + "name": dbFile.Name, }, })