mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
The restore handler was overwriting the live SQLite database while the old *sql.DB handle remained open. Now: broadcasts server_restart to clients, checkpoints WAL, closes the DB connection, then copies the backup file over the closed database. Server must restart after restore.
69 lines
3.0 KiB
Go
69 lines
3.0 KiB
Go
package admin
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/owncord/server/auth"
|
|
"github.com/owncord/server/db"
|
|
"github.com/owncord/server/updater"
|
|
)
|
|
|
|
// ─── NewAdminAPI ──────────────────────────────────────────────────────────────
|
|
|
|
// NewAdminAPI returns a chi router with all /admin/api/* routes. All routes
|
|
// are protected by adminAuthMiddleware which requires the ADMINISTRATOR bit,
|
|
// except for the setup endpoints which are unauthenticated.
|
|
func NewAdminAPI(database *db.DB, version string, hub HubBroadcaster, u *updater.Updater, logBuf *RingBuffer) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
// Setup endpoints — unauthenticated, only functional when no users exist.
|
|
setupLimiter := auth.NewRateLimiter()
|
|
r.Get("/setup/status", handleSetupStatus(database))
|
|
r.Post("/setup", handleSetup(database, setupLimiter))
|
|
|
|
// SSE log stream — auth is via a single-use ticket from POST /logs/ticket.
|
|
// EventSource cannot send Authorization headers, so the client first
|
|
// obtains a short-lived ticket via the authenticated ticket endpoint,
|
|
// then passes it as ?ticket= to the SSE stream.
|
|
if logBuf != nil {
|
|
r.Get("/logs/stream", handleLogStream(database, logBuf))
|
|
}
|
|
|
|
// All remaining routes require authentication and ADMINISTRATOR permission.
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(adminAuthMiddleware(database))
|
|
|
|
// Log stream ticket — issues a single-use, 30s TTL ticket for SSE auth.
|
|
r.Post("/logs/ticket", handleLogTicket(database))
|
|
|
|
r.Get("/stats", handleGetStats(database, hub))
|
|
r.Get("/users", handleListUsers(database))
|
|
r.Patch("/users/{id}", handlePatchUser(database, hub))
|
|
r.Delete("/users/{id}/sessions", handleForceLogout(database))
|
|
r.Get("/channels", handleListChannels(database))
|
|
r.Post("/channels", handleCreateChannel(database, hub))
|
|
r.Patch("/channels/{id}", handlePatchChannel(database, hub))
|
|
r.Delete("/channels/{id}", handleDeleteChannel(database, hub))
|
|
r.Get("/audit-log", handleGetAuditLog(database))
|
|
r.Get("/settings", handleGetSettings(database))
|
|
r.Patch("/settings", handlePatchSettings(database))
|
|
r.Post("/backup", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
ownerOnlyMiddleware(database, handleBackup(database)).ServeHTTP(w, req)
|
|
}))
|
|
r.Get("/backups", handleListBackups())
|
|
r.Delete("/backups/{name}", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
ownerOnlyMiddleware(database, handleDeleteBackup(database)).ServeHTTP(w, req)
|
|
}))
|
|
r.Post("/backups/{name}/restore", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
ownerOnlyMiddleware(database, handleRestoreBackup(database, hub)).ServeHTTP(w, req)
|
|
}))
|
|
r.Get("/updates", handleCheckUpdate(u))
|
|
r.Post("/updates/apply", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
ownerOnlyMiddleware(database, handleApplyUpdate(u, hub, version)).ServeHTTP(w, req)
|
|
}))
|
|
})
|
|
|
|
return r
|
|
}
|