Files
lab/src/middleware.ts
T
kovakmedya 2c6c074a06 feat: job status/step flow, file upload, finance sync, notifications
Job lifecycle
  - acceptJobAction (lab): pending → in_progress + currentStep=olcu
  - advanceStepAction (lab): step ilerletir, son adım sonrası status=sent
  - markDeliveredAction (clinic): sent → delivered
  - cancelJobAction: pending iş iptali (her iki taraf)
  - job_status_history her step transition'da idempotent kayıt
  - Detay sayfası interactive panel + Aşama Geçmişi kartı

Job files (Appwrite Storage job-files bucket, 30MB/file)
  - uploadJobFilesAction: çoklu dosya, mimeType'tan kind sınıflandırma
    (scan/image/document), her iki team'e read permission, partial-fail
    rollback (storage + row temizliği)
  - deleteJobFileAction: yetkilendirilmiş silme, file + row birlikte
  - JobFilesPanel: client-side select + upload + liste + indir + sil
  - next.config bodySizeLimit 3mb → 100mb (toplu yükleme için)

Finance sync (idempotent)
  - syncFinanceForJob helper: sent/delivered transition'larında klinik
    payable + lab receivable rows (jobId+tenantId+type unique kontrolü,
    her tarafta tek satır garanti)
  - markFinancePaidAction / reopenFinanceAction: manuel ödendi/geri al
  - /finance sayfası: stat kartlar (bekleyen alacak/borç, aylık gelir/gider)
    + hareketler tablosu, role-aware kopyalar
  - Memory rule [[feedback_cross_entity_sync_helpers]]: best-effort, never
    re-throws

Notifications
  - createNotification helper, connection (request/approve) ve job
    (create/accept/sent/delivered) eventlerinde tetikleniyor
  - /notifications sayfası + tek tek / hepsi okundu işaretle
  - Header'a Bell ikonu + okunmamış count badge (layout SSR'de besler)
  - Middleware PROTECTED_PREFIXES'e /notifications ekli
2026-05-21 20:17:33 +03:00

65 lines
1.5 KiB
TypeScript

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const AUTH_COOKIE = "lab-session";
const PUBLIC_AUTH_PATHS = [
"/sign-in",
"/sign-in-2",
"/sign-in-3",
"/sign-up",
"/sign-up-2",
"/sign-up-3",
"/forgot-password",
"/forgot-password-2",
"/forgot-password-3",
"/reset-password",
];
const PROTECTED_PREFIXES = [
"/dashboard",
"/onboarding",
"/settings",
"/jobs",
"/products",
"/finance",
"/connections",
"/notifications",
];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const session = request.cookies.get(AUTH_COOKIE)?.value;
// Legacy redirects
if (pathname === "/login") {
return NextResponse.redirect(new URL("/sign-in", request.url));
}
if (pathname === "/register") {
return NextResponse.redirect(new URL("/sign-up", request.url));
}
const isAuthPath = PUBLIC_AUTH_PATHS.some(
(p) => pathname === p || pathname.startsWith(`${p}/`),
);
const isProtected = PROTECTED_PREFIXES.some(
(p) => pathname === p || pathname.startsWith(`${p}/`),
);
if (isProtected && !session) {
const url = new URL("/sign-in", request.url);
url.searchParams.set("redirect", pathname);
return NextResponse.redirect(url);
}
if (isAuthPath && session) {
return NextResponse.redirect(new URL("/dashboard", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};