fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
638
ai-compliance-sdk/internal/api/handlers/funding_handlers.go
Normal file
638
ai-compliance-sdk/internal/api/handlers/funding_handlers.go
Normal file
@@ -0,0 +1,638 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/funding"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/llm"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// FundingHandlers handles funding application API endpoints
|
||||
type FundingHandlers struct {
|
||||
store funding.Store
|
||||
providerRegistry *llm.ProviderRegistry
|
||||
wizardSchema *WizardSchema
|
||||
bundeslandProfiles map[string]*BundeslandProfile
|
||||
}
|
||||
|
||||
// WizardSchema represents the loaded wizard schema
|
||||
type WizardSchema struct {
|
||||
Metadata struct {
|
||||
Version string `yaml:"version"`
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
TotalSteps int `yaml:"total_steps"`
|
||||
} `yaml:"metadata"`
|
||||
Steps []WizardStep `yaml:"steps"`
|
||||
FundingAssistant struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Model string `yaml:"model"`
|
||||
SystemPrompt string `yaml:"system_prompt"`
|
||||
StepContexts map[int]string `yaml:"step_contexts"`
|
||||
QuickPrompts []QuickPrompt `yaml:"quick_prompts"`
|
||||
} `yaml:"funding_assistant"`
|
||||
Presets map[string]Preset `yaml:"presets"`
|
||||
}
|
||||
|
||||
// WizardStep represents a step in the wizard
|
||||
type WizardStep struct {
|
||||
Number int `yaml:"number" json:"number"`
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Title string `yaml:"title" json:"title"`
|
||||
Subtitle string `yaml:"subtitle" json:"subtitle"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
Icon string `yaml:"icon" json:"icon"`
|
||||
IsRequired bool `yaml:"is_required" json:"is_required"`
|
||||
Fields []WizardField `yaml:"fields" json:"fields"`
|
||||
AssistantContext string `yaml:"assistant_context" json:"assistant_context"`
|
||||
}
|
||||
|
||||
// WizardField represents a field in the wizard
|
||||
type WizardField struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Type string `yaml:"type" json:"type"`
|
||||
Label string `yaml:"label" json:"label"`
|
||||
Placeholder string `yaml:"placeholder,omitempty" json:"placeholder,omitempty"`
|
||||
Required bool `yaml:"required,omitempty" json:"required,omitempty"`
|
||||
Options []FieldOption `yaml:"options,omitempty" json:"options,omitempty"`
|
||||
HelpText string `yaml:"help_text,omitempty" json:"help_text,omitempty"`
|
||||
MaxLength int `yaml:"max_length,omitempty" json:"max_length,omitempty"`
|
||||
Min *int `yaml:"min,omitempty" json:"min,omitempty"`
|
||||
Max *int `yaml:"max,omitempty" json:"max,omitempty"`
|
||||
Default interface{} `yaml:"default,omitempty" json:"default,omitempty"`
|
||||
Conditional string `yaml:"conditional,omitempty" json:"conditional,omitempty"`
|
||||
}
|
||||
|
||||
// FieldOption represents an option for select fields
|
||||
type FieldOption struct {
|
||||
Value string `yaml:"value" json:"value"`
|
||||
Label string `yaml:"label" json:"label"`
|
||||
Description string `yaml:"description,omitempty" json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// QuickPrompt represents a quick prompt for the assistant
|
||||
type QuickPrompt struct {
|
||||
Label string `yaml:"label" json:"label"`
|
||||
Prompt string `yaml:"prompt" json:"prompt"`
|
||||
}
|
||||
|
||||
// Preset represents a BreakPilot preset
|
||||
type Preset struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
BudgetItems []funding.BudgetItem `yaml:"budget_items" json:"budget_items"`
|
||||
AutoFill map[string]interface{} `yaml:"auto_fill" json:"auto_fill"`
|
||||
}
|
||||
|
||||
// BundeslandProfile represents a federal state profile
|
||||
type BundeslandProfile struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Short string `yaml:"short" json:"short"`
|
||||
FundingPrograms []string `yaml:"funding_programs" json:"funding_programs"`
|
||||
DefaultFundingRate float64 `yaml:"default_funding_rate" json:"default_funding_rate"`
|
||||
RequiresMEP bool `yaml:"requires_mep" json:"requires_mep"`
|
||||
ContactAuthority ContactAuthority `yaml:"contact_authority" json:"contact_authority"`
|
||||
SpecialRequirements []string `yaml:"special_requirements" json:"special_requirements"`
|
||||
}
|
||||
|
||||
// ContactAuthority represents a contact authority
|
||||
type ContactAuthority struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Department string `yaml:"department,omitempty" json:"department,omitempty"`
|
||||
Website string `yaml:"website" json:"website"`
|
||||
Email string `yaml:"email,omitempty" json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// NewFundingHandlers creates new funding handlers
|
||||
func NewFundingHandlers(store funding.Store, providerRegistry *llm.ProviderRegistry) *FundingHandlers {
|
||||
h := &FundingHandlers{
|
||||
store: store,
|
||||
providerRegistry: providerRegistry,
|
||||
}
|
||||
|
||||
// Load wizard schema
|
||||
if err := h.loadWizardSchema(); err != nil {
|
||||
fmt.Printf("Warning: Could not load wizard schema: %v\n", err)
|
||||
}
|
||||
|
||||
// Load bundesland profiles
|
||||
if err := h.loadBundeslandProfiles(); err != nil {
|
||||
fmt.Printf("Warning: Could not load bundesland profiles: %v\n", err)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *FundingHandlers) loadWizardSchema() error {
|
||||
data, err := os.ReadFile("policies/funding/foerderantrag_wizard_v1.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.wizardSchema = &WizardSchema{}
|
||||
return yaml.Unmarshal(data, h.wizardSchema)
|
||||
}
|
||||
|
||||
func (h *FundingHandlers) loadBundeslandProfiles() error {
|
||||
data, err := os.ReadFile("policies/funding/bundesland_profiles.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var profiles struct {
|
||||
Bundeslaender map[string]*BundeslandProfile `yaml:"bundeslaender"`
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &profiles); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.bundeslandProfiles = profiles.Bundeslaender
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Application CRUD
|
||||
// ============================================================================
|
||||
|
||||
// CreateApplication creates a new funding application
|
||||
// POST /sdk/v1/funding/applications
|
||||
func (h *FundingHandlers) CreateApplication(c *gin.Context) {
|
||||
tenantID := rbac.GetTenantID(c)
|
||||
userID := rbac.GetUserID(c)
|
||||
if tenantID == uuid.Nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req funding.CreateApplicationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
app := &funding.FundingApplication{
|
||||
TenantID: tenantID,
|
||||
Title: req.Title,
|
||||
FundingProgram: req.FundingProgram,
|
||||
Status: funding.ApplicationStatusDraft,
|
||||
CurrentStep: 1,
|
||||
TotalSteps: 8,
|
||||
WizardData: make(map[string]interface{}),
|
||||
CreatedBy: userID,
|
||||
UpdatedBy: userID,
|
||||
}
|
||||
|
||||
// Initialize school profile with federal state
|
||||
app.SchoolProfile = &funding.SchoolProfile{
|
||||
FederalState: req.FederalState,
|
||||
}
|
||||
|
||||
// Apply preset if specified
|
||||
if req.PresetID != "" && h.wizardSchema != nil {
|
||||
if preset, ok := h.wizardSchema.Presets[req.PresetID]; ok {
|
||||
app.Budget = &funding.Budget{
|
||||
BudgetItems: preset.BudgetItems,
|
||||
}
|
||||
app.WizardData["preset_id"] = req.PresetID
|
||||
app.WizardData["preset_applied"] = true
|
||||
for k, v := range preset.AutoFill {
|
||||
app.WizardData[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.store.CreateApplication(c.Request.Context(), app); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Add history entry
|
||||
h.store.AddHistoryEntry(c.Request.Context(), &funding.ApplicationHistoryEntry{
|
||||
ApplicationID: app.ID,
|
||||
Action: "created",
|
||||
PerformedBy: userID,
|
||||
Notes: "Antrag erstellt",
|
||||
})
|
||||
|
||||
c.JSON(http.StatusCreated, app)
|
||||
}
|
||||
|
||||
// GetApplication retrieves a funding application
|
||||
// GET /sdk/v1/funding/applications/:id
|
||||
func (h *FundingHandlers) GetApplication(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.store.GetApplication(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, app)
|
||||
}
|
||||
|
||||
// ListApplications returns a list of funding applications
|
||||
// GET /sdk/v1/funding/applications
|
||||
func (h *FundingHandlers) ListApplications(c *gin.Context) {
|
||||
tenantID := rbac.GetTenantID(c)
|
||||
if tenantID == uuid.Nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
filter := funding.ApplicationFilter{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
if status := c.Query("status"); status != "" {
|
||||
s := funding.ApplicationStatus(status)
|
||||
filter.Status = &s
|
||||
}
|
||||
if program := c.Query("program"); program != "" {
|
||||
p := funding.FundingProgram(program)
|
||||
filter.FundingProgram = &p
|
||||
}
|
||||
|
||||
result, err := h.store.ListApplications(c.Request.Context(), tenantID, filter)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// UpdateApplication updates a funding application
|
||||
// PUT /sdk/v1/funding/applications/:id
|
||||
func (h *FundingHandlers) UpdateApplication(c *gin.Context) {
|
||||
userID := rbac.GetUserID(c)
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.store.GetApplication(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var req funding.UpdateApplicationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Title != nil {
|
||||
app.Title = *req.Title
|
||||
}
|
||||
if req.WizardData != nil {
|
||||
for k, v := range req.WizardData {
|
||||
app.WizardData[k] = v
|
||||
}
|
||||
}
|
||||
if req.CurrentStep != nil {
|
||||
app.CurrentStep = *req.CurrentStep
|
||||
}
|
||||
app.UpdatedBy = userID
|
||||
|
||||
if err := h.store.UpdateApplication(c.Request.Context(), app); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, app)
|
||||
}
|
||||
|
||||
// DeleteApplication deletes a funding application
|
||||
// DELETE /sdk/v1/funding/applications/:id
|
||||
func (h *FundingHandlers) DeleteApplication(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.store.DeleteApplication(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "application archived"})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Wizard Endpoints
|
||||
// ============================================================================
|
||||
|
||||
// GetWizardSchema returns the wizard schema
|
||||
// GET /sdk/v1/funding/wizard/schema
|
||||
func (h *FundingHandlers) GetWizardSchema(c *gin.Context) {
|
||||
if h.wizardSchema == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "wizard schema not loaded"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"metadata": h.wizardSchema.Metadata,
|
||||
"steps": h.wizardSchema.Steps,
|
||||
"presets": h.wizardSchema.Presets,
|
||||
"assistant": gin.H{
|
||||
"enabled": h.wizardSchema.FundingAssistant.Enabled,
|
||||
"quick_prompts": h.wizardSchema.FundingAssistant.QuickPrompts,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SaveWizardStep saves wizard step data
|
||||
// POST /sdk/v1/funding/applications/:id/wizard
|
||||
func (h *FundingHandlers) SaveWizardStep(c *gin.Context) {
|
||||
userID := rbac.GetUserID(c)
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req funding.SaveWizardStepRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Save step data
|
||||
if err := h.store.SaveWizardStep(c.Request.Context(), id, req.Step, req.Data); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Get updated progress
|
||||
progress, err := h.store.GetWizardProgress(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Add history entry
|
||||
h.store.AddHistoryEntry(c.Request.Context(), &funding.ApplicationHistoryEntry{
|
||||
ApplicationID: id,
|
||||
Action: "wizard_step_saved",
|
||||
PerformedBy: userID,
|
||||
Notes: fmt.Sprintf("Schritt %d gespeichert", req.Step),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, progress)
|
||||
}
|
||||
|
||||
// AskAssistant handles LLM assistant queries
|
||||
// POST /sdk/v1/funding/wizard/ask
|
||||
func (h *FundingHandlers) AskAssistant(c *gin.Context) {
|
||||
var req funding.AssistantRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if h.wizardSchema == nil || !h.wizardSchema.FundingAssistant.Enabled {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "assistant not available"})
|
||||
return
|
||||
}
|
||||
|
||||
// Build system prompt with step context
|
||||
systemPrompt := h.wizardSchema.FundingAssistant.SystemPrompt
|
||||
if stepContext, ok := h.wizardSchema.FundingAssistant.StepContexts[req.CurrentStep]; ok {
|
||||
systemPrompt += "\n\nKontext fuer diesen Schritt:\n" + stepContext
|
||||
}
|
||||
|
||||
// Build messages
|
||||
messages := []llm.Message{
|
||||
{Role: "system", Content: systemPrompt},
|
||||
}
|
||||
for _, msg := range req.History {
|
||||
messages = append(messages, llm.Message{
|
||||
Role: msg.Role,
|
||||
Content: msg.Content,
|
||||
})
|
||||
}
|
||||
messages = append(messages, llm.Message{
|
||||
Role: "user",
|
||||
Content: req.Question,
|
||||
})
|
||||
|
||||
// Generate response using registry
|
||||
chatReq := &llm.ChatRequest{
|
||||
Messages: messages,
|
||||
Temperature: 0.3,
|
||||
MaxTokens: 1000,
|
||||
}
|
||||
|
||||
response, err := h.providerRegistry.Chat(c.Request.Context(), chatReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, funding.AssistantResponse{
|
||||
Answer: response.Message.Content,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Status Endpoints
|
||||
// ============================================================================
|
||||
|
||||
// SubmitApplication submits an application for review
|
||||
// POST /sdk/v1/funding/applications/:id/submit
|
||||
func (h *FundingHandlers) SubmitApplication(c *gin.Context) {
|
||||
userID := rbac.GetUserID(c)
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.store.GetApplication(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate that all required steps are completed
|
||||
progress, _ := h.store.GetWizardProgress(c.Request.Context(), id)
|
||||
if progress == nil || len(progress.CompletedSteps) < app.TotalSteps {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "not all required steps completed"})
|
||||
return
|
||||
}
|
||||
|
||||
// Update status
|
||||
app.Status = funding.ApplicationStatusSubmitted
|
||||
now := time.Now()
|
||||
app.SubmittedAt = &now
|
||||
app.UpdatedBy = userID
|
||||
|
||||
if err := h.store.UpdateApplication(c.Request.Context(), app); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Add history entry
|
||||
h.store.AddHistoryEntry(c.Request.Context(), &funding.ApplicationHistoryEntry{
|
||||
ApplicationID: id,
|
||||
Action: "submitted",
|
||||
PerformedBy: userID,
|
||||
Notes: "Antrag eingereicht",
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, app)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Export Endpoints
|
||||
// ============================================================================
|
||||
|
||||
// ExportApplication exports all documents as ZIP
|
||||
// GET /sdk/v1/funding/applications/:id/export
|
||||
func (h *FundingHandlers) ExportApplication(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.store.GetApplication(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate export (this will be implemented in export.go)
|
||||
// For now, return a placeholder response
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Export generation initiated",
|
||||
"application_id": app.ID,
|
||||
"status": "processing",
|
||||
})
|
||||
}
|
||||
|
||||
// PreviewApplication generates a PDF preview
|
||||
// GET /sdk/v1/funding/applications/:id/preview
|
||||
func (h *FundingHandlers) PreviewApplication(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.store.GetApplication(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Generate PDF preview (placeholder)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Preview generation initiated",
|
||||
"application_id": app.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Bundesland Profile Endpoints
|
||||
// ============================================================================
|
||||
|
||||
// GetBundeslandProfiles returns all bundesland profiles
|
||||
// GET /sdk/v1/funding/bundeslaender
|
||||
func (h *FundingHandlers) GetBundeslandProfiles(c *gin.Context) {
|
||||
if h.bundeslandProfiles == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "bundesland profiles not loaded"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, h.bundeslandProfiles)
|
||||
}
|
||||
|
||||
// GetBundeslandProfile returns a specific bundesland profile
|
||||
// GET /sdk/v1/funding/bundeslaender/:state
|
||||
func (h *FundingHandlers) GetBundeslandProfile(c *gin.Context) {
|
||||
state := c.Param("state")
|
||||
|
||||
if h.bundeslandProfiles == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "bundesland profiles not loaded"})
|
||||
return
|
||||
}
|
||||
|
||||
profile, ok := h.bundeslandProfiles[state]
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "bundesland not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, profile)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Statistics Endpoint
|
||||
// ============================================================================
|
||||
|
||||
// GetStatistics returns funding statistics
|
||||
// GET /sdk/v1/funding/statistics
|
||||
func (h *FundingHandlers) GetStatistics(c *gin.Context) {
|
||||
tenantID := rbac.GetTenantID(c)
|
||||
if tenantID == uuid.Nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||
return
|
||||
}
|
||||
|
||||
stats, err := h.store.GetStatistics(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// History Endpoint
|
||||
// ============================================================================
|
||||
|
||||
// GetApplicationHistory returns the audit trail
|
||||
// GET /sdk/v1/funding/applications/:id/history
|
||||
func (h *FundingHandlers) GetApplicationHistory(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid application ID"})
|
||||
return
|
||||
}
|
||||
|
||||
history, err := h.store.GetHistory(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, history)
|
||||
}
|
||||
Reference in New Issue
Block a user