All checks were successful
check / check (push) Successful in 2m24s
Complete rename of the application from `chat` to `neoirc` with binary name `neoircd`. closes #46 ## Changes - **Go module path**: `git.eeqj.de/sneak/chat` → `git.eeqj.de/sneak/neoirc` - **Server binary**: `chatd` → `neoircd` - **CLI binary**: `chat-cli` → `neoirc-cli` - **Cmd directories**: `cmd/chatd` → `cmd/neoircd`, `cmd/chat-cli` → `cmd/neoirc-cli` - **Go package**: `chatapi` → `neoircapi` - **Makefile**: binary name, build targets, docker image tag, clean target - **Dockerfile**: binary paths, user/group names (`chat` → `neoirc`), ENTRYPOINT - **`.gitignore`/`.dockerignore`**: artifact names - **All Go imports and doc comments** - **Default server name**: `chat` → `neoirc` - **Web client**: localStorage keys (`chat_token`/`chat_channels` → `neoirc_token`/`neoirc_channels`), page title, default server display name - **Schema files**: all `$id` URLs and example hostnames - **README.md**: project name, all binary references, examples, directory tree - **AGENTS.md**: build command reference - **Test fixtures**: app name and channel names Docker build passes. All tests pass. <!-- session: agent:sdlc-manager:subagent:a4b8dbd3-a7c8-4fad-8239-bb5a64a9b3d6 --> Co-authored-by: clawbot <clawbot@noreply.eeqj.de> Reviewed-on: #47 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
169 lines
3.1 KiB
Go
169 lines
3.1 KiB
Go
package server
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/neoirc/web"
|
|
|
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
|
"github.com/go-chi/chi"
|
|
"github.com/go-chi/chi/middleware"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
const routeTimeout = 60 * time.Second
|
|
|
|
// SetupRoutes configures the HTTP routes and middleware.
|
|
func (srv *Server) SetupRoutes() {
|
|
srv.router = chi.NewRouter()
|
|
|
|
srv.router.Use(middleware.Recoverer)
|
|
srv.router.Use(middleware.RequestID)
|
|
srv.router.Use(srv.mw.Logging())
|
|
|
|
if viper.GetString("METRICS_USERNAME") != "" {
|
|
srv.router.Use(srv.mw.Metrics())
|
|
}
|
|
|
|
srv.router.Use(srv.mw.CORS())
|
|
srv.router.Use(middleware.Timeout(routeTimeout))
|
|
|
|
if srv.sentryEnabled {
|
|
sentryHandler := sentryhttp.New(
|
|
sentryhttp.Options{ //nolint:exhaustruct // optional fields
|
|
Repanic: true,
|
|
},
|
|
)
|
|
|
|
srv.router.Use(sentryHandler.Handle)
|
|
}
|
|
|
|
// Health check.
|
|
srv.router.Get(
|
|
"/.well-known/healthcheck.json",
|
|
srv.handlers.HandleHealthCheck(),
|
|
)
|
|
|
|
// Protected metrics endpoint.
|
|
if viper.GetString("METRICS_USERNAME") != "" {
|
|
srv.router.Group(func(router chi.Router) {
|
|
router.Use(srv.mw.MetricsAuth())
|
|
router.Get("/metrics",
|
|
http.HandlerFunc(
|
|
promhttp.Handler().ServeHTTP,
|
|
))
|
|
})
|
|
}
|
|
|
|
// API v1.
|
|
srv.router.Route("/api/v1", srv.setupAPIv1)
|
|
|
|
// Serve embedded SPA.
|
|
srv.setupSPA()
|
|
}
|
|
|
|
func (srv *Server) setupAPIv1(router chi.Router) {
|
|
router.Get(
|
|
"/server",
|
|
srv.handlers.HandleServerInfo(),
|
|
)
|
|
router.Post(
|
|
"/session",
|
|
srv.handlers.HandleCreateSession(),
|
|
)
|
|
router.Post(
|
|
"/register",
|
|
srv.handlers.HandleRegister(),
|
|
)
|
|
router.Post(
|
|
"/login",
|
|
srv.handlers.HandleLogin(),
|
|
)
|
|
router.Get(
|
|
"/state",
|
|
srv.handlers.HandleState(),
|
|
)
|
|
router.Post(
|
|
"/logout",
|
|
srv.handlers.HandleLogout(),
|
|
)
|
|
router.Get(
|
|
"/users/me",
|
|
srv.handlers.HandleUsersMe(),
|
|
)
|
|
router.Get(
|
|
"/messages",
|
|
srv.handlers.HandleGetMessages(),
|
|
)
|
|
router.Post(
|
|
"/messages",
|
|
srv.handlers.HandleSendCommand(),
|
|
)
|
|
router.Get(
|
|
"/history",
|
|
srv.handlers.HandleGetHistory(),
|
|
)
|
|
router.Get(
|
|
"/channels",
|
|
srv.handlers.HandleListAllChannels(),
|
|
)
|
|
router.Get(
|
|
"/channels/{channel}/members",
|
|
srv.handlers.HandleChannelMembers(),
|
|
)
|
|
}
|
|
|
|
func (srv *Server) setupSPA() {
|
|
distFS, err := fs.Sub(web.Dist, "dist")
|
|
if err != nil {
|
|
srv.log.Error(
|
|
"failed to get web dist filesystem",
|
|
"error", err,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
fileServer := http.FileServer(http.FS(distFS))
|
|
|
|
srv.router.Get("/*", func(
|
|
writer http.ResponseWriter,
|
|
request *http.Request,
|
|
) {
|
|
readFS, ok := distFS.(fs.ReadFileFS)
|
|
if !ok {
|
|
fileServer.ServeHTTP(writer, request)
|
|
|
|
return
|
|
}
|
|
|
|
fileData, readErr := readFS.ReadFile(
|
|
request.URL.Path[1:],
|
|
)
|
|
if readErr != nil || len(fileData) == 0 {
|
|
indexHTML, indexErr := readFS.ReadFile(
|
|
"index.html",
|
|
)
|
|
if indexErr != nil {
|
|
http.NotFound(writer, request)
|
|
|
|
return
|
|
}
|
|
|
|
writer.Header().Set(
|
|
"Content-Type",
|
|
"text/html; charset=utf-8",
|
|
)
|
|
writer.WriteHeader(http.StatusOK)
|
|
_, _ = writer.Write(indexHTML)
|
|
|
|
return
|
|
}
|
|
|
|
fileServer.ServeHTTP(writer, request)
|
|
})
|
|
}
|