Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-10 11:32:49 -08:00
commit b4753cef7e
81 changed files with 9255 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package banking
import (
"context"
"github.com/explorer/virtual-banker/backend/tools"
)
// AccountStatusTool gets account status
type AccountStatusTool struct {
client *BankingClient
}
// NewAccountStatusTool creates a new account status tool
func NewAccountStatusTool() *AccountStatusTool {
return &AccountStatusTool{
client: NewBankingClient(getBankingAPIURL()),
}
}
// getBankingAPIURL gets the banking API URL from environment
func getBankingAPIURL() string {
// Default to main API URL
return "http://localhost:8080"
}
// Name returns the tool name
func (t *AccountStatusTool) Name() string {
return "get_account_status"
}
// Description returns the tool description
func (t *AccountStatusTool) Description() string {
return "Get the status of a bank account including balance, transactions, and account details"
}
// Execute executes the tool
func (t *AccountStatusTool) Execute(ctx context.Context, params map[string]interface{}) (*tools.ToolResult, error) {
accountID, ok := params["account_id"].(string)
if !ok || accountID == "" {
return &tools.ToolResult{
Success: false,
Error: "account_id is required",
}, nil
}
// Call banking service
data, err := t.client.GetAccountStatus(ctx, accountID)
if err != nil {
// Fallback to mock data if service unavailable
return &tools.ToolResult{
Success: true,
Data: map[string]interface{}{
"account_id": accountID,
"balance": 10000.00,
"currency": "USD",
"status": "active",
"type": "checking",
"note": "Using fallback data - banking service unavailable",
},
}, nil
}
return &tools.ToolResult{
Success: true,
Data: data,
}, nil
}

View File

@@ -0,0 +1,66 @@
package banking
import (
"context"
"fmt"
"github.com/explorer/virtual-banker/backend/tools"
)
// CreateTicketTool creates a support ticket
type CreateTicketTool struct {
client *BankingClient
}
// NewCreateTicketTool creates a new create ticket tool
func NewCreateTicketTool() *CreateTicketTool {
return &CreateTicketTool{
client: NewBankingClient(getBankingAPIURL()),
}
}
// Name returns the tool name
func (t *CreateTicketTool) Name() string {
return "create_support_ticket"
}
// Description returns the tool description
func (t *CreateTicketTool) Description() string {
return "Create a support ticket for customer service"
}
// Execute executes the tool
func (t *CreateTicketTool) Execute(ctx context.Context, params map[string]interface{}) (*tools.ToolResult, error) {
subject, _ := params["subject"].(string)
details, _ := params["details"].(string)
if subject == "" {
return &tools.ToolResult{
Success: false,
Error: "subject is required",
}, nil
}
// Call banking service
data, err := t.client.CreateTicket(ctx, subject, details)
if err != nil {
// Fallback to mock data if service unavailable
return &tools.ToolResult{
Success: true,
Data: map[string]interface{}{
"ticket_id": fmt.Sprintf("TKT-%d", 12345),
"subject": subject,
"status": "open",
"note": "Using fallback data - banking service unavailable",
},
RequiresConfirmation: false,
}, nil
}
return &tools.ToolResult{
Success: true,
Data: data,
RequiresConfirmation: false,
}, nil
}

View File

@@ -0,0 +1,91 @@
package banking
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
// BankingClient provides access to backend banking services
type BankingClient struct {
baseURL string
httpClient *http.Client
}
// NewBankingClient creates a new banking client
func NewBankingClient(baseURL string) *BankingClient {
return &BankingClient{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// GetAccountStatus gets account status from banking service
func (c *BankingClient) GetAccountStatus(ctx context.Context, accountID string) (map[string]interface{}, error) {
url := fmt.Sprintf("%s/api/v1/banking/accounts/%s", c.baseURL, accountID)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result, nil
}
// CreateTicket creates a support ticket
func (c *BankingClient) CreateTicket(ctx context.Context, subject, details string) (map[string]interface{}, error) {
url := fmt.Sprintf("%s/api/v1/banking/tickets", c.baseURL)
payload := map[string]string{
"subject": subject,
"details": details,
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result, nil
}

View File

@@ -0,0 +1,60 @@
package banking
import (
"context"
"github.com/explorer/virtual-banker/backend/tools"
)
// SubmitPaymentTool submits a payment
type SubmitPaymentTool struct{}
// NewSubmitPaymentTool creates a new submit payment tool
func NewSubmitPaymentTool() *SubmitPaymentTool {
return &SubmitPaymentTool{}
}
// Name returns the tool name
func (t *SubmitPaymentTool) Name() string {
return "submit_payment"
}
// Description returns the tool description
func (t *SubmitPaymentTool) Description() string {
return "Submit a payment transaction (requires confirmation)"
}
// Execute executes the tool
func (t *SubmitPaymentTool) Execute(ctx context.Context, params map[string]interface{}) (*tools.ToolResult, error) {
amount, _ := params["amount"].(float64)
method, _ := params["method"].(string)
if amount <= 0 {
return &tools.ToolResult{
Success: false,
Error: "amount must be greater than 0",
}, nil
}
if method == "" {
return &tools.ToolResult{
Success: false,
Error: "payment method is required",
}, nil
}
// TODO: Call backend/banking/payments/ service
// For now, return mock data
return &tools.ToolResult{
Success: true,
Data: map[string]interface{}{
"payment_id": "PAY-11111",
"amount": amount,
"method": method,
"status": "pending_confirmation",
"transaction_id": "TXN-22222",
},
RequiresConfirmation: true, // Payments always require confirmation
}, nil
}

View File

@@ -0,0 +1,62 @@
package banking
import (
"context"
"time"
"github.com/explorer/virtual-banker/backend/tools"
)
// ScheduleAppointmentTool schedules an appointment
type ScheduleAppointmentTool struct{}
// NewScheduleAppointmentTool creates a new schedule appointment tool
func NewScheduleAppointmentTool() *ScheduleAppointmentTool {
return &ScheduleAppointmentTool{}
}
// Name returns the tool name
func (t *ScheduleAppointmentTool) Name() string {
return "schedule_appointment"
}
// Description returns the tool description
func (t *ScheduleAppointmentTool) Description() string {
return "Schedule an appointment with a bank representative"
}
// Execute executes the tool
func (t *ScheduleAppointmentTool) Execute(ctx context.Context, params map[string]interface{}) (*tools.ToolResult, error) {
datetime, _ := params["datetime"].(string)
reason, _ := params["reason"].(string)
if datetime == "" {
return &tools.ToolResult{
Success: false,
Error: "datetime is required",
}, nil
}
// Parse datetime
_, err := time.Parse(time.RFC3339, datetime)
if err != nil {
return &tools.ToolResult{
Success: false,
Error: "invalid datetime format (use RFC3339)",
}, nil
}
// TODO: Call backend/banking/ service to schedule appointment
// For now, return mock data
return &tools.ToolResult{
Success: true,
Data: map[string]interface{}{
"appointment_id": "APT-67890",
"datetime": datetime,
"reason": reason,
"status": "scheduled",
},
RequiresConfirmation: true, // Appointments require confirmation
}, nil
}