feat: sdk migration in progress

This commit is contained in:
allanice001
2025-11-02 13:19:30 +00:00
commit 0d10d42442
492 changed files with 71067 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
package handlers
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"net/http"
"time"
"github.com/glueops/autoglue/internal/api/httpmiddleware"
"github.com/glueops/autoglue/internal/auth"
"github.com/glueops/autoglue/internal/models"
"github.com/glueops/autoglue/internal/utils"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"gorm.io/gorm"
)
type userAPIKeyOut struct {
ID uuid.UUID `json:"id" format:"uuid"`
Name *string `json:"name,omitempty"`
Scope string `json:"scope"` // "user"
CreatedAt time.Time `json:"created_at"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
Plain *string `json:"plain,omitempty"` // Shown only on create:
}
// ListUserAPIKeys godoc
// @ID ListUserAPIKeys
// @Summary List my API keys
// @Tags Me / API Keys
// @Produce json
// @Success 200 {array} userAPIKeyOut
// @Router /me/api-keys [get]
// @Security BearerAuth
// @Security ApiKeyAuth
func ListUserAPIKeys(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
u, ok := httpmiddleware.UserFrom(r.Context())
if !ok {
utils.WriteError(w, http.StatusUnauthorized, "unauthorized", "not signed in")
return
}
var rows []models.APIKey
if err := db.
Where("scope = ? AND user_id = ?", "user", u.ID).
Order("created_at desc").
Find(&rows).Error; err != nil {
utils.WriteError(w, 500, "db_error", err.Error())
return
}
out := make([]userAPIKeyOut, 0, len(rows))
for _, k := range rows {
out = append(out, toUserKeyOut(k, nil))
}
utils.WriteJSON(w, 200, out)
}
}
type createUserKeyRequest struct {
Name string `json:"name,omitempty"`
ExpiresInHours *int `json:"expires_in_hours,omitempty"` // optional TTL
}
// CreateUserAPIKey godoc
// @ID CreateUserAPIKey
// @Summary Create a new user API key
// @Description Returns the plaintext key once. Store it securely on the client side.
// @Tags Me / API Keys
// @Accept json
// @Produce json
// @Param body body createUserKeyRequest true "Key options"
// @Success 201 {object} userAPIKeyOut
// @Router /me/api-keys [post]
// @Security BearerAuth
// @Security ApiKeyAuth
func CreateUserAPIKey(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
u, ok := httpmiddleware.UserFrom(r.Context())
if !ok {
utils.WriteError(w, http.StatusUnauthorized, "unauthorized", "not signed in")
return
}
var req createUserKeyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
utils.WriteError(w, 400, "invalid_json", err.Error())
return
}
plain, err := generateUserAPIKey()
if err != nil {
utils.WriteError(w, 500, "gen_failed", err.Error())
return
}
hash := auth.SHA256Hex(plain)
var exp *time.Time
if req.ExpiresInHours != nil && *req.ExpiresInHours > 0 {
t := time.Now().Add(time.Duration(*req.ExpiresInHours) * time.Hour)
exp = &t
}
rec := models.APIKey{
Scope: "user",
UserID: &u.ID,
KeyHash: hash,
Name: req.Name, // if field exists
ExpiresAt: exp,
// SecretHash: nil (not used for user keys)
}
if err := db.Create(&rec).Error; err != nil {
utils.WriteError(w, 500, "db_error", err.Error())
return
}
utils.WriteJSON(w, http.StatusCreated, toUserKeyOut(rec, &plain))
}
}
// DeleteUserAPIKey godoc
// @ID DeleteUserAPIKey
// @Summary Delete a user API key
// @Tags Me / API Keys
// @Produce json
// @Param id path string true "Key ID (UUID)"
// @Success 204 "No Content"
// @Router /me/api-keys/{id} [delete]
// @Security BearerAuth
func DeleteUserAPIKey(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
u, ok := httpmiddleware.UserFrom(r.Context())
if !ok {
utils.WriteError(w, http.StatusUnauthorized, "unauthorized", "not signed in")
return
}
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
utils.WriteError(w, 400, "invalid_id", "must be uuid")
return
}
tx := db.Where("id = ? AND scope = ? AND user_id = ?", id, "user", u.ID).
Delete(&models.APIKey{})
if tx.Error != nil {
utils.WriteError(w, 500, "db_error", tx.Error.Error())
return
}
if tx.RowsAffected == 0 {
utils.WriteError(w, 404, "not_found", "key not found")
return
}
w.WriteHeader(http.StatusNoContent)
}
}
func toUserKeyOut(k models.APIKey, plain *string) userAPIKeyOut {
return userAPIKeyOut{
ID: k.ID,
Name: &k.Name, // if your model has it; else remove
Scope: k.Scope,
CreatedAt: k.CreatedAt,
ExpiresAt: k.ExpiresAt,
LastUsedAt: k.LastUsedAt, // if present; else remove
Plain: plain,
}
}
func generateUserAPIKey() (string, error) {
// 24 random bytes → base64url (no padding), with "u_" prefix
b := make([]byte, 24)
if _, err := rand.Read(b); err != nil {
return "", err
}
s := base64.RawURLEncoding.EncodeToString(b)
return "u_" + s, nil
}