feat: store layer with file CRUD

This commit is contained in:
2026-08-26 21:41:47 +08:00
parent a547b69725
commit 245497999e
4 changed files with 182 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
package store
import "database/sql"
type File struct {
ID string
OriginalName string
StorageName string
Dir string
Size int64
CreatedAt int64
}
func (s *Store) FileCreate(f *File) error {
_, err := s.db.Exec(
`INSERT INTO files (id, original_name, storage_name, dir, size, created_at)
VALUES (?, ?, ?, ?, ?, ?)`,
f.ID, f.OriginalName, f.StorageName, f.Dir, f.Size, f.CreatedAt,
)
return err
}
func (s *Store) FileGet(id string) (*File, error) {
row := s.db.QueryRow(
`SELECT id, original_name, storage_name, dir, size, created_at
FROM files WHERE id = ?`, id,
)
return scanFile(row)
}
func (s *Store) FileList(dir string) ([]*File, error) {
query := `SELECT id, original_name, storage_name, dir, size, created_at FROM files`
var args []any
if dir != "" {
query += ` WHERE dir = ?`
args = []any{dir}
}
query += ` ORDER BY created_at DESC`
rows, err := s.db.Query(query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var files []*File
for rows.Next() {
f, err := scanFile(rows)
if err != nil {
return nil, err
}
files = append(files, f)
}
return files, rows.Err()
}
func (s *Store) FileDelete(id string) error {
_, err := s.db.Exec(`DELETE FROM files WHERE id = ?`, id)
return err
}
func scanFile(row interface {
Scan(dest ...any) error
}) (*File, error) {
f := &File{}
err := row.Scan(&f.ID, &f.OriginalName, &f.StorageName, &f.Dir, &f.Size, &f.CreatedAt)
if err == sql.ErrNoRows {
return nil, err
}
return f, err
}
+36
View File
@@ -0,0 +1,36 @@
package store
import (
"path/filepath"
"testing"
)
func TestFileCreate(t *testing.T) {
dir := t.TempDir()
s, err := Open(filepath.Join(dir, "test.db"))
if err != nil {
t.Fatal(err)
}
defer s.Close()
f := &File{
ID: "test-id",
OriginalName: "test.pdf",
StorageName: "storage-uuid",
Dir: "inbox",
Size: 1024,
CreatedAt: 1750000000,
}
if err := s.FileCreate(f); err != nil {
t.Fatalf("FileCreate failed: %v", err)
}
got, err := s.FileGet("test-id")
if err != nil {
t.Fatalf("FileGet failed: %v", err)
}
if got.OriginalName != "test.pdf" {
t.Errorf("OriginalName = %q, want %q", got.OriginalName, "test.pdf")
}
}
+53
View File
@@ -0,0 +1,53 @@
package store
import (
"database/sql"
_ "modernc.org/sqlite"
)
type Store struct {
db *sql.DB
}
func Open(dbPath string) (*Store, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, err
}
if err := migrate(db); err != nil {
db.Close()
return nil, err
}
return &Store{db: db}, nil
}
func (s *Store) Close() error {
return s.db.Close()
}
func migrate(db *sql.DB) error {
schema := `
CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
original_name TEXT NOT NULL,
storage_name TEXT NOT NULL UNIQUE,
dir TEXT NOT NULL CHECK (dir IN ('inbox','outbox')),
size INTEGER NOT NULL,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS shares (
id TEXT PRIMARY KEY,
file_id TEXT NOT NULL REFERENCES files(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
password_hash TEXT,
expires_at INTEGER,
created_at INTEGER NOT NULL,
last_accessed_at INTEGER
);
`
_, err := db.Exec(schema)
return err
}
+22
View File
@@ -0,0 +1,22 @@
package store
import (
"os"
"path/filepath"
"testing"
)
func TestOpen(t *testing.T) {
dir := t.TempDir()
dbPath := filepath.Join(dir, "test.db")
s, err := Open(dbPath)
if err != nil {
t.Fatalf("Open failed: %v", err)
}
defer s.Close()
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
t.Error("Database file not created")
}
}