Extract shared service layer for sandbox, API key, and template operations

Moves business logic from API handlers into internal/service/ so that
both the REST API and the upcoming dashboard can share the same operations
without duplicating code. API handlers now delegate to the service layer
and only handle HTTP-specific concerns (request parsing, response formatting).
This commit is contained in:
2026-03-16 04:13:11 +06:00
parent 931b7d54b3
commit f38d5812d1
8 changed files with 389 additions and 239 deletions

View File

@ -8,15 +8,15 @@ import (
"git.omukk.dev/wrenn/sandbox/internal/auth"
"git.omukk.dev/wrenn/sandbox/internal/db"
"git.omukk.dev/wrenn/sandbox/internal/id"
"git.omukk.dev/wrenn/sandbox/internal/service"
)
type apiKeyHandler struct {
db *db.Queries
svc *service.APIKeyService
}
func newAPIKeyHandler(db *db.Queries) *apiKeyHandler {
return &apiKeyHandler{db: db}
func newAPIKeyHandler(svc *service.APIKeyService) *apiKeyHandler {
return &apiKeyHandler{svc: svc}
}
type createAPIKeyRequest struct {
@ -60,32 +60,14 @@ func (h *apiKeyHandler) Create(w http.ResponseWriter, r *http.Request) {
return
}
if req.Name == "" {
req.Name = "Unnamed API Key"
}
plaintext, hash, err := auth.GenerateAPIKey()
result, err := h.svc.Create(r.Context(), ac.TeamID, ac.UserID, req.Name)
if err != nil {
writeError(w, http.StatusInternalServerError, "internal_error", "failed to generate API key")
writeError(w, http.StatusInternalServerError, "internal_error", "failed to create API key")
return
}
keyID := id.NewAPIKeyID()
row, err := h.db.InsertAPIKey(r.Context(), db.InsertAPIKeyParams{
ID: keyID,
TeamID: ac.TeamID,
Name: req.Name,
KeyHash: hash,
KeyPrefix: auth.APIKeyPrefix(plaintext),
CreatedBy: ac.UserID,
})
if err != nil {
writeError(w, http.StatusInternalServerError, "db_error", "failed to create API key")
return
}
resp := apiKeyToResponse(row)
resp.Key = &plaintext
resp := apiKeyToResponse(result.Row)
resp.Key = &result.Plaintext
writeJSON(w, http.StatusCreated, resp)
}
@ -94,7 +76,7 @@ func (h *apiKeyHandler) Create(w http.ResponseWriter, r *http.Request) {
func (h *apiKeyHandler) List(w http.ResponseWriter, r *http.Request) {
ac := auth.MustFromContext(r.Context())
keys, err := h.db.ListAPIKeysByTeam(r.Context(), ac.TeamID)
keys, err := h.svc.List(r.Context(), ac.TeamID)
if err != nil {
writeError(w, http.StatusInternalServerError, "db_error", "failed to list API keys")
return
@ -113,10 +95,7 @@ func (h *apiKeyHandler) Delete(w http.ResponseWriter, r *http.Request) {
ac := auth.MustFromContext(r.Context())
keyID := chi.URLParam(r, "id")
if err := h.db.DeleteAPIKey(r.Context(), db.DeleteAPIKeyParams{
ID: keyID,
TeamID: ac.TeamID,
}); err != nil {
if err := h.svc.Delete(r.Context(), keyID, ac.TeamID); err != nil {
writeError(w, http.StatusInternalServerError, "db_error", "failed to delete API key")
return
}