refactor: split sqlite.go and server.go by domain (FLAW-20)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m31s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m31s
Pure moves, no behaviour change: the storage layer now has users, apikeys, thoughts, tasklists, documents, folders, tags, lists, teams and system files next to sqlite.go (store, schema, migrations); the API has handlers_*.go per area plus realtime.go and alarms.go next to server.go (server, router, middleware). Declarations were moved verbatim with an AST-based tool; imports pruned with goimports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7bba174454
commit
74c394efaa
26 files changed
+7702
-7467
No files matched your search
@@ -0,0 +1,208 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/sandrodach/mindsift/internal/domain"
|
||||
)
|
||||
|
||||
// --- STATS, SEARCH & EXPORT ---
|
||||
|
||||
func (s *Store) GetDashboardStats(userID string) (*domain.DashboardStats, error) {
|
||||
st := &domain.DashboardStats{}
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND status = 'inbox'`, userID).Scan(&st.InboxCount)
|
||||
// Same tasks as GetTasks without a list ("Alle Aufgaben"): own ones plus those in shared task lists
|
||||
taskScope := `(user_id = ? OR task_list_id IN (` + s.accessibleTaskListIDsQuery() + `)) AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task'))`
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE `+taskScope+` AND is_completed = 0`, userID, userID, userID, userID).Scan(&st.TasksOpenCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE `+taskScope+` AND is_completed = 1`, userID, userID, userID, userID).Scan(&st.TasksDoneCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM documents WHERE user_id = ? AND is_archived = 0`, userID).Scan(&st.DocsCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND status = 'discarded'`, userID).Scan(&st.DiscardedCount)
|
||||
_ = s.db.QueryRow(`
|
||||
SELECT COUNT(DISTINCT l.id)
|
||||
FROM lists l
|
||||
LEFT JOIN list_shares ls ON ls.list_id = l.id AND (
|
||||
(ls.target_type = 'user' AND ls.target_id = ?)
|
||||
OR (ls.target_type = 'team' AND ls.target_id IN (SELECT team_id FROM team_members WHERE user_id = ?))
|
||||
)
|
||||
WHERE l.user_id = ? OR ls.id IS NOT NULL`,
|
||||
userID, userID, userID).Scan(&st.ListsCount)
|
||||
|
||||
// Count open (uncompleted) To-Dos across accessible workspace documents (owned or shared)
|
||||
docRows, err := s.db.Query(`
|
||||
SELECT blocks_json FROM documents
|
||||
WHERE (user_id = ? OR id IN (SELECT doc_id FROM doc_shares WHERE user_id = ?))
|
||||
AND is_archived = 0`,
|
||||
userID, userID,
|
||||
)
|
||||
if err == nil {
|
||||
for docRows.Next() {
|
||||
var bj string
|
||||
if err := docRows.Scan(&bj); err == nil && bj != "" {
|
||||
var blocks []domain.DocumentBlock
|
||||
if err := json.Unmarshal([]byte(bj), &blocks); err == nil {
|
||||
for _, b := range blocks {
|
||||
if b.Type == "todo" && !b.Checked {
|
||||
st.DocsTodosOpenCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = docRows.Close()
|
||||
}
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) SearchAll(userID, query string) ([]domain.Thought, []domain.Document, error) {
|
||||
res, err := s.SearchGlobal(userID, query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return res.Thoughts, res.Documents, nil
|
||||
}
|
||||
|
||||
func (s *Store) ExportUserData(userID string) (*domain.FullExport, error) {
|
||||
user, err := s.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thoughts, _ := s.GetThoughts(userID, "all", 5000)
|
||||
docs, _ := s.GetDocuments(userID)
|
||||
lists, _ := s.GetAllListsForUser(userID)
|
||||
taskLists, _ := s.GetTaskLists(userID)
|
||||
|
||||
return &domain.FullExport{
|
||||
ExportedAt: domain.NowISO(),
|
||||
Version: "1.0.0",
|
||||
User: *user,
|
||||
Thoughts: thoughts,
|
||||
Documents: docs,
|
||||
Lists: lists,
|
||||
TaskLists: taskLists,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// --- SYSTEM SETTINGS ---
|
||||
|
||||
func (s *Store) SeedSMTPDefaultsFromEnv(host, port, user, pass, from string, tls, skipVerify bool) {
|
||||
cur, err := s.GetSystemSettings()
|
||||
if err != nil || cur == nil {
|
||||
return
|
||||
}
|
||||
// Only populate DB from env if DB has no SMTP host configured yet and env provides one
|
||||
if cur.SMTPHost == "" && strings.TrimSpace(host) != "" {
|
||||
cur.SMTPHost = strings.TrimSpace(host)
|
||||
if strings.TrimSpace(port) != "" {
|
||||
cur.SMTPPort = strings.TrimSpace(port)
|
||||
}
|
||||
cur.SMTPUser = strings.TrimSpace(user)
|
||||
cur.SMTPPass = pass
|
||||
cur.SMTPFrom = strings.TrimSpace(from)
|
||||
cur.SMTPTLS = tls
|
||||
cur.SMTPSkipVerify = skipVerify
|
||||
_ = s.UpdateSystemSettings(cur)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) GetSystemSettings() (*domain.SystemSettings, error) {
|
||||
settings := &domain.SystemSettings{
|
||||
SharingMode: SharingOrganizations,
|
||||
AllowRegistration: false,
|
||||
InstanceName: "MindSift",
|
||||
SMTPPort: "587",
|
||||
SMTPTLS: true,
|
||||
}
|
||||
|
||||
rows, err := s.db.Query(`SELECT key, value FROM system_settings`)
|
||||
if err != nil {
|
||||
return settings, nil
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var k, v string
|
||||
if err := rows.Scan(&k, &v); err == nil {
|
||||
switch k {
|
||||
case "sharing_mode":
|
||||
settings.SharingMode = v
|
||||
case "allow_registration":
|
||||
settings.AllowRegistration = (v == "true" || v == "1")
|
||||
case "instance_name":
|
||||
settings.InstanceName = v
|
||||
case "smtp_host":
|
||||
settings.SMTPHost = v
|
||||
case "smtp_port":
|
||||
if v != "" {
|
||||
settings.SMTPPort = v
|
||||
}
|
||||
case "smtp_user":
|
||||
settings.SMTPUser = v
|
||||
case "smtp_pass":
|
||||
settings.SMTPPass = v
|
||||
case "smtp_from":
|
||||
settings.SMTPFrom = v
|
||||
case "smtp_tls":
|
||||
settings.SMTPTLS = (v != "false" && v != "0")
|
||||
case "smtp_skip_verify":
|
||||
settings.SMTPSkipVerify = v == "true"
|
||||
case "allow_private_webhooks":
|
||||
settings.AllowPrivateWebhooks = v == "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func (s *Store) UpdateSystemSettings(settings *domain.SystemSettings) error {
|
||||
regStr := "false"
|
||||
if settings.AllowRegistration {
|
||||
regStr = "true"
|
||||
}
|
||||
tlsStr := "false"
|
||||
if settings.SMTPTLS {
|
||||
tlsStr = "true"
|
||||
}
|
||||
skipVerifyStr := "false"
|
||||
if settings.SMTPSkipVerify {
|
||||
skipVerifyStr = "true"
|
||||
}
|
||||
privateHooksStr := "false"
|
||||
if settings.AllowPrivateWebhooks {
|
||||
privateHooksStr = "true"
|
||||
}
|
||||
if settings.SMTPPort == "" {
|
||||
settings.SMTPPort = "587"
|
||||
}
|
||||
settings.SharingMode = NormalizeSharingMode(settings.SharingMode)
|
||||
if settings.InstanceName == "" {
|
||||
settings.InstanceName = "MindSift"
|
||||
}
|
||||
_, err := s.db.Exec(`INSERT INTO system_settings (key, value) VALUES
|
||||
('sharing_mode', ?),
|
||||
('allow_registration', ?),
|
||||
('instance_name', ?),
|
||||
('smtp_host', ?),
|
||||
('smtp_port', ?),
|
||||
('smtp_user', ?),
|
||||
('smtp_pass', ?),
|
||||
('smtp_from', ?),
|
||||
('smtp_tls', ?),
|
||||
('smtp_skip_verify', ?),
|
||||
('allow_private_webhooks', ?)
|
||||
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
|
||||
settings.SharingMode,
|
||||
regStr,
|
||||
settings.InstanceName,
|
||||
strings.TrimSpace(settings.SMTPHost),
|
||||
strings.TrimSpace(settings.SMTPPort),
|
||||
strings.TrimSpace(settings.SMTPUser),
|
||||
settings.SMTPPass,
|
||||
strings.TrimSpace(settings.SMTPFrom),
|
||||
tlsStr,
|
||||
skipVerifyStr,
|
||||
privateHooksStr,
|
||||
)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user