mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 13:17:45 +00:00
Compare commits
6 commits
90c2f4dc94
...
0d11b41a45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d11b41a45 | ||
| a000447b0a | |||
| 0ede4c7260 | |||
| ed9a480385 | |||
| c24cc3dc01 | |||
|
|
25dafcc24b |
13 changed files with 69 additions and 33 deletions
|
|
@ -30,5 +30,14 @@ export async function POST(request: NextRequest) {
|
|||
return rateLimit.sendResponse({ error: "Failed to update description" }, 500);
|
||||
}
|
||||
|
||||
// Tell Cloudflare to purge cache
|
||||
fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.CLOUDFLARE_ZONE_ID}/purge_cache`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
files: [`${process.env.NEXT_PUBLIC_BASE_URL}/api/profile/${session.user?.id}/info`],
|
||||
}),
|
||||
});
|
||||
|
||||
return rateLimit.sendResponse({ success: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,5 +33,14 @@ export async function POST(request: NextRequest) {
|
|||
return rateLimit.sendResponse({ error: "Failed to update name" }, 500);
|
||||
}
|
||||
|
||||
// Tell Cloudflare to purge cache
|
||||
fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.CLOUDFLARE_ZONE_ID}/purge_cache`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
files: [`${process.env.NEXT_PUBLIC_BASE_URL}/api/profile/${session.user.id}/info`],
|
||||
}),
|
||||
});
|
||||
|
||||
return rateLimit.sendResponse({ success: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,5 +81,17 @@ export async function POST(request: NextRequest) {
|
|||
return rateLimit.sendResponse({ error: "Failed to update profile picture" }, 500);
|
||||
}
|
||||
|
||||
// Tell Cloudflare to purge cache
|
||||
fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.CLOUDFLARE_ZONE_ID}/purge_cache`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
files: [
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/api/profile/${session.user?.id}/info`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/profile/${session.user?.id}/picture`,
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
return rateLimit.sendResponse({ success: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
|||
if (quarantined && needsFixingReason && session.user?.id?.toString() !== process.env.NEXT_PUBLIC_ADMIN_USER_ID)
|
||||
return rateLimit.sendResponse({ error: `You're not an admin!` }, 401);
|
||||
|
||||
const clearImages = formData.get("clearImages") === "true";
|
||||
|
||||
// Edit Mii in database
|
||||
const updateData: Prisma.MiiUpdateInput = {};
|
||||
if (name !== undefined) updateData.name = profanity.censor(name); // Censor potentially inappropriate words
|
||||
|
|
@ -168,8 +170,9 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
|||
if (youtubeId !== undefined) updateData.youtubeId = youtubeId;
|
||||
if (instructions !== undefined) updateData.instructions = instructions;
|
||||
if (customImages.length > 0) updateData.imageCount = customImages.length;
|
||||
else if (clearImages) updateData.imageCount = 0;
|
||||
|
||||
const imagesChanged = customImages.length > 0 || miiPortraitImage || miiFeaturesImage;
|
||||
const imagesChanged = customImages.length > 0 || clearImages || miiPortraitImage || miiFeaturesImage;
|
||||
if (settings.queueEnabled && imagesChanged) updateData.in_queue = true;
|
||||
|
||||
if (Object.keys(updateData).length === 0) return rateLimit.sendResponse({ error: "Nothing was changed" }, 400);
|
||||
|
|
@ -192,7 +195,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
|||
await fs.mkdir(miiUploadsDirectory, { recursive: true });
|
||||
|
||||
// Only touch files if new images were uploaded
|
||||
if (customImages.length > 0) {
|
||||
if (customImages.length > 0 || clearImages) {
|
||||
// Delete all custom images
|
||||
const files = await fs.readdir(miiUploadsDirectory);
|
||||
await Promise.all(files.filter((file) => file.startsWith("image")).map((file) => fs.unlink(path.join(miiUploadsDirectory, file))));
|
||||
|
|
@ -266,9 +269,12 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
|||
headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
files: [
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/api/mii/${miiId}/info`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=mii`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=features`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=image0`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=image1`,
|
||||
`${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=image2`,
|
||||
],
|
||||
}),
|
||||
}).catch((err) => {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default async function RandomPage() {
|
|||
|
||||
const randomIndex = Math.floor(Math.random() * count);
|
||||
const randomMii = await prisma.mii.findFirst({
|
||||
where: { in_queue: false, quarantined: false, needsFixing: { not: null } },
|
||||
where: { in_queue: false, quarantined: false, needsFixing: null },
|
||||
skip: randomIndex,
|
||||
take: 1,
|
||||
select: { id: true },
|
||||
|
|
|
|||
|
|
@ -69,7 +69,13 @@ export default function Header() {
|
|||
data-tooltip="Your Profile"
|
||||
>
|
||||
<img
|
||||
src={$session.user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${$session.user.image}` : $session.user.image}
|
||||
src={
|
||||
$session?.user?.image
|
||||
? $session.user.image.startsWith("/profile")
|
||||
? `${import.meta.env.VITE_API_URL}${$session.user.image}`
|
||||
: $session.user.image
|
||||
: "/guest.png"
|
||||
}
|
||||
onError={(e) => {
|
||||
e.currentTarget.onerror = null; // Prevent infinite loops
|
||||
e.currentTarget.src = "/guest.png";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useTransition } from "react";
|
||||
import { useTransition } from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import type { MiiGender, MiiPlatform } from "@tomodachi-share/shared";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
|
@ -8,12 +8,11 @@ export default function GenderSelect() {
|
|||
const [searchParams] = useSearchParams();
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
const [selected, setSelected] = useState<MiiGender | null>((searchParams.get("gender") as MiiGender) ?? null);
|
||||
const selected = (searchParams.get("gender") as MiiGender) ?? null;
|
||||
const platform = (searchParams.get("platform") as MiiPlatform) || undefined;
|
||||
|
||||
const handleClick = (gender: MiiGender) => {
|
||||
const filter = selected === gender ? null : gender;
|
||||
setSelected(filter);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useTransition } from "react";
|
||||
import { useTransition } from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import type { MiiMakeup } from "@tomodachi-share/shared";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
|
@ -8,11 +8,10 @@ export default function MakeupSelect() {
|
|||
const [searchParams] = useSearchParams();
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
const [selected, setSelected] = useState<MiiMakeup | null>((searchParams.get("makeup") as MiiMakeup) ?? null);
|
||||
const selected = (searchParams.get("makeup") as MiiMakeup) ?? null;
|
||||
|
||||
const handleClick = (makeup: MiiMakeup) => {
|
||||
const filter = selected === makeup ? null : makeup;
|
||||
setSelected(filter);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { MiiPlatform } from "@tomodachi-share/shared";
|
||||
import { type ChangeEvent, useState, useTransition } from "react";
|
||||
import { type ChangeEvent, useTransition } from "react";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
export default function OtherFilters() {
|
||||
|
|
@ -9,16 +9,14 @@ export default function OtherFilters() {
|
|||
const [, startTransition] = useTransition();
|
||||
|
||||
const platform = (searchParams.get("platform") as MiiPlatform) || undefined;
|
||||
const [allowCopying, setAllowCopying] = useState<boolean>((searchParams.get("allowCopying") as unknown as boolean) ?? false);
|
||||
const [quarantined, setQuarantined] = useState<boolean>((searchParams.get("quarantined") as unknown as boolean) ?? false);
|
||||
const allowCopying = searchParams.get("allowCopying") === "true";
|
||||
const quarantined = searchParams.get("quarantined") === "true";
|
||||
|
||||
const handleChangeAllowCopying = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setAllowCopying(e.target.checked);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
||||
if (!allowCopying) {
|
||||
if (e.target.checked) {
|
||||
params.set("allowCopying", "true");
|
||||
} else {
|
||||
params.delete("allowCopying");
|
||||
|
|
@ -30,12 +28,10 @@ export default function OtherFilters() {
|
|||
};
|
||||
|
||||
const handleChangeQuarantined = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setQuarantined(e.target.checked);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
||||
if (!quarantined) {
|
||||
if (e.target.checked) {
|
||||
params.set("quarantined", "true");
|
||||
} else {
|
||||
params.delete("quarantined");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useTransition } from "react";
|
||||
import { useTransition } from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import type { MiiPlatform } from "@tomodachi-share/shared";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
|
|
@ -7,19 +7,22 @@ export default function PlatformSelect() {
|
|||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
const [selected, setSelected] = useState<MiiPlatform | null>((searchParams.get("platform") as MiiPlatform) ?? null);
|
||||
const selected = (searchParams.get("platform") as MiiPlatform) ?? null;
|
||||
|
||||
const handleClick = (platform: MiiPlatform) => {
|
||||
const filter = selected === platform ? null : platform;
|
||||
setSelected(filter);
|
||||
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", "1");
|
||||
|
||||
if (filter) {
|
||||
params.set("platform", filter);
|
||||
} else {
|
||||
params.delete("platform");
|
||||
}
|
||||
if (params.get("gender") === "NONBINARY") params.delete("gender");
|
||||
params.delete("makeup");
|
||||
params.delete("allowCopying");
|
||||
|
||||
startTransition(() => {
|
||||
navigate(`?${params.toString()}`);
|
||||
|
|
|
|||
|
|
@ -87,10 +87,9 @@ export default function EditMiiPage() {
|
|||
formData.append("instructions", JSON.stringify(instructions.current));
|
||||
|
||||
if (hasCustomImagesChanged.current) {
|
||||
files.forEach((file, index) => {
|
||||
// image1, image2, etc.
|
||||
formData.append(`image${index + 1}`, file);
|
||||
});
|
||||
files.forEach((file, index) => formData.append(`image${index + 1}`, file));
|
||||
if (files.length === 0) formData.append("clearImages", "true");
|
||||
}
|
||||
|
||||
// Switch pictures
|
||||
|
|
|
|||
|
|
@ -51,9 +51,7 @@ export default function ProfileLayout() {
|
|||
const joinDate = new Date(user.createdAt).toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
||||
const metaTitle = `${user.name} - TomodachiShare`;
|
||||
const metaDescription = `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`;
|
||||
const metaImage = user.image.startsWith("/profile")
|
||||
? `${import.meta.env.VITE_API_URL}${user.image}`
|
||||
: (user.image ?? `${import.meta.env.VITE_API_URL}/guest.png`);
|
||||
const metaImage = user.image ? (user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${user.image}` : user.image) : "/guest.png";
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -81,7 +79,7 @@ export default function ProfileLayout() {
|
|||
{/* Profile picture */}
|
||||
<Link to={`/profile/${user.id}`} className="size-28 aspect-square">
|
||||
<img
|
||||
src={user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${user.image}` : user.image}
|
||||
src={user.image ? (user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${user.image}` : user.image) : "/guest.png"}
|
||||
onError={(e) => {
|
||||
e.currentTarget.onerror = null; // Prevent infinite loops
|
||||
e.currentTarget.src = "/guest.png";
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default function ReportUserPage() {
|
|||
|
||||
<div className="bg-orange-100 rounded-xl border-2 border-orange-400 flex p-4 gap-4">
|
||||
<img
|
||||
src={user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${user.image}` : user.image}
|
||||
src={user.image ? (user.image.startsWith("/profile") ? `${import.meta.env.VITE_API_URL}${user.image}` : user.image) : "/guest.png"}
|
||||
onError={(e) => {
|
||||
e.currentTarget.onerror = null; // Prevent infinite loops
|
||||
e.currentTarget.src = "/guest.png";
|
||||
|
|
|
|||
Loading…
Reference in a new issue