feat: scaffold ai gateway identity and design
This commit is contained in:
@@ -2,8 +2,10 @@ package httpapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
|
||||
@@ -12,9 +14,10 @@ import (
|
||||
|
||||
func (s *Server) health(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"ok": true,
|
||||
"service": "easyai-ai-gateway",
|
||||
"env": s.cfg.AppEnv,
|
||||
"ok": true,
|
||||
"service": "easyai-ai-gateway",
|
||||
"env": s.cfg.AppEnv,
|
||||
"identityMode": s.cfg.IdentityMode,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,6 +34,100 @@ func (s *Server) me(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (s *Server) register(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.localIdentityEnabled() {
|
||||
writeError(w, http.StatusForbidden, "local registration is disabled")
|
||||
return
|
||||
}
|
||||
var input store.LocalRegisterInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid json body")
|
||||
return
|
||||
}
|
||||
user, err := s.store.RegisterLocalUser(r.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrWeakPassword) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if errors.Is(err, store.ErrInvalidInvitation) {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
s.logger.Error("register local user failed", "error", err)
|
||||
writeError(w, http.StatusConflict, "user already exists or tenant is unavailable")
|
||||
return
|
||||
}
|
||||
s.writeAuthResponse(w, http.StatusCreated, user)
|
||||
}
|
||||
|
||||
func (s *Server) login(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.localIdentityEnabled() {
|
||||
writeError(w, http.StatusForbidden, "local login is disabled")
|
||||
return
|
||||
}
|
||||
var input store.LocalLoginInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid json body")
|
||||
return
|
||||
}
|
||||
user, err := s.store.AuthenticateLocalUser(r.Context(), input)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrInvalidCredentials) {
|
||||
writeError(w, http.StatusUnauthorized, "invalid account or password")
|
||||
return
|
||||
}
|
||||
s.logger.Error("login local user failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "login failed")
|
||||
return
|
||||
}
|
||||
s.writeAuthResponse(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (s *Server) localIdentityEnabled() bool {
|
||||
mode := strings.ToLower(strings.TrimSpace(s.cfg.IdentityMode))
|
||||
return mode == "" || mode == "standalone" || mode == "hybrid"
|
||||
}
|
||||
|
||||
func (s *Server) writeAuthResponse(w http.ResponseWriter, status int, user store.GatewayUser) {
|
||||
authUser := authUserFromGatewayUser(user)
|
||||
const ttl = 24 * time.Hour
|
||||
token, err := s.auth.SignJWT(authUser, ttl)
|
||||
if err != nil {
|
||||
s.logger.Error("sign local jwt failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "token sign failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, status, map[string]any{
|
||||
"accessToken": token,
|
||||
"tokenType": "Bearer",
|
||||
"expiresIn": int(ttl.Seconds()),
|
||||
"user": authUser,
|
||||
})
|
||||
}
|
||||
|
||||
func authUserFromGatewayUser(user store.GatewayUser) *auth.User {
|
||||
roles := user.Roles
|
||||
if len(roles) == 0 {
|
||||
roles = []string{"user"}
|
||||
}
|
||||
tenantID := user.TenantID
|
||||
if tenantID == "" {
|
||||
tenantID = user.TenantKey
|
||||
}
|
||||
return &auth.User{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Roles: roles,
|
||||
TenantID: tenantID,
|
||||
GatewayTenantID: user.GatewayTenantID,
|
||||
TenantKey: user.TenantKey,
|
||||
Source: "gateway",
|
||||
GatewayUserID: user.ID,
|
||||
UserGroupID: user.DefaultUserGroupID,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) listPlatforms(w http.ResponseWriter, r *http.Request) {
|
||||
platforms, err := s.store.ListPlatforms(r.Context())
|
||||
if err != nil {
|
||||
@@ -103,6 +200,36 @@ func (s *Server) listPricingRules(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (s *Server) listTenants(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := s.store.ListTenants(r.Context())
|
||||
if err != nil {
|
||||
s.logger.Error("list tenants failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "list tenants failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (s *Server) listUsers(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := s.store.ListUsers(r.Context())
|
||||
if err != nil {
|
||||
s.logger.Error("list users failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "list users failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (s *Server) listUserGroups(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := s.store.ListUserGroups(r.Context())
|
||||
if err != nil {
|
||||
s.logger.Error("list user groups failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "list user groups failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (s *Server) estimatePricing(w http.ResponseWriter, r *http.Request) {
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user