mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 13:17:45 +00:00
fix: login issues
This commit is contained in:
parent
46202b22b0
commit
11df9261da
9 changed files with 47 additions and 172 deletions
|
|
@ -13,42 +13,30 @@
|
|||
"dependencies": {
|
||||
"@2toad/profanity": "^3.3.0",
|
||||
"@auth/prisma-adapter": "2.11.1",
|
||||
"@bprogress/next": "^3.2.12",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@prisma/client": "^6.19.2",
|
||||
"bit-buffer": "^0.3.0",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
"dayjs": "^1.11.20",
|
||||
"downshift": "^9.3.2",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"file-type": "^22.0.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"next": "16.2.3",
|
||||
"next-auth": "5.0.0-beta.30",
|
||||
"qrcode-generator": "^2.0.4",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"react-dropzone": "^15.0.0",
|
||||
"react-image-crop": "^11.0.10",
|
||||
"redis": "^5.11.0",
|
||||
"satori": "^0.26.0",
|
||||
"seedrandom": "^3.0.5",
|
||||
"sharp": "^0.34.5",
|
||||
"sjcl-with-all": "1.0.8",
|
||||
"swr": "^2.4.1",
|
||||
"zod": "^4.3.6",
|
||||
"@tomodachi-share/shared": "workspace:*"
|
||||
|
||||
"@tomodachi-share/shared": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.5",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@tailwindcss/postcss": "^4.2.2",
|
||||
"@types/canvas-confetti": "^1.9.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/seedrandom": "^3.0.8",
|
||||
"@types/sjcl": "^1.0.34",
|
||||
"eslint": "^10.2.0",
|
||||
"eslint-config-next": "16.2.3",
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|||
import { prisma } from "@/lib/prisma";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { searchSchema } from "@tomodachi-share/shared/schemas";
|
||||
import { RateLimit } from "@/lib/rate-limit";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import crypto from "crypto";
|
||||
import seedrandom from "seedrandom";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const session = await auth();
|
||||
|
|
@ -94,75 +91,37 @@ export async function GET(request: NextRequest) {
|
|||
},
|
||||
};
|
||||
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
let totalCount: number;
|
||||
let filteredCount: number;
|
||||
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 },
|
||||
});
|
||||
// Sorting by likes, newest, or oldest
|
||||
let orderBy: Prisma.MiiOrderByWithRelationInput[];
|
||||
|
||||
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,
|
||||
});
|
||||
if (sort === "likes") {
|
||||
orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }];
|
||||
} else if (sort === "oldest") {
|
||||
orderBy = [{ createdAt: "asc" }, { name: "asc" }];
|
||||
} else {
|
||||
// Sorting by likes, newest, or oldest
|
||||
let orderBy: Prisma.MiiOrderByWithRelationInput[];
|
||||
|
||||
if (sort === "likes") {
|
||||
orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }];
|
||||
} else if (sort === "oldest") {
|
||||
orderBy = [{ createdAt: "asc" }, { name: "asc" }];
|
||||
} else {
|
||||
// default to newest
|
||||
orderBy = [{ createdAt: "desc" }, { name: "asc" }];
|
||||
}
|
||||
|
||||
[totalCount, filteredCount, miis] = await Promise.all([
|
||||
prisma.mii.count({ where: { ...where } }), // TODO: User id
|
||||
prisma.mii.count({ where, skip, take: limit }),
|
||||
prisma.mii.findMany({
|
||||
where,
|
||||
orderBy,
|
||||
select,
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
}),
|
||||
]);
|
||||
// default to newest
|
||||
orderBy = [{ createdAt: "desc" }, { name: "asc" }];
|
||||
}
|
||||
|
||||
[totalCount, miis] = await Promise.all([
|
||||
prisma.mii.count({ where: { ...where } }), // TODO: User id
|
||||
prisma.mii.findMany({
|
||||
where,
|
||||
orderBy,
|
||||
select,
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
}),
|
||||
]);
|
||||
|
||||
const lastPage = Math.ceil(totalCount / limit);
|
||||
|
||||
return NextResponse.json({
|
||||
miis,
|
||||
totalCount,
|
||||
filteredCount,
|
||||
lastPage,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
|||
},
|
||||
|
||||
async redirect({ url, baseUrl }) {
|
||||
return process.env.FRONTEND_URL ?? "http://localhost:4321";
|
||||
return process.env.NEXT_PUBLIC_FRONTEND_URL ?? "http://localhost:4321";
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
"react-dropzone": "^15.0.0",
|
||||
"react-image-crop": "^11.0.10",
|
||||
"react-router": "^7.14.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ export default function Header() {
|
|||
</li>
|
||||
<li>
|
||||
<a href={"/submit"} className="pill button h-full">
|
||||
{" "}
|
||||
Submit{" "}
|
||||
Submit
|
||||
</a>
|
||||
</li>
|
||||
<HeaderProfile />
|
||||
|
|
|
|||
|
|
@ -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 SortSelect from "../components/mii/list/sort-select";
|
||||
import MiiGrid from "../components/mii/list/mii-grid";
|
||||
|
|
@ -7,13 +7,12 @@ import Skeleton from "../components/mii/list/skeleton";
|
|||
|
||||
interface ApiResponse {
|
||||
totalCount: number;
|
||||
filteredCount: number;
|
||||
miis: any[];
|
||||
lastPage: number;
|
||||
}
|
||||
|
||||
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 [loading, setLoading] = useState(true);
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ export default function IndexPage() {
|
|||
console.error(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
}, [searchParams]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -46,19 +45,8 @@ export default function IndexPage() {
|
|||
<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="flex items-center gap-2">
|
||||
{data.totalCount == data.filteredCount ? (
|
||||
<>
|
||||
<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-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>
|
||||
</>
|
||||
)}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div className="relative flex items-center justify-end gap-2 w-full md:max-w-2/3 max-md:justify-center">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
import { Icon } from "@iconify/react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { session } from "../session";
|
||||
|
||||
export default function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const $session = useStore(session);
|
||||
|
||||
if ($session) navigate("/");
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL;
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import SubmitForm from "../components/submit-form";
|
||||
|
||||
export default function SubmitPage() {
|
||||
return <SubmitForm />;
|
||||
}
|
||||
import { useStore } from "@nanostores/react";
|
||||
import SubmitForm from "../components/submit-form";
|
||||
import { session } from "../session";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export default function SubmitPage() {
|
||||
const navigate = useNavigate();
|
||||
const $session = useStore(session);
|
||||
|
||||
if (!$session) navigate("/login");
|
||||
return <SubmitForm />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@ importers:
|
|||
'@auth/prisma-adapter':
|
||||
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))
|
||||
'@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':
|
||||
specifier: ^6.19.2
|
||||
version: 6.19.3(prisma@6.19.3(typescript@6.0.2))(typescript@6.0.2)
|
||||
|
|
@ -31,24 +25,15 @@ importers:
|
|||
bit-buffer:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0
|
||||
canvas-confetti:
|
||||
specifier: ^1.9.4
|
||||
version: 1.9.4
|
||||
dayjs:
|
||||
specifier: ^1.11.20
|
||||
version: 1.11.20
|
||||
downshift:
|
||||
specifier: ^9.3.2
|
||||
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:
|
||||
specifier: ^22.0.1
|
||||
version: 22.0.1
|
||||
jsqr:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
next:
|
||||
specifier: 16.2.3
|
||||
version: 16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
|
|
@ -64,30 +49,18 @@ importers:
|
|||
react-dom:
|
||||
specifier: ^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:
|
||||
specifier: ^5.11.0
|
||||
version: 5.12.1
|
||||
satori:
|
||||
specifier: ^0.26.0
|
||||
version: 0.26.0
|
||||
seedrandom:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
sharp:
|
||||
specifier: ^0.34.5
|
||||
version: 0.34.5
|
||||
sjcl-with-all:
|
||||
specifier: 1.0.8
|
||||
version: 1.0.8
|
||||
swr:
|
||||
specifier: ^2.4.1
|
||||
version: 2.4.1(react@19.2.5)
|
||||
zod:
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
|
|
@ -101,9 +74,6 @@ importers:
|
|||
'@tailwindcss/postcss':
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
'@types/canvas-confetti':
|
||||
specifier: ^1.9.0
|
||||
version: 1.9.0
|
||||
'@types/node':
|
||||
specifier: ^25.6.0
|
||||
version: 25.6.0
|
||||
|
|
@ -113,9 +83,6 @@ importers:
|
|||
'@types/react-dom':
|
||||
specifier: ^19.2.3
|
||||
version: 19.2.3(@types/react@19.2.14)
|
||||
'@types/seedrandom':
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.8
|
||||
'@types/sjcl':
|
||||
specifier: ^1.0.34
|
||||
version: 1.0.34
|
||||
|
|
@ -200,9 +167,6 @@ importers:
|
|||
react-router:
|
||||
specifier: ^7.14.1
|
||||
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:
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
|
|
@ -392,13 +356,6 @@ packages:
|
|||
'@bprogress/core@1.3.4':
|
||||
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':
|
||||
resolution: {integrity: sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -1717,10 +1674,6 @@ packages:
|
|||
defu@6.1.7:
|
||||
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
|
||||
|
||||
dequal@2.0.3:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
destr@2.0.5:
|
||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||
|
||||
|
|
@ -2801,9 +2754,6 @@ packages:
|
|||
schema-dts@2.0.0:
|
||||
resolution: {integrity: sha512-t7NoCy3Rn5GHGx6p7s1qIYK/AeIb8ZxJNR9WUNFkwMv2CiiGZBmqqYWc2FlZVm5ZbiHMY4OvBWhj7QtyrFO2Jw==}
|
||||
|
||||
seedrandom@3.0.5:
|
||||
resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==}
|
||||
|
||||
semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
|
|
@ -2936,11 +2886,6 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
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:
|
||||
resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
|
||||
|
||||
|
|
@ -3277,14 +3222,6 @@ snapshots:
|
|||
|
||||
'@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)':
|
||||
dependencies:
|
||||
'@bprogress/core': 1.3.4
|
||||
|
|
@ -4429,8 +4366,6 @@ snapshots:
|
|||
|
||||
defu@6.1.7: {}
|
||||
|
||||
dequal@2.0.3: {}
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
|
@ -5722,8 +5657,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
seedrandom@3.0.5: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.7.4: {}
|
||||
|
|
@ -5908,12 +5841,6 @@ snapshots:
|
|||
|
||||
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: {}
|
||||
|
||||
tapable@2.3.2: {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue