perf+fix: file download proxy + drop awaits on audit/notifications/finance sync
Two problems reported by the user: 1. File downloads broken on the lab side. The link in JobFilesPanel pointed straight at Appwrite's /storage/.../view URL. Storage permissions are scoped to the job's two teams, but the browser only has a session cookie for our app domain, not for db.kovaksoft.com — so the cross-origin request hit Appwrite as a guest and 401'd. New /api/jobs/[jobId]/files/[fileId]/download route. requireTenant() first, then verify the caller's tenant is one of (clinicTenantId, labTenantId) on the parent job, then storage.getFileDownload via the admin SDK and stream the buffer back with Content-Disposition: attachment so the browser saves it under the original filename. listJobFiles now hands out that relative URL instead of the Appwrite one — same anchor in the panel, just routed through us. 2. Saves and edits feel slow whenever a notification is involved. Every mutation was awaiting logAudit, createNotification and syncFinanceForJob in sequence. None of these need to block the user response — audit is best-effort logging, notifications are async UX, and the finance sync is idempotent and re-runs on the next mutation anyway. Switched all 46 call sites across the action modules to void-fire-and-forget (matching the pattern we already used in clinic-pricing-actions). Net effect: each mutation drops ~3 sequential Appwrite roundtrips before the server action returns.
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import { BUCKETS, DATABASE_ID, TABLES, type Job, type JobFile } from "@/lib/appwrite/schema";
|
||||||
|
import { createAdminClient } from "@/lib/appwrite/server";
|
||||||
|
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side download proxy. The Appwrite bucket files are scoped to the
|
||||||
|
* job's two teams (clinic + lab) and the lab's frontend domain doesn't carry
|
||||||
|
* an Appwrite session cookie, so a direct browser → Appwrite link 401s. We
|
||||||
|
* authenticate the caller via the lab session, verify they actually have
|
||||||
|
* access to the job, then stream the file out with a forced attachment
|
||||||
|
* disposition.
|
||||||
|
*/
|
||||||
|
export async function GET(
|
||||||
|
_request: Request,
|
||||||
|
{ params }: { params: Promise<{ jobId: string; fileId: string }> },
|
||||||
|
) {
|
||||||
|
const { jobId, fileId } = await params;
|
||||||
|
|
||||||
|
let ctx;
|
||||||
|
try {
|
||||||
|
ctx = await requireTenant();
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tablesDB, storage } = createAdminClient();
|
||||||
|
|
||||||
|
let job: Job;
|
||||||
|
let file: JobFile;
|
||||||
|
try {
|
||||||
|
const [j, f] = await Promise.all([
|
||||||
|
tablesDB.getRow(DATABASE_ID, TABLES.jobs, jobId) as Promise<unknown>,
|
||||||
|
tablesDB.getRow(DATABASE_ID, TABLES.jobFiles, fileId) as Promise<unknown>,
|
||||||
|
]);
|
||||||
|
job = j as Job;
|
||||||
|
file = f as JobFile;
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.jobId !== jobId) {
|
||||||
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
if (ctx.tenantId !== job.clinicTenantId && ctx.tenantId !== job.labTenantId) {
|
||||||
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = (await storage.getFileDownload(BUCKETS.jobFiles, file.fileId)) as
|
||||||
|
| ArrayBuffer
|
||||||
|
| Buffer;
|
||||||
|
const body =
|
||||||
|
buf instanceof ArrayBuffer ? new Uint8Array(buf) : new Uint8Array(buf);
|
||||||
|
|
||||||
|
// Quote the filename so spaces / non-ASCII don't break the header.
|
||||||
|
const safeName = file.name.replace(/["\\]/g, "_");
|
||||||
|
return new NextResponse(body, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"Content-Disposition": `attachment; filename="${safeName}"; filename*=UTF-8''${encodeURIComponent(file.name)}`,
|
||||||
|
"Cache-Control": "private, no-store",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -144,7 +144,7 @@ export async function POST(
|
|||||||
createdRowIds.push(row.$id);
|
createdRowIds.push(row.$id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: tenantCtx.tenantId,
|
tenantId: tenantCtx.tenantId,
|
||||||
userId: tenantCtx.user.id,
|
userId: tenantCtx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export async function requestConnectionAction(
|
|||||||
approvedAt: null,
|
approvedAt: null,
|
||||||
rejectedAt: null,
|
rejectedAt: null,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -132,7 +132,7 @@ export async function requestConnectionAction(
|
|||||||
},
|
},
|
||||||
connectionPermissions(clinicTenantId, labTenantId),
|
connectionPermissions(clinicTenantId, labTenantId),
|
||||||
);
|
);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -141,7 +141,7 @@ export async function requestConnectionAction(
|
|||||||
changes: { clinicTenantId, labTenantId, status: "pending" },
|
changes: { clinicTenantId, labTenantId, status: "pending" },
|
||||||
});
|
});
|
||||||
const counterpartId = counterpart.tenantId;
|
const counterpartId = counterpart.tenantId;
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: counterpartId,
|
tenantId: counterpartId,
|
||||||
connectionId: created.$id,
|
connectionId: created.$id,
|
||||||
message: `${ctx.settings?.companyName ?? "Bir hesap"} bağlantı talebi gönderdi.`,
|
message: `${ctx.settings?.companyName ?? "Bir hesap"} bağlantı talebi gönderdi.`,
|
||||||
@@ -217,7 +217,7 @@ export async function approveConnectionAction(
|
|||||||
approvedAt: new Date().toISOString(),
|
approvedAt: new Date().toISOString(),
|
||||||
rejectedAt: null,
|
rejectedAt: null,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -227,7 +227,7 @@ export async function approveConnectionAction(
|
|||||||
});
|
});
|
||||||
const requesterTenant =
|
const requesterTenant =
|
||||||
conn.clinicTenantId === ctx.tenantId ? conn.labTenantId : conn.clinicTenantId;
|
conn.clinicTenantId === ctx.tenantId ? conn.labTenantId : conn.clinicTenantId;
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: requesterTenant,
|
tenantId: requesterTenant,
|
||||||
connectionId,
|
connectionId,
|
||||||
message: `${ctx.settings?.companyName ?? "Karşı taraf"} bağlantı talebinizi onayladı.`,
|
message: `${ctx.settings?.companyName ?? "Karşı taraf"} bağlantı talebinizi onayladı.`,
|
||||||
@@ -270,7 +270,7 @@ export async function rejectConnectionAction(
|
|||||||
status: "rejected",
|
status: "rejected",
|
||||||
rejectedAt: new Date().toISOString(),
|
rejectedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -313,7 +313,7 @@ export async function cancelConnectionAction(
|
|||||||
try {
|
try {
|
||||||
const { tablesDB } = createAdminClient();
|
const { tablesDB } = createAdminClient();
|
||||||
await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
|
await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
@@ -350,7 +350,7 @@ export async function deleteConnectionAction(
|
|||||||
try {
|
try {
|
||||||
const { tablesDB } = createAdminClient();
|
const { tablesDB } = createAdminClient();
|
||||||
await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
|
await tablesDB.deleteRow(DATABASE_ID, TABLES.connections, connectionId);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export async function markFinancePaidAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.financeEntries, id, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.financeEntries, id, {
|
||||||
status: "paid",
|
status: "paid",
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -95,7 +95,7 @@ export async function reopenFinanceAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.financeEntries, id, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.financeEntries, id, {
|
||||||
status: "pending",
|
status: "pending",
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ export async function createJobAction(
|
|||||||
},
|
},
|
||||||
jobPermissions(ctx.tenantId, parsed.data.labTenantId),
|
jobPermissions(ctx.tenantId, parsed.data.labTenantId),
|
||||||
);
|
);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -209,7 +209,7 @@ export async function createJobAction(
|
|||||||
entityId: created.$id,
|
entityId: created.$id,
|
||||||
changes: { labTenantId: parsed.data.labTenantId, patientCode },
|
changes: { labTenantId: parsed.data.labTenantId, patientCode },
|
||||||
});
|
});
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: parsed.data.labTenantId,
|
tenantId: parsed.data.labTenantId,
|
||||||
jobId: created.$id,
|
jobId: created.$id,
|
||||||
message: `${ctx.settings?.companyName ?? "Bir klinik"} yeni iş yayınladı (${patientCode}).`,
|
message: `${ctx.settings?.companyName ?? "Bir klinik"} yeni iş yayınladı (${patientCode}).`,
|
||||||
@@ -302,7 +302,7 @@ export async function acceptJobAction(
|
|||||||
currentStep: "olcu",
|
currentStep: "olcu",
|
||||||
});
|
});
|
||||||
await appendJobHistory({ job, step: "olcu", completedBy: ctx.user.id });
|
await appendJobHistory({ job, step: "olcu", completedBy: ctx.user.id });
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -310,7 +310,7 @@ export async function acceptJobAction(
|
|||||||
entityId: jobId,
|
entityId: jobId,
|
||||||
changes: { status: "in_progress", currentStep: "olcu" },
|
changes: { status: "in_progress", currentStep: "olcu" },
|
||||||
});
|
});
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: job.clinicTenantId,
|
tenantId: job.clinicTenantId,
|
||||||
jobId,
|
jobId,
|
||||||
message: `${ctx.settings?.companyName ?? "Lab"} hasta ${job.patientCode} işini işleme aldı.`,
|
message: `${ctx.settings?.companyName ?? "Lab"} hasta ${job.patientCode} işini işleme aldı.`,
|
||||||
@@ -361,7 +361,7 @@ export async function advanceStepAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
||||||
status: "sent",
|
status: "sent",
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -380,7 +380,7 @@ export async function advanceStepAction(
|
|||||||
completedBy: ctx.user.id,
|
completedBy: ctx.user.id,
|
||||||
note,
|
note,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -401,8 +401,8 @@ export async function advanceStepAction(
|
|||||||
completedBy: ctx.user.id,
|
completedBy: ctx.user.id,
|
||||||
note,
|
note,
|
||||||
});
|
});
|
||||||
await syncFinanceForJob({ ...job, status: "sent" });
|
void syncFinanceForJob({ ...job, status: "sent" });
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: job.clinicTenantId,
|
tenantId: job.clinicTenantId,
|
||||||
jobId,
|
jobId,
|
||||||
message: `Hasta ${job.patientCode} işi gönderildi. Teslim alındığında onaylayın.`,
|
message: `Hasta ${job.patientCode} işi gönderildi. Teslim alındığında onaylayın.`,
|
||||||
@@ -445,7 +445,7 @@ export async function markDeliveredAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
||||||
status: "delivered",
|
status: "delivered",
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -453,8 +453,8 @@ export async function markDeliveredAction(
|
|||||||
entityId: jobId,
|
entityId: jobId,
|
||||||
changes: { status: "delivered" },
|
changes: { status: "delivered" },
|
||||||
});
|
});
|
||||||
await syncFinanceForJob({ ...job, status: "delivered" });
|
void syncFinanceForJob({ ...job, status: "delivered" });
|
||||||
await createNotification({
|
void createNotification({
|
||||||
tenantId: job.labTenantId,
|
tenantId: job.labTenantId,
|
||||||
jobId,
|
jobId,
|
||||||
message: `Hasta ${job.patientCode} işi teslim alındı.`,
|
message: `Hasta ${job.patientCode} işi teslim alındı.`,
|
||||||
@@ -502,7 +502,7 @@ export async function cancelJobAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
||||||
status: "cancelled",
|
status: "cancelled",
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export async function uploadJobFilesAction(
|
|||||||
createdRowIds.push(row.$id);
|
createdRowIds.push(row.$id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -206,7 +206,7 @@ export async function deleteJobFileAction(
|
|||||||
// File may already be gone; row is the source of truth.
|
// File may already be gone; row is the source of truth.
|
||||||
}
|
}
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import "server-only";
|
|||||||
|
|
||||||
import { Query } from "node-appwrite";
|
import { Query } from "node-appwrite";
|
||||||
|
|
||||||
import { BUCKETS, DATABASE_ID, TABLES, type JobFile } from "./schema";
|
import { DATABASE_ID, TABLES, type JobFile } from "./schema";
|
||||||
import { createAdminClient } from "./server";
|
import { createAdminClient } from "./server";
|
||||||
import { toPlain } from "./serialize";
|
import { toPlain } from "./serialize";
|
||||||
import { getFileViewUrl } from "./storage";
|
|
||||||
|
|
||||||
export type JobFileWithUrl = JobFile & {
|
export type JobFileWithUrl = JobFile & {
|
||||||
|
/** Server-side download proxy. Browser → our app → admin SDK → bucket. */
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export async function listJobFiles(jobId: string): Promise<JobFileWithUrl[]> {
|
|||||||
return toPlain(
|
return toPlain(
|
||||||
rows.map((r) => ({
|
rows.map((r) => ({
|
||||||
...r,
|
...r,
|
||||||
url: getFileViewUrl(BUCKETS.jobFiles, r.fileId),
|
url: `/api/jobs/${jobId}/files/${r.$id}/download`,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export async function uploadLogoAction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -151,7 +151,7 @@ export async function removeLogoAction(): Promise<LogoActionState> {
|
|||||||
/* file already gone, fine */
|
/* file already gone, fine */
|
||||||
}
|
}
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export async function createPatientAction(
|
|||||||
},
|
},
|
||||||
patientPermissions(ctx.tenantId),
|
patientPermissions(ctx.tenantId),
|
||||||
);
|
);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -191,7 +191,7 @@ export async function updatePatientAction(
|
|||||||
lastName: parsed.data.lastName,
|
lastName: parsed.data.lastName,
|
||||||
notes: parsed.data.notes,
|
notes: parsed.data.notes,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -236,7 +236,7 @@ export async function archivePatientAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.patients, id, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.patients, id, {
|
||||||
archived: !row.archived,
|
archived: !row.archived,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ async function audit(action: "update", entityType: string, changes: Record<strin
|
|||||||
const session = await createSessionClient();
|
const session = await createSessionClient();
|
||||||
const user = await session.account.get();
|
const user = await session.account.get();
|
||||||
const tenantId = (await getActiveTenantId()) ?? "global";
|
const tenantId = (await getActiveTenantId()) ?? "global";
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId,
|
tenantId,
|
||||||
userId: user.$id,
|
userId: user.$id,
|
||||||
action,
|
action,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export async function createProstheticAction(
|
|||||||
},
|
},
|
||||||
prostheticPermissions(ctx.tenantId),
|
prostheticPermissions(ctx.tenantId),
|
||||||
);
|
);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -134,7 +134,7 @@ export async function updateProstheticAction(
|
|||||||
unitPrice: parsed.data.unitPrice,
|
unitPrice: parsed.data.unitPrice,
|
||||||
currency: parsed.data.currency,
|
currency: parsed.data.currency,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -179,7 +179,7 @@ export async function archiveProstheticAction(
|
|||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.prosthetics, id, {
|
await tablesDB.updateRow(DATABASE_ID, TABLES.prosthetics, id, {
|
||||||
archived: !row.archived,
|
archived: !row.archived,
|
||||||
});
|
});
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -222,7 +222,7 @@ export async function deleteProstheticAction(
|
|||||||
return { ok: false, error: "Bu ürünü silme yetkiniz yok." };
|
return { ok: false, error: "Bu ürünü silme yetkiniz yok." };
|
||||||
}
|
}
|
||||||
await tablesDB.deleteRow(DATABASE_ID, TABLES.prosthetics, id);
|
await tablesDB.deleteRow(DATABASE_ID, TABLES.prosthetics, id);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export async function inviteMemberAction(
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
@@ -182,7 +182,7 @@ export async function cancelInviteAction(
|
|||||||
status: "cancelled",
|
status: "cancelled",
|
||||||
});
|
});
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -228,7 +228,7 @@ export async function removeMemberAction(
|
|||||||
|
|
||||||
await teams.deleteMembership(ctx.tenantId, membershipId);
|
await teams.deleteMembership(ctx.tenantId, membershipId);
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
@@ -280,7 +280,7 @@ export async function leaveWorkspaceAction(): Promise<MemberActionState> {
|
|||||||
|
|
||||||
await admin.teams.deleteMembership(ctx.tenantId, me.$id);
|
await admin.teams.deleteMembership(ctx.tenantId, me.$id);
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "delete",
|
action: "delete",
|
||||||
@@ -334,7 +334,7 @@ export async function updateMemberRoleAction(
|
|||||||
const { teams } = createAdminClient();
|
const { teams } = createAdminClient();
|
||||||
await teams.updateMembership(ctx.tenantId, membershipId, [role]);
|
await teams.updateMembership(ctx.tenantId, membershipId, [role]);
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -456,7 +456,7 @@ export async function acceptInviteAction(code: string): Promise<MemberActionStat
|
|||||||
acceptedBy: user.$id,
|
acceptedBy: user.$id,
|
||||||
});
|
});
|
||||||
|
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: invite.tenantId,
|
tenantId: invite.tenantId,
|
||||||
userId: user.$id,
|
userId: user.$id,
|
||||||
action: "create",
|
action: "create",
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export async function updateWorkspaceSettingsAction(
|
|||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
await tablesDB.updateRow(DATABASE_ID, TABLES.tenantSettings, row.$id, parsed.data);
|
await tablesDB.updateRow(DATABASE_ID, TABLES.tenantSettings, row.$id, parsed.data);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "update",
|
action: "update",
|
||||||
@@ -86,7 +86,7 @@ export async function updateWorkspaceSettingsAction(
|
|||||||
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
await logAudit({
|
void logAudit({
|
||||||
tenantId: ctx.tenantId,
|
tenantId: ctx.tenantId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
action: "create",
|
action: "create",
|
||||||
|
|||||||
Reference in New Issue
Block a user