dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- New /developers/ page with API docs, SDKs, pricing, use cases - OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics - Case study: Glasskiosken i Flatenbadet — complete ROI analysis - Updated /order/ with recurring missions and frequency dropdown
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* quiXzoom Frilans Payout — Database Layer
|
||||
* Raw SQL via pg (no ORM per REXO standards)
|
||||
*/
|
||||
|
||||
import pg from 'pg';
|
||||
const { Pool } = pg;
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL || 'postgresql://wavult_admin:EgoVVPQuVSioCiMgJWmMoVvnjgiJPaD6hPR8xkZo@wavult-identity-core.cvi0qcksmsfj.eu-north-1.rds.amazonaws.com:5432/wavult_identity',
|
||||
ssl: { rejectUnauthorized: false },
|
||||
});
|
||||
|
||||
// ── Frilans Accounts ────────────────────────────────────────────────────────
|
||||
|
||||
export async function createAccount(data) {
|
||||
const { userId, country, currency, taxHandling, personalNumber, payoutMethod } = data;
|
||||
|
||||
const result = await pool.query(`
|
||||
INSERT INTO frilans_accounts (user_id, country, currency, tax_handling, personal_number, payout_method)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *
|
||||
`, [userId, country, currency, taxHandling, personalNumber, payoutMethod]);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
export async function getAccountByUserId(userId) {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM frilans_accounts WHERE user_id = $1 AND status = $2',
|
||||
[userId, 'active']
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
export async function updateAccount(accountId, updates) {
|
||||
const fields = Object.keys(updates);
|
||||
const values = Object.values(updates);
|
||||
const setClause = fields.map((f, i) => `${f} = $${i + 2}`).join(', ');
|
||||
|
||||
const result = await pool.query(`
|
||||
UPDATE frilans_accounts
|
||||
SET ${setClause}, updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *
|
||||
`, [accountId, ...values]);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
// ── Payouts ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function createPayout(data) {
|
||||
const { accountId, amount, netAmount, currency, platformFee, processingFee, taxAmount, method, scheduledDate } = data;
|
||||
|
||||
const result = await pool.query(`
|
||||
INSERT INTO frilans_payouts (account_id, amount, net_amount, currency, platform_fee, processing_fee, tax_amount, method, scheduled_date)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING *
|
||||
`, [accountId, amount, netAmount, currency, platformFee, processingFee, taxAmount, method, scheduledDate]);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
export async function listPayouts(accountId) {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM frilans_payouts WHERE account_id = $1 ORDER BY created_at DESC',
|
||||
[accountId]
|
||||
);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
export async function getPayoutById(payoutId) {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM frilans_payouts WHERE id = $1',
|
||||
[payoutId]
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
// ── Documents ───────────────────────────────────────────────────────────────
|
||||
|
||||
export async function createDocument(data) {
|
||||
const { accountId, type, s3Url } = data;
|
||||
|
||||
const result = await pool.query(`
|
||||
INSERT INTO frilans_documents (account_id, type, s3_url, status)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *
|
||||
`, [accountId, type, s3Url, 'verified']);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
export async function listDocuments(accountId) {
|
||||
const result = await pool.query(
|
||||
'SELECT * FROM frilans_documents WHERE account_id = $1 ORDER BY uploaded_at DESC',
|
||||
[accountId]
|
||||
);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
// ── Tax Summary ─────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getTaxSummary(accountId, year) {
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(SUM(amount), 0) as total_earnings,
|
||||
COALESCE(SUM(tax_amount), 0) as total_tax_paid,
|
||||
COALESCE(SUM(platform_fee + processing_fee), 0) as total_fees_paid,
|
||||
COALESCE(SUM(net_amount), 0) as net_earnings
|
||||
FROM frilans_payouts
|
||||
WHERE account_id = $1
|
||||
AND EXTRACT(YEAR FROM created_at) = $2
|
||||
AND status = 'completed'
|
||||
`, [accountId, year]);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
// ── Health Check ────────────────────────────────────────────────────────────
|
||||
|
||||
export async function healthCheck() {
|
||||
const result = await pool.query('SELECT NOW()');
|
||||
return result.rows[0];
|
||||
}
|
||||
Reference in New Issue
Block a user