4f3113199b
- JWT: validate signing algorithm (prevent alg confusion) - Login: rate limiting (10 attempts per 5 min per IP) - Request body: 10MB size limit (prevent DoS) - WebSocket: require JWT auth (token query param or cookie) - Daemon endpoints: require admin role (not just any user) - io.LimitReader on all request body decoding
83 lines
1.9 KiB
Go
83 lines
1.9 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(func(w http.ResponseWriter, r *http.Request) {
|
|
hub.HandleWebSocket(w, r, secret)
|
|
}))
|
|
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
|
|
}
|