fix workspace access and vendor correct fonts
Test and publish / verify (push) Successful in 2m22s

This commit is contained in:
2026-08-04 20:07:35 +03:30
parent 29d7f1636f
commit 9380f116d7
46 changed files with 320 additions and 122 deletions
+56 -8
View File
@@ -28,6 +28,7 @@ type User struct {
Role string
AvatarURL string
Active bool
WorkspaceID int64 `json:"-"`
}
type Workspace struct {
@@ -265,6 +266,7 @@ CREATE TABLE IF NOT EXISTS work_updates (
CREATE INDEX IF NOT EXISTS idx_work_updates_period ON work_updates(period_start,period_end);
CREATE TABLE IF NOT EXISTS board_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
workspace_id INTEGER NOT NULL DEFAULT 1,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'backlog' CHECK(status IN ('backlog','todo','in_progress','blocked','done')),
@@ -315,6 +317,7 @@ CREATE INDEX IF NOT EXISTS idx_board_task_tags_tag ON board_task_tags(tag_id,tas
if err := s.ensureColumn("board_tasks", "importance", "importance TEXT NOT NULL DEFAULT 'normal' CHECK(importance IN ('low','normal','high','urgent'))"); err != nil {
return err
}
if err := s.ensureColumn("board_tasks", "workspace_id", "workspace_id INTEGER NOT NULL DEFAULT 1"); err != nil { return err }
if err := s.ensureBoardTaskTodoStatus(); err != nil {
return err
}
@@ -572,6 +575,11 @@ func (s *Store) Workspaces(userID int64) ([]Workspace, error) {
return out, rows.Err()
}
func (s *Store) AllWorkspaces() ([]Workspace, error) {
rows, err := s.db.Query(`SELECT id,name,slug,created_at FROM workspaces ORDER BY name`); if err != nil { return nil,err }; defer rows.Close()
var out []Workspace; for rows.Next() { var w Workspace; if err:=rows.Scan(&w.ID,&w.Name,&w.Slug,&w.CreatedAt); err!=nil{return nil,err}; out=append(out,w) }; return out,rows.Err()
}
func (s *Store) CreateWorkspace(name, slug string, ownerID int64) (int64, error) {
result, err := s.db.Exec(`INSERT INTO workspaces(name,slug) VALUES(?,?)`, strings.TrimSpace(name), strings.TrimSpace(strings.ToLower(slug)))
if err != nil { return 0, err }
@@ -586,10 +594,31 @@ func (s *Store) WorkspaceMember(workspaceID, userID int64) (WorkspaceMember, err
return m, err
}
func (s *Store) SetWorkspaceMembers(workspaceID int64, userIDs []int64) error {
tx, err := s.db.Begin(); if err != nil { return err }
defer tx.Rollback()
if _, err = tx.Exec(`DELETE FROM workspace_members WHERE workspace_id=?`, workspaceID); err != nil { return err }
for _, userID := range userIDs {
if _, err = tx.Exec(`INSERT INTO workspace_members(workspace_id,user_id,role) SELECT ?,id,CASE WHEN role='admin' THEN 'admin' ELSE 'member' END FROM users WHERE id=?`, workspaceID,userID); err != nil { return err }
}
return tx.Commit()
}
func (s *Store) WorkspaceMemberIDs(workspaceID int64) (map[int64]bool, error) {
rows, err := s.db.Query(`SELECT user_id FROM workspace_members WHERE workspace_id=?`, workspaceID); if err != nil { return nil, err }; defer rows.Close()
out := map[int64]bool{}; for rows.Next() { var id int64; if err:=rows.Scan(&id); err != nil { return nil,err }; out[id]=true }; return out, rows.Err()
}
func (s *Store) ActiveUsers() ([]User, error) {
return s.users(true)
}
func (s *Store) ActiveUsersInWorkspace(workspaceID int64) ([]User, error) {
rows, err := s.db.Query(`SELECT u.id,u.username,u.display_name,COALESCE(u.email,''),u.role,u.avatar_url,u.active FROM users u JOIN workspace_members m ON m.user_id=u.id WHERE m.workspace_id=? AND u.active=1 ORDER BY u.display_name`, workspaceID)
if err != nil { return nil, err }; defer rows.Close()
var out []User; for rows.Next() { var u User; if err:=rows.Scan(&u.ID,&u.Username,&u.DisplayName,&u.Email,&u.Role,&u.AvatarURL,&u.Active); err!=nil{return nil,err}; out=append(out,u) }; return out,rows.Err()
}
func (s *Store) users(activeOnly bool) ([]User, error) {
query := `SELECT id,username,display_name,COALESCE(email,''),role,avatar_url,active FROM users`
if activeOnly {
@@ -767,6 +796,11 @@ func (s *Store) SetUserActive(userID, actorID int64, active bool) error {
return tx.Commit()
}
func (s *Store) SetUserAvatar(userID int64, avatarURL string) error {
_, err := s.db.Exec(`UPDATE users SET avatar_url=? WHERE id=?`, avatarURL, userID)
return err
}
func (s *Store) TodayAttendance(userID int64, day string) (*Attendance, error) {
var a Attendance
err := s.db.QueryRow(`SELECT id,user_id,day,
@@ -1063,6 +1097,10 @@ func validBoardImportance(importance string) bool {
}
func (s *Store) CreateBoardTask(creatorID int64, title, description, status, importance string, assigneeIDs []int64, dueDate string) (int64, error) {
return s.CreateBoardTaskInWorkspace(1, creatorID, title, description, status, importance, assigneeIDs, dueDate)
}
func (s *Store) CreateBoardTaskInWorkspace(workspaceID, creatorID int64, title, description, status, importance string, assigneeIDs []int64, dueDate string) (int64, error) {
title = strings.TrimSpace(title)
description = strings.TrimSpace(description)
if title == "" {
@@ -1086,8 +1124,8 @@ func (s *Store) CreateBoardTask(creatorID int64, title, description, status, imp
return 0, err
}
defer tx.Rollback()
result, err := tx.Exec(`INSERT INTO board_tasks(title,description,status,importance,creator_id,due_date,created_at)
VALUES(?,?,?,?,?,?,?)`, title, description, status, importance, creatorID, due, time.Now().UTC())
result, err := tx.Exec(`INSERT INTO board_tasks(workspace_id,title,description,status,importance,creator_id,due_date,created_at)
VALUES(?,?,?,?,?,?,?,?)`, workspaceID, title, description, status, importance, creatorID, due, time.Now().UTC())
if err != nil {
return 0, err
}
@@ -1102,14 +1140,22 @@ func (s *Store) CreateBoardTask(creatorID int64, title, description, status, imp
}
func (s *Store) BoardTasks() ([]BoardTask, error) {
return s.boardTasks(false)
return s.boardTasks(false, 0)
}
func (s *Store) BoardTasksInWorkspace(workspaceID int64) ([]BoardTask, error) {
return s.boardTasks(false, workspaceID)
}
func (s *Store) ArchivedBoardTasks() ([]BoardTask, error) {
return s.boardTasks(true)
return s.boardTasks(true, 0)
}
func (s *Store) boardTasks(archived bool) ([]BoardTask, error) {
func (s *Store) ArchivedBoardTasksInWorkspace(workspaceID int64) ([]BoardTask, error) {
return s.boardTasks(true, workspaceID)
}
func (s *Store) boardTasks(archived bool, workspaceID int64) ([]BoardTask, error) {
query := `SELECT
t.id,t.title,t.description,t.status,t.importance,
t.creator_id,c.display_name,COALESCE(t.due_date,''),CAST(t.created_at AS TEXT),
@@ -1117,15 +1163,17 @@ func (s *Store) boardTasks(archived bool) ([]BoardTask, error) {
FROM board_tasks t
JOIN users c ON c.id=t.creator_id`
if archived {
query += ` WHERE t.archived_at IS NOT NULL
query += ` WHERE t.archived_at IS NOT NULL` + func() string { if workspaceID > 0 { return ` AND t.workspace_id=?` }; return `` }() + `
ORDER BY t.archived_at DESC,t.id DESC`
} else {
query += ` WHERE t.archived_at IS NULL
query += ` WHERE t.archived_at IS NULL` + func() string { if workspaceID > 0 { return ` AND t.workspace_id=?` }; return `` }() + `
ORDER BY CASE t.status
WHEN 'backlog' THEN 0 WHEN 'todo' THEN 1 WHEN 'in_progress' THEN 2 WHEN 'blocked' THEN 3 ELSE 4 END,
t.created_at DESC`
}
rows, err := s.db.Query(query)
var rows *sql.Rows
var err error
if workspaceID > 0 { rows, err = s.db.Query(query, workspaceID) } else { rows, err = s.db.Query(query) }
if err != nil {
return nil, err
}