feat: production readiness — 3-extension deploy with security hardening and observability
- Strip extensions to enable-banking, ai-categorization, ai-chat only - Remove push-notifications cron from vercel.json - Add security headers (HSTS, CSP, X-Frame-Options, Permissions-Policy) - Add /api/health endpoint for uptime monitoring - Add env var validation in ensureInitialized() - Fix SIE4 #IB opening balance records from year-end closing entry - Replace in-memory ai-chat rate limiter with Supabase-backed distributed rate limiting - Add Sentry error tracking scaffolding (@sentry/nextjs, instrumentation hook) - Add AI token usage tracking (migration 047, usage-tracker, wired into both AI extensions) - Include pending enable-banking and dashboard improvements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1a1c6ba40f
commit
13725ffc16
@@ -140,22 +140,42 @@ export default async function DashboardPage() {
|
||||
(inv) => inv.status === 'overdue'
|
||||
).length
|
||||
|
||||
// Fetch bank balance (if connected)
|
||||
// Fetch bank balance and consent info (if connected)
|
||||
const { data: bankConnections } = await supabase
|
||||
.from('bank_connections')
|
||||
.select('accounts, status')
|
||||
.select('id, accounts_data, status, consent_expires, bank_name')
|
||||
.eq('user_id', user.id)
|
||||
.eq('status', 'active')
|
||||
.limit(1)
|
||||
|
||||
let bankBalance: number | null = null
|
||||
if (bankConnections && bankConnections.length > 0) {
|
||||
const accounts = bankConnections[0].accounts as { balance: number }[] | null
|
||||
if (accounts && accounts.length > 0) {
|
||||
bankBalance = accounts.reduce((sum, acc) => sum + (acc.balance || 0), 0)
|
||||
const allBalances = bankConnections.flatMap(conn => {
|
||||
const accounts = conn.accounts_data as { balance: number }[] | null
|
||||
return accounts || []
|
||||
})
|
||||
if (allBalances.length > 0) {
|
||||
bankBalance = allBalances.reduce((sum, acc) => sum + (acc.balance || 0), 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Compute expiring bank connections (consent expires within 14 days)
|
||||
const nowMs = new Date().getTime()
|
||||
const expiringBankConnections = (bankConnections || [])
|
||||
.filter(conn => {
|
||||
if (!conn.consent_expires) return false
|
||||
const daysLeft = Math.ceil(
|
||||
(new Date(conn.consent_expires).getTime() - nowMs) / (1000 * 60 * 60 * 24)
|
||||
)
|
||||
return daysLeft > 0 && daysLeft <= 14
|
||||
})
|
||||
.map(conn => ({
|
||||
id: conn.id as string,
|
||||
bank_name: conn.bank_name as string,
|
||||
days_left: Math.ceil(
|
||||
(new Date(conn.consent_expires!).getTime() - nowMs) / (1000 * 60 * 60 * 24)
|
||||
),
|
||||
}))
|
||||
|
||||
// Fetch upcoming deadlines (next 7 days + overdue)
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
|
||||
@@ -274,6 +294,7 @@ export default async function DashboardPage() {
|
||||
unpaidVatTotal,
|
||||
overdueInvoicesCount: overdueCount,
|
||||
bankBalance,
|
||||
expiringBankConnections,
|
||||
deadlines: (deadlines || []) as Deadline[],
|
||||
receiptQueue,
|
||||
missingUnderlagCount,
|
||||
|
||||
@@ -63,9 +63,30 @@ export default function SettingsPage() {
|
||||
if (bankConnected === 'true') {
|
||||
toast({
|
||||
title: 'Bank ansluten!',
|
||||
description: 'Din bank är nu kopplad och transaktioner kan hämtas.',
|
||||
description: 'Din bank är nu kopplad. Transaktioner hämtas...',
|
||||
})
|
||||
router.replace('/settings')
|
||||
|
||||
// Auto-sync transactions after connection
|
||||
const connectionId = searchParams.get('connection_id')
|
||||
if (connectionId) {
|
||||
fetch('/api/extensions/ext/enable-banking/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ connection_id: connectionId, days_back: 90 }),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.imported > 0) {
|
||||
toast({
|
||||
title: 'Transaktioner hämtade',
|
||||
description: `${data.imported} transaktioner importerade`,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
router.replace('/settings?tab=banking')
|
||||
}
|
||||
|
||||
if (bankError) {
|
||||
|
||||
Reference in New Issue
Block a user