Files
markdown-hub/cmd/server/main.go
T
anders ed4d0b261f Real-time collaboration (Yjs + WebSocket)
- Go WebSocket hub: rooms per document, broadcast updates, persist state
- Yjs integration: connect/disconnect, sync document state
- Collab toggle button in toolbar (Solo/Live)
- When Live: edits broadcast to all connected users in real-time
- Yjs state persisted to SQLite (survives server restart)
- gorilla/websocket dependency added
2026-05-22 23:49:12 +02:00

81 lines
1.8 KiB
Go

package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/google/uuid"
"markdownhub/internal/api"
"markdownhub/internal/auth"
"markdownhub/internal/collab"
"markdownhub/internal/db"
"markdownhub/internal/files"
)
func main() {
dataDir := envOr("MH_DATA_DIR", "./data")
port := envOr("MH_PORT", "8080")
secret := envOr("MH_SECRET", "dev-secret-change-me")
database, err := db.Open(dataDir + "/db/markdownhub.db")
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
defer database.Close()
if err := db.Migrate(database); err != nil {
log.Fatalf("Failed to run migrations: %v", err)
}
ensureAdminUser(database, dataDir)
router := api.NewRouter(database, dataDir, secret)
// Collab WebSocket hub
hub := collab.NewHub(database)
mux := http.NewServeMux()
mux.Handle("/ws/collab/", http.HandlerFunc(hub.HandleWebSocket))
mux.Handle("/", router)
fmt.Printf("MarkdownHub listening on :%s\n", port)
log.Fatal(http.ListenAndServe(":"+port, mux))
}
func ensureAdminUser(database *db.DB, dataDir string) {
var count int
database.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
if count > 0 {
return
}
email := envOr("MH_ADMIN_EMAIL", "admin@localhost")
password := envOr("MH_ADMIN_PASSWORD", "admin")
hash, err := auth.HashPassword(password)
if err != nil {
log.Fatalf("Failed to hash admin password: %v", err)
}
id := uuid.New().String()
_, err = database.Exec(
"INSERT INTO users (id, username, email, password_hash, is_admin) VALUES (?, ?, ?, ?, ?)",
id, "admin", email, hash, true,
)
if err != nil {
log.Fatalf("Failed to create admin user: %v", err)
}
files.EnsureUserDir(dataDir, id)
fmt.Printf("Created admin user: %s\n", email)
}
func envOr(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}