fix: login issues

This commit is contained in:
trafficlunar 2026-04-17 17:20:51 +01:00
parent 46202b22b0
commit 11df9261da
9 changed files with 47 additions and 172 deletions

View file

@ -13,42 +13,30 @@
"dependencies": { "dependencies": {
"@2toad/profanity": "^3.3.0", "@2toad/profanity": "^3.3.0",
"@auth/prisma-adapter": "2.11.1", "@auth/prisma-adapter": "2.11.1",
"@bprogress/next": "^3.2.12",
"@hello-pangea/dnd": "^18.0.1",
"@prisma/client": "^6.19.2", "@prisma/client": "^6.19.2",
"bit-buffer": "^0.3.0", "bit-buffer": "^0.3.0",
"canvas-confetti": "^1.9.4",
"dayjs": "^1.11.20", "dayjs": "^1.11.20",
"downshift": "^9.3.2", "downshift": "^9.3.2",
"embla-carousel-react": "^8.6.0",
"file-type": "^22.0.1", "file-type": "^22.0.1",
"jsqr": "^1.4.0",
"next": "16.2.3", "next": "16.2.3",
"next-auth": "5.0.0-beta.30", "next-auth": "5.0.0-beta.30",
"qrcode-generator": "^2.0.4", "qrcode-generator": "^2.0.4",
"react": "^19.2.5", "react": "^19.2.5",
"react-dom": "^19.2.5", "react-dom": "^19.2.5",
"react-dropzone": "^15.0.0",
"react-image-crop": "^11.0.10",
"redis": "^5.11.0", "redis": "^5.11.0",
"satori": "^0.26.0", "satori": "^0.26.0",
"seedrandom": "^3.0.5",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"sjcl-with-all": "1.0.8", "sjcl-with-all": "1.0.8",
"swr": "^2.4.1",
"zod": "^4.3.6", "zod": "^4.3.6",
"@tomodachi-share/shared": "workspace:*" "@tomodachi-share/shared": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.3.5", "@eslint/eslintrc": "^3.3.5",
"@iconify/react": "^6.0.2", "@iconify/react": "^6.0.2",
"@tailwindcss/postcss": "^4.2.2", "@tailwindcss/postcss": "^4.2.2",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^25.6.0", "@types/node": "^25.6.0",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/seedrandom": "^3.0.8",
"@types/sjcl": "^1.0.34", "@types/sjcl": "^1.0.34",
"eslint": "^10.2.0", "eslint": "^10.2.0",
"eslint-config-next": "16.2.3", "eslint-config-next": "16.2.3",

View file

@ -2,10 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import { searchSchema } from "@tomodachi-share/shared/schemas"; import { searchSchema } from "@tomodachi-share/shared/schemas";
import { RateLimit } from "@/lib/rate-limit";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import crypto from "crypto";
import seedrandom from "seedrandom";
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const session = await auth(); const session = await auth();
@ -94,44 +91,9 @@ export async function GET(request: NextRequest) {
}, },
}; };
const skip = (page - 1) * limit;
let totalCount: number; let totalCount: number;
let filteredCount: number;
let miis: Prisma.MiiGetPayload<{ select: typeof select }>[]; let miis: Prisma.MiiGetPayload<{ select: typeof select }>[];
if (sort === "random") {
// Get all IDs that match the where conditions
const matchingIds = await prisma.mii.findMany({
where,
select: { id: true },
});
totalCount = matchingIds.length;
filteredCount = Math.max(0, Math.min(limit, totalCount - skip));
if (matchingIds.length === 0) return;
// Use seed for consistent random results
const randomSeed = seed || crypto.randomInt(0, 1_000_000_000);
const rng = seedrandom(randomSeed.toString());
// Randomize all IDs using the Durstenfeld algorithm
for (let i = matchingIds.length - 1; i > 0; i--) {
const j = Math.floor(rng() * (i + 1));
[matchingIds[i], matchingIds[j]] = [matchingIds[j], matchingIds[i]];
}
// Convert to number[] array
const selectedIds = matchingIds.slice(skip, skip + limit).map((i) => i.id);
miis = await prisma.mii.findMany({
where: {
id: { in: selectedIds },
},
select,
});
} else {
// Sorting by likes, newest, or oldest // Sorting by likes, newest, or oldest
let orderBy: Prisma.MiiOrderByWithRelationInput[]; let orderBy: Prisma.MiiOrderByWithRelationInput[];
@ -144,9 +106,8 @@ export async function GET(request: NextRequest) {
orderBy = [{ createdAt: "desc" }, { name: "asc" }]; orderBy = [{ createdAt: "desc" }, { name: "asc" }];
} }
[totalCount, filteredCount, miis] = await Promise.all([ [totalCount, miis] = await Promise.all([
prisma.mii.count({ where: { ...where } }), // TODO: User id prisma.mii.count({ where: { ...where } }), // TODO: User id
prisma.mii.count({ where, skip, take: limit }),
prisma.mii.findMany({ prisma.mii.findMany({
where, where,
orderBy, orderBy,
@ -155,14 +116,12 @@ export async function GET(request: NextRequest) {
take: limit, take: limit,
}), }),
]); ]);
}
const lastPage = Math.ceil(totalCount / limit); const lastPage = Math.ceil(totalCount / limit);
return NextResponse.json({ return NextResponse.json({
miis, miis,
totalCount, totalCount,
filteredCount,
lastPage, lastPage,
}); });
} }

View file

@ -43,7 +43,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
}, },
async redirect({ url, baseUrl }) { async redirect({ url, baseUrl }) {
return process.env.FRONTEND_URL ?? "http://localhost:4321"; return process.env.NEXT_PUBLIC_FRONTEND_URL ?? "http://localhost:4321";
}, },
}, },
}); });

View file

@ -30,7 +30,6 @@
"react-dropzone": "^15.0.0", "react-dropzone": "^15.0.0",
"react-image-crop": "^11.0.10", "react-image-crop": "^11.0.10",
"react-router": "^7.14.1", "react-router": "^7.14.1",
"seedrandom": "^3.0.5",
"tailwindcss": "^4.2.2", "tailwindcss": "^4.2.2",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },

View file

@ -31,8 +31,7 @@ export default function Header() {
</li> </li>
<li> <li>
<a href={"/submit"} className="pill button h-full"> <a href={"/submit"} className="pill button h-full">
{" "} Submit
Submit{" "}
</a> </a>
</li> </li>
<HeaderProfile /> <HeaderProfile />

View file

@ -1,4 +1,4 @@
import { Suspense, useEffect, useState } from "react"; import { Suspense, useEffect, useMemo, useState } from "react";
import FilterMenu from "../components/mii/list/filter-menu"; import FilterMenu from "../components/mii/list/filter-menu";
import SortSelect from "../components/mii/list/sort-select"; import SortSelect from "../components/mii/list/sort-select";
import MiiGrid from "../components/mii/list/mii-grid"; import MiiGrid from "../components/mii/list/mii-grid";
@ -7,13 +7,12 @@ import Skeleton from "../components/mii/list/skeleton";
interface ApiResponse { interface ApiResponse {
totalCount: number; totalCount: number;
filteredCount: number;
miis: any[]; miis: any[];
lastPage: number; lastPage: number;
} }
export default function IndexPage() { export default function IndexPage() {
const searchParams = new URLSearchParams(location.search); const searchParams = useMemo(() => new URLSearchParams(location.search), []);
const [data, setData] = useState<ApiResponse | null>(null); const [data, setData] = useState<ApiResponse | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -31,7 +30,7 @@ export default function IndexPage() {
console.error(err); console.error(err);
setLoading(false); setLoading(false);
}); });
}, []); }, [searchParams]);
return ( return (
<> <>
@ -46,19 +45,8 @@ export default function IndexPage() {
<div className="w-full"> <div className="w-full">
<div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 flex justify-between items-center gap-2 mb-2 max-md:flex-col"> <div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 flex justify-between items-center gap-2 mb-2 max-md:flex-col">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{data.totalCount == data.filteredCount ? (
<>
<span className="text-2xl font-bold text-amber-900">{data.totalCount}</span> <span className="text-2xl font-bold text-amber-900">{data.totalCount}</span>
<span className="text-lg text-amber-700">{data.totalCount === 1 ? "Mii" : "Miis"}</span> <span className="text-lg text-amber-700">{data.totalCount === 1 ? "Mii" : "Miis"}</span>
</>
) : (
<>
<span className="text-2xl font-bold text-amber-900">{data.filteredCount}</span>
<span className="text-sm text-amber-700">of</span>
<span className="text-lg font-semibold text-amber-800">{data.totalCount}</span>
<span className="text-lg text-amber-700">Miis</span>
</>
)}
</div> </div>
<div className="relative flex items-center justify-end gap-2 w-full md:max-w-2/3 max-md:justify-center"> <div className="relative flex items-center justify-end gap-2 w-full md:max-w-2/3 max-md:justify-center">

View file

@ -1,6 +1,14 @@
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
import { useStore } from "@nanostores/react";
import { useNavigate } from "react-router";
import { session } from "../session";
export default function LoginPage() { export default function LoginPage() {
const navigate = useNavigate();
const $session = useStore(session);
if ($session) navigate("/");
const API_URL = import.meta.env.VITE_API_URL; const API_URL = import.meta.env.VITE_API_URL;
return ( return (

View file

@ -1,5 +1,12 @@
import { useStore } from "@nanostores/react";
import SubmitForm from "../components/submit-form"; import SubmitForm from "../components/submit-form";
import { session } from "../session";
import { useNavigate } from "react-router";
export default function SubmitPage() { export default function SubmitPage() {
const navigate = useNavigate();
const $session = useStore(session);
if (!$session) navigate("/login");
return <SubmitForm />; return <SubmitForm />;
} }

View file

@ -16,12 +16,6 @@ importers:
'@auth/prisma-adapter': '@auth/prisma-adapter':
specifier: 2.11.1 specifier: 2.11.1
version: 2.11.1(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.2))(typescript@6.0.2)) version: 2.11.1(@prisma/client@6.19.3(prisma@6.19.3(typescript@6.0.2))(typescript@6.0.2))
'@bprogress/next':
specifier: ^3.2.12
version: 3.2.12(next@16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@hello-pangea/dnd':
specifier: ^18.0.1
version: 18.0.1(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@prisma/client': '@prisma/client':
specifier: ^6.19.2 specifier: ^6.19.2
version: 6.19.3(prisma@6.19.3(typescript@6.0.2))(typescript@6.0.2) version: 6.19.3(prisma@6.19.3(typescript@6.0.2))(typescript@6.0.2)
@ -31,24 +25,15 @@ importers:
bit-buffer: bit-buffer:
specifier: ^0.3.0 specifier: ^0.3.0
version: 0.3.0 version: 0.3.0
canvas-confetti:
specifier: ^1.9.4
version: 1.9.4
dayjs: dayjs:
specifier: ^1.11.20 specifier: ^1.11.20
version: 1.11.20 version: 1.11.20
downshift: downshift:
specifier: ^9.3.2 specifier: ^9.3.2
version: 9.3.2(react@19.2.5) version: 9.3.2(react@19.2.5)
embla-carousel-react:
specifier: ^8.6.0
version: 8.6.0(react@19.2.5)
file-type: file-type:
specifier: ^22.0.1 specifier: ^22.0.1
version: 22.0.1 version: 22.0.1
jsqr:
specifier: ^1.4.0
version: 1.4.0
next: next:
specifier: 16.2.3 specifier: 16.2.3
version: 16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) version: 16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@ -64,30 +49,18 @@ importers:
react-dom: react-dom:
specifier: ^19.2.5 specifier: ^19.2.5
version: 19.2.5(react@19.2.5) version: 19.2.5(react@19.2.5)
react-dropzone:
specifier: ^15.0.0
version: 15.0.0(react@19.2.5)
react-image-crop:
specifier: ^11.0.10
version: 11.0.10(react@19.2.5)
redis: redis:
specifier: ^5.11.0 specifier: ^5.11.0
version: 5.12.1 version: 5.12.1
satori: satori:
specifier: ^0.26.0 specifier: ^0.26.0
version: 0.26.0 version: 0.26.0
seedrandom:
specifier: ^3.0.5
version: 3.0.5
sharp: sharp:
specifier: ^0.34.5 specifier: ^0.34.5
version: 0.34.5 version: 0.34.5
sjcl-with-all: sjcl-with-all:
specifier: 1.0.8 specifier: 1.0.8
version: 1.0.8 version: 1.0.8
swr:
specifier: ^2.4.1
version: 2.4.1(react@19.2.5)
zod: zod:
specifier: ^4.3.6 specifier: ^4.3.6
version: 4.3.6 version: 4.3.6
@ -101,9 +74,6 @@ importers:
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.2.2 specifier: ^4.2.2
version: 4.2.2 version: 4.2.2
'@types/canvas-confetti':
specifier: ^1.9.0
version: 1.9.0
'@types/node': '@types/node':
specifier: ^25.6.0 specifier: ^25.6.0
version: 25.6.0 version: 25.6.0
@ -113,9 +83,6 @@ importers:
'@types/react-dom': '@types/react-dom':
specifier: ^19.2.3 specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.14) version: 19.2.3(@types/react@19.2.14)
'@types/seedrandom':
specifier: ^3.0.8
version: 3.0.8
'@types/sjcl': '@types/sjcl':
specifier: ^1.0.34 specifier: ^1.0.34
version: 1.0.34 version: 1.0.34
@ -200,9 +167,6 @@ importers:
react-router: react-router:
specifier: ^7.14.1 specifier: ^7.14.1
version: 7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) version: 7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
seedrandom:
specifier: ^3.0.5
version: 3.0.5
tailwindcss: tailwindcss:
specifier: ^4.2.2 specifier: ^4.2.2
version: 4.2.2 version: 4.2.2
@ -392,13 +356,6 @@ packages:
'@bprogress/core@1.3.4': '@bprogress/core@1.3.4':
resolution: {integrity: sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==} resolution: {integrity: sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==}
'@bprogress/next@3.2.12':
resolution: {integrity: sha512-/ZvNwbAd0ty9QiQwCfT2AfwWVdAaEyCPx5RUz3CfiiJS/OLBohhDz/IC/srhwK9GnXeXavvtiUrpKzN5GJDwlw==}
peerDependencies:
next: '>=13.0.0'
react: '>=18.0.0'
react-dom: '>=18.0.0'
'@bprogress/react@1.2.7': '@bprogress/react@1.2.7':
resolution: {integrity: sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==} resolution: {integrity: sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==}
peerDependencies: peerDependencies:
@ -1717,10 +1674,6 @@ packages:
defu@6.1.7: defu@6.1.7:
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
destr@2.0.5: destr@2.0.5:
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
@ -2801,9 +2754,6 @@ packages:
schema-dts@2.0.0: schema-dts@2.0.0:
resolution: {integrity: sha512-t7NoCy3Rn5GHGx6p7s1qIYK/AeIb8ZxJNR9WUNFkwMv2CiiGZBmqqYWc2FlZVm5ZbiHMY4OvBWhj7QtyrFO2Jw==} resolution: {integrity: sha512-t7NoCy3Rn5GHGx6p7s1qIYK/AeIb8ZxJNR9WUNFkwMv2CiiGZBmqqYWc2FlZVm5ZbiHMY4OvBWhj7QtyrFO2Jw==}
seedrandom@3.0.5:
resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==}
semver@6.3.1: semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
@ -2936,11 +2886,6 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
swr@2.4.1:
resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
tailwindcss@4.2.2: tailwindcss@4.2.2:
resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
@ -3277,14 +3222,6 @@ snapshots:
'@bprogress/core@1.3.4': {} '@bprogress/core@1.3.4': {}
'@bprogress/next@3.2.12(next@16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
'@bprogress/core': 1.3.4
'@bprogress/react': 1.2.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
next: 16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
'@bprogress/react@1.2.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': '@bprogress/react@1.2.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies: dependencies:
'@bprogress/core': 1.3.4 '@bprogress/core': 1.3.4
@ -4429,8 +4366,6 @@ snapshots:
defu@6.1.7: {} defu@6.1.7: {}
dequal@2.0.3: {}
destr@2.0.5: {} destr@2.0.5: {}
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
@ -5722,8 +5657,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- typescript - typescript
seedrandom@3.0.5: {}
semver@6.3.1: {} semver@6.3.1: {}
semver@7.7.4: {} semver@7.7.4: {}
@ -5908,12 +5841,6 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
swr@2.4.1(react@19.2.5):
dependencies:
dequal: 2.0.3
react: 19.2.5
use-sync-external-store: 1.6.0(react@19.2.5)
tailwindcss@4.2.2: {} tailwindcss@4.2.2: {}
tapable@2.3.2: {} tapable@2.3.2: {}