fix: Schema alignment and code corrections (COA 2)
All checks were successful
Test Asgard Runner / test (push) Successful in 3s

- Replace owner_id → owner_user_id in all queries
- Replace auth_token → companion_agent_token in server_connections
- Replace l.active → (l.status = 'active') checks using ENUM
- Fix AppError → ApiError in all new API files
- Add missing imports (Path, PanelAdapter trait)
- Fix StoreConfig nullable type mismatches

Resolves 122 compilation errors. Only sqlx cache generation remains.

Phase 3: EXECUTE complete per V4_WORKFLOW
This commit is contained in:
Vantz Stockwell
2026-02-15 18:23:33 -05:00
parent d7dddca106
commit 500d92cbe3
8 changed files with 342 additions and 82 deletions

View File

@@ -10,7 +10,7 @@ use std::sync::Arc;
use uuid::Uuid;
use crate::{
models::error::AppError,
models::error::ApiError,
services::payment_processor::PayPalProcessor,
AppState,
};
@@ -35,14 +35,14 @@ struct PublicStoreInfo {
async fn get_store_info(
State(state): State<Arc<AppState>>,
Path(subdomain): Path<String>,
) -> Result<impl IntoResponse, AppError> {
) -> Result<impl IntoResponse, ApiError> {
// Get license_id from subdomain
let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
.fetch_optional(&state.db)
.await?;
let license_id = license
.ok_or_else(|| AppError::NotFound("Store not found".to_string()))?
.ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
.id;
// Get store config
@@ -58,11 +58,11 @@ async fn get_store_info(
if let Some(config) = config {
if !config.enabled {
return Err(AppError::NotFound("Store is currently disabled".to_string()));
return Err(ApiError::NotFound("Store is currently disabled".to_string()));
}
Ok(Json(config))
} else {
Err(AppError::NotFound("Store not configured".to_string()))
Err(ApiError::NotFound("Store not configured".to_string()))
}
}
@@ -82,13 +82,13 @@ struct PublicStoreItem {
async fn get_store_items(
State(state): State<Arc<AppState>>,
Path(subdomain): Path<String>,
) -> Result<impl IntoResponse, AppError> {
) -> Result<impl IntoResponse, ApiError> {
let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
.fetch_optional(&state.db)
.await?;
let license_id = license
.ok_or_else(|| AppError::NotFound("Store not found".to_string()))?
.ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
.id;
// Check if store is enabled
@@ -98,7 +98,7 @@ async fn get_store_items(
.unwrap_or(false);
if !enabled {
return Err(AppError::NotFound("Store is currently disabled".to_string()));
return Err(ApiError::NotFound("Store is currently disabled".to_string()));
}
type Row = (
@@ -166,13 +166,13 @@ async fn create_purchase_order(
State(state): State<Arc<AppState>>,
Path(subdomain): Path<String>,
Json(req): Json<CreatePurchaseRequest>,
) -> Result<impl IntoResponse, AppError> {
) -> Result<impl IntoResponse, ApiError> {
let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
.fetch_optional(&state.db)
.await?;
let license_id = license
.ok_or_else(|| AppError::NotFound("Store not found".to_string()))?
.ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
.id;
// Get store config and check if enabled
@@ -184,10 +184,10 @@ async fn create_purchase_order(
)
.fetch_optional(&state.db)
.await?
.ok_or_else(|| AppError::NotFound("Store not configured".to_string()))?;
.ok_or_else(|| ApiError::NotFound("Store not configured".to_string()))?;
if !store_config.enabled {
return Err(AppError::BadRequest("Store is currently disabled".to_string()));
return Err(ApiError::BadRequest("Store is currently disabled".to_string()));
}
// Get item details
@@ -198,7 +198,7 @@ async fn create_purchase_order(
)
.fetch_optional(&state.db)
.await?
.ok_or_else(|| AppError::NotFound("Item not found or disabled".to_string()))?;
.ok_or_else(|| ApiError::NotFound("Item not found or disabled".to_string()))?;
// Check purchase limit if set
if let Some(limit) = sqlx::query_scalar!(
@@ -219,17 +219,17 @@ async fn create_purchase_order(
.unwrap_or(0);
if purchase_count >= limit as i64 {
return Err(AppError::BadRequest("Purchase limit reached for this item".to_string()));
return Err(ApiError::BadRequest("Purchase limit reached for this item".to_string()));
}
}
// Create PayPal processor using store owner's credentials
let client_id = store_config.paypal_client_id.ok_or_else(|| {
AppError::Internal("Store PayPal credentials not configured".to_string())
ApiError::Internal("Store PayPal credentials not configured".to_string())
})?;
let client_secret = store_config.paypal_client_secret.ok_or_else(|| {
AppError::Internal("Store PayPal credentials not configured".to_string())
ApiError::Internal("Store PayPal credentials not configured".to_string())
})?;
// TODO: Decrypt client_secret using encryption service
@@ -282,7 +282,7 @@ async fn handle_purchase_webhook(
State(state): State<Arc<AppState>>,
Path(subdomain): Path<String>,
Json(webhook): Json<serde_json::Value>,
) -> Result<impl IntoResponse, AppError> {
) -> Result<impl IntoResponse, ApiError> {
tracing::info!("Store purchase webhook for {}: {:?}", subdomain, webhook);
let license = sqlx::query!("SELECT id FROM licenses WHERE subdomain = $1", subdomain)
@@ -290,7 +290,7 @@ async fn handle_purchase_webhook(
.await?;
let license_id = license
.ok_or_else(|| AppError::NotFound("Store not found".to_string()))?
.ok_or_else(|| ApiError::NotFound("Store not found".to_string()))?
.id;
// TODO: Verify webhook signature
@@ -320,11 +320,11 @@ async fn handle_payment_completed(
state: &AppState,
license_id: Uuid,
webhook: serde_json::Value,
) -> Result<(), AppError> {
) -> Result<(), ApiError> {
let order_id = webhook
.pointer("/resource/supplementary_data/related_ids/order_id")
.and_then(|v| v.as_str())
.ok_or_else(|| AppError::Internal("Missing order_id in webhook".to_string()))?;
.ok_or_else(|| ApiError::Internal("Missing order_id in webhook".to_string()))?;
// Update transaction status
let transaction = sqlx::query!(
@@ -367,7 +367,7 @@ async fn handle_payment_completed(
let payload = serde_json::json!({ "command": command });
nats.publish(subject, serde_json::to_vec(&payload)?.into())
.await
.map_err(|e| AppError::Internal(format!("NATS publish failed: {}", e)))?;
.map_err(|e| ApiError::Internal(format!("NATS publish failed: {}", e)))?;
}
// Mark as delivered
@@ -391,11 +391,11 @@ async fn handle_payment_failed(
state: &AppState,
license_id: Uuid,
webhook: serde_json::Value,
) -> Result<(), AppError> {
) -> Result<(), ApiError> {
let order_id = webhook
.pointer("/resource/supplementary_data/related_ids/order_id")
.and_then(|v| v.as_str())
.ok_or_else(|| AppError::Internal("Missing order_id in webhook".to_string()))?;
.ok_or_else(|| ApiError::Internal("Missing order_id in webhook".to_string()))?;
sqlx::query!(
"UPDATE store_transactions SET status = 'failed' WHERE paypal_order_id = $1 AND license_id = $2",