style(project): format Go with gofmt, set up Prettier for the frontend

Run gofmt -w across the backend, normalising the manually-aligned := blocks
to the gofmt standard. No code behaviour changes.

Add Prettier (+ prettier-plugin-svelte) to the frontend with the SvelteKit
default config (tabs, single quotes) so formatting is reproducible, then run
it over the whole tree. Add format / format:check npm scripts and a
.prettierignore (build output, generated schema.ts, static assets).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 11:01:29 +03:00
parent f5f7db6c2a
commit 0e7890a465
70 changed files with 2753 additions and 1047 deletions
+23 -23
View File
@@ -59,17 +59,17 @@ func main() {
}
// Repositories
userRepo := postgres.NewUserRepo(pool)
sessionRepo := postgres.NewSessionRepo(pool)
fileRepo := postgres.NewFileRepo(pool)
mimeRepo := postgres.NewMimeRepo(pool)
aclRepo := postgres.NewACLRepo(pool)
auditRepo := postgres.NewAuditRepo(pool)
tagRepo := postgres.NewTagRepo(pool)
tagRuleRepo := postgres.NewTagRuleRepo(pool)
userRepo := postgres.NewUserRepo(pool)
sessionRepo := postgres.NewSessionRepo(pool)
fileRepo := postgres.NewFileRepo(pool)
mimeRepo := postgres.NewMimeRepo(pool)
aclRepo := postgres.NewACLRepo(pool)
auditRepo := postgres.NewAuditRepo(pool)
tagRepo := postgres.NewTagRepo(pool)
tagRuleRepo := postgres.NewTagRuleRepo(pool)
categoryRepo := postgres.NewCategoryRepo(pool)
poolRepo := postgres.NewPoolRepo(pool)
transactor := postgres.NewTransactor(pool)
poolRepo := postgres.NewPoolRepo(pool)
transactor := postgres.NewTransactor(pool)
// Services
authSvc := service.NewAuthService(
@@ -79,12 +79,12 @@ func main() {
cfg.JWTAccessTTL,
cfg.JWTRefreshTTL,
)
aclSvc := service.NewACLService(aclRepo, fileRepo, tagRepo, categoryRepo, poolRepo, transactor)
auditSvc := service.NewAuditService(auditRepo)
tagSvc := service.NewTagService(tagRepo, tagRuleRepo, aclSvc, auditSvc, transactor)
aclSvc := service.NewACLService(aclRepo, fileRepo, tagRepo, categoryRepo, poolRepo, transactor)
auditSvc := service.NewAuditService(auditRepo)
tagSvc := service.NewTagService(tagRepo, tagRuleRepo, aclSvc, auditSvc, transactor)
categorySvc := service.NewCategoryService(categoryRepo, tagRepo, aclSvc, auditSvc)
poolSvc := service.NewPoolService(poolRepo, aclSvc, auditSvc)
fileSvc := service.NewFileService(
poolSvc := service.NewPoolService(poolRepo, aclSvc, auditSvc)
fileSvc := service.NewFileService(
fileRepo,
mimeRepo,
diskStorage,
@@ -103,15 +103,15 @@ func main() {
}
// Handlers
authMiddleware := handler.NewAuthMiddleware(authSvc)
authHandler := handler.NewAuthHandler(authSvc)
fileHandler := handler.NewFileHandler(fileSvc, tagSvc, cfg.MaxUploadBytes)
tagHandler := handler.NewTagHandler(tagSvc, fileSvc)
authMiddleware := handler.NewAuthMiddleware(authSvc)
authHandler := handler.NewAuthHandler(authSvc)
fileHandler := handler.NewFileHandler(fileSvc, tagSvc, cfg.MaxUploadBytes)
tagHandler := handler.NewTagHandler(tagSvc, fileSvc)
categoryHandler := handler.NewCategoryHandler(categorySvc)
poolHandler := handler.NewPoolHandler(poolSvc)
userHandler := handler.NewUserHandler(userSvc)
aclHandler := handler.NewACLHandler(aclSvc)
auditHandler := handler.NewAuditHandler(auditSvc)
poolHandler := handler.NewPoolHandler(poolSvc)
userHandler := handler.NewUserHandler(userSvc)
aclHandler := handler.NewACLHandler(aclSvc)
auditHandler := handler.NewAuditHandler(auditSvc)
r := handler.NewRouter(
authMiddleware, authHandler,
@@ -300,4 +300,4 @@ func (r *CategoryRepo) Delete(ctx context.Context, id uuid.UUID) error {
return domain.ErrNotFound
}
return nil
}
}
+2 -2
View File
@@ -142,7 +142,7 @@ func makeCursor(r fileRow, sort, order string) fileCursor {
}
case "mime":
val = r.MIMEType
// "created": val is empty; f.id is the sort key.
// "created": val is empty; f.id is the sort key.
}
return fileCursor{Sort: sort, Order: order, ID: r.ID.String(), Val: val}
}
@@ -569,7 +569,7 @@ func (r *FileRepo) List(ctx context.Context, params domain.FileListParams) (*dom
cursorVal = av.OriginalName
case "mime":
cursorVal = av.MIMEType
// "created": cursorVal stays ""; cursorID is the sort key.
// "created": cursorVal stays ""; cursorID is the sort key.
}
hasCursor = true
isAnchor = true
@@ -15,7 +15,7 @@ import (
type filterTokenKind int
const (
ftkAnd filterTokenKind = iota
ftkAnd filterTokenKind = iota
ftkOr
ftkNot
ftkLParen
@@ -44,9 +44,9 @@ type filterNode interface {
toSQL(n int, args []any) (string, int, []any)
}
type andNode struct{ left, right filterNode }
type orNode struct{ left, right filterNode }
type notNode struct{ child filterNode }
type andNode struct{ left, right filterNode }
type orNode struct{ left, right filterNode }
type notNode struct{ child filterNode }
type leafNode struct{ tok filterToken }
func (a *andNode) toSQL(n int, args []any) (string, int, []any) {
+1 -1
View File
@@ -712,4 +712,4 @@ func (r *PoolRepo) Reorder(ctx context.Context, poolID uuid.UUID, fileIDs []uuid
}
return r.reassignPositions(ctx, q, poolID, ordered)
}
}
+12 -12
View File
@@ -23,16 +23,16 @@ import (
type tagRow struct {
ID uuid.UUID `db:"id"`
Name string `db:"name"`
Notes *string `db:"notes"`
Color *string `db:"color"`
Name string `db:"name"`
Notes *string `db:"notes"`
Color *string `db:"color"`
CategoryID *uuid.UUID `db:"category_id"`
CategoryName *string `db:"category_name"`
CategoryColor *string `db:"category_color"`
Metadata []byte `db:"metadata"`
CreatorID int16 `db:"creator_id"`
CreatorName string `db:"creator_name"`
IsPublic bool `db:"is_public"`
CategoryName *string `db:"category_name"`
CategoryColor *string `db:"category_color"`
Metadata []byte `db:"metadata"`
CreatorID int16 `db:"creator_id"`
CreatorName string `db:"creator_name"`
IsPublic bool `db:"is_public"`
}
type tagRowWithTotal struct {
@@ -43,8 +43,8 @@ type tagRowWithTotal struct {
type tagRuleRow struct {
WhenTagID uuid.UUID `db:"when_tag_id"`
ThenTagID uuid.UUID `db:"then_tag_id"`
ThenTagName string `db:"then_tag_name"`
IsActive bool `db:"is_active"`
ThenTagName string `db:"then_tag_name"`
IsActive bool `db:"is_active"`
}
// ---------------------------------------------------------------------------
@@ -637,4 +637,4 @@ WHERE when_tag_id = $1 AND then_tag_id = $2`
return domain.ErrNotFound
}
return nil
}
}
+6 -6
View File
@@ -10,10 +10,10 @@ type ObjectType struct {
// Permission represents a per-object access entry for a user.
type Permission struct {
UserID int16
UserName string // denormalized
ObjectTypeID int16
ObjectID uuid.UUID
CanView bool
CanEdit bool
UserID int16
UserName string // denormalized
ObjectTypeID int16
ObjectID uuid.UUID
CanView bool
CanEdit bool
}
+8 -8
View File
@@ -15,14 +15,14 @@ type ActionType struct {
// AuditEntry is a single audit log record.
type AuditEntry struct {
ID int64
UserID int16
UserName string // denormalized
Action string // action type name, e.g. "file_create"
ObjectType *string
ObjectID *uuid.UUID
Details json.RawMessage
PerformedAt time.Time
ID int64
UserID int16
UserName string // denormalized
Action string // action type name, e.g. "file_create"
ObjectType *string
ObjectID *uuid.UUID
Details json.RawMessage
PerformedAt time.Time
}
// AuditPage is an offset-based page of audit log entries.
+1 -1
View File
@@ -143,4 +143,4 @@ func (h *ACLHandler) SetPermissions(c *gin.Context) {
out[i] = toPermissionJSON(p)
}
respondJSON(c, http.StatusOK, out)
}
}
+1 -1
View File
@@ -117,4 +117,4 @@ func (h *AuditHandler) List(c *gin.Context) {
"offset": page.Offset,
"limit": page.Limit,
})
}
}
+1 -1
View File
@@ -232,4 +232,4 @@ func (h *CategoryHandler) ListTags(c *gin.Context) {
"offset": page.Offset,
"limit": page.Limit,
})
}
}
+9 -9
View File
@@ -89,16 +89,16 @@ type fileJSON struct {
func toTagJSON(t domain.Tag) tagJSON {
j := tagJSON{
ID: t.ID.String(),
Name: t.Name,
Notes: t.Notes,
Color: t.Color,
ID: t.ID.String(),
Name: t.Name,
Notes: t.Notes,
Color: t.Color,
CategoryName: t.CategoryName,
CategoryColor: t.CategoryColor,
CreatorID: t.CreatorID,
CreatorName: t.CreatorName,
IsPublic: t.IsPublic,
CreatedAt: t.CreatedAt.Format(time.RFC3339),
CreatorID: t.CreatorID,
CreatorName: t.CreatorName,
IsPublic: t.IsPublic,
CreatedAt: t.CreatedAt.Format(time.RFC3339),
}
if t.CategoryID != nil {
s := t.CategoryID.String()
@@ -672,4 +672,4 @@ func parseUUIDs(strs []string) ([]uuid.UUID, error) {
ids = append(ids, id)
}
return ids, nil
}
}
+1 -1
View File
@@ -353,4 +353,4 @@ func (h *PoolHandler) Reorder(c *gin.Context) {
return
}
c.Status(http.StatusNoContent)
}
}
+6 -6
View File
@@ -106,11 +106,11 @@ func (h *TagHandler) List(c *gin.Context) {
func (h *TagHandler) Create(c *gin.Context) {
var body struct {
Name string `json:"name" binding:"required"`
Notes *string `json:"notes"`
Color *string `json:"color"`
CategoryID *string `json:"category_id"`
IsPublic *bool `json:"is_public"`
Name string `json:"name" binding:"required"`
Notes *string `json:"notes"`
Color *string `json:"color"`
CategoryID *string `json:"category_id"`
IsPublic *bool `json:"is_public"`
}
if err := c.ShouldBindJSON(&body); err != nil {
respondError(c, domain.ErrValidation)
@@ -549,4 +549,4 @@ func (h *TagHandler) FileRemoveTag(c *gin.Context) {
// Helpers
// ---------------------------------------------------------------------------
func ptr(s string) *string { return &s }
func ptr(s string) *string { return &s }
+1 -1
View File
@@ -255,4 +255,4 @@ func (h *UserHandler) Delete(c *gin.Context) {
return
}
c.Status(http.StatusNoContent)
}
}
+29 -29
View File
@@ -111,42 +111,42 @@ func setupSuite(t *testing.T) *harness {
require.NoError(t, err)
// --- Repositories --------------------------------------------------------
userRepo := postgres.NewUserRepo(pool)
sessionRepo := postgres.NewSessionRepo(pool)
fileRepo := postgres.NewFileRepo(pool)
mimeRepo := postgres.NewMimeRepo(pool)
aclRepo := postgres.NewACLRepo(pool)
auditRepo := postgres.NewAuditRepo(pool)
tagRepo := postgres.NewTagRepo(pool)
tagRuleRepo := postgres.NewTagRuleRepo(pool)
userRepo := postgres.NewUserRepo(pool)
sessionRepo := postgres.NewSessionRepo(pool)
fileRepo := postgres.NewFileRepo(pool)
mimeRepo := postgres.NewMimeRepo(pool)
aclRepo := postgres.NewACLRepo(pool)
auditRepo := postgres.NewAuditRepo(pool)
tagRepo := postgres.NewTagRepo(pool)
tagRuleRepo := postgres.NewTagRuleRepo(pool)
categoryRepo := postgres.NewCategoryRepo(pool)
poolRepo := postgres.NewPoolRepo(pool)
transactor := postgres.NewTransactor(pool)
poolRepo := postgres.NewPoolRepo(pool)
transactor := postgres.NewTransactor(pool)
// --- Services ------------------------------------------------------------
authSvc := service.NewAuthService(userRepo, sessionRepo, "test-secret", 15*time.Minute, 720*time.Hour)
aclSvc := service.NewACLService(aclRepo, fileRepo, tagRepo, categoryRepo, poolRepo, transactor)
auditSvc := service.NewAuditService(auditRepo)
tagSvc := service.NewTagService(tagRepo, tagRuleRepo, aclSvc, auditSvc, transactor)
authSvc := service.NewAuthService(userRepo, sessionRepo, "test-secret", 15*time.Minute, 720*time.Hour)
aclSvc := service.NewACLService(aclRepo, fileRepo, tagRepo, categoryRepo, poolRepo, transactor)
auditSvc := service.NewAuditService(auditRepo)
tagSvc := service.NewTagService(tagRepo, tagRuleRepo, aclSvc, auditSvc, transactor)
categorySvc := service.NewCategoryService(categoryRepo, tagRepo, aclSvc, auditSvc)
poolSvc := service.NewPoolService(poolRepo, aclSvc, auditSvc)
fileSvc := service.NewFileService(fileRepo, mimeRepo, diskStorage, aclSvc, auditSvc, tagSvc, transactor, filesDir)
userSvc := service.NewUserService(userRepo, sessionRepo, auditSvc)
poolSvc := service.NewPoolService(poolRepo, aclSvc, auditSvc)
fileSvc := service.NewFileService(fileRepo, mimeRepo, diskStorage, aclSvc, auditSvc, tagSvc, transactor, filesDir)
userSvc := service.NewUserService(userRepo, sessionRepo, auditSvc)
// Bootstrap the admin account the suite logs in with (replaces the old
// hardcoded seed credentials).
require.NoError(t, userSvc.EnsureAdmin(ctx, "admin", "admin"))
// --- Handlers ------------------------------------------------------------
authMiddleware := handler.NewAuthMiddleware(authSvc)
authHandler := handler.NewAuthHandler(authSvc)
fileHandler := handler.NewFileHandler(fileSvc, tagSvc, 500<<20)
tagHandler := handler.NewTagHandler(tagSvc, fileSvc)
authMiddleware := handler.NewAuthMiddleware(authSvc)
authHandler := handler.NewAuthHandler(authSvc)
fileHandler := handler.NewFileHandler(fileSvc, tagSvc, 500<<20)
tagHandler := handler.NewTagHandler(tagSvc, fileSvc)
categoryHandler := handler.NewCategoryHandler(categorySvc)
poolHandler := handler.NewPoolHandler(poolSvc)
userHandler := handler.NewUserHandler(userSvc)
aclHandler := handler.NewACLHandler(aclSvc)
auditHandler := handler.NewAuditHandler(auditSvc)
poolHandler := handler.NewPoolHandler(poolSvc)
userHandler := handler.NewUserHandler(userSvc)
aclHandler := handler.NewACLHandler(aclSvc)
auditHandler := handler.NewAuditHandler(auditSvc)
r := handler.NewRouter(
authMiddleware, authHandler,
@@ -289,7 +289,7 @@ func TestFullFlow(t *testing.T) {
// 3. Log in as alice
// =========================================================================
aliceToken := h.login("alice", "alicepass")
bobToken := h.login("bob", "bobpass")
bobToken := h.login("bob", "bobpass")
// =========================================================================
// 4. Alice uploads a private JPEG
@@ -619,7 +619,7 @@ func TestTagRuleActivateApplyToExisting(t *testing.T) {
// Activate A→B WITHOUT apply_to_existing — existing file must not change.
resp = h.doJSON("PATCH", "/tags/"+tagA+"/rules/"+tagB, map[string]any{
"is_active": true,
"is_active": true,
"apply_to_existing": false,
}, tok)
require.Equal(t, http.StatusOK, resp.StatusCode, resp.String())
@@ -634,7 +634,7 @@ func TestTagRuleActivateApplyToExisting(t *testing.T) {
// Activate A→B WITH apply_to_existing=true.
// Expectation: file gets B directly, and C transitively via the active B→C rule.
resp = h.doJSON("PATCH", "/tags/"+tagA+"/rules/"+tagB, map[string]any{
"is_active": true,
"is_active": true,
"apply_to_existing": true,
}, tok)
require.Equal(t, http.StatusOK, resp.StatusCode, resp.String())
@@ -1023,4 +1023,4 @@ func writeFile(t *testing.T, dir, name string, content []byte) string {
var (
_ = freePort
_ = writeFile
)
)
+4 -4
View File
@@ -10,14 +10,14 @@ import (
"tanabata/backend/internal/port"
)
const categoryObjectType = "category"
const categoryObjectTypeID int16 = 3 // third row in 007_seed_data.sql object_types
const categoryObjectType = "category"
const categoryObjectTypeID int16 = 3 // third row in 007_seed_data.sql object_types
// CategoryParams holds the fields for creating or patching a category.
type CategoryParams struct {
Name string
Notes *string
Color *string // nil = no change; pointer to empty string = clear
Color *string // nil = no change; pointer to empty string = clear
Metadata json.RawMessage
IsPublic *bool
}
@@ -176,4 +176,4 @@ func (s *CategoryService) Delete(ctx context.Context, id uuid.UUID) error {
func (s *CategoryService) ListTags(ctx context.Context, categoryID uuid.UUID, params port.OffsetParams) (*domain.TagOffsetPage, error) {
params.ViewerID, params.ViewerIsAdmin, _ = domain.UserFromContext(ctx)
return s.tags.ListByCategory(ctx, categoryID, params)
}
}
+1 -1
View File
@@ -627,4 +627,4 @@ func extractEXIFWithDatetime(data []byte) (json.RawMessage, *time.Time) {
dt = &t
}
return json.RawMessage(b), dt
}
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"tanabata/backend/internal/port"
)
const poolObjectType = "pool"
const poolObjectType = "pool"
const poolObjectTypeID int16 = 4 // fourth row in 007_seed_data.sql object_types
// PoolParams holds the fields for creating or patching a pool.
@@ -238,4 +238,4 @@ func (s *PoolService) Reorder(ctx context.Context, poolID uuid.UUID, fileIDs []u
return err
}
return s.pools.Reorder(ctx, poolID, fileIDs)
}
}
+5 -5
View File
@@ -10,15 +10,15 @@ import (
"tanabata/backend/internal/port"
)
const tagObjectType = "tag"
const tagObjectTypeID int16 = 2 // second row in 007_seed_data.sql object_types
const tagObjectType = "tag"
const tagObjectTypeID int16 = 2 // second row in 007_seed_data.sql object_types
// TagParams holds the fields for creating or patching a tag.
type TagParams struct {
Name string
Notes *string
Color *string // nil = no change; pointer to empty string = clear
CategoryID *uuid.UUID // nil = no change; Nil UUID = unassign
Color *string // nil = no change; pointer to empty string = clear
CategoryID *uuid.UUID // nil = no change; Nil UUID = unassign
Metadata json.RawMessage
IsPublic *bool
}
@@ -422,4 +422,4 @@ func (s *TagService) expandTagSet(ctx context.Context, seeds []uuid.UUID) ([]uui
}
return queue, nil
}
}
+1 -1
View File
@@ -189,4 +189,4 @@ func (s *UserService) Delete(ctx context.Context, id int16) error {
}
_ = s.audit.Log(ctx, "user_delete", nil, nil, map[string]any{"target_user_id": id})
return nil
}
}
+3 -3
View File
@@ -5,12 +5,12 @@ import (
"bytes"
"context"
"fmt"
_ "golang.org/x/image/webp" // register WebP decoder
"image"
"image/color"
_ "image/gif" // register GIF decoder
"image/jpeg"
_ "image/gif" // register GIF decoder
_ "image/png" // register PNG decoder
_ "golang.org/x/image/webp" // register WebP decoder
_ "image/png" // register PNG decoder
"io"
"os"
"os/exec"