Add ACMCC app source, Supabase backend, and project config

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 20:19:26 -04:00
parent 313b51b412
commit 183fe0a93c
1422 changed files with 259271 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.1";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
};
serve(async (req) => {
if (req.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
const QBO_CLIENT_ID = Deno.env.get("QBO_CLIENT_ID");
const QBO_CLIENT_SECRET = Deno.env.get("QBO_CLIENT_SECRET");
if (!QBO_CLIENT_ID || !QBO_CLIENT_SECRET) {
return new Response(JSON.stringify({ error: "QBO credentials not configured" }), {
status: 500,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
const supabaseAdmin = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
const url = new URL(req.url);
try {
if (req.method === "GET") {
// Handle OAuth callback from QBO
const code = url.searchParams.get("code");
const realmId = url.searchParams.get("realmId");
if (!code) {
return new Response("Missing authorization code", { status: 400 });
}
// Determine the redirect URI (same as this function URL)
const redirectUri = `${SUPABASE_URL}/functions/v1/qbo-auth`;
// Exchange code for tokens
const tokenResp = await fetch("https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": `Basic ${btoa(`${QBO_CLIENT_ID}:${QBO_CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: redirectUri,
}),
});
if (!tokenResp.ok) {
const errText = await tokenResp.text();
console.error("Token exchange failed:", errText);
return new Response(`Token exchange failed: ${errText}`, { status: 500 });
}
const tokens = await tokenResp.json();
const expiry = (Date.now() + tokens.expires_in * 1000).toString();
// Store tokens in company_settings
const entries: Record<string, string> = {
qbo_access_token: tokens.access_token,
qbo_refresh_token: tokens.refresh_token,
qbo_token_expiry: expiry,
};
if (realmId) {
entries.qbo_realm_id = realmId;
}
for (const [key, value] of Object.entries(entries)) {
const { data: existing } = await supabaseAdmin.from("company_settings").select("id").eq("key", key).maybeSingle();
if (existing) {
await supabaseAdmin.from("company_settings").update({ value }).eq("key", key);
} else {
await supabaseAdmin.from("company_settings").insert({ key, value });
}
}
// Return a success HTML page
return new Response(
`<!DOCTYPE html><html><body style="font-family:sans-serif;text-align:center;padding:60px">
<h1>✅ QuickBooks Connected!</h1>
<p>Your QuickBooks Online account has been successfully linked.</p>
<p>You can close this window and return to the app.</p>
<script>setTimeout(()=>window.close(),3000)</script>
</body></html>`,
{ headers: { "Content-Type": "text/html" } },
);
}
if (req.method === "POST") {
// Generate the QBO authorization URL
const body = await req.json();
const { action } = body;
if (action === "get_auth_url") {
const redirectUri = `${SUPABASE_URL}/functions/v1/qbo-auth`;
const authUrl = `https://appcenter.intuit.com/connect/oauth2?` +
`client_id=${QBO_CLIENT_ID}` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&scope=com.intuit.quickbooks.accounting` +
`&response_type=code` +
`&state=qbo_connect`;
return new Response(JSON.stringify({ authUrl, redirectUri }), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
if (action === "check_status") {
const { data: tokenSetting } = await supabaseAdmin
.from("company_settings")
.select("value")
.eq("key", "qbo_refresh_token")
.maybeSingle();
return new Response(JSON.stringify({
connected: !!tokenSetting?.value,
}), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
}
return new Response(JSON.stringify({ error: "Method not allowed" }), {
status: 405,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (err) {
console.error("QBO auth error:", err);
return new Response(JSON.stringify({ error: err instanceof Error ? err.message : "Unknown error" }), {
status: 500,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
});