mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
chore(release): 1.2.5-rc.3
This commit is contained in:
@@ -1,286 +1,169 @@
|
||||
// import {NextResponse} from "next/server";
|
||||
// import {isUuidv4} from "@/utils/verify-uuid";
|
||||
// import {v4 as uuidv4} from "uuid";
|
||||
// import * as drizzleDb from "@/db";
|
||||
// import {db} from "@/db";
|
||||
// import {Backup} from "@/db/schema/07_database";
|
||||
// import {and, eq} from "drizzle-orm";
|
||||
// import {withUpdatedAt} from "@/db/utils";
|
||||
// import {decryptedDump, getFileExtension} from "./helpers";
|
||||
// import {sendNotificationsBackupRestore} from "@/features/notifications/helpers";
|
||||
// import {storeBackupFiles} from "@/features/storages/helpers";
|
||||
// import {eventEmitter} from "@/features/shared/event";
|
||||
//
|
||||
// export async function POST(
|
||||
// request: Request,
|
||||
// {params}: { params: Promise<{ agentId: string }> }
|
||||
// ) {
|
||||
// try {
|
||||
// const contentType = request.headers.get("Content-Type");
|
||||
//
|
||||
// if (!contentType || !contentType.includes("multipart/form-data")) {
|
||||
// return NextResponse.json(
|
||||
// {error: "Unsupported or missing Content-Type"},
|
||||
// {status: 400}
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// eventEmitter.emit('modification', {update: true});
|
||||
//
|
||||
// const agentId = (await params).agentId;
|
||||
// const formData = await request.formData();
|
||||
// const aesKeyHex = formData.get("aes_key") as string;
|
||||
// const ivHex = formData.get("iv") as string;
|
||||
// const generatedId = formData.get("generatedId") as string | null;
|
||||
// const method = formData.get("method") as string | null;
|
||||
//
|
||||
//
|
||||
// if (!generatedId || !isUuidv4(generatedId)) {
|
||||
// return NextResponse.json(
|
||||
// {error: "generatedId is not a valid UUID"},
|
||||
// {status: 400}
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// const agent = await db.query.agent.findFirst({
|
||||
// where: eq(drizzleDb.schemas.agent.id, agentId),
|
||||
// });
|
||||
//
|
||||
// if (!agent) {
|
||||
// return NextResponse.json(
|
||||
// {error: "Agent not found"},
|
||||
// {status: 404}
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// const database = await db.query.database.findFirst({
|
||||
// where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
|
||||
// with: {
|
||||
// project: true,
|
||||
// alertPolicies: true,
|
||||
// storagePolicies: true
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// if (!database) {
|
||||
// return NextResponse.json(
|
||||
// {error: "Database associated with generatedId not found"},
|
||||
// {status: 404}
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// let backup: Backup | null | undefined = null;
|
||||
//
|
||||
// if (method === "automatic") {
|
||||
// [backup] = await db
|
||||
// .insert(drizzleDb.schemas.backup)
|
||||
// .values({
|
||||
// status: 'ongoing',
|
||||
// databaseId: database.id,
|
||||
// })
|
||||
// .returning();
|
||||
//
|
||||
//
|
||||
// if (!backup) {
|
||||
// return NextResponse.json(
|
||||
// {error: "Unable to create an automatic backup"},
|
||||
// {status: 500}
|
||||
// );
|
||||
// }
|
||||
// } else {
|
||||
// backup = await db.query.backup.findFirst({
|
||||
// where: and(
|
||||
// eq(drizzleDb.schemas.backup.status, 'ongoing'),
|
||||
// eq(drizzleDb.schemas.backup.databaseId, database.id),
|
||||
// ),
|
||||
// });
|
||||
//
|
||||
//
|
||||
// if (!backup) {
|
||||
// return NextResponse.json(
|
||||
// {error: "Unable to find the corresponding backup"},
|
||||
// {status: 404}
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const status = formData.get("status") as string | null;
|
||||
//
|
||||
// if (status === "success") {
|
||||
// const file = formData.get("file") as File | null;
|
||||
// const extension = formData.get("extension") as string | null;
|
||||
// if (!aesKeyHex || !ivHex) {
|
||||
// return NextResponse.json({error: "Missing fields"}, {status: 400});
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (!file) {
|
||||
// return NextResponse.json(
|
||||
// {error: "File is required for successful backup"},
|
||||
// {status: 400}
|
||||
// );
|
||||
// }
|
||||
// const fileSizeBytes = file.size;
|
||||
// // const fileExtension = '.' + (file.name.split('.').pop()?.toLowerCase() || '');
|
||||
// const fileExtension = extension ? extension : getFileExtension(database.dbms)
|
||||
// const decryptedFile = await decryptedDump(file, aesKeyHex, ivHex, fileExtension);
|
||||
// const uuid = uuidv4();
|
||||
// const fileName = `${uuid}${fileExtension}`;
|
||||
// const buffer = Buffer.from(await decryptedFile.arrayBuffer());
|
||||
//
|
||||
//
|
||||
// const storageResults = await storeBackupFiles(backup, database, buffer, fileName)
|
||||
// eventEmitter.emit('modification', {update: true});
|
||||
//
|
||||
// await sendNotificationsBackupRestore(database, "success_backup");
|
||||
//
|
||||
// return NextResponse.json(
|
||||
// {
|
||||
// message: "Backup successfully uploaded",
|
||||
// },
|
||||
// {status: 200}
|
||||
// );
|
||||
// } else {
|
||||
//
|
||||
// await db
|
||||
// .update(drizzleDb.schemas.backup)
|
||||
// .set(withUpdatedAt({status: 'failed'}))
|
||||
// .where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
//
|
||||
// eventEmitter.emit('modification', {update: true});
|
||||
//
|
||||
// await sendNotificationsBackupRestore(database, "error_backup");
|
||||
//
|
||||
//
|
||||
// return NextResponse.json(
|
||||
// {
|
||||
// message: "Backup successfully updated with status failed",
|
||||
// },
|
||||
// {status: 200}
|
||||
// );
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("Error in POST handler:", error);
|
||||
// return NextResponse.json(
|
||||
// {error: "Internal server error"},
|
||||
// {status: 500}
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {NextResponse} from "next/server";
|
||||
import {isUuidv4} from "@/utils/verify-uuid";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import * as drizzleDb from "@/db";
|
||||
import { db } from "@/db";
|
||||
import { isUuidv4 } from "@/utils/verify-uuid";
|
||||
import uploadTempFileToProviders, { createDecryptionStream } from "@/features/api/upload/helpers/common";
|
||||
import { getFileExtension, saveStreamToTempFile } from "@/features/api/upload/helpers/file";
|
||||
import { Backup } from "@/db/schema/07_database";
|
||||
import { withUpdatedAt } from "@/db/utils";
|
||||
import { eventEmitter } from "@/features/shared/event";
|
||||
import { sendNotificationsBackupRestore } from "@/features/notifications/helpers";
|
||||
import {Readable} from "node:stream";
|
||||
import {db} from "@/db";
|
||||
import {Backup} from "@/db/schema/07_database";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import {withUpdatedAt} from "@/db/utils";
|
||||
import {decryptedDump, getFileExtension} from "./helpers";
|
||||
import {sendNotificationsBackupRestore} from "@/features/notifications/helpers";
|
||||
import {storeBackupFiles} from "@/features/storages/helpers";
|
||||
import {eventEmitter} from "@/features/shared/event";
|
||||
|
||||
export async function POST(request: Request, { params }: { params: Promise<{ agentId: string }> }) {
|
||||
export async function POST(
|
||||
request: Request,
|
||||
{params}: { params: Promise<{ agentId: string }> }
|
||||
) {
|
||||
try {
|
||||
console.log("POST");
|
||||
console.log("data");
|
||||
const agentId = (await params).agentId;
|
||||
const headers = request.headers;
|
||||
const contentType = request.headers.get("Content-Type");
|
||||
|
||||
console.log("headers", headers);
|
||||
console.log("agentId", agentId);
|
||||
|
||||
const generatedId = headers.get("x-generated-id");
|
||||
const status = headers.get("x-status");
|
||||
const encryptedAesKeyHex = headers.get("x-aes-key");
|
||||
const ivHex = headers.get("x-iv");
|
||||
const method = headers.get("x-method") ?? "manual";
|
||||
const extension = headers.get("x-extension");
|
||||
|
||||
if (!agentId || !generatedId || !status || !encryptedAesKeyHex || !ivHex) {
|
||||
return NextResponse.json({ error: "Missing required headers/params" }, { status: 400 });
|
||||
if (!contentType || !contentType.includes("multipart/form-data")) {
|
||||
return NextResponse.json(
|
||||
{error: "Unsupported or missing Content-Type"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
|
||||
if (!isUuidv4(generatedId)) {
|
||||
return NextResponse.json({ error: "generatedId is not a valid UUID" }, { status: 400 });
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
const agentId = (await params).agentId;
|
||||
const formData = await request.formData();
|
||||
const aesKeyHex = formData.get("aes_key") as string;
|
||||
const ivHex = formData.get("iv") as string;
|
||||
const generatedId = formData.get("generatedId") as string | null;
|
||||
const method = formData.get("method") as string | null;
|
||||
|
||||
|
||||
if (!generatedId || !isUuidv4(generatedId)) {
|
||||
return NextResponse.json(
|
||||
{error: "generatedId is not a valid UUID"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
|
||||
const agent = await db.query.agent.findFirst({
|
||||
where: eq(drizzleDb.schemas.agent.id, agentId),
|
||||
});
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json({ error: "Agent not found" }, { status: 404 });
|
||||
return NextResponse.json(
|
||||
{error: "Agent not found"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
|
||||
const database = await db.query.database.findFirst({
|
||||
where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
|
||||
with: { project: true, storagePolicies: true },
|
||||
with: {
|
||||
project: true,
|
||||
alertPolicies: true,
|
||||
storagePolicies: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!database) {
|
||||
return NextResponse.json({ error: "Database not found" }, { status: 404 });
|
||||
return NextResponse.json(
|
||||
{error: "Database associated with generatedId not found"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
|
||||
let backup: Backup | null | undefined = null;
|
||||
|
||||
if (method === "automatic") {
|
||||
[backup] = await db
|
||||
.insert(drizzleDb.schemas.backup)
|
||||
.values({ status: "ongoing", databaseId: database.id })
|
||||
.values({
|
||||
status: 'ongoing',
|
||||
databaseId: database.id,
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
||||
if (!backup) {
|
||||
return NextResponse.json(
|
||||
{error: "Unable to create an automatic backup"},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
backup = await db.query.backup.findFirst({
|
||||
where: and(
|
||||
eq(drizzleDb.schemas.backup.status, 'ongoing'),
|
||||
eq(drizzleDb.schemas.backup.databaseId, database.id),
|
||||
eq(drizzleDb.schemas.backup.status, "ongoing")
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
if (!backup) {
|
||||
return NextResponse.json(
|
||||
{error: "Unable to find the corresponding backup"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup) {
|
||||
return NextResponse.json({ error: "Backup not found" }, { status: 404 });
|
||||
}
|
||||
const status = formData.get("status") as string | null;
|
||||
|
||||
if (status === "success") {
|
||||
|
||||
const decipher = createDecryptionStream(encryptedAesKeyHex, ivHex);
|
||||
const fileExt = extension || getFileExtension(database.dbms);
|
||||
const fileName = `${uuidv4()}${fileExt}`;
|
||||
|
||||
|
||||
const nodeStream = webStreamToNode(request.body!);
|
||||
|
||||
const tmpPath = await saveStreamToTempFile(
|
||||
nodeStream.pipe(decipher),
|
||||
fileName
|
||||
);
|
||||
|
||||
if (!tmpPath) {
|
||||
return NextResponse.json({ error: "Unable to save temporary backup file" }, { status: 500 });
|
||||
const file = formData.get("file") as File | null;
|
||||
const extension = formData.get("extension") as string | null;
|
||||
if (!aesKeyHex || !ivHex) {
|
||||
return NextResponse.json({error: "Missing fields"}, {status: 400});
|
||||
}
|
||||
|
||||
uploadTempFileToProviders(backup, database, tmpPath, fileName);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{error: "File is required for successful backup"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
const fileSizeBytes = file.size;
|
||||
// const fileExtension = '.' + (file.name.split('.').pop()?.toLowerCase() || '');
|
||||
const fileExtension = extension ? extension : getFileExtension(database.dbms)
|
||||
const decryptedFile = await decryptedDump(file, aesKeyHex, ivHex, fileExtension);
|
||||
const uuid = uuidv4();
|
||||
const fileName = `${uuid}${fileExtension}`;
|
||||
const buffer = Buffer.from(await decryptedFile.arrayBuffer());
|
||||
|
||||
|
||||
const storageResults = await storeBackupFiles(backup, database, buffer, fileName)
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
await sendNotificationsBackupRestore(database, "success_backup");
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: "Backup successfully uploaded",
|
||||
},
|
||||
{status: 200}
|
||||
);
|
||||
} else {
|
||||
|
||||
await db
|
||||
.update(drizzleDb.schemas.backup)
|
||||
.set(withUpdatedAt({ status: "failed" }))
|
||||
.set(withUpdatedAt({status: 'failed'}))
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
|
||||
eventEmitter.emit("modification", { update: true });
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
await sendNotificationsBackupRestore(database, "error_backup");
|
||||
|
||||
return NextResponse.json({ message: "Backup updated with status failed" }, { status: 200 });
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: "Backup successfully updated with status failed",
|
||||
},
|
||||
{status: 200}
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Upload error:", err);
|
||||
return NextResponse.json({ error: "Upload failed", detail: err.message }, { status: 500 });
|
||||
} catch (error) {
|
||||
console.error("Error in POST handler:", error);
|
||||
return NextResponse.json(
|
||||
{error: "Internal server error"},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function webStreamToNode(stream: ReadableStream<Uint8Array>) {
|
||||
return Readable.fromWeb(stream);
|
||||
}
|
||||
@@ -84,8 +84,8 @@ RUN chown -R nextjs:nodejs /app/private
|
||||
RUN chown -R nextjs:nodejs /app/public
|
||||
|
||||
COPY --from=builder /app/next.config.ts ./
|
||||
#COPY --from=builder /app/server ./server
|
||||
#COPY --from=builder /app/src ./src
|
||||
COPY --from=builder /app/server ./server
|
||||
COPY --from=builder /app/src ./src
|
||||
|
||||
COPY --from=builder /app/portabase.config.ts ./
|
||||
COPY --from=builder /app/drizzle.config.ts ./
|
||||
@@ -93,7 +93,7 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
COPY --chown=nextjs:nodejs src/db ./src/db
|
||||
|
||||
#COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ else
|
||||
echo "[WARN] No TZ provided, using default container timezone"
|
||||
fi
|
||||
|
||||
#pnpm run start
|
||||
node server.js
|
||||
pnpm run start
|
||||
#node server.js
|
||||
|
||||
exec "$@"
|
||||
|
||||
|
||||
@@ -41,13 +41,11 @@ const nextConfig: NextConfig = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
|
||||
experimental: {
|
||||
serverActions: {
|
||||
bodySizeLimit: "10gb",
|
||||
},
|
||||
proxyClientMaxBodySize: '10gb',
|
||||
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@
|
||||
"version": "1.2.5-rc.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack -p 8887",
|
||||
"dev": "NODE_ENV=development tsx watch server/server.ts",
|
||||
"build": "next build --experimental-build-mode compile",
|
||||
"start": "next start",
|
||||
"start": "NODE_ENV=production node server/server.js",
|
||||
|
||||
"lint": "next lint",
|
||||
"email": "email dev --dir ./src/components/emails",
|
||||
|
||||
Generated
+36
@@ -303,6 +303,9 @@ importers:
|
||||
baseline-browser-mapping:
|
||||
specifier: ^2.9.19
|
||||
version: 2.9.19
|
||||
concurrently:
|
||||
specifier: ^9.2.1
|
||||
version: 9.2.1
|
||||
drizzle-kit:
|
||||
specifier: ^0.31.1
|
||||
version: 0.31.8
|
||||
@@ -3894,6 +3897,11 @@ packages:
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
concurrently@9.2.1:
|
||||
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
confbox@0.2.2:
|
||||
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
|
||||
|
||||
@@ -6192,6 +6200,9 @@ packages:
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
safe-array-concat@1.1.3:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
@@ -6286,6 +6297,10 @@ packages:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shell-quote@1.8.3:
|
||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -6602,6 +6617,10 @@ packages:
|
||||
resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
|
||||
ts-api-utils@2.4.0:
|
||||
resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
|
||||
engines: {node: '>=18.12'}
|
||||
@@ -11224,6 +11243,15 @@ snapshots:
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
concurrently@9.2.1:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
rxjs: 7.8.2
|
||||
shell-quote: 1.8.3
|
||||
supports-color: 8.1.1
|
||||
tree-kill: 1.2.2
|
||||
yargs: 17.7.2
|
||||
|
||||
confbox@0.2.2: {}
|
||||
|
||||
consola@3.4.2: {}
|
||||
@@ -13747,6 +13775,10 @@ snapshots:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
safe-array-concat@1.1.3:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -13920,6 +13952,8 @@ snapshots:
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shell-quote@1.8.3: {}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -14316,6 +14350,8 @@ snapshots:
|
||||
typedarray.prototype.slice: 1.0.5
|
||||
which-typed-array: 1.1.19
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
ts-api-utils@2.4.0(typescript@5.9.3):
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express, {Router} from "express";
|
||||
import express, { Router } from "express";
|
||||
import uploadRouter from "./upload";
|
||||
import {loggingMiddleware} from "@/features/api/middleware";
|
||||
import { loggingMiddleware } from "@/features/api/middleware";
|
||||
|
||||
const router: Router = express.Router();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user