mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
135 lines
3.5 KiB
Go
135 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgerrcode"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/zoriya/kyoo/keibi/dbc"
|
|
)
|
|
|
|
type ApiKey struct {
|
|
Name string `json:"name" example:"myapp"`
|
|
CreatedAt time.Time `json:"createAt" example:"2025-03-29T18:20:05.267Z"`
|
|
LastUsed time.Time `json:"lastUsed" example:"2025-03-29T18:20:05.267Z"`
|
|
Claims jwt.MapClaims `json:"claims" example:"isAdmin: true"`
|
|
}
|
|
|
|
type ApiKeyWToken struct {
|
|
ApiKey
|
|
Token string `json:"token" example:"myapp-lyHzTYm9yi+pkEv3m2tamAeeK7Dj7N3QRP7xv7dPU5q9MAe8tU4ySwYczE0RaMr4fijsA=="`
|
|
}
|
|
|
|
type ApiKeyDto struct {
|
|
Name string `json:"name" example:"my-app" validate:"alpha"`
|
|
Claims jwt.MapClaims `json:"claims" example:"isAdmin: true"`
|
|
}
|
|
|
|
func MapDbKey(key *dbc.Apikey) ApiKeyWToken {
|
|
return ApiKeyWToken{
|
|
ApiKey: ApiKey{
|
|
Name: key.Name,
|
|
Claims: key.Claims,
|
|
CreatedAt: key.CreatedAt,
|
|
LastUsed: key.LastUsed,
|
|
},
|
|
Token: key.Token,
|
|
}
|
|
}
|
|
|
|
// @Summary Create API key
|
|
// @Description Create a new API key
|
|
// @Tags apikeys
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security Jwt[apikeys.write]
|
|
// @Param key body ApiKeyDto false "Api key info"
|
|
// @Success 201 {object} ApiKeyWToken
|
|
// @Failure 409 {object} KError "Duplicated api key"
|
|
// @Failure 422 {object} KError "Invalid create body"
|
|
// @Router /keys [post]
|
|
func (h *Handler) CreateApiKey(c echo.Context) error {
|
|
var req ApiKeyDto
|
|
err := c.Bind(&req)
|
|
if err != nil {
|
|
return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
|
|
}
|
|
if err = c.Validate(&req); err != nil {
|
|
return err
|
|
}
|
|
|
|
id := make([]byte, 64)
|
|
_, err = rand.Read(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dbkey, err := h.db.CreateApiKey(context.Background(), dbc.CreateApiKeyParams{
|
|
Name: req.Name,
|
|
Token: fmt.Sprintf("%s-%s", req.Name, base64.RawURLEncoding.EncodeToString(id)),
|
|
Claims: req.Claims,
|
|
})
|
|
if ErrIs(err, pgerrcode.UniqueViolation) {
|
|
return echo.NewHTTPError(409, "An apikey with the same name already exists.")
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
return c.JSON(201, MapDbKey(&dbkey))
|
|
}
|
|
|
|
// @Summary Delete API key
|
|
// @Description Delete an existing API key
|
|
// @Tags apikeys
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security Jwt[apikeys.write]
|
|
// @Success 200 {object} ApiKey
|
|
// @Failure 404 {object} KError "Invalid id"
|
|
// @Failure 422 {object} KError "Invalid id format"
|
|
// @Router /keys [delete]
|
|
func (h *Handler) DeleteApiKey(c echo.Context) error {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
return echo.NewHTTPError(422, "Invalid id given: not an uuid")
|
|
}
|
|
|
|
dbkey, err := h.db.DeleteApiKey(context.Background(), id)
|
|
if err == pgx.ErrNoRows {
|
|
return echo.NewHTTPError(404, "No apikey found")
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
return c.JSON(200, MapDbKey(&dbkey).ApiKey)
|
|
}
|
|
|
|
// @Summary List API keys
|
|
// @Description List all api keys
|
|
// @Tags apikeys
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security Jwt[apikeys.read]
|
|
// @Success 200 {object} Page[ApiKey]
|
|
// @Router /keys [get]
|
|
func (h *Handler) ListApiKey(c echo.Context) error {
|
|
dbkeys, err := h.db.ListApiKeys(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var ret []ApiKey
|
|
for _, key := range dbkeys {
|
|
ret = append(ret, MapDbKey(&key).ApiKey)
|
|
}
|
|
return c.JSON(200, Page[ApiKey]{
|
|
Items: ret,
|
|
This: c.Request().URL.String(),
|
|
})
|
|
}
|