feat(webdemo): add database-backed usage dashboard
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package webdemo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"billing/internal/plugin"
|
||||
)
|
||||
|
||||
// DatabaseInput exposes the billing management API against a standalone SQLite copy.
|
||||
type DatabaseInput struct {
|
||||
app *plugin.App
|
||||
}
|
||||
|
||||
func NewDatabaseInput(databasePath string) (*DatabaseInput, error) {
|
||||
absolutePath, err := filepath.Abs(databasePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve demo database path: %w", err)
|
||||
}
|
||||
app := plugin.NewApp()
|
||||
config := fmt.Sprintf("database_path: %q\nenabled: true\ncodex_only: false\n", absolutePath)
|
||||
lifecycle, err := json.Marshal(plugin.LifecycleRequest{ConfigYAML: []byte(config), SchemaVersion: plugin.SchemaVersion})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode demo lifecycle request: %w", err)
|
||||
}
|
||||
if _, err := app.HandleMethod(plugin.MethodPluginRegister, lifecycle); err != nil {
|
||||
app.Shutdown()
|
||||
return nil, fmt.Errorf("open demo database: %w", err)
|
||||
}
|
||||
return &DatabaseInput{app: app}, nil
|
||||
}
|
||||
|
||||
func (input *DatabaseInput) Close() error {
|
||||
input.app.Shutdown()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (input *DatabaseInput) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
||||
body, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
writeJSON(response, http.StatusBadRequest, map[string]any{"error": map[string]string{"code": "request_body_error", "message": err.Error()}})
|
||||
return
|
||||
}
|
||||
wireRequest, err := json.Marshal(plugin.ManagementRequest{
|
||||
Method: request.Method, Path: request.URL.Path, Headers: request.Header.Clone(), Query: request.URL.Query(), Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
writeJSON(response, http.StatusInternalServerError, map[string]any{"error": map[string]string{"code": "request_encode_error", "message": err.Error()}})
|
||||
return
|
||||
}
|
||||
raw, err := input.app.HandleMethod(plugin.MethodManagementHandle, wireRequest)
|
||||
if err != nil {
|
||||
writeJSON(response, http.StatusInternalServerError, map[string]any{"error": map[string]string{"code": "management_error", "message": err.Error()}})
|
||||
return
|
||||
}
|
||||
var envelope plugin.Envelope
|
||||
if err := json.Unmarshal(raw, &envelope); err != nil || !envelope.OK {
|
||||
message := "invalid management response"
|
||||
if envelope.Error != nil {
|
||||
message = envelope.Error.Message
|
||||
}
|
||||
writeJSON(response, http.StatusInternalServerError, map[string]any{"error": map[string]string{"code": "management_envelope_error", "message": message}})
|
||||
return
|
||||
}
|
||||
var result plugin.ManagementResponse
|
||||
if err := json.Unmarshal(envelope.Result, &result); err != nil {
|
||||
writeJSON(response, http.StatusInternalServerError, map[string]any{"error": map[string]string{"code": "response_decode_error", "message": err.Error()}})
|
||||
return
|
||||
}
|
||||
for name, values := range result.Headers {
|
||||
for _, value := range values {
|
||||
response.Header().Add(name, value)
|
||||
}
|
||||
}
|
||||
status := result.StatusCode
|
||||
if status == 0 {
|
||||
status = http.StatusOK
|
||||
}
|
||||
response.WriteHeader(status)
|
||||
_, _ = response.Write(result.Body)
|
||||
}
|
||||
Reference in New Issue
Block a user