MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m1s
454 lines
15 KiB
Go
454 lines
15 KiB
Go
package domain
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// NewID generates a readable prefixed ID as specified in BRAIN.md (e.g. th_4x8z19a0b1)
|
|
func NewID(prefix string) string {
|
|
b := make([]byte, 5)
|
|
_, _ = rand.Read(b)
|
|
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(b))
|
|
}
|
|
|
|
// NowISO returns the current UTC timestamp in ISO-8601 format
|
|
func NowISO() string {
|
|
return time.Now().UTC().Format(time.RFC3339)
|
|
}
|
|
|
|
var hashtagRegex = regexp.MustCompile(`#([a-zA-Z0-9äöüÄÖÜß_-]+)`)
|
|
|
|
// ExtractTags extracts #hashtags from content
|
|
func ExtractTags(content string) []string {
|
|
matches := hashtagRegex.FindAllStringSubmatch(content, -1)
|
|
seen := make(map[string]bool)
|
|
tags := make([]string, 0, len(matches))
|
|
for _, m := range matches {
|
|
if len(m) > 1 {
|
|
tag := strings.ToLower(m[1])
|
|
if !seen[tag] {
|
|
seen[tag] = true
|
|
tags = append(tags, tag)
|
|
}
|
|
}
|
|
}
|
|
return tags
|
|
}
|
|
|
|
// FormatGermanDateTime formats an ISO-8601 string into "25. Okt. 2026 - 15:40 Uhr"
|
|
func FormatGermanDateTime(isoStr string) string {
|
|
if isoStr == "" {
|
|
return ""
|
|
}
|
|
var t time.Time
|
|
var err error
|
|
formats := []string{
|
|
time.RFC3339Nano,
|
|
time.RFC3339,
|
|
"2006-01-02T15:04:05Z07:00",
|
|
"2006-01-02T15:04:05",
|
|
"2006-01-02 15:04:05",
|
|
"2006-01-02",
|
|
}
|
|
for _, f := range formats {
|
|
if parsed, parseErr := time.Parse(f, isoStr); parseErr == nil {
|
|
t = parsed
|
|
err = nil
|
|
break
|
|
} else {
|
|
err = parseErr
|
|
}
|
|
}
|
|
if err != nil {
|
|
return isoStr
|
|
}
|
|
|
|
t = t.Local()
|
|
|
|
germanMonths := map[time.Month]string{
|
|
time.January: "Jan.",
|
|
time.February: "Feb.",
|
|
time.March: "März",
|
|
time.April: "Apr.",
|
|
time.May: "Mai",
|
|
time.June: "Juni",
|
|
time.July: "Juli",
|
|
time.August: "Aug.",
|
|
time.September: "Sept.",
|
|
time.October: "Okt.",
|
|
time.November: "Nov.",
|
|
time.December: "Dez.",
|
|
}
|
|
mStr := germanMonths[t.Month()]
|
|
if mStr == "" {
|
|
mStr = t.Format("Jan")
|
|
}
|
|
|
|
return fmt.Sprintf("%d. %s %d - %02d:%02d Uhr", t.Day(), mStr, t.Year(), t.Hour(), t.Minute())
|
|
}
|
|
|
|
type User struct {
|
|
ID string `json:"id"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username,omitempty"`
|
|
PasswordHash string `json:"-"`
|
|
DisplayName string `json:"displayName"`
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
Role string `json:"role"` // admin | member
|
|
IsActive bool `json:"isActive"`
|
|
AlarmChannels []string `json:"alarmChannels"` // email | discord | webhook
|
|
AlarmEmail string `json:"alarmEmail"`
|
|
DiscordWebhookURL string `json:"discordWebhookUrl"`
|
|
CustomWebhookURL string `json:"customWebhookUrl"`
|
|
NotifyBeforeMinutes int `json:"notifyBeforeMinutes"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type UserInvite struct {
|
|
ID string `json:"id"`
|
|
Code string `json:"code"`
|
|
Email string `json:"email,omitempty"`
|
|
Role string `json:"role"` // member | admin
|
|
TeamID string `json:"teamId,omitempty"`
|
|
TeamName string `json:"teamName,omitempty"`
|
|
CreatedBy string `json:"createdBy"`
|
|
ExpiresAt string `json:"expiresAt"`
|
|
UsedBy string `json:"usedBy,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type ThoughtStatus string
|
|
|
|
const (
|
|
StatusInbox ThoughtStatus = "inbox"
|
|
StatusKept ThoughtStatus = "kept"
|
|
StatusDiscarded ThoughtStatus = "discarded"
|
|
StatusConverted ThoughtStatus = "converted"
|
|
)
|
|
|
|
type SubTask struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
IsCompleted bool `json:"isCompleted"`
|
|
CreatedAt string `json:"createdAt,omitempty"`
|
|
}
|
|
|
|
type Thought struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
Content string `json:"content"`
|
|
Tags []string `json:"tags"`
|
|
Priority string `json:"priority"` // low | normal | high | urgent
|
|
Source string `json:"source"` // manual | voice | webhook
|
|
Status ThoughtStatus `json:"status"`
|
|
IsCompleted bool `json:"isCompleted"`
|
|
ConvertedType string `json:"convertedType,omitempty"` // task | document | list_item
|
|
ConvertedRefID string `json:"convertedRefId,omitempty"` // referenced entity ID
|
|
DueDate string `json:"dueDate,omitempty"`
|
|
TaskListID string `json:"taskListId,omitempty"`
|
|
KanbanStatus string `json:"kanbanStatus,omitempty"` // todo | in_progress | done
|
|
Notes string `json:"notes,omitempty"`
|
|
Subtasks []SubTask `json:"subtasks"`
|
|
AlarmSent bool `json:"alarmSent,omitempty"`
|
|
SortOrder int `json:"sortOrder"`
|
|
CreatedAt string `json:"createdAt"`
|
|
TriagedAt string `json:"triagedAt,omitempty"`
|
|
}
|
|
|
|
type ThoughtReorderItem struct {
|
|
ID string `json:"id"`
|
|
SortOrder int `json:"sortOrder"`
|
|
KanbanStatus *string `json:"kanbanStatus,omitempty"`
|
|
}
|
|
|
|
type ListItemReorderItem struct {
|
|
ID string `json:"id"`
|
|
SortOrder int `json:"sortOrder"`
|
|
Category *string `json:"category,omitempty"`
|
|
Content *string `json:"content,omitempty"`
|
|
}
|
|
|
|
type TaskListShare struct {
|
|
ID string `json:"id"`
|
|
TaskListID string `json:"taskListId"`
|
|
TargetType string `json:"targetType"` // team | user
|
|
TargetID string `json:"targetId"`
|
|
TargetName string `json:"targetName,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type TaskListMember struct {
|
|
UserID string `json:"userId"`
|
|
DisplayName string `json:"displayName"`
|
|
Email string `json:"email"`
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
Role string `json:"role"` // owner | member
|
|
}
|
|
|
|
type KanbanColumn struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Color string `json:"color,omitempty"`
|
|
IsDone bool `json:"isDone,omitempty"`
|
|
}
|
|
|
|
type TaskList struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
SpaceID string `json:"spaceId,omitempty"`
|
|
SpaceName string `json:"spaceName,omitempty"`
|
|
TeamID string `json:"teamId,omitempty"`
|
|
TeamName string `json:"teamName,omitempty"`
|
|
OwnerName string `json:"ownerName,omitempty"`
|
|
IsOwner bool `json:"isOwner"`
|
|
Members []TaskListMember `json:"members,omitempty"`
|
|
Shares []TaskListShare `json:"shares,omitempty"`
|
|
Columns []KanbanColumn `json:"columns,omitempty"`
|
|
Title string `json:"title"`
|
|
Icon string `json:"icon"`
|
|
Color string `json:"color"`
|
|
TasksCount int `json:"tasksCount,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
}
|
|
|
|
type Organization struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
OwnerID string `json:"ownerId"`
|
|
Teams []Team `json:"teams,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type Team struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId,omitempty"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Members []TeamMember `json:"members"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type TeamMember struct {
|
|
TeamID string `json:"teamId"`
|
|
UserID string `json:"userId"`
|
|
DisplayName string `json:"displayName"`
|
|
Email string `json:"email"`
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
Role string `json:"role"` // admin | member
|
|
JoinedAt string `json:"joinedAt"`
|
|
}
|
|
|
|
type SystemSettings struct {
|
|
SharingMode string `json:"sharingMode"` // open | teams_only | strict_private
|
|
AllowRegistration bool `json:"allowRegistration"`
|
|
InstanceName string `json:"instanceName"`
|
|
SMTPHost string `json:"smtpHost"`
|
|
SMTPPort string `json:"smtpPort"`
|
|
SMTPUser string `json:"smtpUser"`
|
|
SMTPPass string `json:"smtpPass"`
|
|
SMTPFrom string `json:"smtpFrom"`
|
|
SMTPTLS bool `json:"smtpTls"`
|
|
SMTPConfiguredViaEnv bool `json:"smtpConfiguredViaEnv,omitempty"`
|
|
}
|
|
|
|
type DocumentBlock struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"` // h1 | h2 | h3 | paragraph | todo | bullet | code | callout | divider | table | progress
|
|
Content string `json:"content"`
|
|
Checked bool `json:"checked,omitempty"`
|
|
Lang string `json:"lang,omitempty"`
|
|
Level int `json:"level,omitempty"`
|
|
Collapsed bool `json:"collapsed,omitempty"`
|
|
}
|
|
|
|
type DocShare struct {
|
|
ID string `json:"id"`
|
|
DocID string `json:"docId"`
|
|
TargetType string `json:"targetType,omitempty"` // user | team
|
|
TargetID string `json:"targetId,omitempty"`
|
|
TargetName string `json:"targetName,omitempty"`
|
|
UserID string `json:"userId,omitempty"`
|
|
UserEmail string `json:"userEmail,omitempty"`
|
|
DisplayName string `json:"displayName,omitempty"`
|
|
Permission string `json:"permission"` // read | write
|
|
CreatedBy string `json:"createdBy"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type DocLockInfo struct {
|
|
DocID string `json:"docId"`
|
|
UserID string `json:"userId"`
|
|
UserName string `json:"userName"`
|
|
ExpiresAt string `json:"expiresAt"`
|
|
}
|
|
|
|
type Document struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
SpaceID string `json:"spaceId,omitempty"`
|
|
Title string `json:"title"`
|
|
Icon string `json:"icon"`
|
|
Content string `json:"content"`
|
|
Blocks []DocumentBlock `json:"blocks"`
|
|
Tags []string `json:"tags"`
|
|
FolderPath string `json:"folderPath,omitempty"`
|
|
IsPinned bool `json:"isPinned"`
|
|
IsArchived bool `json:"isArchived"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
|
|
// Collaboration & Permissions
|
|
Permission string `json:"permission"` // owner | write | read
|
|
IsOwner bool `json:"isOwner"`
|
|
OwnerName string `json:"ownerName,omitempty"`
|
|
SharedWith []DocShare `json:"sharedWith,omitempty"`
|
|
SharedFolderID string `json:"sharedFolderId,omitempty"`
|
|
SubFolderPath string `json:"subFolderPath,omitempty"`
|
|
IsSharedFolder bool `json:"isSharedFolder,omitempty"`
|
|
FolderPermission string `json:"folderPermission,omitempty"`
|
|
}
|
|
|
|
type TagSummary struct {
|
|
Tag string `json:"tag"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
type MatchedTagBlock struct {
|
|
RootBlock DocumentBlock `json:"rootBlock"`
|
|
Children []DocumentBlock `json:"children"`
|
|
}
|
|
|
|
type TagDocumentGroup struct {
|
|
DocID string `json:"docId"`
|
|
Title string `json:"title"`
|
|
Icon string `json:"icon"`
|
|
FolderPath string `json:"folderPath"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
IsOwner bool `json:"isOwner"`
|
|
Permission string `json:"permission"`
|
|
OwnerName string `json:"ownerName,omitempty"`
|
|
MatchedBlocks []MatchedTagBlock `json:"matchedBlocks"`
|
|
}
|
|
|
|
type TagDetails struct {
|
|
Tag string `json:"tag"`
|
|
NoteContent string `json:"noteContent"`
|
|
NoteUpdatedAt string `json:"noteUpdatedAt,omitempty"`
|
|
Documents []TagDocumentGroup `json:"documents"`
|
|
TotalMatches int `json:"totalMatches"`
|
|
}
|
|
|
|
type ListShare struct {
|
|
ID string `json:"id"`
|
|
ListID string `json:"listId"`
|
|
TargetType string `json:"targetType"` // team | user
|
|
TargetID string `json:"targetId"`
|
|
TargetName string `json:"targetName,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Role string `json:"role"` // editor | viewer
|
|
CreatedBy string `json:"createdBy"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type ListItem struct {
|
|
ID string `json:"id"`
|
|
ListID string `json:"listId"`
|
|
Content string `json:"content"`
|
|
Category string `json:"category,omitempty"`
|
|
Quantity string `json:"quantity,omitempty"`
|
|
IsCompleted bool `json:"isCompleted"`
|
|
CompletedBy string `json:"completedBy,omitempty"`
|
|
CompletedByName string `json:"completedByName,omitempty"`
|
|
SortOrder int `json:"sortOrder"`
|
|
CreatedAt string `json:"createdAt"`
|
|
CompletedAt string `json:"completedAt,omitempty"`
|
|
}
|
|
|
|
type SharedList struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
OwnerName string `json:"ownerName,omitempty"`
|
|
IsOwner bool `json:"isOwner"`
|
|
Role string `json:"role,omitempty"` // editor | viewer
|
|
Title string `json:"title"`
|
|
Icon string `json:"icon"`
|
|
ListType string `json:"listType"` // shopping | checklist | vacation
|
|
Items []ListItem `json:"items"`
|
|
Shares []ListShare `json:"shares,omitempty"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type APIKey struct {
|
|
ID string `json:"id"`
|
|
UserID string `json:"userId"`
|
|
Name string `json:"name"`
|
|
KeyPrefix string `json:"keyPrefix"`
|
|
KeyHash string `json:"-"`
|
|
RawKey string `json:"rawKey,omitempty"` // Only returned once on creation
|
|
Scopes string `json:"scopes"`
|
|
LastUsedAt string `json:"lastUsedAt,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|
|
|
|
type WSMessage struct {
|
|
Event string `json:"event"` // ITEM_CREATED | ITEM_UPDATED | ITEM_TOGGLED | ITEM_DELETED | LIST_CLEARED | USER_PRESENCE
|
|
ListID string `json:"list_id"`
|
|
Payload interface{} `json:"payload"`
|
|
Timestamp string `json:"timestamp"`
|
|
}
|
|
|
|
type DashboardStats struct {
|
|
InboxCount int `json:"inboxCount"`
|
|
TasksOpenCount int `json:"tasksOpenCount"`
|
|
TasksDoneCount int `json:"tasksDoneCount"`
|
|
DocsCount int `json:"docsCount"`
|
|
DocsTodosOpenCount int `json:"docsTodosOpenCount"`
|
|
ListsCount int `json:"listsCount"`
|
|
DiscardedCount int `json:"discardedCount"`
|
|
}
|
|
|
|
type FullExport struct {
|
|
ExportedAt string `json:"exportedAt"`
|
|
Version string `json:"version"`
|
|
User User `json:"user"`
|
|
Thoughts []Thought `json:"thoughts"`
|
|
Documents []Document `json:"documents"`
|
|
Lists []SharedList `json:"lists"`
|
|
TaskLists []TaskList `json:"taskLists,omitempty"`
|
|
}
|
|
|
|
type SharedFolder struct {
|
|
ID string `json:"id"`
|
|
OwnerID string `json:"ownerId"`
|
|
OwnerName string `json:"ownerName,omitempty"`
|
|
Name string `json:"name"`
|
|
MountPath string `json:"mountPath"`
|
|
IsOwner bool `json:"isOwner"`
|
|
Permission string `json:"permission"` // owner | write | read
|
|
Shares []FolderShare `json:"shares,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
}
|
|
|
|
type FolderShare struct {
|
|
ID string `json:"id"`
|
|
FolderID string `json:"folderId"`
|
|
TargetType string `json:"targetType"` // team | user
|
|
TargetID string `json:"targetId"`
|
|
TargetName string `json:"targetName,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
Permission string `json:"permission"` // read | write
|
|
CreatedBy string `json:"createdBy"`
|
|
CreatedAt string `json:"createdAt"`
|
|
}
|