Refactor code
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { getArtworksPage } from "@/lib/queryArtworks";
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
|
||||
// Public API for paginated artworks listing.
|
||||
export async function GET(req: NextRequest) {
|
||||
const publishedParam = req.nextUrl.searchParams.get("published") ?? "all";
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { toNextJsHandler } from "better-auth/next-js";
|
||||
|
||||
export const { POST, GET } = toNextJsHandler(auth);
|
||||
// Better Auth route handlers.
|
||||
export const { POST, GET } = toNextJsHandler(auth);
|
||||
|
||||
@ -1,10 +1,27 @@
|
||||
import { s3 } from "@/lib/s3";
|
||||
import type { S3Body } from "@/types/s3";
|
||||
import { GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { Readable } from "stream";
|
||||
|
||||
function isWebReadableStream(value: unknown): value is ReadableStream<Uint8Array> {
|
||||
return !!value && typeof (value as ReadableStream<Uint8Array>).getReader === "function";
|
||||
}
|
||||
|
||||
function toBodyInit(body: S3Body): BodyInit {
|
||||
if (body instanceof Readable) {
|
||||
return Readable.toWeb(body) as ReadableStream<Uint8Array>;
|
||||
}
|
||||
if (isWebReadableStream(body)) {
|
||||
return body;
|
||||
}
|
||||
return body as BodyInit;
|
||||
}
|
||||
|
||||
// Streams images from S3 for the admin app.
|
||||
export async function GET(_req: NextRequest, context: { params: Promise<{ key: string[] }> }) {
|
||||
const { key } = await context.params;
|
||||
const s3Key = key.join("/");
|
||||
const s3Key = key.join("/");
|
||||
|
||||
try {
|
||||
const command = new GetObjectCommand({
|
||||
@ -20,7 +37,7 @@ export async function GET(_req: NextRequest, context: { params: Promise<{ key: s
|
||||
|
||||
const contentType = response.ContentType ?? "application/octet-stream";
|
||||
|
||||
return new Response(response.Body as ReadableStream, {
|
||||
return new Response(toBodyInit(response.Body as S3Body), {
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
"Cache-Control": "public, max-age=3600",
|
||||
@ -28,7 +45,7 @@ export async function GET(_req: NextRequest, context: { params: Promise<{ key: s
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
console.error(err);
|
||||
return new Response("Image not found", { status: 404 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { s3 } from "@/lib/s3";
|
||||
import type { S3Body } from "@/types/s3";
|
||||
import { GetObjectCommand } from "@aws-sdk/client-s3";
|
||||
import archiver from "archiver";
|
||||
import { NextRequest } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { Readable } from "stream";
|
||||
|
||||
// Streams commission request files (single or zip) from S3.
|
||||
type Mode = "display" | "download" | "bulk";
|
||||
|
||||
function contentDisposition(filename: string, mode: Mode) {
|
||||
@ -17,6 +20,20 @@ function sanitizeZipEntryName(name: string) {
|
||||
return name.replace(/[^\w.\- ()\[\]]+/g, "_").slice(0, 180);
|
||||
}
|
||||
|
||||
function isWebReadableStream(value: unknown): value is ReadableStream<Uint8Array> {
|
||||
return !!value && typeof (value as ReadableStream<Uint8Array>).getReader === "function";
|
||||
}
|
||||
|
||||
function toBodyInit(body: S3Body): BodyInit {
|
||||
if (body instanceof Readable) {
|
||||
return Readable.toWeb(body) as ReadableStream<Uint8Array>;
|
||||
}
|
||||
if (isWebReadableStream(body)) {
|
||||
return body;
|
||||
}
|
||||
return body as BodyInit;
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const bucket = process.env.BUCKET_NAME;
|
||||
@ -52,7 +69,7 @@ export async function GET(req: NextRequest) {
|
||||
|
||||
const contentType = file.fileType || s3Res.ContentType || "application/octet-stream";
|
||||
|
||||
return new Response(s3Res.Body as ReadableStream, {
|
||||
return new Response(toBodyInit(s3Res.Body as S3Body), {
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
// You can tune caching; admin-only content usually should be private.
|
||||
@ -117,8 +134,17 @@ export async function GET(req: NextRequest) {
|
||||
f.originalFile || f.fileKey.split("/").pop() || "file"
|
||||
);
|
||||
|
||||
// obj.Body is a Node stream in Node runtime; works with archiver
|
||||
archive.append(obj.Body as any, { name: entryName });
|
||||
// obj.Body can be a Node Readable, web ReadableStream, or Buffer.
|
||||
const body = obj.Body;
|
||||
if (!body) continue;
|
||||
|
||||
if (body instanceof Readable) {
|
||||
archive.append(body, { name: entryName });
|
||||
} else if (isWebReadableStream(body)) {
|
||||
archive.append(Readable.from(body as AsyncIterable<Uint8Array>), { name: entryName });
|
||||
} else {
|
||||
archive.append(body as Buffer, { name: entryName });
|
||||
}
|
||||
}
|
||||
|
||||
await archive.finalize();
|
||||
|
||||
@ -1,38 +1,11 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { s3 } from "@/lib/s3";
|
||||
import { publicCommissionRequestSchema } from "@/schemas/commissions/publicRequest";
|
||||
import { DeleteObjectsCommand, PutObjectCommand } from "@aws-sdk/client-s3";
|
||||
import { NextResponse } from "next/server";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const payloadSchema = z.object({
|
||||
typeId: z.string().min(1).optional().nullable(),
|
||||
customCardId: z.string().min(1).optional().nullable(),
|
||||
optionId: z.string().min(1).optional().nullable(),
|
||||
extraIds: z.array(z.string().min(1)).default([]),
|
||||
|
||||
customerName: z.string().min(1).max(200),
|
||||
customerEmail: z.string().email().max(320),
|
||||
customerSocials: z.string().max(2000).optional().nullable(),
|
||||
message: z.string().min(1).max(20_000),
|
||||
}).superRefine((data, ctx) => {
|
||||
const hasType = Boolean(data.typeId);
|
||||
const hasCustom = Boolean(data.customCardId);
|
||||
if (!hasType && !hasCustom) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["typeId"],
|
||||
message: "Missing commission type or custom card",
|
||||
});
|
||||
}
|
||||
if (hasType && hasCustom) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["typeId"],
|
||||
message: "Only one of typeId or customCardId is allowed",
|
||||
});
|
||||
}
|
||||
});
|
||||
// Public API endpoint for commission submissions (multipart form).
|
||||
|
||||
function safeJsonParse(input: string) {
|
||||
try {
|
||||
@ -64,7 +37,7 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ error: "Invalid payload JSON" }, { status: 400 });
|
||||
}
|
||||
|
||||
const payload = payloadSchema.safeParse(parsedJson);
|
||||
const payload = publicCommissionRequestSchema.safeParse(parsedJson);
|
||||
if (!payload.success) {
|
||||
return NextResponse.json(
|
||||
{ error: "Validation error", issues: payload.error.issues },
|
||||
|
||||
Reference in New Issue
Block a user