mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
- db: channel_queries (ListChannels, GetChannel, CRUD, permissions),
message_queries (CreateMessage, GetMessage, GetMessages paginated,
EditMessage, DeleteMessage soft, AddReaction, RemoveReaction,
GetReactions, SearchMessages FTS5, UpdateReadState)
- db: fix in-memory DB isolation — SetMaxOpenConns(1) for :memory: path
- ws/hub: replace stub with full Hub (register/unregister, broadcast to
channel/all, send to user, thread-safe, buffered broadcast channel)
- ws/client: Client with send channel, NewTestClient helpers for tests
- ws/handlers: dispatch chat_send/edit/delete, reaction_add/remove,
typing_start, presence_update — all with rate limiting and permission checks
- ws/messages: JSON builder helpers for all server→client message types
- ws/serve: ServeWS HTTP handler, WS auth handshake (10s timeout),
ready payload, writePump/readPump goroutines, graceful disconnect
- api: channel_handler — GET /channels, GET /channels/{id}/messages,
GET /search; fixed double-mount of /api/v1 route group
- api/router: mount channel routes, start hub, register /api/v1/ws
Test coverage: api 77.6%, auth 90.9%, db 84.2%, ws 26.7% (serve.go
requires live WS connection; hub/handlers/messages fully covered)
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
// Package api provides the HTTP router and handlers for the OwnCord server.
|
|
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/owncord/server/auth"
|
|
"github.com/owncord/server/config"
|
|
"github.com/owncord/server/db"
|
|
"github.com/owncord/server/ws"
|
|
)
|
|
|
|
// version is the server version string, overridden at build time via ldflags.
|
|
var version = "dev"
|
|
|
|
// NewRouter builds and returns the fully configured HTTP handler.
|
|
func NewRouter(cfg *config.Config, database *db.DB) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
// Middleware stack.
|
|
r.Use(middleware.RequestID)
|
|
r.Use(setRequestIDHeader) // echo request ID into response header
|
|
r.Use(middleware.RealIP)
|
|
r.Use(middleware.Recoverer)
|
|
|
|
// Health check — unauthenticated, no versioning prefix.
|
|
r.Get("/health", handleHealth)
|
|
|
|
// Shared rate limiter for auth endpoints.
|
|
limiter := auth.NewRateLimiter()
|
|
|
|
// Versioned API routes.
|
|
r.Route("/api/v1", func(r chi.Router) {
|
|
r.Get("/info", handleInfo(cfg))
|
|
})
|
|
|
|
// Auth routes: register, login, logout, me.
|
|
MountAuthRoutes(r, database, limiter)
|
|
|
|
// Invite management routes (require MANAGE_INVITES permission).
|
|
MountInviteRoutes(r, database)
|
|
|
|
// Channel and message REST routes.
|
|
MountChannelRoutes(r, database)
|
|
|
|
// WebSocket hub — WS does its own in-band auth, so no AuthMiddleware here.
|
|
hub := ws.NewHub(database, limiter)
|
|
go hub.Run()
|
|
r.Get("/api/v1/ws", ws.ServeWS(hub, database))
|
|
|
|
return r
|
|
}
|
|
|
|
// healthResponse is the JSON shape returned by GET /health.
|
|
type healthResponse struct {
|
|
Status string `json:"status"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
// infoResponse is the JSON shape returned by GET /api/v1/info.
|
|
type infoResponse struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
func handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(w, http.StatusOK, healthResponse{
|
|
Status: "ok",
|
|
Version: version,
|
|
})
|
|
}
|
|
|
|
func handleInfo(cfg *config.Config) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(w, http.StatusOK, infoResponse{
|
|
Name: cfg.Server.Name,
|
|
Version: version,
|
|
})
|
|
}
|
|
}
|
|
|
|
// setRequestIDHeader copies the request ID from context into the response header.
|
|
func setRequestIDHeader(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
requestID := middleware.GetReqID(r.Context())
|
|
if requestID != "" {
|
|
w.Header().Set("X-Request-Id", requestID)
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// writeJSON encodes v as JSON and writes it to w with the given status code.
|
|
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(v)
|
|
}
|