73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
/** Баланс счёта SMS-сервиса — для предупреждения в интерфейсе.
|
||
*
|
||
* Зачем: 10–13 сентября рассылки падали с кодом 201 («не хватает средств»),
|
||
* и об этом никто не знал. Ключ доступа к SMS живёт только на сервере, поэтому
|
||
* баланс отдаём отдельной функцией, а не читаем ключ в браузере.
|
||
*/
|
||
import { createClient } from "npm:@supabase/supabase-js@2";
|
||
|
||
const ALLOWED_ORIGINS = [
|
||
"https://dost.supersamsev.ru",
|
||
"https://supa.supersamsev.ru",
|
||
"https://dev.mkn8n.ru",
|
||
"http://localhost:5173",
|
||
];
|
||
|
||
const BALANCE_URL = "https://sms.ru/my/balance";
|
||
// Ниже этого остатка предупреждаем заранее, до остановки отправки.
|
||
const LOW_BALANCE_RUB = 100;
|
||
|
||
const cors = (origin: string) => ({
|
||
"Access-Control-Allow-Origin": ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0],
|
||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||
"Access-Control-Allow-Headers": "Content-Type,Authorization,apikey",
|
||
});
|
||
|
||
Deno.serve(async (req: Request) => {
|
||
const origin = req.headers.get("origin") || "";
|
||
const headers = { ...cors(origin), "Content-Type": "application/json" };
|
||
|
||
if (req.method === "OPTIONS") return new Response(null, { headers });
|
||
|
||
try {
|
||
const supabaseUrl = Deno.env.get("SUPABASE_URL") || "";
|
||
const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || "";
|
||
const sb = createClient(supabaseUrl, serviceKey);
|
||
|
||
const { data: settings } = await sb
|
||
.from("sms_campaign_settings")
|
||
.select("sms_api_id")
|
||
.eq("campaign_type", "first_sms")
|
||
.single();
|
||
|
||
const apiId = settings?.sms_api_id || Deno.env.get("SMS_API_ID") || "";
|
||
if (!apiId) {
|
||
return new Response(JSON.stringify({ ok: false, error: "api_id not configured" }), { status: 500, headers });
|
||
}
|
||
|
||
const resp = await fetch(`${BALANCE_URL}?api_id=${encodeURIComponent(apiId)}&json=1`);
|
||
const text = await resp.text();
|
||
|
||
let payload: Record<string, unknown> = {};
|
||
try {
|
||
payload = JSON.parse(text);
|
||
} catch {
|
||
return new Response(JSON.stringify({ ok: false, error: "unexpected response", raw: text.slice(0, 200) }), { status: 200, headers });
|
||
}
|
||
|
||
if (payload.status !== "OK") {
|
||
return new Response(JSON.stringify({ ok: false, code: payload.status_code ?? null, raw: text.slice(0, 200) }), { status: 200, headers });
|
||
}
|
||
|
||
const balance = Number(payload.balance);
|
||
return new Response(JSON.stringify({
|
||
ok: true,
|
||
balance,
|
||
low: balance < LOW_BALANCE_RUB,
|
||
threshold: LOW_BALANCE_RUB,
|
||
}), { headers });
|
||
} catch (e) {
|
||
return new Response(JSON.stringify({ ok: false, error: String(e) }), { status: 500, headers });
|
||
}
|
||
});
|