Staging (#181)
* refactor: remove unnecessary secondary action from EmptyInvoices component * feat: add direct provider layer and provider_consents migration Replace Arcim Sync gateway dependency with direct provider clients for Fortnox, Visma, Briox, Bokio, and Björn Lundén. Adds OAuth config, rate limiting, retry logic, data fetching, and consent storage via new provider_consents/tokens/otc tables. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: migrate arcim extension to direct provider APIs Replace Arcim Sync gateway calls with direct provider API access. Use FortnoxClient.getText() for SIE endpoints that return plain text instead of JSON. OAuth callback now returns HTML with postMessage to communicate with the opener window instead of redirecting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use OAuth popup window instead of new tab Open provider login in a centered popup that auto-closes on completion via postMessage, keeping the user on a single tab. Falls back to redirect flow if popup is blocked. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add connection status, consent reuse, and SIE duplicate detection - Add listConsents() to query active consents by company - Add GET /status route returning consents, SIE import history, and entity counts - /connect reuses existing accepted consent instead of creating duplicates, and cleans up abandoned (status 0) consents - /status only returns accepted (status 1) consents - /sie-data checks each file's SHA-256 hash against sie_imports to report per-file import status (alreadyImported, importedAt) - /sie-data blocks on SIE validation failure (mirrors manual upload) - /import-sie validates unmapped accounts and auto-activates missing BAS accounts in chart_of_accounts (mirrors manual upload) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: show active connections, SIE file status, and smart re-sync UI - ProviderStep shows active connections with last import date, entity counts, "Synka igen" button, and disconnect option - Already-connected providers greyed out in selection grid - OptionsStep shows per-fiscal-year import status (imported vs new) - SIE toggle disabled with explanation when all files already imported - handleStartMigration skips already-imported SIE files - Auto-skip mapping step and disable SIE on re-sync when up to date - Result step hides empty "0 importerade" rows and shows "Allt är uppdaterat" when nothing new was fetched - OptionRow supports disabled state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: adjust COMING_SOON_PROVIDERS based on NODE_ENV for development and production * Removed duplicate * Removed duplicate * refactor: redesign reports page navigation from grid boxes to bordered card layout Replace the 4-column grid of uneven TabsList boxes with a CSS grid card using auto-sized columns separated by 1px border dividers. All sections now share equal height via items-stretch, with clear visual separation between groups. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: trigger Vercel deployment * Update supabase/migrations/20260402010000_provider_consents.sql Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update lib/providers/rate-limiter.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * chore: re-trigger checks after migration sync --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
greptile-apps[bot]
parent
e2d9c8bb85
commit
e42da5c32b
@@ -0,0 +1,112 @@
|
||||
-- Provider consents for direct accounting provider integrations
|
||||
-- Replaces the external dependency on the Arcim Sync gateway
|
||||
|
||||
-- Provider consents (one per connected accounting company)
|
||||
CREATE TABLE IF NOT EXISTS provider_consents (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
status INTEGER NOT NULL DEFAULT 0, -- 0=Created, 1=Accepted, 2=Revoked, 3=Inactive
|
||||
provider TEXT, -- fortnox, visma, briox, bokio, bjornlunden
|
||||
org_number TEXT,
|
||||
company_name TEXT,
|
||||
etag TEXT NOT NULL DEFAULT gen_random_uuid()::text,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
expires_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_provider_consents_company ON provider_consents(company_id);
|
||||
CREATE INDEX idx_provider_consents_company_provider ON provider_consents(company_id, provider);
|
||||
|
||||
ALTER TABLE provider_consents ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY provider_consents_select ON provider_consents
|
||||
FOR SELECT USING (company_id IN (SELECT public.user_company_ids()));
|
||||
|
||||
CREATE POLICY provider_consents_insert ON provider_consents
|
||||
FOR INSERT WITH CHECK (company_id IN (SELECT public.user_company_ids()));
|
||||
|
||||
CREATE POLICY provider_consents_update ON provider_consents
|
||||
FOR UPDATE USING (company_id IN (SELECT public.user_company_ids()));
|
||||
|
||||
CREATE POLICY provider_consents_delete ON provider_consents
|
||||
FOR DELETE USING (company_id IN (SELECT public.user_company_ids()));
|
||||
));
|
||||
|
||||
CREATE TRIGGER update_provider_consents_updated_at
|
||||
BEFORE UPDATE ON provider_consents
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Encrypted token storage for provider OAuth/API tokens
|
||||
CREATE TABLE IF NOT EXISTS provider_consent_tokens (
|
||||
consent_id UUID PRIMARY KEY REFERENCES provider_consents(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL,
|
||||
access_token TEXT NOT NULL,
|
||||
refresh_token TEXT,
|
||||
token_expires_at TIMESTAMPTZ,
|
||||
provider_company_id TEXT,
|
||||
scopes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE provider_consent_tokens ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY provider_consent_tokens_select ON provider_consent_tokens
|
||||
FOR SELECT USING (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
|
||||
CREATE POLICY provider_consent_tokens_insert ON provider_consent_tokens
|
||||
FOR INSERT WITH CHECK (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
|
||||
CREATE POLICY provider_consent_tokens_update ON provider_consent_tokens
|
||||
FOR UPDATE USING (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
|
||||
CREATE TRIGGER update_provider_consent_tokens_updated_at
|
||||
BEFORE UPDATE ON provider_consent_tokens
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- One-time codes for OAuth callback validation
|
||||
CREATE TABLE IF NOT EXISTS provider_otc (
|
||||
code TEXT PRIMARY KEY,
|
||||
consent_id UUID NOT NULL REFERENCES provider_consents(id) ON DELETE CASCADE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
used_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_provider_otc_consent ON provider_otc(consent_id);
|
||||
|
||||
ALTER TABLE provider_otc ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY provider_otc_select ON provider_otc
|
||||
FOR SELECT USING (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
|
||||
CREATE POLICY provider_otc_insert ON provider_otc
|
||||
FOR INSERT WITH CHECK (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
|
||||
CREATE POLICY provider_otc_update ON provider_otc
|
||||
FOR UPDATE USING (consent_id IN (
|
||||
SELECT id FROM provider_consents WHERE company_id IN (
|
||||
SELECT company_id FROM team_members WHERE user_id = auth.uid()
|
||||
)
|
||||
));
|
||||
Reference in New Issue
Block a user