package server import ( "fmt" "html/template" "strings" "github.com/gin-gonic/gin" "github.com/gorilla/sessions" "gobsidian/internal/config" "gobsidian/internal/handlers" "gobsidian/internal/models" "gobsidian/internal/utils" ) type Server struct { config *config.Config router *gin.Engine store *sessions.CookieStore } func New(cfg *config.Config) *Server { if !cfg.Debug { gin.SetMode(gin.ReleaseMode) } router := gin.Default() store := sessions.NewCookieStore([]byte(cfg.SecretKey)) s := &Server{ config: cfg, router: router, store: store, } s.setupRoutes() s.setupStaticFiles() s.setupTemplates() return s } func (s *Server) Start() error { // Ensure notes directory exists if err := utils.EnsureDir(s.config.NotesDir); err != nil { return fmt.Errorf("failed to create notes directory: %w", err) } // Ensure image storage directory exists for mode 2 if s.config.ImageStorageMode == 2 { if err := utils.EnsureDir(s.config.ImageStoragePath); err != nil { return fmt.Errorf("failed to create image storage directory: %w", err) } } addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port) fmt.Printf("Starting Gobsidian server on %s\n", addr) fmt.Printf("Notes directory: %s\n", s.config.NotesDir) return s.router.Run(addr) } func (s *Server) setupRoutes() { h := handlers.New(s.config, s.store) // Main routes s.router.GET("/", h.IndexHandler) s.router.GET("/folder/*path", h.FolderHandler) s.router.GET("/note/*path", h.NoteHandler) // File serving routes s.router.GET("/serve_attached_image/*path", h.ServeAttachedImageHandler) s.router.GET("/serve_stored_image/:filename", h.ServeStoredImageHandler) s.router.GET("/download/*path", h.DownloadHandler) s.router.GET("/view_text/*path", h.ViewTextHandler) // Upload routes s.router.POST("/upload", h.UploadHandler) // Settings routes s.router.GET("/settings", h.SettingsPageHandler) s.router.GET("/settings/image_storage", h.GetImageStorageSettingsHandler) s.router.POST("/settings/image_storage", h.PostImageStorageSettingsHandler) s.router.GET("/settings/notes_dir", h.GetNotesDirSettingsHandler) s.router.POST("/settings/notes_dir", h.PostNotesDirSettingsHandler) s.router.GET("/settings/file_extensions", h.GetFileExtensionsSettingsHandler) s.router.POST("/settings/file_extensions", h.PostFileExtensionsSettingsHandler) // Editor routes s.router.GET("/create", h.CreateNotePageHandler) s.router.POST("/create", h.CreateNoteHandler) s.router.GET("/edit/*path", h.EditNotePageHandler) s.router.POST("/edit/*path", h.EditNoteHandler) s.router.DELETE("/delete/*path", h.DeleteHandler) // API routes s.router.GET("/api/tree", h.TreeAPIHandler) } func (s *Server) setupStaticFiles() { s.router.Static("/static", "./web/static") s.router.StaticFile("/favicon.ico", "./web/static/favicon.ico") } func (s *Server) setupTemplates() { // Add template functions funcMap := template.FuncMap{ "formatSize": utils.FormatFileSize, "formatTime": utils.FormatTime, "join": strings.Join, "contains": func(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false }, "add": func(a, b int) int { return a + b }, "sub": func(a, b int) int { return a - b }, "fileTypeClass": func(fileType models.FileType) string { switch fileType { case models.FileTypeMarkdown: return "text-blue-400" case models.FileTypeDirectory: return "text-yellow-400" case models.FileTypeImage: return "text-green-400" case models.FileTypeText: return "text-gray-400" default: return "text-gray-500" } }, "fileTypeIcon": func(fileType models.FileType) string { switch fileType { case models.FileTypeMarkdown: return "📝" case models.FileTypeDirectory: return "📁" case models.FileTypeImage: return "🖼️" case models.FileTypeText: return "📄" default: return "📄" } }, "dict": func(values ...interface{}) map[string]interface{} { if len(values)%2 != 0 { return nil } dict := make(map[string]interface{}, len(values)/2) for i := 0; i < len(values); i += 2 { key, ok := values[i].(string) if !ok { return nil } dict[key] = values[i+1] } return dict }, "safeHTML": func(s string) template.HTML { return template.HTML(s) }, } // Load templates - make sure base.html is loaded with all the other templates templates := template.Must(template.New("").Funcs(funcMap).ParseGlob("web/templates/*.html")) s.router.SetHTMLTemplate(templates) fmt.Printf("DEBUG: Templates loaded successfully\n") }