209 lines
7.3 KiB
Go
209 lines
7.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"sub2api-cn-relay-manager/internal/store/sqlite"
|
|
)
|
|
|
|
type PortalLogicalGroupInfo struct {
|
|
LogicalGroupID string `json:"logical_group_id"`
|
|
DisplayName string `json:"display_name"`
|
|
Description string `json:"description,omitempty"`
|
|
UsageScenario string `json:"usage_scenario,omitempty"`
|
|
Recommendation string `json:"recommendation,omitempty"`
|
|
NextStepHint string `json:"next_step_hint,omitempty"`
|
|
VisibilityScope string `json:"visibility_scope,omitempty"`
|
|
PackageTier string `json:"package_tier,omitempty"`
|
|
PurchaseCTALabel string `json:"purchase_cta_label,omitempty"`
|
|
PurchaseCTAURL string `json:"purchase_cta_url,omitempty"`
|
|
Status string `json:"status"`
|
|
StickyMode string `json:"sticky_mode,omitempty"`
|
|
RoutePolicy string `json:"route_policy,omitempty"`
|
|
PublicModels []PortalLogicalGroupModel `json:"public_models,omitempty"`
|
|
RouteCount int `json:"route_count"`
|
|
ActiveRouteCount int `json:"active_route_count"`
|
|
CreatedAt string `json:"created_at,omitempty"`
|
|
UpdatedAt string `json:"updated_at,omitempty"`
|
|
}
|
|
|
|
type PortalLogicalGroupModel struct {
|
|
PublicModel string `json:"public_model"`
|
|
Status string `json:"status,omitempty"`
|
|
}
|
|
|
|
func handleListPortalLogicalGroups(w http.ResponseWriter, r *http.Request, fn func(context.Context) ([]PortalLogicalGroupInfo, error)) {
|
|
if fn == nil {
|
|
writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "list-portal-logical-groups action is not configured"})
|
|
return
|
|
}
|
|
groups, err := fn(r.Context())
|
|
if err != nil {
|
|
writeHTTPError(w, classifyError(err))
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"logical_groups": groups})
|
|
}
|
|
|
|
func handleGetPortalLogicalGroup(w http.ResponseWriter, r *http.Request, fn func(context.Context, string) (PortalLogicalGroupInfo, error)) {
|
|
if fn == nil {
|
|
writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "get-portal-logical-group action is not configured"})
|
|
return
|
|
}
|
|
group, err := fn(r.Context(), strings.TrimSpace(r.PathValue("groupID")))
|
|
if err != nil {
|
|
writeHTTPError(w, classifyError(err))
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"logical_group": group})
|
|
}
|
|
|
|
func handleListPortalLogicalGroupModels(w http.ResponseWriter, r *http.Request, fn func(context.Context, string) ([]PortalLogicalGroupModel, error)) {
|
|
if fn == nil {
|
|
writeHTTPError(w, &httpError{StatusCode: http.StatusInternalServerError, Code: "server_misconfigured", Message: "list-portal-logical-group-models action is not configured"})
|
|
return
|
|
}
|
|
models, err := fn(r.Context(), strings.TrimSpace(r.PathValue("groupID")))
|
|
if err != nil {
|
|
writeHTTPError(w, classifyError(err))
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"models": models})
|
|
}
|
|
|
|
func buildListPortalLogicalGroupsAction(sqliteDSN string) func(context.Context) ([]PortalLogicalGroupInfo, error) {
|
|
return func(ctx context.Context) ([]PortalLogicalGroupInfo, error) {
|
|
store, err := sqlite.Open(ctx, sqliteDSN)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer store.Close()
|
|
|
|
rows, err := store.LogicalGroups().List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]PortalLogicalGroupInfo, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !isPortalLogicalGroupVisible(row.Status) {
|
|
continue
|
|
}
|
|
item, buildErr := buildPortalLogicalGroupInfo(ctx, store, row)
|
|
if buildErr != nil {
|
|
return nil, buildErr
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return items, nil
|
|
}
|
|
}
|
|
|
|
func buildGetPortalLogicalGroupAction(sqliteDSN string) func(context.Context, string) (PortalLogicalGroupInfo, error) {
|
|
return func(ctx context.Context, logicalGroupID string) (PortalLogicalGroupInfo, error) {
|
|
store, err := sqlite.Open(ctx, sqliteDSN)
|
|
if err != nil {
|
|
return PortalLogicalGroupInfo{}, err
|
|
}
|
|
defer store.Close()
|
|
|
|
group, err := getLogicalGroupRow(ctx, store, logicalGroupID)
|
|
if err != nil {
|
|
return PortalLogicalGroupInfo{}, err
|
|
}
|
|
if !isPortalLogicalGroupVisible(group.Status) {
|
|
return PortalLogicalGroupInfo{}, fmt.Errorf("logical group %q not found", strings.TrimSpace(logicalGroupID))
|
|
}
|
|
return buildPortalLogicalGroupInfo(ctx, store, group)
|
|
}
|
|
}
|
|
|
|
func buildListPortalLogicalGroupModelsAction(sqliteDSN string) func(context.Context, string) ([]PortalLogicalGroupModel, error) {
|
|
return func(ctx context.Context, logicalGroupID string) ([]PortalLogicalGroupModel, error) {
|
|
store, err := sqlite.Open(ctx, sqliteDSN)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer store.Close()
|
|
|
|
group, err := getLogicalGroupRow(ctx, store, logicalGroupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !isPortalLogicalGroupVisible(group.Status) {
|
|
return nil, fmt.Errorf("logical group %q not found", strings.TrimSpace(logicalGroupID))
|
|
}
|
|
rows, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, group.LogicalGroupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return portalLogicalGroupModelsFromRows(rows), nil
|
|
}
|
|
}
|
|
|
|
func buildPortalLogicalGroupInfo(ctx context.Context, store *sqlite.DB, group sqlite.LogicalGroup) (PortalLogicalGroupInfo, error) {
|
|
modelRows, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, group.LogicalGroupID)
|
|
if err != nil {
|
|
return PortalLogicalGroupInfo{}, err
|
|
}
|
|
routeRows, err := store.LogicalGroupRoutes().ListByLogicalGroupID(ctx, group.LogicalGroupID)
|
|
if err != nil {
|
|
return PortalLogicalGroupInfo{}, err
|
|
}
|
|
|
|
routeCount := 0
|
|
activeRouteCount := 0
|
|
for _, route := range routeRows {
|
|
routeCount++
|
|
if strings.EqualFold(strings.TrimSpace(route.Status), "active") {
|
|
activeRouteCount++
|
|
}
|
|
}
|
|
|
|
return PortalLogicalGroupInfo{
|
|
LogicalGroupID: group.LogicalGroupID,
|
|
DisplayName: group.DisplayName,
|
|
Description: group.Description,
|
|
UsageScenario: group.UsageScenario,
|
|
Recommendation: group.Recommendation,
|
|
NextStepHint: group.NextStepHint,
|
|
VisibilityScope: group.VisibilityScope,
|
|
PackageTier: group.PackageTier,
|
|
PurchaseCTALabel: group.PurchaseCTALabel,
|
|
PurchaseCTAURL: group.PurchaseCTAURL,
|
|
Status: group.Status,
|
|
StickyMode: group.StickyMode,
|
|
RoutePolicy: group.RoutePolicy,
|
|
PublicModels: portalLogicalGroupModelsFromRows(modelRows),
|
|
RouteCount: routeCount,
|
|
ActiveRouteCount: activeRouteCount,
|
|
CreatedAt: group.CreatedAt,
|
|
UpdatedAt: group.UpdatedAt,
|
|
}, nil
|
|
}
|
|
|
|
func portalLogicalGroupModelsFromRows(rows []sqlite.LogicalGroupModel) []PortalLogicalGroupModel {
|
|
result := make([]PortalLogicalGroupModel, 0, len(rows))
|
|
for _, row := range rows {
|
|
if !isPortalLogicalGroupModelVisible(row.Status) {
|
|
continue
|
|
}
|
|
result = append(result, PortalLogicalGroupModel{
|
|
PublicModel: row.PublicModel,
|
|
Status: row.Status,
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
func isPortalLogicalGroupVisible(status string) bool {
|
|
return strings.EqualFold(strings.TrimSpace(status), "active")
|
|
}
|
|
|
|
func isPortalLogicalGroupModelVisible(status string) bool {
|
|
status = strings.TrimSpace(status)
|
|
return status == "" || strings.EqualFold(status, "active")
|
|
}
|