mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
first test
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AISummary represents an AI-generated summary
|
||||
type AISummary struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Source content
|
||||
ContentType string `json:"content_type" gorm:"not null"` // "bookmark", "note", "file"
|
||||
ContentID uint `json:"content_id" gorm:"not null"`
|
||||
|
||||
// Summary data
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary" gorm:"type:text"`
|
||||
KeyPoints string `json:"key_points" gorm:"type:text"` // JSON array of key points
|
||||
Tags string `json:"tags" gorm:"type:text"` // JSON array of suggested tags
|
||||
ReadTime int `json:"read_time"` // Estimated reading time in minutes
|
||||
Complexity string `json:"complexity" gorm:"default:'medium'"` // "low", "medium", "high"
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
ProcessingMs int64 `json:"processing_ms"`
|
||||
TokenCount int `json:"token_count"`
|
||||
Confidence float64 `json:"confidence"` // AI confidence score 0-1
|
||||
LastAnalyzed time.Time `json:"last_analyzed"`
|
||||
}
|
||||
|
||||
// AITaskSuggestion represents AI-generated task suggestions
|
||||
type AITaskSuggestion struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Suggestion data
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Priority string `json:"priority" gorm:"default:'medium'"` // "low", "medium", "high", "urgent"
|
||||
Category string `json:"category"` // "work", "personal", "learning", "health", etc.
|
||||
|
||||
// Context and reasoning
|
||||
Reasoning string `json:"reasoning" gorm:"type:text"` // Why this task is suggested
|
||||
ContextType string `json:"context_type"` // "calendar", "bookmarks", "deadlines", "habits"
|
||||
ContextData string `json:"context_data" gorm:"type:text"` // JSON data about context
|
||||
Deadline *time.Time `json:"deadline"`
|
||||
EstimatedTime int `json:"estimated_time"` // in minutes
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
Confidence float64 `json:"confidence"` // AI confidence score 0-1
|
||||
Accepted bool `json:"accepted" gorm:"default:false"`
|
||||
Dismissed bool `json:"dismissed" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// AITagSuggestion represents AI-generated tag suggestions
|
||||
type AITagSuggestion struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Target content
|
||||
ContentType string `json:"content_type" gorm:"not null"` // "bookmark", "note", "task", "file"
|
||||
ContentID uint `json:"content_id" gorm:"not null"`
|
||||
|
||||
// Tag suggestions
|
||||
SuggestedTags string `json:"suggested_tags" gorm:"type:text"` // JSON array of suggested tags
|
||||
ExistingTags string `json:"existing_tags" gorm:"type:text"` // JSON array of current tags
|
||||
Relevance float64 `json:"relevance"` // Relevance score 0-1
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Applied bool `json:"applied" gorm:"default:false"`
|
||||
Dismissed bool `json:"dismissed" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// AIContentGeneration represents AI-generated content
|
||||
type AIContentGeneration struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Generation request
|
||||
Prompt string `json:"prompt" gorm:"type:text"`
|
||||
ContentType string `json:"content_type" gorm:"not null"` // "blog", "code", "email", "summary", "outline"
|
||||
Context string `json:"context" gorm:"type:text"` // Additional context for generation
|
||||
|
||||
// Generated content
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
WordCount int `json:"word_count"`
|
||||
ReadTime int `json:"read_time"` // Estimated reading time in minutes
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
ProcessingMs int64 `json:"processing_ms"`
|
||||
TokenCount int `json:"token_count"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
Used bool `json:"used" gorm:"default:false"`
|
||||
Rating *int `json:"rating"` // User rating 1-5
|
||||
Feedback string `json:"feedback" gorm:"type:text"`
|
||||
}
|
||||
|
||||
// AICodeReview represents AI code review analysis
|
||||
type AICodeReview struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Code context
|
||||
RepositoryURL string `json:"repository_url"`
|
||||
CommitHash string `json:"commit_hash"`
|
||||
FilePath string `json:"file_path"`
|
||||
PRNumber int `json:"pr_number"`
|
||||
BranchName string `json:"branch_name"`
|
||||
|
||||
// Review data
|
||||
OriginalCode string `json:"original_code" gorm:"type:text"`
|
||||
Suggestions string `json:"suggestions" gorm:"type:text"` // JSON array of suggestions
|
||||
Issues string `json:"issues" gorm:"type:text"` // JSON array of issues found
|
||||
Score int `json:"score" gorm:"default:0"` // Code quality score 0-100
|
||||
SecurityIssues string `json:"security_issues" gorm:"type:text"` // JSON array of security issues
|
||||
Performance string `json:"performance" gorm:"type:text"` // Performance suggestions
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
ProcessingMs int64 `json:"processing_ms"`
|
||||
TokenCount int `json:"token_count"`
|
||||
Applied bool `json:"applied" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// AILearningRecommendation represents AI-generated learning recommendations
|
||||
type AILearningRecommendation struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Recommendation data
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category"` // "programming", "design", "business", etc.
|
||||
Difficulty string `json:"difficulty"` // "beginner", "intermediate", "advanced"
|
||||
EstimatedHours int `json:"estimated_hours"` // Estimated time to complete
|
||||
Prerequisites string `json:"prerequisites" gorm:"type:text"` // JSON array of prerequisites
|
||||
Resources string `json:"resources" gorm:"type:text"` // JSON array of learning resources
|
||||
CourseID *uint `json:"course_id"` // Link to existing course if applicable
|
||||
|
||||
// Personalization
|
||||
Reasoning string `json:"reasoning" gorm:"type:text"` // Why this is recommended
|
||||
RelevanceScore float64 `json:"relevance_score"` // How relevant to user 0-1
|
||||
CareerImpact string `json:"career_impact"` // Career impact description
|
||||
SkillGained string `json:"skill_gained"` // Primary skill gained
|
||||
|
||||
// AI metadata
|
||||
ModelUsed string `json:"model_used"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Started bool `json:"started" gorm:"default:false"`
|
||||
Completed bool `json:"completed" gorm:"default:false"`
|
||||
Rating *int `json:"rating"` // User rating 1-5
|
||||
Feedback string `json:"feedback" gorm:"type:text"`
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AIRecommendation represents an AI-generated recommendation
|
||||
type AIRecommendation struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// User information
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Recommendation details
|
||||
RecommendationType string `json:"recommendation_type" gorm:"not null;index"` // content, task, learning, connection
|
||||
ContentType string `json:"content_type" gorm:"index"` // bookmark, note, task, course, user
|
||||
ContentID *uint `json:"content_id,omitempty" gorm:"index"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Reasoning string `json:"reasoning"` // Why this was recommended
|
||||
|
||||
// Content details (for display without additional queries)
|
||||
ContentTitle string `json:"content_title"`
|
||||
ContentURL string `json:"content_url"`
|
||||
ContentPreview string `json:"content_preview"`
|
||||
AuthorName string `json:"author_name"`
|
||||
Tags string `json:"tags" gorm:"serializer:json"`
|
||||
|
||||
// Recommendation metadata
|
||||
Confidence float64 `json:"confidence" gorm:"default:0.0"` // 0.0 to 1.0
|
||||
Priority string `json:"priority" gorm:"default:medium"` // low, medium, high
|
||||
Category string `json:"category"` // productivity, learning, collaboration, etc.
|
||||
ExpiresAt *time.Time `json:"expires_at"`
|
||||
Clicked bool `json:"clicked" gorm:"default:false"`
|
||||
Dismissed bool `json:"dismissed" gorm:"default:false"`
|
||||
ClickedAt *time.Time `json:"clicked_at"`
|
||||
DismissedAt *time.Time `json:"dismissed_at"`
|
||||
|
||||
// Feedback
|
||||
Feedback string `json:"feedback"` // helpful, not_helpful, irrelevant
|
||||
FeedbackAt *time.Time `json:"feedback_at"`
|
||||
FeedbackText string `json:"feedback_text"`
|
||||
|
||||
// Source information
|
||||
SourceModel string `json:"source_model"` // Which AI model generated this
|
||||
SourceVersion string `json:"source_version"` // Version of the recommendation engine
|
||||
TrainingData string `json:"training_data"` // What data was used for training
|
||||
}
|
||||
|
||||
// UserPreference represents user preferences for recommendations
|
||||
type UserPreference struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// User information
|
||||
UserID uint `json:"user_id" gorm:"not null;uniqueIndex"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Recommendation preferences
|
||||
EnableRecommendations bool `json:"enable_recommendations" gorm:"default:true"`
|
||||
ContentRecommendations bool `json:"content_recommendations" gorm:"default:true"`
|
||||
TaskRecommendations bool `json:"task_recommendations" gorm:"default:true"`
|
||||
LearningRecommendations bool `json:"learning_recommendations" gorm:"default:true"`
|
||||
ConnectionRecommendations bool `json:"connection_recommendations" gorm:"default:false"`
|
||||
|
||||
// Frequency and timing
|
||||
MaxRecommendationsPerDay int `json:"max_recommendations_per_day" gorm:"default:5"`
|
||||
PreferredCategories []string `json:"preferred_categories" gorm:"serializer:json"`
|
||||
BlockedCategories []string `json:"blocked_categories" gorm:"serializer:json"`
|
||||
PreferredContentTypes []string `json:"preferred_content_types" gorm:"serializer:json"`
|
||||
|
||||
// Quality thresholds
|
||||
MinConfidenceThreshold float64 `json:"min_confidence_threshold" gorm:"default:0.6"`
|
||||
MaxAgeHours int `json:"max_age_hours" gorm:"default:168"` // 1 week
|
||||
|
||||
// Learning and adaptation
|
||||
EnablePersonalization bool `json:"enable_personalization" gorm:"default:true"`
|
||||
EnableFeedbackLearning bool `json:"enable_feedback_learning" gorm:"default:true"`
|
||||
LastRecommendationAt *time.Time `json:"last_recommendation_at"`
|
||||
}
|
||||
|
||||
// RecommendationInteraction tracks user interactions with recommendations
|
||||
type RecommendationInteraction struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Related entities
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
RecommendationID uint `json:"recommendation_id" gorm:"not null;index"`
|
||||
Recommendation AIRecommendation `json:"recommendation,omitempty" gorm:"foreignKey:RecommendationID"`
|
||||
|
||||
// Interaction details
|
||||
InteractionType string `json:"interaction_type" gorm:"not null;index"` // view, click, dismiss, feedback, share
|
||||
InteractionData string `json:"interaction_data" gorm:"serializer:json"` // Additional context
|
||||
Duration int `json:"duration"` // Time spent in seconds (for views)
|
||||
Context string `json:"context"` // Where the interaction occurred (dashboard, search, etc.)
|
||||
|
||||
// Machine learning features
|
||||
UserActivityBefore string `json:"user_activity_before"` // What user was doing before
|
||||
UserActivityAfter string `json:"user_activity_after"` // What user did after
|
||||
SessionID string `json:"session_id"`
|
||||
DeviceType string `json:"device_type"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for AIRecommendation
|
||||
func (AIRecommendation) TableName() string {
|
||||
return "ai_recommendations"
|
||||
}
|
||||
|
||||
// TableName returns the table name for UserPreference
|
||||
func (UserPreference) TableName() string {
|
||||
return "user_preferences"
|
||||
}
|
||||
|
||||
// TableName returns the table name for RecommendationInteraction
|
||||
func (RecommendationInteraction) TableName() string {
|
||||
return "recommendation_interactions"
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (r *AIRecommendation) BeforeCreate(tx *gorm.DB) error {
|
||||
if r.Priority == "" {
|
||||
r.Priority = "medium"
|
||||
}
|
||||
if r.Confidence == 0 {
|
||||
r.Confidence = 0.5
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (up *UserPreference) BeforeCreate(tx *gorm.DB) error {
|
||||
if up.MaxRecommendationsPerDay == 0 {
|
||||
up.MaxRecommendationsPerDay = 5
|
||||
}
|
||||
if up.MinConfidenceThreshold == 0 {
|
||||
up.MinConfidenceThreshold = 0.6
|
||||
}
|
||||
if up.MaxAgeHours == 0 {
|
||||
up.MaxAgeHours = 168 // 1 week
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserAISettings stores user-specific AI provider configurations
|
||||
type UserAISettings struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;uniqueIndex"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Mistral Settings
|
||||
MistralEnabled *bool `json:"mistral_enabled" gorm:"default:false"`
|
||||
MistralAPIKey string `json:"-" gorm:"column:mistral_api_key"` // Encrypted
|
||||
MistralModel string `json:"mistral_model" gorm:"default:mistral-small-latest"`
|
||||
MistralModelThinking string `json:"mistral_model_thinking" gorm:"default:mistral-large-latest"`
|
||||
|
||||
// Grok Settings
|
||||
GrokEnabled *bool `json:"grok_enabled" gorm:"default:false"`
|
||||
GrokAPIKey string `json:"-" gorm:"column:grok_api_key"` // Encrypted
|
||||
GrokBaseURL string `json:"grok_base_url" gorm:"default:https://api.x.ai/v1"`
|
||||
GrokModel string `json:"grok_model" gorm:"default:grok-4-1-fast-non-reasoning-latest"`
|
||||
GrokModelThinking string `json:"grok_model_thinking" gorm:"default:grok-4-1-fast-reasoning-latest"`
|
||||
|
||||
// DeepSeek Settings
|
||||
DeepSeekEnabled *bool `json:"deepseek_enabled" gorm:"default:false"`
|
||||
DeepSeekAPIKey string `json:"-" gorm:"column:deepseek_api_key"` // Encrypted
|
||||
DeepSeekBaseURL string `json:"deepseek_base_url" gorm:"default:https://api.deepseek.com"`
|
||||
DeepSeekModel string `json:"deepseek_model" gorm:"default:deepseek-chat"`
|
||||
DeepSeekModelThinking string `json:"deepseek_model_thinking" gorm:"default:deepseek-reasoner"`
|
||||
|
||||
// Ollama Settings
|
||||
OllamaEnabled *bool `json:"ollama_enabled" gorm:"default:false"`
|
||||
OllamaBaseURL string `json:"ollama_base_url" gorm:"default:http://localhost:11434"`
|
||||
OllamaModel string `json:"ollama_model" gorm:"default:llama3.1"`
|
||||
OllamaModelThinking string `json:"ollama_model_thinking" gorm:"default:llama3.1"`
|
||||
|
||||
// LongCat Settings
|
||||
LongCatEnabled *bool `json:"longcat_enabled" gorm:"default:false"`
|
||||
LongCatAPIKey string `json:"-" gorm:"column:longcat_api_key"` // Encrypted
|
||||
LongCatBaseURL string `json:"longcat_base_url" gorm:"default:https://api.longcat.chat"`
|
||||
LongCatOpenAIEndpoint string `json:"longcat_openai_endpoint" gorm:"default:https://api.longcat.chat/openai"`
|
||||
LongCatAnthropicEndpoint string `json:"longcat_anthropic_endpoint" gorm:"default:https://api.longcat.chat/anthropic"`
|
||||
LongCatModel string `json:"longcat_model" gorm:"default:LongCat-Flash-Chat"`
|
||||
LongCatModelThinking string `json:"longcat_model_thinking" gorm:"default:LongCat-Flash-Thinking"`
|
||||
LongCatModelThinkingUpgraded string `json:"longcat_model_thinking_upgraded" gorm:"default:LongCat-Flash-Thinking-2601"`
|
||||
LongCatFormat string `json:"longcat_format" gorm:"default:openai"`
|
||||
|
||||
// OpenRouter Settings
|
||||
OpenRouterEnabled *bool `json:"openrouter_enabled" gorm:"default:false"`
|
||||
OpenRouterAPIKey string `json:"-" gorm:"column:openrouter_api_key"` // Encrypted
|
||||
OpenRouterBaseURL string `json:"openrouter_base_url" gorm:"default:https://openrouter.ai/api"`
|
||||
OpenRouterModel string `json:"openrouter_model" gorm:"default:openrouter/auto"`
|
||||
OpenRouterModelThinking string `json:"openrouter_model_thinking" gorm:"default:openrouter/auto"`
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Analytics represents user analytics data
|
||||
type Analytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Analytics data
|
||||
Date time.Time `json:"date" gorm:"not null;index"`
|
||||
HoursTracked float64 `json:"hours_tracked"`
|
||||
TasksCompleted int `json:"tasks_completed"`
|
||||
BookmarksAdded int `json:"bookmarks_added"`
|
||||
NotesCreated int `json:"notes_created"`
|
||||
CoursesStarted int `json:"courses_started"`
|
||||
CoursesCompleted int `json:"courses_completed"`
|
||||
GitHubCommits int `json:"github_commits"`
|
||||
GitHubPRs int `json:"github_prs"`
|
||||
StudyStreak int `json:"study_streak"`
|
||||
ProductivityScore float64 `json:"productivity_score"`
|
||||
}
|
||||
|
||||
// ProductivityMetrics represents productivity analytics
|
||||
type ProductivityMetrics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Productivity data
|
||||
Period string `json:"period"` // daily, weekly, monthly, yearly
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
TotalHours float64 `json:"total_hours"`
|
||||
BillableHours float64 `json:"billable_hours"`
|
||||
NonBillableHours float64 `json:"non_billable_hours"`
|
||||
TasksCompleted int `json:"tasks_completed"`
|
||||
AverageTaskTime float64 `json:"average_task_time"`
|
||||
PeakProductivityHour int `json:"peak_productivity_hour"`
|
||||
FocusScore float64 `json:"focus_score"`
|
||||
EfficiencyScore float64 `json:"efficiency_score"`
|
||||
}
|
||||
|
||||
// LearningAnalytics represents learning progress analytics
|
||||
type LearningAnalytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Learning data
|
||||
CourseID uint `json:"course_id" gorm:"not null;index"`
|
||||
Course Course `json:"course,omitempty" gorm:"foreignKey:CourseID"`
|
||||
StartDate time.Time `json:"start_date"`
|
||||
LastAccessed time.Time `json:"last_accessed"`
|
||||
TimeSpent float64 `json:"time_spent"` // in hours
|
||||
Progress float64 `json:"progress"` // percentage 0-100
|
||||
ModulesCompleted int `json:"modules_completed"`
|
||||
TotalModules int `json:"total_modules"`
|
||||
QuizScores []float64 `json:"quiz_scores" gorm:"serializer:json"`
|
||||
AverageScore float64 `json:"average_score"`
|
||||
StreakDays int `json:"streak_days"`
|
||||
SkillsAcquired []string `json:"skills_acquired" gorm:"serializer:json"`
|
||||
CourseCompleted bool `json:"course_completed" gorm:"default:false"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
}
|
||||
|
||||
// ContentAnalytics represents content consumption patterns
|
||||
type ContentAnalytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Content data
|
||||
ContentType string `json:"content_type"` // bookmark, note, file, task
|
||||
ContentID uint `json:"content_id"`
|
||||
FirstAccessed time.Time `json:"first_accessed"`
|
||||
LastAccessed time.Time `json:"last_accessed"`
|
||||
AccessCount int `json:"access_count"`
|
||||
TimeSpent float64 `json:"time_spent"` // in minutes
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:content_analytics_tags;"`
|
||||
Category string `json:"category"`
|
||||
Priority string `json:"priority"`
|
||||
UsefulnessScore float64 `json:"usefulness_score"` // user-rated 1-5
|
||||
}
|
||||
|
||||
// GitHubAnalytics represents GitHub contribution analytics
|
||||
type GitHubAnalytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// GitHub data
|
||||
Date time.Time `json:"date" gorm:"not null;index"`
|
||||
Commits int `json:"commits"`
|
||||
PullRequests int `json:"pull_requests"`
|
||||
IssuesOpened int `json:"issues_opened"`
|
||||
IssuesClosed int `json:"issues_closed"`
|
||||
Reviews int `json:"reviews"`
|
||||
Contributions int `json:"contributions"`
|
||||
Languages map[string]int `json:"languages" gorm:"serializer:json"`
|
||||
Repositories []string `json:"repositories" gorm:"serializer:json"`
|
||||
ActivityScore float64 `json:"activity_score"`
|
||||
}
|
||||
|
||||
// HabitAnalytics represents habit formation insights
|
||||
type HabitAnalytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Habit data
|
||||
HabitName string `json:"habit_name"`
|
||||
StartDate time.Time `json:"start_date"`
|
||||
LastCompleted time.Time `json:"last_completed"`
|
||||
Streak int `json:"streak"`
|
||||
BestStreak int `json:"best_streak"`
|
||||
TotalDays int `json:"total_days"`
|
||||
CompletionRate float64 `json:"completion_rate"`
|
||||
Frequency string `json:"frequency"` // daily, weekly, monthly
|
||||
Category string `json:"category"` // productivity, learning, health, etc.
|
||||
GoalTarget int `json:"goal_target"`
|
||||
GoalAchieved bool `json:"goal_achieved"`
|
||||
}
|
||||
|
||||
// Goal represents user goals for tracking
|
||||
type Goal struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Goal data
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"` // learning, productivity, health, career
|
||||
TargetValue float64 `json:"target_value"`
|
||||
CurrentValue float64 `json:"current_value"`
|
||||
Unit string `json:"unit"`
|
||||
Deadline time.Time `json:"deadline"`
|
||||
Status string `json:"status"` // active, completed, paused, cancelled
|
||||
Priority string `json:"priority"` // low, medium, high, urgent
|
||||
Progress float64 `json:"progress"` // percentage 0-100
|
||||
IsCompleted bool `json:"is_completed" gorm:"default:false"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
|
||||
// Relationships
|
||||
Milestones []Milestone `json:"milestones,omitempty" gorm:"foreignKey:GoalID"`
|
||||
}
|
||||
|
||||
// Milestone represents goal milestones
|
||||
type Milestone struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
GoalID uint `json:"goal_id" gorm:"not null;index"`
|
||||
Goal Goal `json:"goal,omitempty" gorm:"foreignKey:GoalID"`
|
||||
|
||||
// Milestone data
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
CurrentValue float64 `json:"current_value"`
|
||||
Deadline time.Time `json:"deadline"`
|
||||
Status string `json:"status"` // pending, completed, overdue
|
||||
IsCompleted bool `json:"is_completed" gorm:"default:false"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
Order int `json:"order"`
|
||||
}
|
||||
|
||||
// AnalyticsReport represents a comprehensive analytics report
|
||||
type AnalyticsReport struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Report data
|
||||
ReportType string `json:"report_type"` // daily, weekly, monthly, yearly, custom
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
Data map[string]interface{} `json:"data" gorm:"serializer:json"`
|
||||
Insights []string `json:"insights" gorm:"serializer:json"`
|
||||
Recommendations []string `json:"recommendations" gorm:"serializer:json"`
|
||||
ShareableLink string `json:"shareable_link"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// BeforeCreate hooks for default values
|
||||
func (a *Analytics) BeforeCreate(tx *gorm.DB) error {
|
||||
if a.Date.IsZero() {
|
||||
a.Date = time.Now().Truncate(24 * time.Hour)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProductivityMetrics) BeforeCreate(tx *gorm.DB) error {
|
||||
if p.StartDate.IsZero() {
|
||||
p.StartDate = time.Now().Truncate(24 * time.Hour)
|
||||
}
|
||||
if p.EndDate.IsZero() {
|
||||
p.EndDate = p.StartDate.Add(24 * time.Hour)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LearningAnalytics) BeforeCreate(tx *gorm.DB) error {
|
||||
if l.StartDate.IsZero() {
|
||||
l.StartDate = time.Now()
|
||||
}
|
||||
if l.LastAccessed.IsZero() {
|
||||
l.LastAccessed = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ContentAnalytics) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.FirstAccessed.IsZero() {
|
||||
c.FirstAccessed = time.Now()
|
||||
}
|
||||
if c.LastAccessed.IsZero() {
|
||||
c.LastAccessed = time.Now()
|
||||
}
|
||||
if c.AccessCount == 0 {
|
||||
c.AccessCount = 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GitHubAnalytics) BeforeCreate(tx *gorm.DB) error {
|
||||
if g.Date.IsZero() {
|
||||
g.Date = time.Now().Truncate(24 * time.Hour)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HabitAnalytics) BeforeCreate(tx *gorm.DB) error {
|
||||
if h.StartDate.IsZero() {
|
||||
h.StartDate = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Goal) BeforeCreate(tx *gorm.DB) error {
|
||||
if g.Status == "" {
|
||||
g.Status = "active"
|
||||
}
|
||||
if g.Priority == "" {
|
||||
g.Priority = "medium"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Milestone) BeforeCreate(tx *gorm.DB) error {
|
||||
if m.Status == "" {
|
||||
m.Status = "pending"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AuditAction represents the type of action performed
|
||||
type AuditAction string
|
||||
|
||||
const (
|
||||
AuditActionCreate AuditAction = "create"
|
||||
AuditActionRead AuditAction = "read"
|
||||
AuditActionUpdate AuditAction = "update"
|
||||
AuditActionDelete AuditAction = "delete"
|
||||
AuditActionLogin AuditAction = "login"
|
||||
AuditActionLogout AuditAction = "logout"
|
||||
AuditActionLoginFail AuditAction = "login_failed"
|
||||
AuditActionExport AuditAction = "export"
|
||||
AuditActionImport AuditAction = "import"
|
||||
AuditActionEnable AuditAction = "enable"
|
||||
AuditActionDisable AuditAction = "disable"
|
||||
AuditActionUpload AuditAction = "upload"
|
||||
AuditActionDownload AuditAction = "download"
|
||||
AuditActionShare AuditAction = "share"
|
||||
AuditActionAccess AuditAction = "access"
|
||||
)
|
||||
|
||||
// AuditResource represents the resource type
|
||||
type AuditResource string
|
||||
|
||||
const (
|
||||
AuditResourceUser AuditResource = "user"
|
||||
AuditResourceNote AuditResource = "note"
|
||||
AuditResourceFile AuditResource = "file"
|
||||
AuditResourceBookmark AuditResource = "bookmark"
|
||||
AuditResourceTask AuditResource = "task"
|
||||
AuditResourceTimeEntry AuditResource = "time_entry"
|
||||
AuditResourceIntegration AuditResource = "integration"
|
||||
AuditResourceTeam AuditResource = "team"
|
||||
AuditResourceGoal AuditResource = "goal"
|
||||
AuditResourceHabit AuditResource = "habit"
|
||||
AuditResourceCalendar AuditResource = "calendar"
|
||||
AuditResourceSearch AuditResource = "search"
|
||||
AuditResourceAI AuditResource = "ai"
|
||||
AuditResourceAnalytics AuditResource = "analytics"
|
||||
AuditResourceSecurity AuditResource = "security"
|
||||
AuditResourceSystem AuditResource = "system"
|
||||
)
|
||||
|
||||
// AuditLog represents an audit log entry
|
||||
type AuditLog struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// User information
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
UserEmail string `json:"user_email" gorm:"not null"`
|
||||
UserIP string `json:"user_ip"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
|
||||
// Action information
|
||||
Action AuditAction `json:"action" gorm:"not null;index"`
|
||||
Resource AuditResource `json:"resource" gorm:"not null;index"`
|
||||
ResourceID *uint `json:"resource_id,omitempty" gorm:"index"`
|
||||
|
||||
// Details
|
||||
Description string `json:"description"`
|
||||
Details map[string]interface{} `json:"details" gorm:"serializer:json"`
|
||||
OldValues map[string]interface{} `json:"old_values" gorm:"serializer:json"`
|
||||
NewValues map[string]interface{} `json:"new_values" gorm:"serializer:json"`
|
||||
|
||||
// Security context
|
||||
SessionID string `json:"session_id"`
|
||||
Success bool `json:"success" gorm:"default:true"`
|
||||
FailureReason string `json:"failure_reason"`
|
||||
|
||||
// Geographic and device info
|
||||
Country string `json:"country"`
|
||||
City string `json:"city"`
|
||||
Device string `json:"device"`
|
||||
Platform string `json:"platform"`
|
||||
Browser string `json:"browser"`
|
||||
|
||||
// Risk assessment
|
||||
RiskLevel string `json:"risk_level" gorm:"default:low"` // low, medium, high, critical
|
||||
Suspicious bool `json:"suspicious" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for AuditLog
|
||||
func (AuditLog) TableName() string {
|
||||
return "audit_logs"
|
||||
}
|
||||
|
||||
// BeforeCreate hook to set default values
|
||||
func (a *AuditLog) BeforeCreate(tx *gorm.DB) error {
|
||||
if a.RiskLevel == "" {
|
||||
a.RiskLevel = "low"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -19,21 +19,21 @@ type Bookmark struct {
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
URL string `json:"url" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
|
||||
|
||||
// Organization
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:bookmark_tags;"`
|
||||
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:bookmark_tags;"`
|
||||
|
||||
// Metadata
|
||||
Favicon string `json:"favicon"`
|
||||
Screenshot string `json:"screenshot"`
|
||||
IsRead bool `json:"is_read" gorm:"default:false"`
|
||||
IsFavorite bool `json:"is_favorite" gorm:"default:false"`
|
||||
|
||||
|
||||
// Content extraction
|
||||
Content string `json:"content"`
|
||||
Author string `json:"author"`
|
||||
Content string `json:"content"`
|
||||
Author string `json:"author"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
|
||||
|
||||
// Reading tracking
|
||||
ReadAt *time.Time `json:"read_at"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CalendarEvent represents an event in the calendar
|
||||
type CalendarEvent struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
|
||||
// Timing
|
||||
StartTime time.Time `json:"start_time" gorm:"not null"`
|
||||
EndTime time.Time `json:"end_time" gorm:"not null"`
|
||||
|
||||
// Classification
|
||||
Type string `json:"type" gorm:"default:'reminder'"` // task, meeting, deadline, reminder, habit
|
||||
Priority string `json:"priority" gorm:"default:'medium'"` // low, medium, high, urgent
|
||||
|
||||
// Location and attendees
|
||||
Location string `json:"location"`
|
||||
Attendees string `json:"attendees"` // JSON string of attendee emails/names
|
||||
|
||||
// Recurrence
|
||||
Recurring bool `json:"recurring" gorm:"default:false"`
|
||||
Rrule string `json:"rrule"` // RRULE format for recurrence
|
||||
|
||||
// Source integration
|
||||
Source string `json:"source" gorm:"default:'trackeep'"` // trackeep, google, outlook, manual
|
||||
|
||||
// Associations
|
||||
TaskID *uint `json:"task_id,omitempty"`
|
||||
Task *Task `json:"task,omitempty" gorm:"foreignKey:TaskID"`
|
||||
BookmarkID *uint `json:"bookmark_id,omitempty"`
|
||||
Bookmark *Bookmark `json:"bookmark,omitempty" gorm:"foreignKey:BookmarkID"`
|
||||
NoteID *uint `json:"note_id,omitempty"`
|
||||
Note *Note `json:"note,omitempty" gorm:"foreignKey:NoteID"`
|
||||
|
||||
// Status
|
||||
IsCompleted bool `json:"is_completed" gorm:"default:false"`
|
||||
IsAllDay bool `json:"is_all_day" gorm:"default:false"`
|
||||
|
||||
// Notifications
|
||||
ReminderMinutes int `json:"reminder_minutes"` // Minutes before event to remind
|
||||
}
|
||||
|
||||
// RecurrenceRule represents recurrence patterns for events
|
||||
type RecurrenceRule struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Frequency string `json:"frequency" gorm:"not null"` // daily, weekly, monthly, yearly
|
||||
Interval int `json:"interval" gorm:"default:1"` // Every N days/weeks/months/years
|
||||
|
||||
// End conditions
|
||||
EndDate *time.Time `json:"end_date"`
|
||||
Count *int `json:"count"` // Number of occurrences
|
||||
IsForever bool `json:"is_forever" gorm:"default:false"`
|
||||
|
||||
// Weekly specifics
|
||||
DaysOfWeek string `json:"days_of_week"` // JSON array: [0,1,2,3,4,5,6] where 0=Sunday
|
||||
|
||||
// Monthly specifics
|
||||
DayOfMonth *int `json:"day_of_month"` // 1-31
|
||||
WeekOfMonth *int `json:"week_of_month"` // 1-5 (first to fifth week)
|
||||
DayOfWeek *int `json:"day_of_week"` // 0-6 (Sunday to Saturday)
|
||||
|
||||
// Event association
|
||||
EventID uint `json:"event_id" gorm:"not null"`
|
||||
Event CalendarEvent `json:"event,omitempty" gorm:"foreignKey:EventID"`
|
||||
}
|
||||
|
||||
// CalendarSettings represents user calendar preferences
|
||||
type CalendarSettings struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;uniqueIndex"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Display preferences
|
||||
DefaultView string `json:"default_view" gorm:"default:'week'"` // month, week, day
|
||||
WeekStartsOn int `json:"week_starts_on" gorm:"default:0"` // 0=Sunday, 1=Monday
|
||||
Timezone string `json:"timezone" gorm:"default:'UTC'"`
|
||||
TimeFormat24Hour bool `json:"time_format_24_hour" gorm:"default:true"`
|
||||
|
||||
// Integration settings
|
||||
GoogleCalendarEnabled bool `json:"google_calendar_enabled" gorm:"default:false"`
|
||||
GoogleCalendarToken string `json:"google_calendar_token"`
|
||||
OutlookEnabled bool `json:"outlook_enabled" gorm:"default:false"`
|
||||
OutlookToken string `json:"outlook_token"`
|
||||
|
||||
// Notification settings
|
||||
DefaultReminderMinutes int `json:"default_reminder_minutes" gorm:"default:15"`
|
||||
EmailRemindersEnabled bool `json:"email_reminders_enabled" gorm:"default:true"`
|
||||
PushRemindersEnabled bool `json:"push_reminders_enabled" gorm:"default:true"`
|
||||
}
|
||||
|
||||
// GetDuration returns the duration of the event
|
||||
func (e *CalendarEvent) GetDuration() time.Duration {
|
||||
return e.EndTime.Sub(e.StartTime)
|
||||
}
|
||||
|
||||
// IsOverdue checks if the event is overdue
|
||||
func (e *CalendarEvent) IsOverdue() bool {
|
||||
return !e.IsCompleted && time.Now().After(e.EndTime)
|
||||
}
|
||||
|
||||
// IsToday checks if the event occurs today
|
||||
func (e *CalendarEvent) IsToday() bool {
|
||||
now := time.Now()
|
||||
return e.StartTime.Year() == now.Year() &&
|
||||
e.StartTime.Month() == now.Month() &&
|
||||
e.StartTime.Day() == now.Day()
|
||||
}
|
||||
|
||||
// IsUpcoming checks if the event is in the next 7 days
|
||||
func (e *CalendarEvent) IsUpcoming() bool {
|
||||
now := time.Now()
|
||||
weekLater := now.AddDate(0, 0, 7)
|
||||
return e.StartTime.After(now) && e.StartTime.Before(weekLater)
|
||||
}
|
||||
|
||||
// GetPriorityColor returns a color based on priority
|
||||
func (e *CalendarEvent) GetPriorityColor() string {
|
||||
switch e.Priority {
|
||||
case "urgent":
|
||||
return "#ef4444" // red
|
||||
case "high":
|
||||
return "#f97316" // orange
|
||||
case "medium":
|
||||
return "#eab308" // yellow
|
||||
case "low":
|
||||
return "#22c55e" // green
|
||||
default:
|
||||
return "#6b7280" // gray
|
||||
}
|
||||
}
|
||||
|
||||
// GetTypeColor returns a color based on event type
|
||||
func (e *CalendarEvent) GetTypeColor() string {
|
||||
switch e.Type {
|
||||
case "task":
|
||||
return "#3b82f6" // blue
|
||||
case "meeting":
|
||||
return "#8b5cf6" // purple
|
||||
case "deadline":
|
||||
return "#ef4444" // red
|
||||
case "reminder":
|
||||
return "#06b6d4" // cyan
|
||||
case "habit":
|
||||
return "#10b981" // emerald
|
||||
default:
|
||||
return "#6b7280" // gray
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ChatMessage represents a chat message in the AI conversation
|
||||
type ChatMessage struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Content string `json:"content" gorm:"not null"`
|
||||
Role string `json:"role" gorm:"not null"` // "user" or "assistant"
|
||||
|
||||
// Session tracking
|
||||
SessionID string `json:"session_id" gorm:"not null;index"`
|
||||
|
||||
// Metadata
|
||||
TokenCount int `json:"token_count"`
|
||||
ModelUsed string `json:"model_used"`
|
||||
ProcessingMs int64 `json:"processing_ms"`
|
||||
ContextItems []string `json:"context_items" gorm:"serializer:json"` // IDs of referenced items
|
||||
}
|
||||
|
||||
// ChatSession represents a chat session
|
||||
type ChatSession struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Title string `json:"title"`
|
||||
|
||||
// Session metadata
|
||||
MessageCount int `json:"message_count" gorm:"default:0"`
|
||||
LastMessageAt *time.Time `json:"last_message_at"`
|
||||
|
||||
// Context configuration
|
||||
IncludeBookmarks bool `json:"include_bookmarks" gorm:"default:true"`
|
||||
IncludeTasks bool `json:"include_tasks" gorm:"default:true"`
|
||||
IncludeFiles bool `json:"include_files" gorm:"default:true"`
|
||||
IncludeNotes bool `json:"include_notes" gorm:"default:true"`
|
||||
|
||||
// Relationships
|
||||
Messages []ChatMessage `json:"messages,omitempty" gorm:"foreignKey:SessionID"`
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Challenge represents a community challenge
|
||||
type Challenge struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Creator information
|
||||
CreatorID uint `json:"creator_id" gorm:"not null;index"`
|
||||
Creator User `json:"creator,omitempty" gorm:"foreignKey:CreatorID"`
|
||||
|
||||
// Basic information
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category" gorm:"not null"` // learning, productivity, fitness, creativity, technical
|
||||
|
||||
// Challenge details
|
||||
Difficulty string `json:"difficulty" gorm:"not null"` // beginner, intermediate, advanced, expert
|
||||
Duration int `json:"duration" gorm:"not null"` // Duration in days
|
||||
Requirements string `json:"requirements" gorm:"type:text"`
|
||||
Rewards string `json:"rewards" gorm:"type:text"`
|
||||
Rules string `json:"rules" gorm:"type:text"`
|
||||
|
||||
// Timeline
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
|
||||
// Participation settings
|
||||
MaxParticipants *int `json:"max_participants,omitempty"` // nil for unlimited
|
||||
IsTeamChallenge bool `json:"is_team_challenge" gorm:"default:false"`
|
||||
TeamSize int `json:"team_size" gorm:"default:1"`
|
||||
|
||||
// Status and visibility
|
||||
Status string `json:"status" gorm:"default:draft"` // draft, active, completed, cancelled
|
||||
IsPublic bool `json:"is_public" gorm:"default:true"`
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false"`
|
||||
|
||||
// Tags and metadata
|
||||
Tags []ChallengeTag `json:"tags,omitempty" gorm:"many2many:challenge_tags;"`
|
||||
Image string `json:"image"` // Challenge banner/image
|
||||
Badge string `json:"badge"` // Completion badge
|
||||
|
||||
// Analytics
|
||||
ParticipantCount int `json:"participant_count" gorm:"default:0"`
|
||||
CompletionCount int `json:"completion_count" gorm:"default:0"`
|
||||
CompletionRate float64 `json:"completion_rate" gorm:"default:0"`
|
||||
LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
|
||||
|
||||
// Relationships
|
||||
Participants []ChallengeParticipant `json:"participants,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
Milestones []ChallengeMilestone `json:"milestones,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
Resources []ChallengeResource `json:"resources,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
}
|
||||
|
||||
// ChallengeParticipant represents a user's participation in a challenge
|
||||
type ChallengeParticipant struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Participation information
|
||||
ChallengeID uint `json:"challenge_id" gorm:"not null;index"`
|
||||
Challenge Challenge `json:"challenge,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Team information (for team challenges)
|
||||
TeamID *uint `json:"team_id,omitempty"`
|
||||
Team *ChallengeTeam `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Role string `json:"role" gorm:"default:participant"` // participant, team_leader
|
||||
|
||||
// Progress tracking
|
||||
Status string `json:"status" gorm:"default:joined"` // joined, in_progress, completed, dropped_out
|
||||
Progress float64 `json:"progress" gorm:"default:0"` // Progress percentage (0-100)
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
DroppedOutAt *time.Time `json:"dropped_out_at,omitempty"`
|
||||
|
||||
// Performance metrics
|
||||
Score int `json:"score" gorm:"default:0"`
|
||||
Rank int `json:"rank"`
|
||||
BadgeEarned bool `json:"badge_earned" gorm:"default:false"`
|
||||
LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
|
||||
|
||||
// Notes and reflection
|
||||
Notes string `json:"notes" gorm:"type:text"`
|
||||
Reflection string `json:"reflection" gorm:"type:text"`
|
||||
}
|
||||
|
||||
// ChallengeTeam represents a team in a team challenge
|
||||
type ChallengeTeam struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Team information
|
||||
ChallengeID uint `json:"challenge_id" gorm:"not null;index"`
|
||||
Challenge Challenge `json:"challenge,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Avatar string `json:"avatar"`
|
||||
|
||||
// Team settings
|
||||
IsPrivate bool `json:"is_private" gorm:"default:false"`
|
||||
MaxMembers int `json:"max_members" gorm:"default:5"`
|
||||
|
||||
// Team progress
|
||||
Status string `json:"status" gorm:"default:active"` // active, completed, disbanded
|
||||
Progress float64 `json:"progress" gorm:"default:0"`
|
||||
Score int `json:"score" gorm:"default:0"`
|
||||
Rank int `json:"rank"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
|
||||
|
||||
// Relationships
|
||||
LeaderID uint `json:"leader_id" gorm:"not null"`
|
||||
Leader User `json:"leader,omitempty" gorm:"foreignKey:LeaderID"`
|
||||
Members []ChallengeParticipant `json:"members,omitempty" gorm:"foreignKey:TeamID"`
|
||||
}
|
||||
|
||||
// ChallengeMilestone represents a milestone in a challenge
|
||||
type ChallengeMilestone struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Milestone information
|
||||
ChallengeID uint `json:"challenge_id" gorm:"not null;index"`
|
||||
Challenge Challenge `json:"challenge,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
|
||||
// Milestone details
|
||||
Order int `json:"order" gorm:"not null"` // Order in the challenge
|
||||
TargetValue float64 `json:"target_value" gorm:"not null"`
|
||||
Unit string `json:"unit"` // days, points, hours, etc.
|
||||
Deadline time.Time `json:"deadline"`
|
||||
PointsAwarded int `json:"points_awarded" gorm:"default:0"`
|
||||
|
||||
// Status
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
|
||||
// Relationships
|
||||
Completions []ChallengeMilestoneCompletion `json:"completions,omitempty" gorm:"foreignKey:MilestoneID"`
|
||||
}
|
||||
|
||||
// ChallengeMilestoneCompletion represents a user's completion of a milestone
|
||||
type ChallengeMilestoneCompletion struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Completion information
|
||||
MilestoneID uint `json:"milestone_id" gorm:"not null;index"`
|
||||
Milestone ChallengeMilestone `json:"milestone,omitempty" gorm:"foreignKey:MilestoneID"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
TeamID *uint `json:"team_id,omitempty"`
|
||||
|
||||
// Completion details
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
Notes string `json:"notes" gorm:"type:text"`
|
||||
Evidence string `json:"evidence" gorm:"type:text"` // Proof of completion
|
||||
PointsEarned int `json:"points_earned"`
|
||||
}
|
||||
|
||||
// ChallengeResource represents a resource for a challenge
|
||||
type ChallengeResource struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Resource information
|
||||
ChallengeID uint `json:"challenge_id" gorm:"not null;index"`
|
||||
Challenge Challenge `json:"challenge,omitempty" gorm:"foreignKey:ChallengeID"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type" gorm:"not null"` // article, video, tool, template, guide
|
||||
|
||||
// Resource details
|
||||
Order int `json:"order" gorm:"default:0"`
|
||||
IsRequired bool `json:"is_required" gorm:"default:false"`
|
||||
Duration int `json:"duration"` // Estimated duration in minutes
|
||||
Tags string `json:"tags"` // Comma-separated tags
|
||||
}
|
||||
|
||||
// ChallengeTag represents tags for challenges
|
||||
type ChallengeTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Name string `json:"name" gorm:"uniqueIndex;not null"`
|
||||
Description string `json:"description"`
|
||||
Color string `json:"color" gorm:"default:#10b981"` // Tag color
|
||||
UsageCount int `json:"usage_count" gorm:"default:0"`
|
||||
}
|
||||
|
||||
// Mentorship represents a mentorship relationship
|
||||
type Mentorship struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Participants
|
||||
MentorID uint `json:"mentor_id" gorm:"not null;index"`
|
||||
Mentor User `json:"mentor,omitempty" gorm:"foreignKey:MentorID"`
|
||||
MenteeID uint `json:"mentee_id" gorm:"not null;index"`
|
||||
Mentee User `json:"mentee,omitempty" gorm:"foreignKey:MenteeID"`
|
||||
|
||||
// Mentorship details
|
||||
Category string `json:"category" gorm:"not null"` // career, technical, business, personal
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Goals string `json:"goals" gorm:"type:text"`
|
||||
|
||||
// Timeline
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate *time.Time `json:"end_date,omitempty"`
|
||||
|
||||
// Status and settings
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, active, paused, completed, terminated
|
||||
IsPaid bool `json:"is_paid" gorm:"default:false"`
|
||||
Rate float64 `json:"rate" gorm:"default:0"` // Hourly rate or monthly rate
|
||||
Currency string `json:"currency" gorm:"default:USD"`
|
||||
SessionLimit int `json:"session_limit" gorm:"default:0"` // 0 for unlimited
|
||||
|
||||
// Matching preferences
|
||||
MentorPreferences string `json:"mentor_preferences" gorm:"type:text"`
|
||||
MenteePreferences string `json:"mentee_preferences" gorm:"type:text"`
|
||||
|
||||
// Analytics
|
||||
SessionCount int `json:"session_count" gorm:"default:0"`
|
||||
TotalHours float64 `json:"total_hours" gorm:"default:0"`
|
||||
LastSessionAt *time.Time `json:"last_session_at,omitempty"`
|
||||
SatisfactionScore float64 `json:"satisfaction_score" gorm:"default:0"` // 1-5 rating
|
||||
|
||||
// Relationships
|
||||
Sessions []MentorshipSession `json:"sessions,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
Reviews []MentorshipReview `json:"reviews,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
Milestones []MentorshipMilestone `json:"milestones,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
}
|
||||
|
||||
// MentorshipSession represents a mentoring session
|
||||
type MentorshipSession struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Session information
|
||||
MentorshipID uint `json:"mentorship_id" gorm:"not null;index"`
|
||||
Mentorship Mentorship `json:"mentorship,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
ScheduledFor time.Time `json:"scheduled_for"`
|
||||
Duration int `json:"duration"` // Duration in minutes
|
||||
Status string `json:"status" gorm:"default:scheduled"` // scheduled, completed, cancelled, no_show
|
||||
|
||||
// Session details
|
||||
Title string `json:"title"`
|
||||
Agenda string `json:"agenda" gorm:"type:text"`
|
||||
Notes string `json:"notes" gorm:"type:text"`
|
||||
RecordingURL string `json:"recording_url"`
|
||||
Materials string `json:"materials" gorm:"type:text"`
|
||||
|
||||
// Completion details
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
MentorNotes string `json:"mentor_notes" gorm:"type:text"`
|
||||
MenteeNotes string `json:"mentee_notes" gorm:"type:text"`
|
||||
ActionItems string `json:"action_items" gorm:"type:text"`
|
||||
NextSteps string `json:"next_steps" gorm:"type:text"`
|
||||
|
||||
// Feedback
|
||||
MentorRating *int `json:"mentor_rating,omitempty"` // 1-5 rating from mentee
|
||||
MenteeRating *int `json:"mentee_rating,omitempty"` // 1-5 rating from mentor
|
||||
MentorFeedback string `json:"mentor_feedback" gorm:"type:text"`
|
||||
MenteeFeedback string `json:"mentee_feedback" gorm:"type:text"`
|
||||
}
|
||||
|
||||
// MentorshipReview represents a review for a mentorship
|
||||
type MentorshipReview struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Review information
|
||||
MentorshipID uint `json:"mentorship_id" gorm:"not null;index"`
|
||||
Mentorship Mentorship `json:"mentorship,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
ReviewerID uint `json:"reviewer_id" gorm:"not null;index"`
|
||||
Reviewer User `json:"reviewer,omitempty" gorm:"foreignKey:ReviewerID"`
|
||||
TargetID uint `json:"target_id" gorm:"not null;index"` // The person being reviewed
|
||||
Target User `json:"target,omitempty" gorm:"foreignKey:TargetID"`
|
||||
|
||||
// Review content
|
||||
Rating int `json:"rating" gorm:"not null;check:rating >= 1 AND rating <= 5"` // 1-5 stars
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
IsVerified bool `json:"is_verified" gorm:"default:false"` // Verified mentorship
|
||||
|
||||
// Review metadata
|
||||
HelpfulCount int `json:"helpful_count" gorm:"default:0"`
|
||||
ReviewType string `json:"review_type" gorm:"not null"` // mentor_review, mentee_review
|
||||
}
|
||||
|
||||
// MentorshipMilestone represents a milestone in a mentorship
|
||||
type MentorshipMilestone struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Milestone information
|
||||
MentorshipID uint `json:"mentorship_id" gorm:"not null;index"`
|
||||
Mentorship Mentorship `json:"mentorship,omitempty" gorm:"foreignKey:MentorshipID"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
|
||||
// Milestone details
|
||||
TargetDate time.Time `json:"target_date"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, completed, overdue
|
||||
Priority string `json:"priority" gorm:"default:medium"` // low, medium, high
|
||||
|
||||
// Progress tracking
|
||||
Progress float64 `json:"progress" gorm:"default:0"` // 0-100
|
||||
Evidence string `json:"evidence" gorm:"type:text"`
|
||||
Notes string `json:"notes" gorm:"type:text"`
|
||||
}
|
||||
|
||||
// MentorshipRequest represents a mentorship request
|
||||
type MentorshipRequest struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Request information
|
||||
FromUserID uint `json:"from_user_id" gorm:"not null;index"`
|
||||
FromUser User `json:"from_user,omitempty" gorm:"foreignKey:FromUserID"`
|
||||
ToUserID uint `json:"to_user_id" gorm:"not null;index"`
|
||||
ToUser User `json:"to_user,omitempty" gorm:"foreignKey:ToUserID"`
|
||||
|
||||
// Request details
|
||||
Role string `json:"role" gorm:"not null"` // mentor, mentee
|
||||
Category string `json:"category" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Goals string `json:"goals" gorm:"type:text"`
|
||||
Availability string `json:"availability" gorm:"type:text"`
|
||||
Duration int `json:"duration"` // Desired duration in months
|
||||
IsPaid bool `json:"is_paid"`
|
||||
Rate float64 `json:"rate"`
|
||||
Currency string `json:"currency" gorm:"default:USD"`
|
||||
|
||||
// Status
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, accepted, rejected, withdrawn
|
||||
RespondedAt *time.Time `json:"responded_at,omitempty"`
|
||||
Response string `json:"response" gorm:"type:text"`
|
||||
|
||||
// Matching score (calculated by matching algorithm)
|
||||
MatchScore float64 `json:"match_score" gorm:"default:0"`
|
||||
MatchReasons string `json:"match_reasons" gorm:"type:text"`
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (c *Challenge) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.Status == "" {
|
||||
c.Status = "draft"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cp *ChallengeParticipant) BeforeCreate(tx *gorm.DB) error {
|
||||
if cp.Status == "" {
|
||||
cp.Status = "joined"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ct *ChallengeTeam) BeforeCreate(tx *gorm.DB) error {
|
||||
if ct.Status == "" {
|
||||
ct.Status = "active"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mentorship) BeforeCreate(tx *gorm.DB) error {
|
||||
if m.Status == "" {
|
||||
m.Status = "pending"
|
||||
}
|
||||
if m.Currency == "" {
|
||||
m.Currency = "USD"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *MentorshipSession) BeforeCreate(tx *gorm.DB) error {
|
||||
if ms.Status == "" {
|
||||
ms.Status = "scheduled"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mr *MentorshipReview) BeforeCreate(tx *gorm.DB) error {
|
||||
if mr.ReviewType == "" {
|
||||
mr.ReviewType = "mentor_review"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mm *MentorshipMilestone) BeforeCreate(tx *gorm.DB) error {
|
||||
if mm.Status == "" {
|
||||
mm.Status = "pending"
|
||||
}
|
||||
if mm.Priority == "" {
|
||||
mm.Priority = "medium"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mr *MentorshipRequest) BeforeCreate(tx *gorm.DB) error {
|
||||
if mr.Status == "" {
|
||||
mr.Status = "pending"
|
||||
}
|
||||
if mr.Currency == "" {
|
||||
mr.Currency = "USD"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Course represents a Zero to Mastery course
|
||||
type Course struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Basic course information
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Slug string `json:"slug" gorm:"uniqueIndex;not null"` // URL-friendly course identifier
|
||||
URL string `json:"url" gorm:"not null"` // ZTM course URL
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Instructor string `json:"instructor"`
|
||||
|
||||
// Course metadata
|
||||
Duration string `json:"duration"` // e.g., "32 Hours"
|
||||
LessonsCount int `json:"lessons_count"` // number of lessons
|
||||
ModuleCount int `json:"module_count"` // number of modules
|
||||
Level string `json:"level"` // beginner, intermediate, advanced
|
||||
Category string `json:"category"` // programming, design, cybersecurity, etc.
|
||||
Price float64 `json:"price"` // course price
|
||||
Rating float64 `json:"rating"` // average rating
|
||||
StudentsCount int `json:"students_count"` // number of enrolled students
|
||||
|
||||
// Course content
|
||||
Prerequisites []string `json:"prerequisites" gorm:"serializer:json"`
|
||||
WhatYouLearn []string `json:"what_you_learn" gorm:"serializer:json"`
|
||||
Topics []string `json:"topics" gorm:"serializer:json"`
|
||||
ToolsAndTech []string `json:"tools_and_tech" gorm:"serializer:json"`
|
||||
|
||||
// ZTM specific data
|
||||
ZTMCourseID string `json:"ztm_course_id"` // internal ZTM course ID
|
||||
ZTMCategory string `json:"ztm_category"` // ZTM category classification
|
||||
IsZTMCourse bool `json:"is_ztm_course" gorm:"default:true"`
|
||||
LastUpdatedZTM *time.Time `json:"last_updated_ztm"` // last sync with ZTM
|
||||
|
||||
// Status
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// LearningPathCourse represents the relationship between learning paths and courses
|
||||
type LearningPathCourse struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
LearningPathID uint `json:"learning_path_id" gorm:"not null;index"`
|
||||
LearningPath LearningPath `json:"learning_path,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
|
||||
CourseID uint `json:"course_id" gorm:"not null;index"`
|
||||
Course Course `json:"course,omitempty" gorm:"foreignKey:CourseID"`
|
||||
|
||||
// Relationship metadata
|
||||
Order int `json:"order" gorm:"not null"` // order in the learning path
|
||||
IsRequired bool `json:"is_required" gorm:"default:true"` // whether this course is required
|
||||
Notes string `json:"notes" gorm:"type:text"` // additional notes about why this course is included
|
||||
EstimatedWeeks int `json:"estimated_weeks"` // estimated weeks to complete this course
|
||||
}
|
||||
+15
-11
@@ -28,24 +28,28 @@ type File struct {
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
OriginalName string `json:"original_name" gorm:"not null"`
|
||||
FileName string `json:"file_name" gorm:"not null;uniqueIndex"`
|
||||
FilePath string `json:"file_path" gorm:"not null"`
|
||||
FileSize int64 `json:"file_size" gorm:"not null"`
|
||||
MimeType string `json:"mime_type" gorm:"not null"`
|
||||
FileType FileType `json:"file_type" gorm:"not null"`
|
||||
|
||||
OriginalName string `json:"original_name" gorm:"not null"`
|
||||
FileName string `json:"file_name" gorm:"not null;uniqueIndex"`
|
||||
FilePath string `json:"file_path" gorm:"not null"`
|
||||
FileSize int64 `json:"file_size" gorm:"not null"`
|
||||
MimeType string `json:"mime_type" gorm:"not null"`
|
||||
FileType FileType `json:"file_type" gorm:"not null"`
|
||||
|
||||
// Encryption
|
||||
IsEncrypted bool `json:"is_encrypted" gorm:"default:false"`
|
||||
EncryptionKey string `json:"-" gorm:"column:encryption_key"` // User-specific encryption key (optional)
|
||||
|
||||
// Organization
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:file_tags;"`
|
||||
|
||||
|
||||
// Metadata
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
|
||||
// Preview/Thumbnail
|
||||
ThumbnailPath string `json:"thumbnail_path"`
|
||||
PreviewPath string `json:"preview_path"`
|
||||
|
||||
|
||||
// Content extraction (for documents)
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// FileAnalysis represents analysis results for a file
|
||||
type FileAnalysis struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// File information
|
||||
FileID uint `json:"file_id" gorm:"not null;uniqueIndex:idx_file_analysis_type"`
|
||||
File File `json:"file,omitempty" gorm:"foreignKey:FileID"`
|
||||
AnalysisType string `json:"analysis_type" gorm:"not null;uniqueIndex:idx_file_analysis_type"` // computer_vision, nlp, metadata
|
||||
|
||||
// Analysis results
|
||||
Results string `json:"results" gorm:"type:text"` // JSON-encoded analysis results
|
||||
Confidence float64 `json:"confidence" gorm:"default:0.0"` // 0.0 to 1.0
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, processing, completed, failed
|
||||
|
||||
// Processing metadata
|
||||
ProcessedAt *time.Time `json:"processed_at"`
|
||||
ProcessingTime int `json:"processing_time"` // in milliseconds
|
||||
ModelVersion string `json:"model_version"` // AI model version used
|
||||
Error string `json:"error"` // Error message if failed
|
||||
|
||||
// Additional metadata
|
||||
Tags string `json:"tags" gorm:"serializer:json"`
|
||||
Metadata string `json:"metadata" gorm:"serializer:json"`
|
||||
ExtractedData string `json:"extracted_data" gorm:"type:text"` // Extracted text, objects, etc.
|
||||
}
|
||||
|
||||
// TableName returns the table name for FileAnalysis
|
||||
func (FileAnalysis) TableName() string {
|
||||
return "file_analyses"
|
||||
}
|
||||
|
||||
// BeforeCreate hook to set default values
|
||||
func (fa *FileAnalysis) BeforeCreate(tx *gorm.DB) error {
|
||||
if fa.Status == "" {
|
||||
fa.Status = "pending"
|
||||
}
|
||||
if fa.Confidence == 0 {
|
||||
fa.Confidence = 0.0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Habit represents a habit that can be tracked
|
||||
type Habit struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Basic habit information
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category" gorm:"default:personal"` // health, productivity, learning, personal
|
||||
|
||||
// Habit tracking
|
||||
TargetFrequency int `json:"target_frequency"` // e.g., 7 times per week
|
||||
FrequencyUnit string `json:"frequency_unit"` // daily, weekly, monthly
|
||||
TargetValue float64 `json:"target_value"` // e.g., 30 minutes, 8 glasses
|
||||
Unit string `json:"unit"` // minutes, glasses, pages, etc.
|
||||
|
||||
// Schedule
|
||||
TimeOfDay string `json:"time_of_day"` // morning, afternoon, evening, night
|
||||
DaysOfWeek []string `json:"days_of_week" gorm:"serializer:json"` // ["monday", "tuesday", etc.]
|
||||
|
||||
// Status and settings
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
Streak int `json:"streak" gorm:"default:0"`
|
||||
LongestStreak int `json:"longest_streak" gorm:"default:0"`
|
||||
CompletionRate float64 `json:"completion_rate" gorm:"default:0"` // percentage
|
||||
|
||||
// Relationships
|
||||
GoalID *uint `json:"goal_id,omitempty"`
|
||||
Goal *Goal `json:"goal,omitempty" gorm:"foreignKey:GoalID"`
|
||||
HabitEntries []HabitEntry `json:"habit_entries,omitempty" gorm:"foreignKey:HabitID"`
|
||||
HabitTags []HabitTag `json:"habit_tags,omitempty" gorm:"foreignKey:HabitID"`
|
||||
}
|
||||
|
||||
// HabitEntry represents a single completion of a habit
|
||||
type HabitEntry struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
HabitID uint `json:"habit_id" gorm:"not null;index"`
|
||||
Habit Habit `json:"habit,omitempty" gorm:"foreignKey:HabitID"`
|
||||
|
||||
EntryDate time.Time `json:"entry_date" gorm:"not null;index"`
|
||||
Value float64 `json:"value"` // actual value completed
|
||||
TargetValue float64 `json:"target_value"` // target value for this entry
|
||||
Unit string `json:"unit"`
|
||||
Notes string `json:"notes" gorm:"type:text"`
|
||||
IsCompleted bool `json:"is_completed" gorm:"default:false"`
|
||||
Quality int `json:"quality" gorm:"default:3"` // 1-5 rating
|
||||
TimeSpent int `json:"time_spent"` // minutes spent
|
||||
Location string `json:"location"`
|
||||
Mood string `json:"mood"` // happy, neutral, stressed, etc.
|
||||
}
|
||||
|
||||
// GoalTag represents tags for goals
|
||||
type GoalTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
GoalID uint `json:"goal_id" gorm:"not null;index"`
|
||||
TagID uint `json:"tag_id" gorm:"not null;index"`
|
||||
Goal Goal `json:"goal,omitempty" gorm:"foreignKey:GoalID"`
|
||||
Tag Tag `json:"tag,omitempty" gorm:"foreignKey:TagID"`
|
||||
}
|
||||
|
||||
// HabitTag represents tags for habits
|
||||
type HabitTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
HabitID uint `json:"habit_id" gorm:"not null;index"`
|
||||
TagID uint `json:"tag_id" gorm:"not null;index"`
|
||||
Habit Habit `json:"habit,omitempty" gorm:"foreignKey:HabitID"`
|
||||
Tag Tag `json:"tag,omitempty" gorm:"foreignKey:TagID"`
|
||||
}
|
||||
|
||||
// GoalTemplate represents templates for creating goals
|
||||
type GoalTemplate struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category"`
|
||||
Unit string `json:"unit"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Duration int `json:"duration"` // suggested duration in days
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
UsageCount int `json:"usage_count" gorm:"default:0"`
|
||||
|
||||
// Template milestones
|
||||
Milestones []GoalTemplateMilestone `json:"milestones,omitempty" gorm:"foreignKey:GoalTemplateID"`
|
||||
}
|
||||
|
||||
// GoalTemplateMilestone represents milestones in goal templates
|
||||
type GoalTemplateMilestone struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
GoalTemplateID uint `json:"goal_template_id" gorm:"not null;index"`
|
||||
GoalTemplate GoalTemplate `json:"goal_template,omitempty" gorm:"foreignKey:GoalTemplateID"`
|
||||
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Unit string `json:"unit"`
|
||||
DayOffset int `json:"day_offset"` // days from start date
|
||||
SortOrder int `json:"sort_order" gorm:"default:0"`
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (h *Habit) BeforeCreate(tx *gorm.DB) error {
|
||||
if h.Category == "" {
|
||||
h.Category = "personal"
|
||||
}
|
||||
if h.FrequencyUnit == "" {
|
||||
h.FrequencyUnit = "daily"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (he *HabitEntry) BeforeCreate(tx *gorm.DB) error {
|
||||
// Set completion based on value vs target
|
||||
if he.Value >= he.TargetValue {
|
||||
he.IsCompleted = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hooks
|
||||
func (h *Habit) BeforeUpdate(tx *gorm.DB) error {
|
||||
// Update completion rate and streak
|
||||
h.updateStreakAndRate()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
// updateStreakAndRate calculates current streak and completion rate
|
||||
func (h *Habit) updateStreakAndRate() {
|
||||
// This would typically involve querying habit entries
|
||||
// For now, we'll keep the existing values
|
||||
// In a real implementation, you'd calculate based on recent entries
|
||||
}
|
||||
|
||||
// GetTodayEntry gets today's habit entry if it exists
|
||||
func (h *Habit) GetTodayEntry() *HabitEntry {
|
||||
// This would typically query the database
|
||||
// For now, return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWeeklyStreak gets the current weekly streak
|
||||
func (h *Habit) GetWeeklyStreak() int {
|
||||
// This would calculate based on habit entries
|
||||
// For now, return the stored streak
|
||||
return h.Streak
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// IntegrationType represents the type of integration
|
||||
type IntegrationType string
|
||||
|
||||
const (
|
||||
IntegrationSlack IntegrationType = "slack"
|
||||
IntegrationDiscord IntegrationType = "discord"
|
||||
IntegrationNotion IntegrationType = "notion"
|
||||
IntegrationPocket IntegrationType = "pocket"
|
||||
IntegrationTodoist IntegrationType = "todoist"
|
||||
IntegrationGoogle IntegrationType = "google"
|
||||
IntegrationGitHub IntegrationType = "github"
|
||||
IntegrationTwitter IntegrationType = "twitter"
|
||||
IntegrationReddit IntegrationType = "reddit"
|
||||
IntegrationObsidian IntegrationType = "obsidian"
|
||||
)
|
||||
|
||||
// IntegrationStatus represents the status of an integration
|
||||
type IntegrationStatus string
|
||||
|
||||
const (
|
||||
StatusActive IntegrationStatus = "active"
|
||||
StatusInactive IntegrationStatus = "inactive"
|
||||
StatusError IntegrationStatus = "error"
|
||||
StatusPending IntegrationStatus = "pending"
|
||||
)
|
||||
|
||||
// Integration represents a third-party service integration
|
||||
type Integration struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
||||
UserID string `json:"userId" gorm:"not null;index;type:uuid"`
|
||||
Type IntegrationType `json:"type" gorm:"not null;index"`
|
||||
Status IntegrationStatus `json:"status" gorm:"not null;default:'pending'"`
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
|
||||
// Configuration stored as JSON
|
||||
Config IntegrationConfig `json:"config" gorm:"type:jsonb"`
|
||||
|
||||
// Authentication tokens (encrypted)
|
||||
AccessToken string `json:"-" gorm:"type:text"` // Encrypted
|
||||
RefreshToken string `json:"-" gorm:"type:text"` // Encrypted
|
||||
|
||||
// Sync settings
|
||||
SyncEnabled bool `json:"syncEnabled" gorm:"default:true"`
|
||||
LastSyncAt *time.Time `json:"lastSyncAt"`
|
||||
SyncInterval int `json:"syncInterval"` // in minutes, 0 = manual
|
||||
|
||||
// Webhook settings
|
||||
WebhookURL string `json:"webhookUrl" gorm:"type:text"`
|
||||
WebhookSecret string `json:"-" gorm:"type:text"` // Encrypted
|
||||
|
||||
// Statistics
|
||||
SyncCount int `json:"syncCount" gorm:"default:0"`
|
||||
ErrorCount int `json:"errorCount" gorm:"default:0"`
|
||||
LastError string `json:"lastError" gorm:"type:text"`
|
||||
|
||||
// Timestamps
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Relationships
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
SyncLogs []SyncLog `json:"syncLogs,omitempty" gorm:"foreignKey:IntegrationID"`
|
||||
}
|
||||
|
||||
// IntegrationConfig holds configuration specific to each integration type
|
||||
type IntegrationConfig struct {
|
||||
// Slack configuration
|
||||
SlackConfig *SlackConfig `json:"slackConfig,omitempty"`
|
||||
|
||||
// Discord configuration
|
||||
DiscordConfig *DiscordConfig `json:"discordConfig,omitempty"`
|
||||
|
||||
// Notion configuration
|
||||
NotionConfig *NotionConfig `json:"notionConfig,omitempty"`
|
||||
|
||||
// Google configuration
|
||||
GoogleConfig *GoogleConfig `json:"googleConfig,omitempty"`
|
||||
|
||||
// Pocket configuration
|
||||
PocketConfig *PocketConfig `json:"pocketConfig,omitempty"`
|
||||
|
||||
// Todoist configuration
|
||||
TodoistConfig *TodoistConfig `json:"todoistConfig,omitempty"`
|
||||
|
||||
// GitHub configuration
|
||||
GitHubConfig *GitHubConfig `json:"gitHubConfig,omitempty"`
|
||||
|
||||
// Twitter configuration
|
||||
TwitterConfig *TwitterConfig `json:"twitterConfig,omitempty"`
|
||||
|
||||
// Reddit configuration
|
||||
RedditConfig *RedditConfig `json:"redditConfig,omitempty"`
|
||||
|
||||
// Obsidian configuration
|
||||
ObsidianConfig *ObsidianConfig `json:"obsidianConfig,omitempty"`
|
||||
}
|
||||
|
||||
// SlackConfig holds Slack-specific configuration
|
||||
type SlackConfig struct {
|
||||
TeamID string `json:"teamId"`
|
||||
TeamName string `json:"teamName"`
|
||||
ChannelID string `json:"channelId"`
|
||||
ChannelName string `json:"channelName"`
|
||||
BotUserID string `json:"botUserId"`
|
||||
Scopes []string `json:"scopes"`
|
||||
|
||||
// Notification settings
|
||||
NotifyTasks bool `json:"notifyTasks"`
|
||||
NotifyBookmarks bool `json:"notifyBookmarks"`
|
||||
NotifyNotes bool `json:"notifyNotes"`
|
||||
NotifyDeadlines bool `json:"notifyDeadlines"`
|
||||
NotifyTimeEntries bool `json:"notifyTimeEntries"`
|
||||
}
|
||||
|
||||
// DiscordConfig holds Discord-specific configuration
|
||||
type DiscordConfig struct {
|
||||
GuildID string `json:"guildId"`
|
||||
GuildName string `json:"guildName"`
|
||||
ChannelID string `json:"channelId"`
|
||||
ChannelName string `json:"channelName"`
|
||||
BotUserID string `json:"botUserId"`
|
||||
Scopes []string `json:"scopes"`
|
||||
|
||||
// Notification settings
|
||||
NotifyTasks bool `json:"notifyTasks"`
|
||||
NotifyBookmarks bool `json:"notifyBookmarks"`
|
||||
NotifyNotes bool `json:"notifyNotes"`
|
||||
NotifyDeadlines bool `json:"notifyDeadlines"`
|
||||
NotifyTimeEntries bool `json:"notifyTimeEntries"`
|
||||
}
|
||||
|
||||
// NotionConfig holds Notion-specific configuration
|
||||
type NotionConfig struct {
|
||||
DatabaseID string `json:"databaseId"`
|
||||
WorkspaceID string `json:"workspaceId"`
|
||||
WorkspaceName string `json:"workspaceName"`
|
||||
|
||||
// Sync settings
|
||||
SyncBookmarks bool `json:"syncBookmarks"`
|
||||
SyncTasks bool `json:"syncTasks"`
|
||||
SyncNotes bool `json:"syncNotes"`
|
||||
SyncFiles bool `json:"syncFiles"`
|
||||
|
||||
// Mapping settings
|
||||
BookmarkDatabaseID string `json:"bookmarkDatabaseId"`
|
||||
TaskDatabaseID string `json:"taskDatabaseId"`
|
||||
NoteDatabaseID string `json:"noteDatabaseId"`
|
||||
FileDatabaseID string `json:"fileDatabaseId"`
|
||||
}
|
||||
|
||||
// GoogleConfig holds Google-specific configuration
|
||||
type GoogleConfig struct {
|
||||
// Google Drive
|
||||
DriveEnabled bool `json:"driveEnabled"`
|
||||
DriveFolderID string `json:"driveFolderId"`
|
||||
|
||||
// Google Calendar
|
||||
CalendarEnabled bool `json:"calendarEnabled"`
|
||||
CalendarIDs []string `json:"calendarIds"`
|
||||
|
||||
// Google Docs
|
||||
DocsEnabled bool `json:"docsEnabled"`
|
||||
|
||||
// Sync settings
|
||||
SyncBookmarks bool `json:"syncBookmarks"`
|
||||
SyncTasks bool `json:"syncTasks"`
|
||||
SyncNotes bool `json:"syncNotes"`
|
||||
SyncFiles bool `json:"syncFiles"`
|
||||
SyncCalendar bool `json:"syncCalendar"`
|
||||
}
|
||||
|
||||
// PocketConfig holds Pocket-specific configuration
|
||||
type PocketConfig struct {
|
||||
Username string `json:"username"`
|
||||
|
||||
// Sync settings
|
||||
SyncBookmarks bool `json:"syncBookmarks"`
|
||||
SyncTags bool `json:"syncTags"`
|
||||
ImportAll bool `json:"importAll"`
|
||||
}
|
||||
|
||||
// TodoistConfig holds Todoist-specific configuration
|
||||
type TodoistConfig struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
|
||||
// Sync settings
|
||||
SyncTasks bool `json:"syncTasks"`
|
||||
SyncProjects bool `json:"syncProjects"`
|
||||
SyncLabels bool `json:"syncLabels"`
|
||||
ImportAll bool `json:"importAll"`
|
||||
}
|
||||
|
||||
// GitHubConfig holds GitHub-specific configuration
|
||||
type GitHubConfig struct {
|
||||
Username string `json:"username"`
|
||||
RepoSync bool `json:"repoSync"`
|
||||
IssueSync bool `json:"issueSync"`
|
||||
PRSync bool `json:"prSync"`
|
||||
StarSync bool `json:"starSync"`
|
||||
WatchSync bool `json:"watchSync"`
|
||||
}
|
||||
|
||||
// TwitterConfig holds Twitter-specific configuration
|
||||
type TwitterConfig struct {
|
||||
Username string `json:"username"`
|
||||
SyncTweets bool `json:"syncTweets"`
|
||||
SyncLikes bool `json:"syncLikes"`
|
||||
SyncBookmarks bool `json:"syncBookmarks"`
|
||||
}
|
||||
|
||||
// RedditConfig holds Reddit-specific configuration
|
||||
type RedditConfig struct {
|
||||
Username string `json:"username"`
|
||||
SyncPosts bool `json:"syncPosts"`
|
||||
SyncComments bool `json:"syncComments"`
|
||||
SyncSaved bool `json:"syncSaved"`
|
||||
SyncUpvoted bool `json:"syncUpvoted"`
|
||||
}
|
||||
|
||||
// ObsidianConfig holds Obsidian-specific configuration
|
||||
type ObsidianConfig struct {
|
||||
VaultPath string `json:"vaultPath"`
|
||||
VaultName string `json:"vaultName"`
|
||||
SyncNotes bool `json:"syncNotes"`
|
||||
SyncBookmarks bool `json:"syncBookmarks"`
|
||||
SyncTasks bool `json:"syncTasks"`
|
||||
AutoSync bool `json:"autoSync"`
|
||||
}
|
||||
|
||||
// SyncLog represents a sync operation log
|
||||
type SyncLog struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
||||
IntegrationID string `json:"integrationId" gorm:"not null;index;type:uuid"`
|
||||
Type string `json:"type"` // "full", "incremental", "manual", "webhook"
|
||||
Status string `json:"status"` // "success", "error", "partial"
|
||||
|
||||
// Sync statistics
|
||||
ItemsProcessed int `json:"itemsProcessed"`
|
||||
ItemsCreated int `json:"itemsCreated"`
|
||||
ItemsUpdated int `json:"itemsUpdated"`
|
||||
ItemsDeleted int `json:"itemsDeleted"`
|
||||
ItemsSkipped int `json:"itemsSkipped"`
|
||||
|
||||
// Timing
|
||||
StartedAt time.Time `json:"startedAt"`
|
||||
CompletedAt *time.Time `json:"completedAt"`
|
||||
Duration int `json:"duration"` // in seconds
|
||||
|
||||
// Error details
|
||||
ErrorMessage string `json:"errorMessage" gorm:"type:text"`
|
||||
ErrorDetails string `json:"errorDetails" gorm:"type:text"`
|
||||
|
||||
// Additional data
|
||||
SyncData string `json:"syncData" gorm:"type:jsonb"`
|
||||
|
||||
// Timestamps
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
// Relationships
|
||||
Integration Integration `json:"integration,omitempty" gorm:"foreignKey:IntegrationID"`
|
||||
}
|
||||
|
||||
// WebhookEvent represents an incoming webhook event
|
||||
type WebhookEvent struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
|
||||
IntegrationID string `json:"integrationId" gorm:"not null;index;type:uuid"`
|
||||
Type string `json:"type"` // "slack", "discord", etc.
|
||||
EventType string `json:"eventType"` // "message", "reaction_added", etc.
|
||||
|
||||
// Event data
|
||||
Payload string `json:"payload" gorm:"type:jsonb"`
|
||||
Processed bool `json:"processed" gorm:"default:false"`
|
||||
|
||||
// Processing details
|
||||
ProcessedAt *time.Time `json:"processedAt"`
|
||||
ErrorMessage string `json:"errorMessage" gorm:"type:text"`
|
||||
|
||||
// Timestamps
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
// Relationships
|
||||
Integration Integration `json:"integration,omitempty" gorm:"foreignKey:IntegrationID"`
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// WikiPage represents a page in the knowledge base/wiki
|
||||
type WikiPage struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Basic page information
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Slug string `json:"slug" gorm:"not null;uniqueIndex"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
Summary string `json:"summary" gorm:"type:text"`
|
||||
Status string `json:"status" gorm:"default:draft"` // draft, published, archived
|
||||
|
||||
// Organization
|
||||
CategoryID *uint `json:"category_id,omitempty"`
|
||||
Category *Category `json:"category,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
ParentID *uint `json:"parent_id,omitempty"`
|
||||
Parent *WikiPage `json:"parent,omitempty" gorm:"foreignKey:ParentID"`
|
||||
Children []WikiPage `json:"children,omitempty" gorm:"foreignKey:ParentID"`
|
||||
|
||||
// Metadata
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:wiki_page_tags;"`
|
||||
Keywords []string `json:"keywords" gorm:"serializer:json"`
|
||||
ReadingTime int `json:"reading_time"` // estimated minutes
|
||||
WordCount int `json:"word_count"`
|
||||
ViewCount int `json:"view_count" gorm:"default:0"`
|
||||
LastViewedAt *time.Time `json:"last_viewed_at,omitempty"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
IsTemplate bool `json:"is_template" gorm:"default:false"`
|
||||
TemplateID *uint `json:"template_id,omitempty"`
|
||||
Template *WikiPage `json:"template,omitempty" gorm:"foreignKey:TemplateID"`
|
||||
|
||||
// Collaboration
|
||||
IsCollaborative bool `json:"is_collaborative" gorm:"default:false"`
|
||||
Collaborators []User `json:"collaborators,omitempty" gorm:"many2many:wiki_collaborators;"`
|
||||
LastEditedBy *uint `json:"last_edited_by,omitempty"`
|
||||
LastEditedUser *User `json:"last_edited_user,omitempty" gorm:"foreignKey:LastEditedBy"`
|
||||
EditCount int `json:"edit_count" gorm:"default:0"`
|
||||
|
||||
// Relationships
|
||||
Versions []WikiVersion `json:"versions,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
Backlinks []WikiBacklink `json:"backlinks,omitempty" gorm:"foreignKey:TargetPageID"`
|
||||
Attachments []WikiAttachment `json:"attachments,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
Bookmarks []Bookmark `json:"bookmarks,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
Notes []Note `json:"notes,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
}
|
||||
|
||||
// Category represents a category for organizing wiki pages
|
||||
type Category struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Slug string `json:"slug" gorm:"not null;uniqueIndex"`
|
||||
Description string `json:"description"`
|
||||
Color string `json:"color" gorm:"default:#6366f1"`
|
||||
Icon string `json:"icon"`
|
||||
ParentID *uint `json:"parent_id,omitempty"`
|
||||
Parent *Category `json:"parent,omitempty" gorm:"foreignKey:ParentID"`
|
||||
Children []Category `json:"children,omitempty" gorm:"foreignKey:ParentID"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
SortOrder int `json:"sort_order" gorm:"default:0"`
|
||||
|
||||
// Relationships
|
||||
Pages []WikiPage `json:"pages,omitempty" gorm:"foreignKey:CategoryID"`
|
||||
}
|
||||
|
||||
// WikiVersion represents a version history of a wiki page
|
||||
type WikiVersion struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
WikiPageID uint `json:"wiki_page_id" gorm:"not null;index"`
|
||||
WikiPage WikiPage `json:"wiki_page,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
|
||||
VersionNumber int `json:"version_number" gorm:"not null"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
Summary string `json:"summary" gorm:"type:text"`
|
||||
ChangeLog string `json:"change_log" gorm:"type:text"`
|
||||
|
||||
// Author information
|
||||
AuthorID uint `json:"author_id" gorm:"not null"`
|
||||
Author User `json:"author,omitempty" gorm:"foreignKey:AuthorID"`
|
||||
|
||||
// Version metadata
|
||||
WordCount int `json:"word_count"`
|
||||
CharactersAdded int `json:"characters_added"`
|
||||
CharactersRemoved int `json:"characters_removed"`
|
||||
IsMinorChange bool `json:"is_minor_change" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// WikiBacklink represents a link between wiki pages
|
||||
type WikiBacklink struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
SourcePageID uint `json:"source_page_id" gorm:"not null;index"`
|
||||
SourcePage WikiPage `json:"source_page,omitempty" gorm:"foreignKey:SourcePageID"`
|
||||
TargetPageID uint `json:"target_page_id" gorm:"not null;index"`
|
||||
TargetPage WikiPage `json:"target_page,omitempty" gorm:"foreignKey:TargetPageID"`
|
||||
|
||||
LinkText string `json:"link_text"`
|
||||
Context string `json:"context" gorm:"type:text"` // Surrounding text where the link appears
|
||||
}
|
||||
|
||||
// WikiAttachment represents files attached to wiki pages
|
||||
type WikiAttachment struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
WikiPageID uint `json:"wiki_page_id" gorm:"not null;index"`
|
||||
WikiPage WikiPage `json:"wiki_page,omitempty" gorm:"foreignKey:WikiPageID"`
|
||||
|
||||
FileName string `json:"file_name" gorm:"not null"`
|
||||
OriginalName string `json:"original_name" gorm:"not null"`
|
||||
FilePath string `json:"file_path" gorm:"not null"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
MimeType string `json:"mime_type"`
|
||||
Width int `json:"width"` // For images
|
||||
Height int `json:"height"` // For images
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
}
|
||||
|
||||
// Template represents reusable page templates
|
||||
type Template struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Slug string `json:"slug" gorm:"not null;uniqueIndex"`
|
||||
Description string `json:"description"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
Category string `json:"category"` // meeting, project, documentation, etc.
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
UsageCount int `json:"usage_count" gorm:"default:0"`
|
||||
|
||||
// Template variables
|
||||
Variables []TemplateVariable `json:"variables,omitempty" gorm:"foreignKey:TemplateID"`
|
||||
}
|
||||
|
||||
// TemplateVariable represents variables in templates
|
||||
type TemplateVariable struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
TemplateID uint `json:"template_id" gorm:"not null;index"`
|
||||
Template Template `json:"template,omitempty" gorm:"foreignKey:TemplateID"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Type string `json:"type" gorm:"not null"` // text, number, date, select
|
||||
DefaultValue string `json:"default_value"`
|
||||
Required bool `json:"required" gorm:"default:false"`
|
||||
Description string `json:"description"`
|
||||
Options string `json:"options" gorm:"serializer:json"` // For select type
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (w *WikiPage) BeforeCreate(tx *gorm.DB) error {
|
||||
if w.Status == "" {
|
||||
w.Status = "draft"
|
||||
}
|
||||
if w.Slug == "" && w.Title != "" {
|
||||
w.Slug = generateSlug(w.Title)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Category) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.Slug == "" && c.Name != "" {
|
||||
c.Slug = generateSlug(c.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Template) BeforeCreate(tx *gorm.DB) error {
|
||||
if t.Slug == "" && t.Name != "" {
|
||||
t.Slug = generateSlug(t.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hooks
|
||||
func (w *WikiPage) BeforeUpdate(tx *gorm.DB) error {
|
||||
if w.Title != "" {
|
||||
w.Slug = generateSlug(w.Title)
|
||||
}
|
||||
if w.Content != "" {
|
||||
w.WordCount = len(strings.Fields(w.Content))
|
||||
w.ReadingTime = estimateReadingTime(w.WordCount)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func generateSlug(title string) string {
|
||||
// Simple slug generation - in production, you'd want more sophisticated handling
|
||||
slug := strings.ToLower(title)
|
||||
slug = strings.ReplaceAll(slug, " ", "-")
|
||||
slug = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(slug, "")
|
||||
slug = regexp.MustCompile(`-+`).ReplaceAllString(slug, "-")
|
||||
slug = strings.Trim(slug, "-")
|
||||
return slug
|
||||
}
|
||||
|
||||
func estimateReadingTime(wordCount int) int {
|
||||
readingSpeed := 225 // words per minute
|
||||
readingTime := wordCount / readingSpeed
|
||||
if readingTime < 1 {
|
||||
readingTime = 1
|
||||
}
|
||||
return readingTime
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// LearningPath represents a structured learning path
|
||||
type LearningPath struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Basic information
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category" gorm:"not null"` // programming, design, business, etc.
|
||||
Difficulty string `json:"difficulty" gorm:"default:beginner"` // beginner, intermediate, advanced
|
||||
|
||||
// Metadata
|
||||
Duration string `json:"duration"` // estimated time to complete
|
||||
Thumbnail string `json:"thumbnail"` // cover image
|
||||
IsPublished bool `json:"is_published" gorm:"default:false"`
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false"`
|
||||
|
||||
// Creator information
|
||||
CreatorID uint `json:"creator_id" gorm:"not null;index"`
|
||||
Creator User `json:"creator,omitempty" gorm:"foreignKey:CreatorID"`
|
||||
|
||||
// Relationships
|
||||
Modules []LearningModule `json:"modules,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
Courses []LearningPathCourse `json:"courses,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:learning_path_tags;"`
|
||||
Enrollments []Enrollment `json:"enrollments,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
|
||||
// Statistics
|
||||
EnrollmentCount int `json:"enrollment_count" gorm:"default:0"`
|
||||
Rating float64 `json:"rating" gorm:"default:0"`
|
||||
ReviewCount int `json:"review_count" gorm:"default:0"`
|
||||
}
|
||||
|
||||
// LearningModule represents a module within a learning path
|
||||
type LearningModule struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Basic information
|
||||
LearningPathID uint `json:"learning_path_id" gorm:"not null;index"`
|
||||
LearningPath LearningPath `json:"learning_path,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
Order int `json:"order" gorm:"not null"`
|
||||
|
||||
// Module type
|
||||
ModuleType string `json:"module_type" gorm:"default:lesson"` // lesson, project, quiz, video, reading
|
||||
|
||||
// Resources
|
||||
Resources []ModuleResource `json:"resources,omitempty" gorm:"foreignKey:LearningModuleID"`
|
||||
|
||||
// Completion tracking
|
||||
EstimatedDuration string `json:"estimated_duration"`
|
||||
IsRequired bool `json:"is_required" gorm:"default:true"`
|
||||
}
|
||||
|
||||
// ModuleResource represents a resource within a learning module
|
||||
type ModuleResource struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
LearningModuleID uint `json:"learning_module_id" gorm:"not null;index"`
|
||||
LearningModule LearningModule `json:"learning_module,omitempty" gorm:"foreignKey:LearningModuleID"`
|
||||
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type" gorm:"not null"` // video, article, book, tool, download
|
||||
Description string `json:"description"`
|
||||
Order int `json:"order" gorm:"not null"`
|
||||
|
||||
// External resource metadata
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Duration string `json:"duration"`
|
||||
IsExternal bool `json:"is_external" gorm:"default:true"`
|
||||
}
|
||||
|
||||
// Enrollment represents a user's enrollment in a learning path
|
||||
type Enrollment struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
LearningPathID uint `json:"learning_path_id" gorm:"not null;index"`
|
||||
LearningPath LearningPath `json:"learning_path,omitempty" gorm:"foreignKey:LearningPathID"`
|
||||
CourseID *uint `json:"course_id,omitempty"` // for direct course enrollment
|
||||
Course *Course `json:"course,omitempty" gorm:"foreignKey:CourseID"`
|
||||
|
||||
// Enrollment status
|
||||
Status string `json:"status" gorm:"default:enrolled"` // enrolled, in_progress, completed, dropped
|
||||
StartedAt *time.Time `json:"started_at"`
|
||||
CompletedAt *time.Time `json:"completed_at"`
|
||||
|
||||
// Progress tracking
|
||||
Progress float64 `json:"progress" gorm:"default:0"` // percentage 0-100
|
||||
CompletedModules []uint `json:"completed_modules" gorm:"serializer:json"`
|
||||
CurrentModuleID *uint `json:"current_module_id"`
|
||||
|
||||
// User feedback
|
||||
Rating *float64 `json:"rating"`
|
||||
Review string `json:"review"`
|
||||
ReviewDate *time.Time `json:"review_date"`
|
||||
}
|
||||
|
||||
// Progress represents a user's progress in a specific module
|
||||
type Progress struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
EnrollmentID uint `json:"enrollment_id" gorm:"not null;index"`
|
||||
LearningModuleID uint `json:"learning_module_id" gorm:"not null;index"`
|
||||
|
||||
// Progress status
|
||||
Status string `json:"status" gorm:"default:not_started"` // not_started, in_progress, completed
|
||||
StartedAt *time.Time `json:"started_at"`
|
||||
CompletedAt *time.Time `json:"completed_at"`
|
||||
|
||||
// Progress details
|
||||
TimeSpent int `json:"time_spent"` // minutes
|
||||
ProgressData string `json:"progress_data" gorm:"type:json"` // additional progress data
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MarketplaceItem represents an item in the knowledge marketplace
|
||||
type MarketplaceItem struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Creator information
|
||||
SellerID uint `json:"seller_id" gorm:"not null;index"`
|
||||
Seller User `json:"seller,omitempty" gorm:"foreignKey:SellerID"`
|
||||
|
||||
// Basic information
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category" gorm:"not null"` // course, template, tool, guide, resource
|
||||
|
||||
// Content type and reference
|
||||
ContentType string `json:"content_type" gorm:"not null"` // bookmark_collection, note_template, course, learning_path, tool
|
||||
ContentID *uint `json:"content_id,omitempty"` // Reference to actual content
|
||||
ContentURL string `json:"content_url"` // Download/access URL
|
||||
PreviewURL string `json:"preview_url"` // Preview/demo URL
|
||||
Thumbnail string `json:"thumbnail"` // Item thumbnail
|
||||
|
||||
// Pricing
|
||||
Price float64 `json:"price" gorm:"default:0"` // Price in USD
|
||||
Currency string `json:"currency" gorm:"default:USD"` // Currency code
|
||||
IsFree bool `json:"is_free" gorm:"default:false"` // Free item
|
||||
Subscription bool `json:"subscription" gorm:"default:false"` // Subscription-based
|
||||
SubscriptionPrice float64 `json:"subscription_price" gorm:"default:0"` // Monthly subscription price
|
||||
|
||||
// Ratings and reviews
|
||||
Rating float64 `json:"rating" gorm:"default:0"` // Average rating (1-5)
|
||||
ReviewCount int `json:"review_count" gorm:"default:0"` // Number of reviews
|
||||
DownloadCount int `json:"download_count" gorm:"default:0"` // Number of downloads
|
||||
|
||||
// Status and visibility
|
||||
Status string `json:"status" gorm:"default:draft"` // draft, published, suspended, removed
|
||||
IsFeatured bool `json:"is_featured" gorm:"default:false"` // Featured item
|
||||
IsApproved bool `json:"is_approved" gorm:"default:false"` // Admin approved
|
||||
ApprovedAt *time.Time `json:"approved_at,omitempty"`
|
||||
ApprovedBy *uint `json:"approved_by,omitempty"`
|
||||
Approver *User `json:"approver,omitempty" gorm:"foreignKey:ApprovedBy"`
|
||||
|
||||
// Tags and metadata
|
||||
Tags []MarketplaceTag `json:"tags,omitempty" gorm:"many2many:marketplace_item_tags;"`
|
||||
License string `json:"license" gorm:"default:standard"` // License type
|
||||
Version string `json:"version" gorm:"default:1.0"` // Version
|
||||
LastUpdated *time.Time `json:"last_updated,omitempty"`
|
||||
|
||||
// Analytics
|
||||
ViewCount int `json:"view_count" gorm:"default:0"`
|
||||
LastViewedAt *time.Time `json:"last_viewed_at,omitempty"`
|
||||
|
||||
// Relationships
|
||||
Reviews []MarketplaceReview `json:"reviews,omitempty" gorm:"foreignKey:ItemID"`
|
||||
Purchases []MarketplacePurchase `json:"purchases,omitempty" gorm:"foreignKey:ItemID"`
|
||||
}
|
||||
|
||||
// MarketplaceTag represents tags for marketplace items
|
||||
type MarketplaceTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Name string `json:"name" gorm:"uniqueIndex;not null"`
|
||||
Description string `json:"description"`
|
||||
Color string `json:"color" gorm:"default:#6366f1"` // Tag color
|
||||
UsageCount int `json:"usage_count" gorm:"default:0"`
|
||||
}
|
||||
|
||||
// MarketplaceReview represents a review for a marketplace item
|
||||
type MarketplaceReview struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Review information
|
||||
ItemID uint `json:"item_id" gorm:"not null;index"`
|
||||
Item MarketplaceItem `json:"item,omitempty" gorm:"foreignKey:ItemID"`
|
||||
ReviewerID uint `json:"reviewer_id" gorm:"not null;index"`
|
||||
Reviewer User `json:"reviewer,omitempty" gorm:"foreignKey:ReviewerID"`
|
||||
|
||||
// Review content
|
||||
Rating int `json:"rating" gorm:"not null;check:rating >= 1 AND rating <= 5"` // 1-5 stars
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
|
||||
// Review metadata
|
||||
HelpfulCount int `json:"helpful_count" gorm:"default:0"`
|
||||
IsVerified bool `json:"is_verified" gorm:"default:false"` // Verified purchase
|
||||
PurchaseID *uint `json:"purchase_id,omitempty"`
|
||||
ReviewedAt time.Time `json:"reviewed_at"`
|
||||
|
||||
// Status
|
||||
Status string `json:"status" gorm:"default:published"` // published, hidden, removed
|
||||
}
|
||||
|
||||
// MarketplacePurchase represents a purchase from the marketplace
|
||||
type MarketplacePurchase struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Purchase information
|
||||
ItemID uint `json:"item_id" gorm:"not null;index"`
|
||||
Item MarketplaceItem `json:"item,omitempty" gorm:"foreignKey:ItemID"`
|
||||
BuyerID uint `json:"buyer_id" gorm:"not null;index"`
|
||||
Buyer User `json:"buyer,omitempty" gorm:"foreignKey:BuyerID"`
|
||||
|
||||
// Purchase details
|
||||
Price float64 `json:"price" gorm:"not null"`
|
||||
Currency string `json:"currency" gorm:"default:USD"`
|
||||
PaymentMethod string `json:"payment_method"` // stripe, paypal, crypto
|
||||
TransactionID string `json:"transaction_id" gorm:"uniqueIndex"`
|
||||
|
||||
// License and access
|
||||
LicenseType string `json:"license_type" gorm:"default:personal"` // personal, commercial, enterprise
|
||||
AccessGranted bool `json:"access_granted" gorm:"default:true"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"` // For subscriptions
|
||||
|
||||
// Status
|
||||
Status string `json:"status" gorm:"default:completed"` // pending, completed, refunded, cancelled
|
||||
RefundedAt *time.Time `json:"refunded_at,omitempty"`
|
||||
RefundReason string `json:"refund_reason,omitempty"`
|
||||
|
||||
// Analytics
|
||||
DownloadCount int `json:"download_count" gorm:"default:0"`
|
||||
LastDownloadAt *time.Time `json:"last_download_at,omitempty"`
|
||||
}
|
||||
|
||||
// ContentShare represents shared content links
|
||||
type ContentShare struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Owner information
|
||||
OwnerID uint `json:"owner_id" gorm:"not null;index"`
|
||||
Owner User `json:"owner,omitempty" gorm:"foreignKey:OwnerID"`
|
||||
|
||||
// Content information
|
||||
ContentType string `json:"content_type" gorm:"not null"` // bookmark, note, file, task, goal
|
||||
ContentID uint `json:"content_id" gorm:"not null"`
|
||||
|
||||
// Share settings
|
||||
ShareToken string `json:"share_token" gorm:"uniqueIndex;not null"`
|
||||
ShareURL string `json:"share_url"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Password string `json:"-" gorm:"column:password"` // Optional password protection
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
|
||||
// Access control
|
||||
AllowDownload bool `json:"allow_download" gorm:"default:true"`
|
||||
AllowComment bool `json:"allow_comment" gorm:"default:false"`
|
||||
AllowEdit bool `json:"allow_edit" gorm:"default:false"`
|
||||
|
||||
// Analytics
|
||||
ViewCount int `json:"view_count" gorm:"default:0"`
|
||||
DownloadCount int `json:"download_count" gorm:"default:0"`
|
||||
LastAccessedAt *time.Time `json:"last_accessed_at,omitempty"`
|
||||
|
||||
// Status
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (m *MarketplaceItem) BeforeCreate(tx *gorm.DB) error {
|
||||
if m.Status == "" {
|
||||
m.Status = "draft"
|
||||
}
|
||||
if m.Currency == "" {
|
||||
m.Currency = "USD"
|
||||
}
|
||||
if m.Version == "" {
|
||||
m.Version = "1.0"
|
||||
}
|
||||
if m.License == "" {
|
||||
m.License = "standard"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *MarketplaceReview) BeforeCreate(tx *gorm.DB) error {
|
||||
if r.Status == "" {
|
||||
r.Status = "published"
|
||||
}
|
||||
r.ReviewedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *MarketplacePurchase) BeforeCreate(tx *gorm.DB) error {
|
||||
if p.Status == "" {
|
||||
p.Status = "completed"
|
||||
}
|
||||
if p.Currency == "" {
|
||||
p.Currency = "USD"
|
||||
}
|
||||
if p.LicenseType == "" {
|
||||
p.LicenseType = "personal"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ContentShare) BeforeCreate(tx *gorm.DB) error {
|
||||
if s.ShareToken == "" {
|
||||
// Generate a unique share token
|
||||
s.ShareToken = generateShareToken()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to generate share tokens
|
||||
func generateShareToken() string {
|
||||
// This should generate a cryptographically secure random token
|
||||
// For now, using a simple implementation
|
||||
return "share_" + time.Now().Format("20060102150405")
|
||||
}
|
||||
@@ -25,5 +25,93 @@ func AutoMigrate() {
|
||||
&Task{},
|
||||
&File{},
|
||||
&Note{},
|
||||
&TimeEntry{},
|
||||
&FileAnalysis{},
|
||||
&ChatSession{},
|
||||
&ChatMessage{},
|
||||
&LearningPath{},
|
||||
&LearningModule{},
|
||||
&ModuleResource{},
|
||||
&Enrollment{},
|
||||
&Progress{},
|
||||
&Course{},
|
||||
&LearningPathCourse{},
|
||||
&CalendarEvent{},
|
||||
&RecurrenceRule{},
|
||||
&CalendarSettings{},
|
||||
// Search models
|
||||
&ContentEmbedding{},
|
||||
&SavedSearch{},
|
||||
&SavedSearchTag{},
|
||||
&SearchAnalytics{},
|
||||
&SearchSuggestion{},
|
||||
// AI Feature models
|
||||
&AISummary{},
|
||||
&AITaskSuggestion{},
|
||||
&UserAISettings{},
|
||||
&AITagSuggestion{},
|
||||
&AIContentGeneration{},
|
||||
&AICodeReview{},
|
||||
&AILearningRecommendation{},
|
||||
// Advanced AI Recommendation models
|
||||
&AIRecommendation{},
|
||||
&UserPreference{},
|
||||
&RecommendationInteraction{},
|
||||
// Integration models
|
||||
&Integration{},
|
||||
&SyncLog{},
|
||||
&WebhookEvent{},
|
||||
// Analytics models
|
||||
&Analytics{},
|
||||
&ProductivityMetrics{},
|
||||
&LearningAnalytics{},
|
||||
&ContentAnalytics{},
|
||||
&GitHubAnalytics{},
|
||||
&HabitAnalytics{},
|
||||
&Goal{},
|
||||
&Milestone{},
|
||||
&AnalyticsReport{},
|
||||
// Social features models
|
||||
&Skill{},
|
||||
&Project{},
|
||||
&ProjectTag{},
|
||||
&SocialLink{},
|
||||
&Follow{},
|
||||
// Team workspace models
|
||||
&Team{},
|
||||
&TeamMember{},
|
||||
&TeamInvitation{},
|
||||
&TeamProject{},
|
||||
&TeamProjectTag{},
|
||||
&TeamBookmark{},
|
||||
&TeamNote{},
|
||||
&TeamTask{},
|
||||
&TeamFile{},
|
||||
&TeamActivity{},
|
||||
// Security models
|
||||
&AuditLog{},
|
||||
// Marketplace models
|
||||
&MarketplaceItem{},
|
||||
&MarketplaceTag{},
|
||||
&MarketplaceReview{},
|
||||
&MarketplacePurchase{},
|
||||
&ContentShare{},
|
||||
// Community models
|
||||
&Challenge{},
|
||||
&ChallengeParticipant{},
|
||||
&ChallengeTeam{},
|
||||
&ChallengeMilestone{},
|
||||
&ChallengeMilestoneCompletion{},
|
||||
&ChallengeResource{},
|
||||
&ChallengeTag{},
|
||||
&Mentorship{},
|
||||
&MentorshipSession{},
|
||||
&MentorshipReview{},
|
||||
&MentorshipMilestone{},
|
||||
&MentorshipRequest{},
|
||||
// YouTube cache models
|
||||
&YouTubeChannelCache{},
|
||||
// Video bookmark models
|
||||
&VideoBookmark{},
|
||||
)
|
||||
}
|
||||
|
||||
+11
-7
@@ -16,22 +16,26 @@ type Note struct {
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
|
||||
|
||||
// Encryption
|
||||
IsEncrypted bool `json:"is_encrypted" gorm:"default:false"`
|
||||
EncryptionKey string `json:"-" gorm:"column:encryption_key"` // User-specific encryption key (optional)
|
||||
|
||||
// Organization
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:note_tags;"`
|
||||
|
||||
|
||||
// Metadata
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
IsPinned bool `json:"is_pinned" gorm:"default:false"`
|
||||
|
||||
|
||||
// Formatting
|
||||
ContentType string `json:"content_type" gorm:"default:markdown"` // markdown, html, plain
|
||||
|
||||
|
||||
// Relationships
|
||||
ParentNoteID *uint `json:"parent_note_id,omitempty"`
|
||||
ParentNote *Note `json:"parent_note,omitempty" gorm:"foreignKey:ParentNoteID"`
|
||||
ParentNoteID *uint `json:"parent_note_id,omitempty"`
|
||||
ParentNote *Note `json:"parent_note,omitempty" gorm:"foreignKey:ParentNoteID"`
|
||||
Subnotes []Note `json:"subnotes,omitempty" gorm:"foreignKey:ParentNoteID"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ContentEmbedding stores vector embeddings for semantic search
|
||||
type ContentEmbedding struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
// Content reference
|
||||
ContentType string `json:"content_type" gorm:"not null;index"` // 'bookmark', 'task', 'note', 'file'
|
||||
ContentID uint `json:"content_id" gorm:"not null;index"`
|
||||
|
||||
// Embedding data
|
||||
Embedding string `json:"embedding" gorm:"type:text"` // JSON array of floats
|
||||
Model string `json:"model" gorm:"not null"` // AI model used
|
||||
Dimensions int `json:"dimensions" gorm:"not null"` // Vector dimensions
|
||||
TextContent string `json:"text_content" gorm:"type:text"` // Original text for embedding
|
||||
|
||||
// Metadata
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// SavedSearch represents a user's saved search query
|
||||
type SavedSearch struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Query string `json:"query" gorm:"not null"`
|
||||
Filters string `json:"filters" gorm:"type:json"` // JSON serialized filters
|
||||
Alert bool `json:"alert" gorm:"default:false"`
|
||||
LastRun *time.Time `json:"last_run"`
|
||||
RunCount int `json:"run_count" gorm:"default:0"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
Description string `json:"description"`
|
||||
Tags []SavedSearchTag `json:"tags,omitempty" gorm:"many2many:saved_search_tags;"`
|
||||
}
|
||||
|
||||
// SavedSearchTag represents tags for saved searches
|
||||
type SavedSearchTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
Name string `json:"name" gorm:"unique;not null"`
|
||||
Color string `json:"color" gorm:"default:#3b82f6"`
|
||||
}
|
||||
|
||||
// SearchAnalytics stores search analytics data
|
||||
type SearchAnalytics struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
Query string `json:"query" gorm:"not null;index"`
|
||||
Filters string `json:"filters" gorm:"type:json"`
|
||||
ResultsCount int `json:"results_count"`
|
||||
Took int `json:"took"` // Time in milliseconds
|
||||
ContentType string `json:"content_type"`
|
||||
ClickedResultID *uint `json:"clicked_result_id"` // Track which result was clicked
|
||||
SessionID string `json:"session_id" gorm:"index"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
}
|
||||
|
||||
// SearchSuggestion represents search suggestions
|
||||
type SearchSuggestion struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Text string `json:"text" gorm:"not null;uniqueIndex"`
|
||||
Type string `json:"type" gorm:"not null"` // 'query', 'tag', 'content'
|
||||
Frequency int `json:"frequency" gorm:"default:1"`
|
||||
LastUsed time.Time `json:"last_used"`
|
||||
ContentType *string `json:"content_type,omitempty"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:true"`
|
||||
}
|
||||
|
||||
// BeforeCreate hook for ContentEmbedding
|
||||
func (ce *ContentEmbedding) BeforeCreate(tx *gorm.DB) error {
|
||||
// Set default model if not specified
|
||||
if ce.Model == "" {
|
||||
ce.Model = "text-embedding-ada-002"
|
||||
}
|
||||
|
||||
// Set default dimensions if not specified
|
||||
if ce.Dimensions == 0 {
|
||||
ce.Dimensions = 1536 // Default for OpenAI embeddings
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Skill represents a user's skill
|
||||
type Skill struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Level string `json:"level" gorm:"default:intermediate"` // beginner, intermediate, advanced, expert
|
||||
Category string `json:"category"` // programming, design, business, etc.
|
||||
Endorsements int `json:"endorsements" gorm:"default:0"`
|
||||
Verified bool `json:"verified" gorm:"default:false"`
|
||||
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// Project represents a user's project showcase
|
||||
type Project struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
RepositoryURL string `json:"repository_url"`
|
||||
LiveURL string `json:"live_url"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Tags []ProjectTag `json:"tags,omitempty" gorm:"foreignKey:ProjectID"`
|
||||
Featured bool `json:"featured" gorm:"default:false"`
|
||||
Views int `json:"views" gorm:"default:0"`
|
||||
Likes int `json:"likes" gorm:"default:0"`
|
||||
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// ProjectTag represents tags for projects
|
||||
type ProjectTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
ProjectID uint `json:"project_id" gorm:"not null;index"`
|
||||
Tag string `json:"tag" gorm:"not null"`
|
||||
}
|
||||
|
||||
// SocialLink represents social media links
|
||||
type SocialLink struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Platform string `json:"platform" gorm:"not null"` // github, linkedin, twitter, etc.
|
||||
URL string `json:"url" gorm:"not null"`
|
||||
Username string `json:"username"`
|
||||
Verified bool `json:"verified" gorm:"default:false"`
|
||||
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// Follow represents user following relationships
|
||||
type Follow struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
FollowerID uint `json:"follower_id" gorm:"not null;index"`
|
||||
FollowingID uint `json:"following_id" gorm:"not null;index"`
|
||||
|
||||
Follower User `json:"follower,omitempty" gorm:"foreignKey:FollowerID"`
|
||||
Following User `json:"following,omitempty" gorm:"foreignKey:FollowingID"`
|
||||
}
|
||||
|
||||
// UserProfileStats represents aggregated user statistics
|
||||
type UserProfileStats struct {
|
||||
UserID uint `json:"user_id"`
|
||||
FollowersCount int `json:"followers_count"`
|
||||
FollowingCount int `json:"following_count"`
|
||||
PublicBookmarks int `json:"public_bookmarks"`
|
||||
PublicNotes int `json:"public_notes"`
|
||||
ProjectsCount int `json:"projects_count"`
|
||||
SkillsCount int `json:"skills_count"`
|
||||
TotalViews int `json:"total_views"`
|
||||
TotalLikes int `json:"total_likes"`
|
||||
ProfileCompletion float64 `json:"profile_completion"` // 0-100 percentage
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Team represents a collaborative workspace
|
||||
type Team struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Avatar string `json:"avatar"`
|
||||
IsPublic bool `json:"is_public" gorm:"default:false"`
|
||||
IsActive bool `json:"is_active" gorm:"default:true"`
|
||||
|
||||
// Owner of the team
|
||||
OwnerID uint `json:"owner_id" gorm:"not null;index"`
|
||||
Owner User `json:"owner,omitempty" gorm:"foreignKey:OwnerID"`
|
||||
|
||||
// Team members and relationships
|
||||
Members []TeamMember `json:"members,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Invitations []TeamInvitation `json:"invitations,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Projects []TeamProject `json:"projects,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Bookmarks []TeamBookmark `json:"bookmarks,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Notes []TeamNote `json:"notes,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Tasks []TeamTask `json:"tasks,omitempty" gorm:"foreignKey:TeamID"`
|
||||
Files []TeamFile `json:"files,omitempty" gorm:"foreignKey:TeamID"`
|
||||
}
|
||||
|
||||
// TeamMember represents a user's membership in a team
|
||||
type TeamMember struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Role string `json:"role" gorm:"default:member"` // owner, admin, member, viewer
|
||||
JoinedAt time.Time `json:"joined_at"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// TeamInvitation represents an invitation to join a team
|
||||
type TeamInvitation struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Email string `json:"email" gorm:"not null"` // Email for non-registered users
|
||||
Role string `json:"role" gorm:"default:member"`
|
||||
Token string `json:"token" gorm:"uniqueIndex;not null"` // Invitation token
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, accepted, declined, expired
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
InvitedBy uint `json:"invited_by" gorm:"not null"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Inviter User `json:"inviter,omitempty" gorm:"foreignKey:InvitedBy"`
|
||||
}
|
||||
|
||||
// TeamProject represents a project within a team
|
||||
type TeamProject struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
Name string `json:"name" gorm:"not null"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status" gorm:"default:active"` // active, archived, completed
|
||||
RepositoryURL string `json:"repository_url"`
|
||||
LiveURL string `json:"live_url"`
|
||||
Tags []TeamProjectTag `json:"tags,omitempty" gorm:"foreignKey:ProjectID"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
}
|
||||
|
||||
// TeamProjectTag represents tags for team projects
|
||||
type TeamProjectTag struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
ProjectID uint `json:"project_id" gorm:"not null;index"`
|
||||
Tag string `json:"tag" gorm:"not null"`
|
||||
}
|
||||
|
||||
// TeamBookmark represents a bookmark shared within a team
|
||||
type TeamBookmark struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
BookmarkID uint `json:"bookmark_id" gorm:"not null;index"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Bookmark Bookmark `json:"bookmark,omitempty" gorm:"foreignKey:BookmarkID"`
|
||||
}
|
||||
|
||||
// TeamNote represents a note shared within a team
|
||||
type TeamNote struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
NoteID uint `json:"note_id" gorm:"not null;index"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Note Note `json:"note,omitempty" gorm:"foreignKey:NoteID"`
|
||||
}
|
||||
|
||||
// TeamTask represents a task within a team
|
||||
type TeamTask struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
TaskID uint `json:"task_id" gorm:"not null;index"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Task Task `json:"task,omitempty" gorm:"foreignKey:TaskID"`
|
||||
}
|
||||
|
||||
// TeamFile represents a file shared within a team
|
||||
type TeamFile struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
FileID uint `json:"file_id" gorm:"not null;index"`
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
File File `json:"file,omitempty" gorm:"foreignKey:FileID"`
|
||||
}
|
||||
|
||||
// TeamActivity represents activity logs for team actions
|
||||
type TeamActivity struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
TeamID uint `json:"team_id" gorm:"not null;index"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
Action string `json:"action" gorm:"not null"` // created, updated, deleted, joined, left, etc.
|
||||
EntityType string `json:"entity_type" gorm:"not null"` // team, project, bookmark, note, task, file
|
||||
EntityID uint `json:"entity_id" gorm:"not null"`
|
||||
Details string `json:"details"` // JSON string with additional details
|
||||
|
||||
Team Team `json:"team,omitempty" gorm:"foreignKey:TeamID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// TeamStats represents aggregated team statistics
|
||||
type TeamStats struct {
|
||||
TeamID uint `json:"team_id"`
|
||||
MembersCount int64 `json:"members_count"`
|
||||
ProjectsCount int64 `json:"projects_count"`
|
||||
BookmarksCount int64 `json:"bookmarks_count"`
|
||||
NotesCount int64 `json:"notes_count"`
|
||||
TasksCount int64 `json:"tasks_count"`
|
||||
FilesCount int64 `json:"files_count"`
|
||||
RecentActivity int64 `json:"recent_activity"` // Activity in last 7 days
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TimeEntry represents a time tracking entry
|
||||
type TimeEntry struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// What is being tracked
|
||||
TaskID *uint `json:"task_id,omitempty"`
|
||||
Task *Task `json:"task,omitempty" gorm:"foreignKey:TaskID"`
|
||||
BookmarkID *uint `json:"bookmark_id,omitempty"`
|
||||
Bookmark *Bookmark `json:"bookmark,omitempty" gorm:"foreignKey:BookmarkID"`
|
||||
NoteID *uint `json:"note_id,omitempty"`
|
||||
Note *Note `json:"note,omitempty" gorm:"foreignKey:NoteID"`
|
||||
|
||||
// Time tracking data
|
||||
StartTime time.Time `json:"start_time" gorm:"not null"`
|
||||
EndTime *time.Time `json:"end_time"`
|
||||
Duration *int `json:"duration"` // Duration in seconds
|
||||
Description string `json:"description"`
|
||||
|
||||
// Organization and metadata
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:time_entry_tags;"`
|
||||
Billable bool `json:"billable" gorm:"default:false"`
|
||||
|
||||
// Billing information
|
||||
HourlyRate *float64 `json:"hourly_rate"`
|
||||
|
||||
// Timer state
|
||||
IsRunning bool `json:"is_running" gorm:"default:false"`
|
||||
|
||||
// Additional metadata
|
||||
Source string `json:"source" gorm:"default:manual"` // manual, auto, pomodoro
|
||||
}
|
||||
|
||||
// BeforeCreate hook to set default values
|
||||
func (t *TimeEntry) BeforeCreate(tx *gorm.DB) error {
|
||||
if t.StartTime.IsZero() {
|
||||
t.StartTime = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeUpdate hook to calculate duration when end time is set
|
||||
func (t *TimeEntry) BeforeUpdate(tx *gorm.DB) error {
|
||||
if t.EndTime != nil && !t.EndTime.IsZero() {
|
||||
duration := int(t.EndTime.Sub(t.StartTime).Seconds())
|
||||
t.Duration = &duration
|
||||
t.IsRunning = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the timer and calculates duration
|
||||
func (t *TimeEntry) Stop() {
|
||||
now := time.Now()
|
||||
t.EndTime = &now
|
||||
duration := int(now.Sub(t.StartTime).Seconds())
|
||||
t.Duration = &duration
|
||||
t.IsRunning = false
|
||||
}
|
||||
|
||||
// GetDuration returns the duration in seconds
|
||||
func (t *TimeEntry) GetDuration() int {
|
||||
if t.Duration != nil {
|
||||
return *t.Duration
|
||||
}
|
||||
if t.IsRunning {
|
||||
return int(time.Since(t.StartTime).Seconds())
|
||||
}
|
||||
if t.EndTime != nil {
|
||||
return int(t.EndTime.Sub(t.StartTime).Seconds())
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetFormattedDuration returns a human-readable duration
|
||||
func (t *TimeEntry) GetFormattedDuration() string {
|
||||
duration := t.GetDuration()
|
||||
hours := duration / 3600
|
||||
minutes := (duration % 3600) / 60
|
||||
seconds := duration % 60
|
||||
|
||||
if hours > 0 {
|
||||
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
|
||||
} else if minutes > 0 {
|
||||
return fmt.Sprintf("%dm %ds", minutes, seconds)
|
||||
}
|
||||
return fmt.Sprintf("%ds", seconds)
|
||||
}
|
||||
+46
-9
@@ -17,15 +17,52 @@ type User struct {
|
||||
Username string `json:"username" gorm:"uniqueIndex;not null"`
|
||||
Password string `json:"-" gorm:"not null"` // Hashed password
|
||||
FullName string `json:"full_name"`
|
||||
|
||||
Role string `json:"role" gorm:"default:user"` // user, admin
|
||||
|
||||
// GitHub OAuth fields
|
||||
GitHubID int `json:"github_id" gorm:"uniqueIndex"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Provider string `json:"provider" gorm:"default:email"` // email, github
|
||||
|
||||
// Preferences
|
||||
Theme string `json:"theme" gorm:"default:dark"`
|
||||
Language string `json:"language" gorm:"default:en"`
|
||||
Timezone string `json:"timezone" gorm:"default:UTC"`
|
||||
|
||||
Theme string `json:"theme" gorm:"default:dark"`
|
||||
Language string `json:"language" gorm:"default:en"`
|
||||
Timezone string `json:"timezone" gorm:"default:UTC"`
|
||||
|
||||
// Social Profile Features
|
||||
Bio string `json:"bio"`
|
||||
Location string `json:"location"`
|
||||
Website string `json:"website"`
|
||||
Company string `json:"company"`
|
||||
JobTitle string `json:"job_title"`
|
||||
Skills []Skill `json:"skills,omitempty" gorm:"foreignKey:UserID"`
|
||||
Projects []Project `json:"projects,omitempty" gorm:"foreignKey:UserID"`
|
||||
SocialLinks []SocialLink `json:"social_links,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Security & 2FA
|
||||
TOTPSecret string `json:"-" gorm:"column:totp_secret"` // Encrypted TOTP secret
|
||||
TOTPEnabled bool `json:"totp_enabled" gorm:"default:false"`
|
||||
BackupCodes string `json:"-" gorm:"column:backup_codes"` // Encrypted backup codes
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
LoginAttempts int `json:"login_attempts" gorm:"default:0"`
|
||||
LockedUntil *time.Time `json:"locked_until"`
|
||||
|
||||
// Privacy Settings
|
||||
ProfileVisibility string `json:"profile_visibility" gorm:"default:public"` // public, private, friends
|
||||
ShowEmail bool `json:"show_email" gorm:"default:false"`
|
||||
ShowActivity bool `json:"show_activity" gorm:"default:true"`
|
||||
AllowMessages bool `json:"allow_messages" gorm:"default:true"`
|
||||
|
||||
// Social Stats
|
||||
FollowersCount int `json:"followers_count" gorm:"default:0"`
|
||||
FollowingCount int `json:"following_count" gorm:"default:0"`
|
||||
PublicBookmarks int `json:"public_bookmarks" gorm:"default:0"`
|
||||
PublicNotes int `json:"public_notes" gorm:"default:0"`
|
||||
|
||||
// Relationships
|
||||
Bookmarks []Bookmark `json:"bookmarks,omitempty" gorm:"foreignKey:UserID"`
|
||||
Tasks []Task `json:"tasks,omitempty" gorm:"foreignKey:UserID"`
|
||||
Files []File `json:"files,omitempty" gorm:"foreignKey:UserID"`
|
||||
Notes []Note `json:"notes,omitempty" gorm:"foreignKey:UserID"`
|
||||
Bookmarks []Bookmark `json:"bookmarks,omitempty" gorm:"foreignKey:UserID"`
|
||||
Tasks []Task `json:"tasks,omitempty" gorm:"foreignKey:UserID"`
|
||||
Files []File `json:"files,omitempty" gorm:"foreignKey:UserID"`
|
||||
Notes []Note `json:"notes,omitempty" gorm:"foreignKey:UserID"`
|
||||
TimeEntries []TimeEntry `json:"time_entries,omitempty" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// VideoBookmark represents a bookmarked YouTube video
|
||||
type VideoBookmark struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
VideoID string `json:"video_id" gorm:"uniqueIndex;not null"` // YouTube video ID
|
||||
Title string `json:"title" gorm:"not null"`
|
||||
Channel string `json:"channel" gorm:"not null"`
|
||||
Thumbnail string `json:"thumbnail" gorm:"not null"`
|
||||
URL string `json:"url" gorm:"not null"`
|
||||
UserID uint `json:"user_id" gorm:"not null"` // Foreign key to User
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Tags string `json:"tags" gorm:"type:text"` // Comma-separated tags
|
||||
IsWatched bool `json:"is_watched" gorm:"default:false"`
|
||||
IsFavorite bool `json:"is_favorite" gorm:"default:false"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName specifies the table name for VideoBookmark
|
||||
func (VideoBookmark) TableName() string {
|
||||
return "video_bookmarks"
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ScrapedContent represents content extracted from web pages
|
||||
type ScrapedContent struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Source information
|
||||
URL string `json:"url" gorm:"not null"`
|
||||
Domain string `json:"domain"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Author string `json:"author"`
|
||||
PublishedDate *time.Time `json:"published_date"`
|
||||
LastScraped time.Time `json:"last_scraped"`
|
||||
|
||||
// Extracted content
|
||||
Content string `json:"content" gorm:"type:text"`
|
||||
Summary string `json:"summary" gorm:"type:text"`
|
||||
Keywords []string `json:"keywords" gorm:"serializer:json"`
|
||||
Tags []Tag `json:"tags,omitempty" gorm:"many2many:scraped_content_tags;"`
|
||||
Images []ScrapedImage `json:"images,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
Links []ScrapedLink `json:"links,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
Videos []ScrapedVideo `json:"videos,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
|
||||
// Content analysis
|
||||
ContentType string `json:"content_type"` // article, blog, news, tutorial, documentation
|
||||
WordCount int `json:"word_count"`
|
||||
ReadingTime int `json:"reading_time"` // estimated minutes
|
||||
Difficulty string `json:"difficulty"` // beginner, intermediate, advanced
|
||||
QualityScore float64 `json:"quality_score"` // 0-100
|
||||
|
||||
// Processing status
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, processing, completed, failed
|
||||
ErrorMessage string `json:"error_message"`
|
||||
ProcessingLog string `json:"processing_log" gorm:"type:text"`
|
||||
|
||||
// Relationships
|
||||
BookmarkID *uint `json:"bookmark_id,omitempty"`
|
||||
Bookmark *Bookmark `json:"bookmark,omitempty" gorm:"foreignKey:BookmarkID"`
|
||||
NoteID *uint `json:"note_id,omitempty"`
|
||||
Note *Note `json:"note,omitempty" gorm:"foreignKey:NoteID"`
|
||||
}
|
||||
|
||||
// ScrapedImage represents images extracted from web pages
|
||||
type ScrapedImage struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
ScrapedContentID uint `json:"scraped_content_id" gorm:"not null;index"`
|
||||
ScrapedContent ScrapedContent `json:"scraped_content,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
|
||||
URL string `json:"url"`
|
||||
AltText string `json:"alt_text"`
|
||||
Title string `json:"title"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Format string `json:"format"` // jpg, png, gif, svg, webp
|
||||
Size int64 `json:"size"` // bytes in bytes
|
||||
IsMainImage bool `json:"is_main_image" gorm:"default:false"`
|
||||
LocalPath string `json:"local_path"` // if downloaded
|
||||
ThumbnailPath string `json:"thumbnail_path"` // if thumbnail generated
|
||||
}
|
||||
|
||||
// ScrapedLink represents links extracted from web pages
|
||||
type ScrapedLink struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
ScrapedContentID uint `json:"scraped_content_id" gorm:"not null;index"`
|
||||
ScrapedContent ScrapedContent `json:"scraped_content,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
|
||||
URL string `json:"url"`
|
||||
Text string `json:"text"`
|
||||
Title string `json:"title"`
|
||||
LinkType string `json:"link_type"` // internal, external, download, email
|
||||
IsNoFollow bool `json:"is_no_follow"`
|
||||
IsSponsored bool `json:"is_sponsored"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
// ScrapedVideo represents videos extracted from web pages
|
||||
type ScrapedVideo struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
ScrapedContentID uint `json:"scraped_content_id" gorm:"not null;index"`
|
||||
ScrapedContent ScrapedContent `json:"scraped_content,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Duration string `json:"duration"` // in format "HH:MM:SS"
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Platform string `json:"platform"` // youtube, vimeo, twitch, etc.
|
||||
VideoID string `json:"video_id"` // platform-specific ID
|
||||
IsEmbeddable bool `json:"is_embeddable"`
|
||||
}
|
||||
|
||||
// ScrapingJob represents a web scraping job
|
||||
type ScrapingJob struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// Job details
|
||||
URL string `json:"url" gorm:"not null"`
|
||||
JobType string `json:"job_type" gorm:"default:full_scrape"` // full_scrape, content_only, images_only, links_only
|
||||
Priority string `json:"priority" gorm:"default:normal"` // low, normal, high, urgent
|
||||
Status string `json:"status" gorm:"default:pending"` // pending, processing, completed, failed, cancelled
|
||||
|
||||
// Processing options
|
||||
ExtractImages bool `json:"extract_images" gorm:"default:true"`
|
||||
ExtractLinks bool `json:"extract_links" gorm:"default:true"`
|
||||
ExtractVideos bool `json:"extract_videos" gorm:"default:true"`
|
||||
GenerateSummary bool `json:"generate_summary" gorm:"default:true"`
|
||||
DownloadImages bool `json:"download_images" gorm:"default:false"`
|
||||
ExtractMetadata bool `json:"extract_metadata" gorm:"default:true"`
|
||||
|
||||
// Timing and results
|
||||
StartedAt *time.Time `json:"started_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
Progress float64 `json:"progress" gorm:"default:0"` // 0-100
|
||||
ErrorMessage string `json:"error_message"`
|
||||
|
||||
// Relationships
|
||||
ScrapedContentID *uint `json:"scraped_content_id,omitempty"`
|
||||
ScrapedContent *ScrapedContent `json:"scraped_content,omitempty" gorm:"foreignKey:ScrapedContentID"`
|
||||
}
|
||||
|
||||
// BeforeCreate hooks
|
||||
func (s *ScrapedContent) BeforeCreate(tx *gorm.DB) error {
|
||||
if s.Status == "" {
|
||||
s.Status = "pending"
|
||||
}
|
||||
if s.LastScraped.IsZero() {
|
||||
s.LastScraped = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *ScrapingJob) BeforeCreate(tx *gorm.DB) error {
|
||||
if j.Status == "" {
|
||||
j.Status = "pending"
|
||||
}
|
||||
if j.Priority == "" {
|
||||
j.Priority = "normal"
|
||||
}
|
||||
if j.JobType == "" {
|
||||
j.JobType = "full_scrape"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// YouTubeChannelCache represents cached YouTube channel data
|
||||
type YouTubeChannelCache struct {
|
||||
ID int `json:"id" gorm:"primaryKey"`
|
||||
ChannelID string `json:"channel_id" gorm:"uniqueIndex"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
ChannelURL string `json:"channel_url"`
|
||||
Videos string `json:"videos" gorm:"type:text"` // JSON array of videos
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName specifies the table name for YouTubeChannelCache
|
||||
func (YouTubeChannelCache) TableName() string {
|
||||
return "youtube_channel_cache"
|
||||
}
|
||||
|
||||
// IsExpired checks if the cache is older than 2 hours
|
||||
func (y *YouTubeChannelCache) IsExpired() bool {
|
||||
return time.Since(y.LastUpdated) > 2*time.Hour
|
||||
}
|
||||
Reference in New Issue
Block a user