mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
small fix, don't worry about it
This commit is contained in:
+33
-33
@@ -31,22 +31,22 @@ const (
|
||||
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"
|
||||
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
|
||||
@@ -57,16 +57,16 @@ type AuditLog struct {
|
||||
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"`
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID;-:migration"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -75,20 +75,20 @@ type AuditLog struct {
|
||||
NewValues map[string]interface{} `json:"new_values" gorm:"serializer:json"`
|
||||
|
||||
// Security context
|
||||
SessionID string `json:"session_id"`
|
||||
Success bool `json:"success" gorm:"default:true"`
|
||||
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"`
|
||||
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"`
|
||||
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
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ControlServiceSession stores the opaque hq.trackeep.org token for a Trackeep user.
|
||||
type ControlServiceSession 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:"column:user_id;not null;uniqueIndex"`
|
||||
ControllerUserID int `json:"controller_user_id" gorm:"column:controller_user_id;not null;index"`
|
||||
GitHubID int `json:"github_id" gorm:"column:github_id;index"`
|
||||
Username string `json:"username" gorm:"size:255"`
|
||||
Email string `json:"email" gorm:"size:255"`
|
||||
Token string `json:"-" gorm:"type:text;not null"`
|
||||
|
||||
LastValidatedAt *time.Time `json:"last_validated_at"`
|
||||
|
||||
User User `json:"-" gorm:"foreignKey:UserID;-:migration"`
|
||||
}
|
||||
@@ -14,7 +14,7 @@ type GitHubAppInstallState struct {
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID;-:migration"`
|
||||
|
||||
State string `json:"state" gorm:"not null;size:128;uniqueIndex"`
|
||||
ExpiresAt time.Time `json:"expires_at" gorm:"not null;index"`
|
||||
@@ -29,7 +29,7 @@ type GitHubAppInstallation struct {
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID;-:migration"`
|
||||
|
||||
InstallationID int64 `json:"installation_id" gorm:"not null;uniqueIndex"`
|
||||
AppSlug string `json:"app_slug" gorm:"size:255"`
|
||||
@@ -46,7 +46,7 @@ type GitHubRepoBackup struct {
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
|
||||
UserID uint `json:"user_id" gorm:"not null;index:idx_github_backup_user_repo,unique"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID;-:migration"`
|
||||
|
||||
RepositoryID int64 `json:"repository_id" gorm:"index"`
|
||||
RepositoryName string `json:"repository_name" gorm:"size:255"`
|
||||
@@ -54,7 +54,7 @@ type GitHubRepoBackup struct {
|
||||
DefaultBranch string `json:"default_branch" gorm:"size:255"`
|
||||
CloneURL string `json:"clone_url" gorm:"type:text"`
|
||||
LocalPath string `json:"local_path" gorm:"not null;type:text"`
|
||||
Source string `json:"source" gorm:"not null;size:32"` // oauth or github_app
|
||||
Source string `json:"source" gorm:"not null;size:32"` // github_user or github_app
|
||||
InstallationID *int64 `json:"installation_id,omitempty"`
|
||||
LastBackupAt *time.Time `json:"last_backup_at"`
|
||||
LastBackupStatus string `json:"last_backup_status" gorm:"not null;default:'pending';size:32"` // pending, success, error
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GitHubUserAuth stores encrypted GitHub App user tokens for a Trackeep user.
|
||||
type GitHubUserAuth 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:"column:user_id;not null;uniqueIndex"`
|
||||
GitHubUserID int `json:"github_user_id" gorm:"column:github_user_id;not null;uniqueIndex"`
|
||||
GitHubLogin string `json:"github_login" gorm:"column:github_login;not null;size:255"`
|
||||
|
||||
AccessToken string `json:"-" gorm:"type:text;not null"`
|
||||
RefreshToken string `json:"-" gorm:"type:text"`
|
||||
|
||||
AccessTokenExpiresAt *time.Time `json:"access_token_expires_at"`
|
||||
RefreshTokenExpiresAt *time.Time `json:"refresh_token_expires_at"`
|
||||
LastRefreshedAt *time.Time `json:"last_refreshed_at"`
|
||||
|
||||
User User `json:"-" gorm:"foreignKey:UserID;-:migration"`
|
||||
}
|
||||
+226
-116
@@ -1,8 +1,12 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/trackeep/backend/config"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// DB is the global database instance
|
||||
@@ -13,120 +17,226 @@ func InitDB() {
|
||||
DB = config.GetDB()
|
||||
}
|
||||
|
||||
// AutoMigrate runs database migrations for all models
|
||||
func AutoMigrate() {
|
||||
db := config.GetDB()
|
||||
|
||||
// Auto migrate all models
|
||||
db.AutoMigrate(
|
||||
&User{},
|
||||
&Tag{},
|
||||
&Bookmark{},
|
||||
&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{},
|
||||
&UserSearchSettings{},
|
||||
&UserUpdateSettings{},
|
||||
&AITagSuggestion{},
|
||||
&AIContentGeneration{},
|
||||
&AICodeReview{},
|
||||
&AILearningRecommendation{},
|
||||
// Advanced AI Recommendation models
|
||||
&AIRecommendation{},
|
||||
&UserPreference{},
|
||||
&RecommendationInteraction{},
|
||||
// Integration models
|
||||
&Integration{},
|
||||
&SyncLog{},
|
||||
&WebhookEvent{},
|
||||
&GitHubAppInstallState{},
|
||||
&GitHubAppInstallation{},
|
||||
&GitHubRepoBackup{},
|
||||
// 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{},
|
||||
// Messaging models
|
||||
&Conversation{},
|
||||
&ConversationMember{},
|
||||
&Message{},
|
||||
&MessageAttachment{},
|
||||
&MessageReference{},
|
||||
&MessageSuggestion{},
|
||||
&MessageReaction{},
|
||||
&PasswordVaultItem{},
|
||||
&PasswordVaultShare{},
|
||||
)
|
||||
func tableHasColumn(db *gorm.DB, tableName, columnName string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw(
|
||||
`SELECT count(*)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = current_schema()
|
||||
AND table_name = ?
|
||||
AND column_name = ?`,
|
||||
tableName,
|
||||
columnName,
|
||||
).Scan(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func tableExists(db *gorm.DB, tableName string) (bool, error) {
|
||||
var count int64
|
||||
err := db.Raw(
|
||||
`SELECT count(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema()
|
||||
AND table_name = ?`,
|
||||
tableName,
|
||||
).Scan(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func repairLegacyBootstrapSchema(db *gorm.DB) error {
|
||||
if db == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
usersTableExists, err := tableExists(db, "users")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !usersTableExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
hasLegacyPasswordHash, err := tableHasColumn(db, "users", "password_hash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hasLegacyPasswordHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
hasCurrentPasswordColumn, err := tableHasColumn(db, "users", "password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasCurrentPasswordColumn {
|
||||
return nil
|
||||
}
|
||||
|
||||
var userCount int64
|
||||
if err := db.Table("users").Count(&userCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if userCount > 0 {
|
||||
return fmt.Errorf("legacy bootstrap schema detected with %d existing users; manual migration is required", userCount)
|
||||
}
|
||||
|
||||
log.Println("Legacy bootstrap schema detected with no users; dropping stale UUID-based tables before auto-migration")
|
||||
return db.Exec(`DROP TABLE IF EXISTS
|
||||
file_tags,
|
||||
note_tags,
|
||||
task_tags,
|
||||
bookmark_tags,
|
||||
audit_logs,
|
||||
files,
|
||||
notes,
|
||||
tasks,
|
||||
bookmarks,
|
||||
tags,
|
||||
users
|
||||
CASCADE`).Error
|
||||
}
|
||||
|
||||
// AutoMigrate runs database migrations for all models
|
||||
func AutoMigrate() error {
|
||||
db := config.GetDB()
|
||||
if db == nil {
|
||||
return fmt.Errorf("database not initialized")
|
||||
}
|
||||
|
||||
if err := repairLegacyBootstrapSchema(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The pgx simple-protocol path used by GORM's PostgreSQL migrator can fail
|
||||
// schema introspection (`SELECT * ... LIMIT 1`) with "insufficient arguments".
|
||||
// Running migrations with prepared statements enabled avoids that path and
|
||||
// allows startup migrations to create missing production tables reliably.
|
||||
migrationDB := db.Session(&gorm.Session{PrepareStmt: true})
|
||||
|
||||
models := []struct {
|
||||
name string
|
||||
model interface{}
|
||||
}{
|
||||
{name: "User", model: &User{}},
|
||||
{name: "Tag", model: &Tag{}},
|
||||
{name: "Bookmark", model: &Bookmark{}},
|
||||
{name: "Task", model: &Task{}},
|
||||
{name: "File", model: &File{}},
|
||||
{name: "Note", model: &Note{}},
|
||||
{name: "APIKey", model: &APIKey{}},
|
||||
{name: "BrowserExtension", model: &BrowserExtension{}},
|
||||
{name: "TimeEntry", model: &TimeEntry{}},
|
||||
{name: "FileAnalysis", model: &FileAnalysis{}},
|
||||
{name: "ChatSession", model: &ChatSession{}},
|
||||
{name: "ChatMessage", model: &ChatMessage{}},
|
||||
{name: "LearningPath", model: &LearningPath{}},
|
||||
{name: "LearningModule", model: &LearningModule{}},
|
||||
{name: "ModuleResource", model: &ModuleResource{}},
|
||||
{name: "Enrollment", model: &Enrollment{}},
|
||||
{name: "Progress", model: &Progress{}},
|
||||
{name: "Course", model: &Course{}},
|
||||
{name: "LearningPathCourse", model: &LearningPathCourse{}},
|
||||
{name: "CalendarEvent", model: &CalendarEvent{}},
|
||||
{name: "RecurrenceRule", model: &RecurrenceRule{}},
|
||||
{name: "CalendarSettings", model: &CalendarSettings{}},
|
||||
{name: "ContentEmbedding", model: &ContentEmbedding{}},
|
||||
{name: "SavedSearch", model: &SavedSearch{}},
|
||||
{name: "SavedSearchTag", model: &SavedSearchTag{}},
|
||||
{name: "SearchAnalytics", model: &SearchAnalytics{}},
|
||||
{name: "SearchSuggestion", model: &SearchSuggestion{}},
|
||||
{name: "AISummary", model: &AISummary{}},
|
||||
{name: "AITaskSuggestion", model: &AITaskSuggestion{}},
|
||||
{name: "UserAISettings", model: &UserAISettings{}},
|
||||
{name: "UserSearchSettings", model: &UserSearchSettings{}},
|
||||
{name: "UserUpdateSettings", model: &UserUpdateSettings{}},
|
||||
{name: "AITagSuggestion", model: &AITagSuggestion{}},
|
||||
{name: "AIContentGeneration", model: &AIContentGeneration{}},
|
||||
{name: "AICodeReview", model: &AICodeReview{}},
|
||||
{name: "AILearningRecommendation", model: &AILearningRecommendation{}},
|
||||
{name: "AIRecommendation", model: &AIRecommendation{}},
|
||||
{name: "UserPreference", model: &UserPreference{}},
|
||||
{name: "RecommendationInteraction", model: &RecommendationInteraction{}},
|
||||
{name: "Integration", model: &Integration{}},
|
||||
{name: "SyncLog", model: &SyncLog{}},
|
||||
{name: "WebhookEvent", model: &WebhookEvent{}},
|
||||
{name: "ControlServiceSession", model: &ControlServiceSession{}},
|
||||
{name: "GitHubUserAuth", model: &GitHubUserAuth{}},
|
||||
{name: "GitHubAppInstallState", model: &GitHubAppInstallState{}},
|
||||
{name: "GitHubAppInstallation", model: &GitHubAppInstallation{}},
|
||||
{name: "GitHubRepoBackup", model: &GitHubRepoBackup{}},
|
||||
{name: "Analytics", model: &Analytics{}},
|
||||
{name: "ProductivityMetrics", model: &ProductivityMetrics{}},
|
||||
{name: "LearningAnalytics", model: &LearningAnalytics{}},
|
||||
{name: "ContentAnalytics", model: &ContentAnalytics{}},
|
||||
{name: "GitHubAnalytics", model: &GitHubAnalytics{}},
|
||||
{name: "HabitAnalytics", model: &HabitAnalytics{}},
|
||||
{name: "Goal", model: &Goal{}},
|
||||
{name: "Milestone", model: &Milestone{}},
|
||||
{name: "AnalyticsReport", model: &AnalyticsReport{}},
|
||||
{name: "Skill", model: &Skill{}},
|
||||
{name: "Project", model: &Project{}},
|
||||
{name: "ProjectTag", model: &ProjectTag{}},
|
||||
{name: "SocialLink", model: &SocialLink{}},
|
||||
{name: "Follow", model: &Follow{}},
|
||||
{name: "Team", model: &Team{}},
|
||||
{name: "TeamMember", model: &TeamMember{}},
|
||||
{name: "TeamInvitation", model: &TeamInvitation{}},
|
||||
{name: "TeamProject", model: &TeamProject{}},
|
||||
{name: "TeamProjectTag", model: &TeamProjectTag{}},
|
||||
{name: "TeamBookmark", model: &TeamBookmark{}},
|
||||
{name: "TeamNote", model: &TeamNote{}},
|
||||
{name: "TeamTask", model: &TeamTask{}},
|
||||
{name: "TeamFile", model: &TeamFile{}},
|
||||
{name: "TeamActivity", model: &TeamActivity{}},
|
||||
{name: "AuditLog", model: &AuditLog{}},
|
||||
{name: "MarketplaceItem", model: &MarketplaceItem{}},
|
||||
{name: "MarketplaceTag", model: &MarketplaceTag{}},
|
||||
{name: "MarketplaceReview", model: &MarketplaceReview{}},
|
||||
{name: "MarketplacePurchase", model: &MarketplacePurchase{}},
|
||||
{name: "ContentShare", model: &ContentShare{}},
|
||||
{name: "Challenge", model: &Challenge{}},
|
||||
{name: "ChallengeParticipant", model: &ChallengeParticipant{}},
|
||||
{name: "ChallengeTeam", model: &ChallengeTeam{}},
|
||||
{name: "ChallengeMilestone", model: &ChallengeMilestone{}},
|
||||
{name: "ChallengeMilestoneCompletion", model: &ChallengeMilestoneCompletion{}},
|
||||
{name: "ChallengeResource", model: &ChallengeResource{}},
|
||||
{name: "ChallengeTag", model: &ChallengeTag{}},
|
||||
{name: "Mentorship", model: &Mentorship{}},
|
||||
{name: "MentorshipSession", model: &MentorshipSession{}},
|
||||
{name: "MentorshipReview", model: &MentorshipReview{}},
|
||||
{name: "MentorshipMilestone", model: &MentorshipMilestone{}},
|
||||
{name: "MentorshipRequest", model: &MentorshipRequest{}},
|
||||
{name: "YouTubeChannelCache", model: &YouTubeChannelCache{}},
|
||||
{name: "VideoBookmark", model: &VideoBookmark{}},
|
||||
{name: "Conversation", model: &Conversation{}},
|
||||
{name: "ConversationMember", model: &ConversationMember{}},
|
||||
{name: "Message", model: &Message{}},
|
||||
{name: "MessageAttachment", model: &MessageAttachment{}},
|
||||
{name: "MessageReference", model: &MessageReference{}},
|
||||
{name: "MessageSuggestion", model: &MessageSuggestion{}},
|
||||
{name: "MessageReaction", model: &MessageReaction{}},
|
||||
{name: "PasswordVaultItem", model: &PasswordVaultItem{}},
|
||||
{name: "PasswordVaultShare", model: &PasswordVaultShare{}},
|
||||
}
|
||||
|
||||
criticalModels := map[string]bool{
|
||||
"User": true,
|
||||
"ControlServiceSession": true,
|
||||
"GitHubUserAuth": true,
|
||||
"GitHubAppInstallState": true,
|
||||
"GitHubAppInstallation": true,
|
||||
"GitHubRepoBackup": true,
|
||||
"AuditLog": true,
|
||||
}
|
||||
|
||||
for _, entry := range models {
|
||||
if err := migrationDB.Omit(clause.Associations).AutoMigrate(entry.model); err != nil {
|
||||
if criticalModels[entry.name] {
|
||||
return fmt.Errorf("auto-migrate %s: %w", entry.name, err)
|
||||
}
|
||||
log.Printf("Warning: skipping auto-migrate for %s: %v", entry.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/trackeep/backend/config"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -17,7 +18,7 @@ type UserUpdateSettings struct {
|
||||
User User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
|
||||
// OAuth Service Configuration
|
||||
OAuthServiceURL string `json:"oauth_service_url" gorm:"default:https://oauth.trackeep.org"`
|
||||
OAuthServiceURL string `json:"oauth_service_url" gorm:"default:https://hq.trackeep.org"`
|
||||
|
||||
// Update Configuration
|
||||
AutoUpdateCheck bool `json:"auto_update_check" gorm:"default:false"`
|
||||
@@ -34,7 +35,7 @@ func GetUserUpdateSettings(userID uint) (*UserUpdateSettings, error) {
|
||||
// Create default settings
|
||||
settings = UserUpdateSettings{
|
||||
UserID: userID,
|
||||
OAuthServiceURL: "https://oauth.trackeep.org",
|
||||
OAuthServiceURL: config.ControlServiceURL,
|
||||
AutoUpdateCheck: false,
|
||||
UpdateCheckInterval: "24h",
|
||||
PrereleaseUpdates: false,
|
||||
@@ -47,6 +48,14 @@ func GetUserUpdateSettings(userID uint) (*UserUpdateSettings, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if settings.OAuthServiceURL != config.ControlServiceURL {
|
||||
settings.OAuthServiceURL = config.ControlServiceURL
|
||||
if err := DB.Model(&settings).Update("oauth_service_url", config.ControlServiceURL).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ type User struct {
|
||||
FullName string `json:"full_name"`
|
||||
Role string `json:"role" gorm:"default:user"` // user, admin
|
||||
|
||||
// GitHub OAuth fields
|
||||
GitHubID int `json:"github_id" gorm:"uniqueIndex"`
|
||||
// GitHub sign-in fields
|
||||
GitHubID int `json:"github_id" gorm:"column:github_id;uniqueIndex"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Provider string `json:"provider" gorm:"default:email"` // email, github
|
||||
|
||||
|
||||
Reference in New Issue
Block a user