// aerOS Superadmin — API layer. Maps django-ninja /platform + /billing + /auth // responses to the shapes the screens consume (SA_DATA.ORGS / PLANS). (function () { const api = window.aerosApi; // Plan: backend {slug,name,price,limits{max_rooms,max_products},features{}} // -> UI {tier,name,price,maxRooms,maxProducts,features[],popular} function adaptPlan(p) { const lim = p.limits || {}; const feats = p.features || {}; const norm = (v) => (v === -1 || v === undefined || v === null ? 9999 : v); return { tier: p.slug, name: p.name, price: Number(p.price) || 0, maxRooms: norm(lim.max_rooms), maxProducts: norm(lim.max_products), features: Array.isArray(feats) ? feats : Object.keys(feats).filter((k) => feats[k]), popular: p.slug === 'growth' || p.slug === 'scale', }; } window.SA_API = { // Login + verify the account is a platform super-admin. async login(username, password) { const resp = await api.post('/auth/login', { username, password }, { auth: false }); const d = (resp && resp.data) || resp; if (d && d.two_factor) return d; // caller shows the 2FA step const tok = d && d.token; if (!tok) throw new Error('no token'); return this._afterToken(tok); }, async twoFactorVerify(challenge, code) { const resp = await api.post('/auth/2fa-verify', { challenge, code }, { auth: false }); const d = (resp && resp.data) || resp; if (!d || !d.token) throw new Error('2fa_failed'); return this._afterToken(d.token); }, async _afterToken(tok) { api.setToken(tok); try { await api.post('/platform/metrics/'); // 200 only for super-admin } catch (e) { api.setToken(''); throw new Error('not_superadmin'); } return true; }, logout() { api.setToken(''); }, async listOrgs() { return api.data(await api.post('/platform/list-organizations/')); }, async metrics() { return api.data(await api.post('/platform/metrics/')); }, async listPlans() { const rows = api.data(await api.post('/billing/list-plans/', {}, { auth: false })) || []; return rows.map(adaptPlan); }, async createOrg(f) { return api.data(await api.post('/platform/create-organization/', { org_name: f.name, slug: f.slug, vertical: f.vertical, plan_slug: f.plan, admin_login: f.adminLogin, admin_email: f.adminMail, admin_password: f.adminPass, })); }, async suspend(orgId) { return api.data(await api.post('/platform/suspend-organization/', { org_id: orgId })); }, async activate(orgId) { return api.data(await api.post('/platform/activate-organization/', { org_id: orgId })); }, async changePlan(orgId, planSlug) { return api.data(await api.post('/platform/change-plan/', { org_id: orgId, plan_slug: planSlug })); }, async impersonate(orgId) { return api.data(await api.post('/platform/impersonate/', { org_id: orgId })); }, // Platform AI settings (shared Anthropic key used by all tenants) async getSettings() { return api.data(await api.post('/platform/settings/get/')) || {}; }, async setSettings(s) { return api.data(await api.post('/platform/settings/set/', s)); }, async testApiKey(key, model) { return api.data(await api.post('/platform/settings/test/', { anthropic_api_key: key || null, anthropic_model: model || null })) || { ok: false, message: 'Hata' }; }, async testSmtp(toEmail) { return api.data(await api.post('/platform/settings/test-smtp/', { to_email: toEmail || null })) || { ok: false, message: 'Hata' }; }, async listUpgradeRequests() { return api.data(await api.post('/platform/upgrade-requests/list/')) || []; }, async resolveUpgrade(id, action) { return api.data(await api.post('/platform/upgrade-requests/resolve/', { id, action })); }, async updatePlan(p) { const fin = (v) => (typeof v === 'number' && isFinite(v)) ? v : null; return api.data(await api.post('/platform/update-plan/', { slug: p.tier, name: p.name, price: Number(p.price) || 0, currency: 'EUR', max_rooms: fin(p.maxRooms), max_products: fin(p.maxProducts), })); }, }; })();