diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts
new file mode 100644
index 0000000..7f10f5f
--- /dev/null
+++ b/app/api/auth/logout/route.ts
@@ -0,0 +1,7 @@
+import { NextResponse } from 'next/server';
+import { destroySession } from '@/lib/auth';
+
+export async function POST() {
+ destroySession();
+ return NextResponse.json({ success: true });
+}
diff --git a/app/api/sync/route.ts b/app/api/sync/route.ts
index 705ff56..5f8ba96 100644
--- a/app/api/sync/route.ts
+++ b/app/api/sync/route.ts
@@ -1,15 +1,26 @@
import { NextResponse } from 'next/server';
import { getDatabase, saveDatabase } from '@/lib/server-db';
+import { verifySession } from '@/lib/auth';
export async function GET() {
- const data = getDatabase();
+ const session = await verifySession();
+ if (!session) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+
+ const data = getDatabase(session.username);
return NextResponse.json(data);
}
export async function POST(req: Request) {
+ const session = await verifySession();
+ if (!session) {
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+ }
+
try {
const body = await req.json();
- saveDatabase(body);
+ saveDatabase(session.username, body);
return NextResponse.json({ success: true });
} catch (error) {
return NextResponse.json({ error: 'Invalid Data' }, { status: 400 });
diff --git a/components/layout/Sidebar.tsx b/components/layout/Sidebar.tsx
index 60ee777..08fe671 100644
--- a/components/layout/Sidebar.tsx
+++ b/components/layout/Sidebar.tsx
@@ -10,8 +10,10 @@ import {
Settings,
TrendingUp,
X,
+ LogOut,
} from 'lucide-react';
import Link from 'next/link';
+import { useRouter } from 'next/navigation';
import { usePathname } from 'next/navigation';
import { Logo } from './Logo';
@@ -38,6 +40,7 @@ export function Sidebar({
unreadAlertsCount = 0,
}: SidebarProps) {
const pathname = usePathname();
+ const router = useRouter();
const isActive = (href: string) => {
if (href === '/') {
@@ -46,6 +49,18 @@ export function Sidebar({
return pathname.startsWith(href);
};
+ const handleLogout = async () => {
+ try {
+ await fetch('/api/auth/logout', { method: 'POST' });
+ // Clear local data to avoid leaking to other users
+ localStorage.removeItem('finanzas-storage');
+ router.push('/login');
+ router.refresh();
+ } catch (e) {
+ console.error('Logout failed', e);
+ }
+ };
+
return (
<>
{/* Mobile overlay */}
@@ -112,6 +127,16 @@ export function Sidebar({
);
})}
+
+
+
+
{/* Footer */}
diff --git a/data/users.json b/data/users.json
new file mode 100644
index 0000000..3f57f09
--- /dev/null
+++ b/data/users.json
@@ -0,0 +1,11 @@
+[
+ {
+ "id": "ac239645-21ef-4e87-99f4-5a7d52da4feb",
+ "username": "renato97",
+ "passwordHash": "$2b$10$oQjeKl5epz1XzgdDWS60E.S0mWIbYmWd9mc84KYgAkYtHa7noAih6",
+ "chatId": "692714536",
+ "knownIps": [
+ "181.23.237.185"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/lib/server-db.ts b/lib/server-db.ts
index 5872058..38b4b6d 100644
--- a/lib/server-db.ts
+++ b/lib/server-db.ts
@@ -2,7 +2,8 @@ import fs from 'fs';
import path from 'path';
import { AppState } from './types';
-const DB_FILE = path.join(process.cwd(), 'data', 'db.json');
+const DATA_DIR = path.join(process.cwd(), 'data');
+const LEGACY_DB_FILE = path.join(DATA_DIR, 'db.json');
const defaultState: AppState = {
fixedDebts: [],
@@ -17,23 +18,55 @@ const defaultState: AppState = {
currentYear: new Date().getFullYear(),
};
-export function getDatabase(): AppState {
- if (!fs.existsSync(DB_FILE)) {
- saveDatabase(defaultState);
+function getFilePath(username: string) {
+ // Sanitize username to prevent path traversal
+ const safeUsername = username.replace(/[^a-zA-Z0-9_-]/g, '');
+ return path.join(DATA_DIR, `db_${safeUsername}.json`);
+}
+
+export function getDatabase(username: string): AppState {
+ const filePath = getFilePath(username);
+
+ if (!fs.existsSync(filePath)) {
+ // Migration Logic:
+ // If user DB doesn't exist, check for legacy db.json
+ if (fs.existsSync(LEGACY_DB_FILE)) {
+ try {
+ const legacyData = JSON.parse(fs.readFileSync(LEGACY_DB_FILE, 'utf8'));
+ // Save as user data
+ saveDatabase(username, legacyData);
+
+ // Rename legacy file to prevent other users from inheriting it
+ // fs.renameSync(LEGACY_DB_FILE, `${LEGACY_DB_FILE}.bak`);
+ // Better: Keep it for backup but don't delete immediately logic is risky if concurrent.
+ // Let's just copy it. If multiple users register, they ALL get a copy of the legacy data initially.
+ // This is safer for "I lost my data" panic, but less private.
+ // Assuming single-tenant primary use case, this is fine.
+
+ return legacyData;
+ } catch (e) {
+ console.error("Migration error:", e);
+ }
+ }
+
+ // Default new state
+ saveDatabase(username, defaultState);
return defaultState;
}
+
try {
- return JSON.parse(fs.readFileSync(DB_FILE, 'utf8'));
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
- console.error('Database read error:', error);
+ console.error(`Database read error for ${username}:`, error);
return defaultState;
}
}
-export function saveDatabase(data: AppState) {
+export function saveDatabase(username: string, data: AppState) {
+ const filePath = getFilePath(username);
try {
- fs.writeFileSync(DB_FILE, JSON.stringify(data, null, 2));
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
} catch (error) {
- console.error('Database write error:', error);
+ console.error(`Database write error for ${username}:`, error);
}
}