Files
flxn-app/src/app/routes/api/teams/upload-logo.ts
T
yohlo 0a7b5fa00f
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Successful in 8s
CI/CD Pipeline / i18n Catalog Check (push) Failing after 10s
CI/CD Pipeline / Build and Push App Docker Image (push) Skipped
CI/CD Pipeline / Deploy to Kubernetes (push) Skipped
i18n
2026-08-08 15:29:40 -07:00

153 lines
4.7 KiB
TypeScript

import { createFileRoute } from "@tanstack/react-router";
import { msg } from "@lingui/core/macro";
import { superTokensRequestMiddleware } from "@/utils/supertokens";
import { pbAdmin } from "@/lib/pocketbase/client";
import { logger } from "@/lib/logger";
import { localizedFor } from "@/lib/i18n/server-messages";
import { z } from "zod";
const uploadSchema = z.object({
teamId: z.string().min(1, "Team ID is required"),
});
export const Route = createFileRoute("/api/teams/upload-logo")({
server: {
middleware: [superTokensRequestMiddleware],
handlers: {
POST: async ({ request, context }) => {
const i18n = localizedFor(context.metadata);
try {
const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin");
if (!userId) return new Response("Unauthenticated", { status: 401 });
const formData = await request.formData();
const teamId = formData.get("teamId") as string;
const logoFile = formData.get("logo") as File;
const validationResult = uploadSchema.safeParse({ teamId });
if (!validationResult.success) {
return new Response(
JSON.stringify({
error: i18n._(msg`Invalid input`),
details: validationResult.error.issues,
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
if (!logoFile || logoFile.size === 0) {
return new Response(
JSON.stringify({
error: i18n._(msg`Logo file is required`),
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const allowedTypes = [
"image/jpeg",
"image/jpg",
"image/png",
"image/gif",
];
if (!allowedTypes.includes(logoFile.type)) {
return new Response(
JSON.stringify({
error: i18n._(
msg`Invalid file type. Only JPEG, PNG and GIF are allowed.`
),
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const maxSize = 10 * 1024 * 1024;
if (logoFile.size > maxSize) {
return new Response(
JSON.stringify({
error: i18n._(msg`File too large. Maximum size is 10MB.`),
}),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const team = await pbAdmin.getTeam(teamId);
if (!team) {
return new Response(
JSON.stringify({
error: i18n._(msg`Team not found`),
}),
{
status: 404,
headers: { "Content-Type": "application/json" },
}
);
}
const user = await pbAdmin.getPlayerByAuthId(userId);
if (!team.players.map((p) => p.id).includes(user?.id!) && !isAdmin)
return new Response("Unauthorized", { status: 403 });
logger.info("Uploading team logo", {
teamId,
fileName: logoFile.name,
fileSize: logoFile.size,
userId,
});
const pbFormData = new FormData();
pbFormData.append("logo", logoFile);
await pbAdmin.updateTeam(teamId, pbFormData as any);
const updatedTeam = await pbAdmin.getTeam(teamId);
if (!updatedTeam) throw new Error("Failed to fetch updated team");
logger.info("Team logo uploaded successfully", {
teamId,
logo: updatedTeam.logo,
});
return new Response(
JSON.stringify({
success: true,
team: updatedTeam,
message: "Logo uploaded successfully",
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
} catch (error: any) {
logger.error("Error uploading team logo:", error);
return new Response(
JSON.stringify({
error: i18n._(msg`Failed to upload logo`),
message: error.message || i18n._(msg`Unknown error occurred`),
}),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
},
},
},
});