- Register by signing service key: GET /v1/service-key, POST /v1/auth/register-by-signature - Login auto-attempts register first for new users - Web: default API URL momswap.produktor.duckdns.org, /libs/ static handler - Docker: webbuild stage for geo-api-client, copy web+libs to runtime - Bin scripts: test.sh, run.sh, up.sh, down.sh - docs/ed25519-security-use-cases.md: use cases, message formats, examples - SERVICE_PUBLIC_KEY env (defaults to ADMIN_PUBLIC_KEY) Made-with: Cursor
This commit is contained in:
@@ -22,11 +22,14 @@ func NewAPI(svc *app.Service) *API {
|
||||
func (a *API) Routes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
staticFiles := http.FileServer(http.Dir("web"))
|
||||
libsFiles := http.FileServer(http.Dir("libs"))
|
||||
|
||||
mux.HandleFunc("GET /healthz", a.health)
|
||||
mux.HandleFunc("GET /v1/service-key", a.getServiceKey)
|
||||
mux.HandleFunc("POST /v1/auth/challenge", a.createChallenge)
|
||||
mux.HandleFunc("POST /v1/auth/login", a.login)
|
||||
mux.HandleFunc("POST /v1/auth/register", a.register)
|
||||
mux.HandleFunc("POST /v1/auth/register-by-signature", a.registerBySignature)
|
||||
mux.HandleFunc("POST /v1/invitations", a.createInvitation)
|
||||
mux.HandleFunc("GET /v1/me/keys", a.me)
|
||||
|
||||
@@ -37,6 +40,7 @@ func (a *API) Routes() http.Handler {
|
||||
mux.HandleFunc("DELETE /v1/features/{id}", a.deleteFeature)
|
||||
|
||||
mux.Handle("/web/", http.StripPrefix("/web/", staticFiles))
|
||||
mux.Handle("/libs/", http.StripPrefix("/libs/", libsFiles))
|
||||
mux.Handle("/", http.RedirectHandler("/web/", http.StatusTemporaryRedirect))
|
||||
|
||||
return withCORS(mux)
|
||||
@@ -82,6 +86,8 @@ func statusFromErr(err error) int {
|
||||
return http.StatusForbidden
|
||||
case errors.Is(err, app.ErrBadRequest):
|
||||
return http.StatusBadRequest
|
||||
case errors.Is(err, app.ErrAlreadyUser):
|
||||
return http.StatusConflict
|
||||
case errors.Is(err, app.ErrInviteInvalid),
|
||||
errors.Is(err, app.ErrInviteExpired),
|
||||
errors.Is(err, app.ErrInviteExhaust):
|
||||
@@ -156,6 +162,15 @@ func (a *API) login(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]string{"accessToken": token})
|
||||
}
|
||||
|
||||
func (a *API) getServiceKey(w http.ResponseWriter, _ *http.Request) {
|
||||
pk := a.service.ServicePublicKey()
|
||||
if pk == "" {
|
||||
writeErr(w, app.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"publicKey": pk})
|
||||
}
|
||||
|
||||
func (a *API) register(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
@@ -174,6 +189,22 @@ func (a *API) register(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusCreated, map[string]string{"status": "registered"})
|
||||
}
|
||||
|
||||
func (a *API) registerBySignature(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
if err := readJSON(r, &req); err != nil {
|
||||
writeErr(w, app.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
if err := a.service.RegisterBySignature(req.PublicKey, req.Signature); err != nil {
|
||||
writeErr(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, map[string]string{"status": "registered"})
|
||||
}
|
||||
|
||||
func (a *API) createInvitation(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := a.authUser(r)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user