package api_impl import ( "database/sql" "log/slog" "net/http" "time" "atlas9.dev/c/core" "atlas9.dev/c/core/dbi" "atlas9.dev/c/core/iam" "atlas9.dev/c/demo/api" "atlas9.dev/c/demo/bots" ) type BotKeysImpl struct { DB *sql.DB KeyStore dbi.Factory[bots.KeyStore] Audit dbi.Factory[iam.AuditStore] } func (k *BotKeysImpl) ServeMux(mux *http.ServeMux) { mux.HandleFunc(api.Path_BotKeys_Create, k.Create) mux.HandleFunc(api.Path_BotKeys_Get, k.Get) mux.HandleFunc(api.Path_BotKeys_List, k.List) mux.HandleFunc(api.Path_BotKeys_SetExpiration, k.SetExpiration) } func (k *BotKeysImpl) Create(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // Read, prepare, and validate the request. var req api.BotKeys_CreateReq if read(w, r, &req) { return } // Create new key key, secret, err := bots.NewKey(req.Tenant, req.Bot) if writeErr(ctx, w, err) { return } if req.ExpiresAt != nil { key.ExpiresAt = *req.ExpiresAt } // Save the key err = dbi.ReadWrite(ctx, k.DB, func(tx dbi.DBI) error { if err := k.KeyStore(tx).Create(ctx, key); err != nil { return err } return audit(ctx, k.Audit(tx), iam.AuditEntry{ Tenant: key.Tenant, Action: "BotKeys_Create", Resource: key.ID.String(), Detail: key.Bot.String(), }) }) // Write the response res := api.BotKeys_CreateRes{ ID: key.ID, Tenant: key.Tenant, Bot: key.Bot, Token: bots.FormatToken(key.ID, secret), CreatedAt: key.CreatedAt, ExpiresAt: key.ExpiresAt, } write(ctx, w, err, res) } func (k *BotKeysImpl) Get(w http.ResponseWriter, r *http.Request) { var req api.BotKeys_GetReq if read(w, r, &req) { return } ctx := r.Context() var key bots.Key err := dbi.ReadOnly(ctx, k.DB, func(tx dbi.DBI) error { return k.KeyStore(tx).Get(ctx, req.Tenant, req.ID, &key) }) res := api.BotKeys_GetRes{ ID: key.ID, Tenant: key.Tenant, Bot: key.Bot, CreatedAt: key.CreatedAt, ExpiresAt: key.ExpiresAt, } write(ctx, w, err, res) } func (k *BotKeysImpl) List(w http.ResponseWriter, r *http.Request) { var req api.BotKeys_ListReq if read(w, r, &req) { return } ctx := r.Context() var page core.Page[bots.Key] err := dbi.ReadOnly(ctx, k.DB, func(tx dbi.DBI) error { return k.KeyStore(tx).List(ctx, req.Tenant, &page) }) // Convert to metadata var res api.BotKeys_ListRes res.Page.Cursor = page.Cursor for _, it := range page.Items { res.Page.Items = append(res.Page.Items, api.BotKeys_ListItem{ ID: it.ID, Tenant: it.Tenant, Bot: it.Bot, CreatedAt: it.CreatedAt, ExpiresAt: it.ExpiresAt, }) } write(ctx, w, err, res) } func (k *BotKeysImpl) SetExpiration(w http.ResponseWriter, r *http.Request) { var req api.BotKeys_SetExpirationReq if read(w, r, &req) { return } ctx := r.Context() err := dbi.ReadWrite(ctx, k.DB, func(tx dbi.DBI) error { if err := k.KeyStore(tx).SetExpiration(ctx, req.Tenant, req.ID, req.ExpiresAt); err != nil { return err } return audit(ctx, k.Audit(tx), iam.AuditEntry{ Tenant: req.Tenant, Action: "BotKeys_SetExpiration", Resource: req.ID.String(), Detail: req.ExpiresAt.UTC().Format(time.RFC3339), }) }) if write(ctx, w, err, api.BotKeys_SetExpirationRes{}) { return } slog.InfoContext(ctx, "api key set expiration", "ID", req.ID) }