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:
+23
-23
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -712,4 +712,4 @@ func (r *PoolRepo) Reorder(ctx context.Context, poolID uuid.UUID, fileIDs []uuid
|
||||
}
|
||||
|
||||
return r.reassignPositions(ctx, q, poolID, ordered)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -143,4 +143,4 @@ func (h *ACLHandler) SetPermissions(c *gin.Context) {
|
||||
out[i] = toPermissionJSON(p)
|
||||
}
|
||||
respondJSON(c, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,4 +117,4 @@ func (h *AuditHandler) List(c *gin.Context) {
|
||||
"offset": page.Offset,
|
||||
"limit": page.Limit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,4 +232,4 @@ func (h *CategoryHandler) ListTags(c *gin.Context) {
|
||||
"offset": page.Offset,
|
||||
"limit": page.Limit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,4 +353,4 @@ func (h *PoolHandler) Reorder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -255,4 +255,4 @@ func (h *UserHandler) Delete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,4 +627,4 @@ func extractEXIFWithDatetime(data []byte) (json.RawMessage, *time.Time) {
|
||||
dt = &t
|
||||
}
|
||||
return json.RawMessage(b), dt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user