Files
codegirl.games-hugo/cms/cmd/server/main.go
T
Cursor Agentandcodegirl007 ce15f54a52 Add lightweight self-hosted Hugo CMS with GitHub API backend
Implements a Go-based CMS for editing Hugo posts and uploading images
via the GitHub REST API, with no database and Git as the source of truth.

Features:
- Single admin auth with bcrypt password and signed session cookies
- Dashboard, post listing with search, create/edit with EasyMDE editor
- Media library for static/uploads/ with drag-drop and clipboard paste
- Client-side autosave, unsaved changes warning, dark mode, responsive UI
- Modular packages for auth, github, posts, media, session, and handlers

Co-authored-by: codegirl007 <s.raide@gmail.com>
2026-07-06 18:56:17 +00:00

70 lines
2.1 KiB
Go

package main
import (
"log"
"net/http"
"github.com/codegirl-007/hugo-cms/internal/auth"
"github.com/codegirl-007/hugo-cms/internal/config"
"github.com/codegirl-007/hugo-cms/internal/github"
"github.com/codegirl-007/hugo-cms/internal/handlers"
"github.com/codegirl-007/hugo-cms/internal/media"
"github.com/codegirl-007/hugo-cms/internal/posts"
"github.com/codegirl-007/hugo-cms/internal/session"
"github.com/codegirl-007/hugo-cms/internal/templates"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatalf("config: %v", err)
}
gh := github.NewClient(cfg.GitHubOwner, cfg.GitHubRepo, cfg.GitHubBranch, cfg.GitHubToken)
authenticator := auth.New(cfg.AdminUsername, cfg.AdminPasswordHash)
sessions := session.NewStore(cfg.SessionSecret, cfg.CookieSecure)
postService := posts.NewService(gh)
mediaService := media.NewService(gh)
renderer, err := templates.New()
if err != nil {
log.Fatalf("templates: %v", err)
}
h := handlers.New(authenticator, sessions, postService, mediaService, renderer)
mux := http.NewServeMux()
// Public routes
mux.HandleFunc("GET /login", h.LoginPage)
mux.HandleFunc("POST /login", h.Login)
mux.HandleFunc("POST /logout", h.Logout)
mux.Handle("GET /assets/", http.StripPrefix("/assets/", templates.StaticHandler()))
// Protected admin routes
mux.HandleFunc("GET /admin", h.Auth(h.Dashboard))
mux.HandleFunc("GET /admin/posts", h.Auth(h.PostsList))
mux.HandleFunc("GET /admin/posts/new", h.Auth(h.PostNew))
mux.HandleFunc("GET /admin/posts/{slug}", h.Auth(h.PostEdit))
mux.HandleFunc("GET /admin/media", h.Auth(h.MediaLibrary))
// Protected API routes
mux.HandleFunc("POST /api/posts/save", h.Auth(h.SavePost))
mux.HandleFunc("POST /api/media/upload", h.Auth(h.UploadMedia))
mux.HandleFunc("GET /api/media", h.Auth(h.ListMedia))
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/admin", http.StatusSeeOther)
})
server := &http.Server{
Addr: cfg.Addr,
Handler: mux,
}
log.Printf("CMS server listening on %s", cfg.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server: %v", err)
}
}