Post stream fix & add debug menu for rubgram
This commit is contained in:
@@ -4,14 +4,15 @@ import {
|
||||
Check,
|
||||
Copy,
|
||||
CopyCheck,
|
||||
ImageOff,
|
||||
Loader2,
|
||||
MessageSquareWarning,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { discordCall } from "../../api";
|
||||
import { debugUploadSlip, discordCall } from "../../api";
|
||||
|
||||
export function EnkaBrowser({ uid }: { uid: string }) {
|
||||
const [id, setId] = useState(uid);
|
||||
@@ -47,7 +48,12 @@ export function CopyButton({ text }: { text: string }) {
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
return (
|
||||
<Button variant="link" onClick={copy} disabled={copied} className="text-white">
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={copy}
|
||||
disabled={copied}
|
||||
className="text-white"
|
||||
>
|
||||
{copied ? <CopyCheck /> : <Copy />} คัดลอก username
|
||||
</Button>
|
||||
);
|
||||
@@ -83,3 +89,31 @@ export function CallButton({ user }: { user: string }) {
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function DebugSlipUpload({ sid }: { sid: string }) {
|
||||
const ref = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="flex flex-col justify-center items-center border rounded-md w-40 bg-card cursor-pointer"
|
||||
onClick={() => ref.current?.click()}
|
||||
type="button"
|
||||
>
|
||||
<ImageOff />
|
||||
ยังไม่จ่าย
|
||||
<span className="text-xs">กดเพื่ออัพสลิป</span>
|
||||
</button>
|
||||
<input
|
||||
ref={ref}
|
||||
hidden
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(ev) => {
|
||||
if (ev.target.files?.length)
|
||||
debugUploadSlip(sid, ev.target.files.item(0)!);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { eq } from "drizzle-orm";
|
||||
import {
|
||||
BadgeDollarSign,
|
||||
Download,
|
||||
ExternalLink,
|
||||
ImageOff,
|
||||
ExternalLink
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { notFound } from "next/navigation";
|
||||
@@ -29,7 +28,7 @@ import {
|
||||
endgameSubmissions,
|
||||
endgameTypes,
|
||||
} from "@/lib/db/schema";
|
||||
import { CallButton, CopyButton } from "./client";
|
||||
import { CallButton, CopyButton, DebugSlipUpload } from "./client";
|
||||
|
||||
export default async function AdminSubmissionView({
|
||||
params,
|
||||
@@ -50,9 +49,7 @@ export default async function AdminSubmissionView({
|
||||
.limit(1);
|
||||
|
||||
const types = await db.select().from(endgameTypes);
|
||||
const typesMap = Object.fromEntries(
|
||||
types.map((v) => [v.id, v.display]),
|
||||
);
|
||||
const typesMap = Object.fromEntries(types.map((v) => [v.id, v.display]));
|
||||
|
||||
return (
|
||||
<div className="p-2 h-full">
|
||||
@@ -126,20 +123,13 @@ export default async function AdminSubmissionView({
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : (
|
||||
) : sub.price <= 0 ? (
|
||||
<div className="flex flex-col justify-center items-center border rounded-md w-40 bg-card">
|
||||
{sub.price <= 0 ? (
|
||||
<>
|
||||
<BadgeDollarSign />
|
||||
ฟรี
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ImageOff />
|
||||
ยังไม่จ่าย
|
||||
</>
|
||||
)}
|
||||
<BadgeDollarSign />
|
||||
ฟรี
|
||||
</div>
|
||||
) : (
|
||||
<DebugSlipUpload sid={sub.id} />
|
||||
)}
|
||||
</div>
|
||||
<div className="border rounded-md h-full overflow-hidden"></div>
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { comms } from "@/lib/comms";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { setFree, setLimit, toggleCheck, toggleLock } from "../api";
|
||||
|
||||
@@ -234,9 +235,17 @@ export function LimitManager({
|
||||
export function SubmissionList({
|
||||
subs,
|
||||
}: {
|
||||
subs: { id: string; name: string; checked: boolean; queue: number }[];
|
||||
subs: {
|
||||
id: string;
|
||||
name: string;
|
||||
checked: boolean;
|
||||
queue: number;
|
||||
paid: boolean;
|
||||
}[];
|
||||
}) {
|
||||
const [query, setQuery] = useState("");
|
||||
const [debug] = comms.var("debug");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
@@ -245,9 +254,17 @@ export function SubmissionList({
|
||||
onChange={(ev) => setQuery(ev.target.value.toLowerCase())}
|
||||
/>
|
||||
{subs
|
||||
.filter((s) => (s.queue + s.name).toLowerCase().includes(query))
|
||||
.filter(
|
||||
(s) =>
|
||||
(debug || s.paid) &&
|
||||
(s.queue + s.name).toLowerCase().includes(query),
|
||||
)
|
||||
.map((s) => (
|
||||
<SidebarMenuButton key={s.id} asChild>
|
||||
<SidebarMenuButton
|
||||
key={s.id}
|
||||
asChild
|
||||
className={cn(debug && !s.paid && "opacity-50")}
|
||||
>
|
||||
<SidebarLink submission={s} />
|
||||
</SidebarMenuButton>
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eq, isNotNull, or } from "drizzle-orm";
|
||||
import { eq, isNotNull, or, type SQL } from "drizzle-orm";
|
||||
import { Dice3, Trash2 } from "lucide-react";
|
||||
import { redirect } from "next/navigation";
|
||||
import {
|
||||
@@ -42,11 +42,12 @@ export default async function AdminLayout({
|
||||
name: endgameSubmissions.name,
|
||||
checked: endgameSubmissions.checked,
|
||||
queue: endgameSubmissions.queue,
|
||||
paid: or(
|
||||
eq(endgameSubmissions.price, 0),
|
||||
isNotNull(endgameSubmissions.slip),
|
||||
) as SQL<boolean>,
|
||||
})
|
||||
.from(endgameSubmissions)
|
||||
.where(
|
||||
or(eq(endgameSubmissions.price, 0), isNotNull(endgameSubmissions.slip)),
|
||||
)
|
||||
.orderBy(endgameSubmissions.id);
|
||||
const config = await getEndgameConfig();
|
||||
return (
|
||||
@@ -101,7 +102,10 @@ export default async function AdminLayout({
|
||||
<SidebarFooter>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>การตั้งค่า</SidebarGroupLabel>
|
||||
<LimitManager config={config} length={subs.length} />
|
||||
<LimitManager
|
||||
config={config}
|
||||
length={subs.reduce((c, s) => c + (s.paid ? 1 : 0), 0)}
|
||||
/>
|
||||
</SidebarGroup>
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
|
||||
+45
-4
@@ -31,8 +31,13 @@ import {
|
||||
} from "@/lib/db/schema";
|
||||
import type { TypedFormData } from "./type";
|
||||
|
||||
const { SLIPOK_API_URL, SLIPOK_API_KEY, DISCORD_WEBHOOK_URL } =
|
||||
process.env as Record<string, string>;
|
||||
const {
|
||||
SLIPOK_API_URL,
|
||||
SLIPOK_API_KEY,
|
||||
DISCORD_WEBHOOK_URL,
|
||||
DISCORD_CLIENT_ID,
|
||||
BASE_URL,
|
||||
} = process.env as Record<string, string>;
|
||||
|
||||
export async function wipe() {
|
||||
if (!(await adminCheck())) throw "Unauthorized";
|
||||
@@ -325,7 +330,9 @@ export async function getDiscordSession() {
|
||||
}
|
||||
|
||||
export async function loginDiscord() {
|
||||
redirect(process.env.DISCORD_OAUTH_URL!);
|
||||
redirect(
|
||||
`https://discord.com/oauth2/authorize?client_id=${encodeURIComponent(DISCORD_CLIENT_ID)}&response_type=code&redirect_uri=${encodeURIComponent(new URL("/rubgram/callback", BASE_URL).href)}&scope=identify+guilds.join+guilds`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function calcPrice(service: string[]) {
|
||||
@@ -397,6 +404,8 @@ export async function cancel(sid: string) {
|
||||
|
||||
await removeExpiredSubmissions();
|
||||
|
||||
ps.publish({}, { topic: "rubgram", event: "update" });
|
||||
|
||||
revalidatePath("/rubgram");
|
||||
}
|
||||
|
||||
@@ -404,7 +413,8 @@ export async function discordCall(id: string) {
|
||||
return await fetch(DISCORD_WEBHOOK_URL, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
content: `<@${id}> ถึงคิวแล้ว ทักหาบุสได้เลย`,
|
||||
content: `<@${id}> ถึงคิวแล้ว ทักหาบุส <@681741453382123521> ได้เลย`,
|
||||
allowed_mentions: { users: [id] },
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -415,6 +425,37 @@ export async function discordCall(id: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function debugUploadSlip(sid: string, image: File) {
|
||||
if (!(await adminCheck())) throw "Unauthorized";
|
||||
|
||||
const arrayBuffer = await image.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
const [{ price }] = await tx
|
||||
.select({ price: endgameSubmissions.price })
|
||||
.from(endgameSubmissions)
|
||||
.where(eq(endgameSubmissions.id, sid));
|
||||
const [{ id }] = await tx
|
||||
.insert(endgameSlips)
|
||||
.values({
|
||||
slip: buffer,
|
||||
ref: `MANUAL-${Bun.randomUUIDv7()}`,
|
||||
amount: price.toString(),
|
||||
data: {} as SlipokResponse,
|
||||
})
|
||||
.returning({ id: endgameSlips.id });
|
||||
await tx
|
||||
.update(endgameSubmissions)
|
||||
.set({ slip: id })
|
||||
.where(eq(endgameSubmissions.id, sid));
|
||||
});
|
||||
|
||||
ps.publish({}, { topic: "rubgram", event: "update" });
|
||||
|
||||
revalidatePath("/rubgram/admin");
|
||||
}
|
||||
|
||||
export type SlipokResponse =
|
||||
| {
|
||||
success: true;
|
||||
|
||||
@@ -220,8 +220,12 @@ export function CancelButton({ sid }: { sid: string }) {
|
||||
}
|
||||
|
||||
export function ClearCookie() {
|
||||
// biome-ignore lint/suspicious/noTsIgnore: typescript issue
|
||||
// @ts-ignore
|
||||
cookieStore.delete("rsid");
|
||||
try {
|
||||
// biome-ignore lint/suspicious/noTsIgnore: typescript issue
|
||||
// @ts-ignore
|
||||
cookieStore.delete("rsid");
|
||||
} catch {
|
||||
console.warn("Cannot clear rubgram cookie");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ export default async function EndgamePage() {
|
||||
|
||||
return (
|
||||
<PageTransition>
|
||||
<div className="flex flex-col justify-around items-center h-svh">
|
||||
<div className="flex flex-col justify-center gap-2 items-center h-svh">
|
||||
<Card className="w-full max-w-md">
|
||||
{q ? (
|
||||
!q.paid ? (
|
||||
@@ -330,6 +330,10 @@ export default async function EndgamePage() {
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<span className="backdrop-blur-md p-1 text-xs m-1 border border-dashed rounded-sm">
|
||||
หากติดปัญหา โปรดแจ้งผ่านทาง
|
||||
<a href="https://discord.gg/HQwDXNhxuK" className="underline">ช่องดิสคอร์ด</a>
|
||||
</span>
|
||||
{sid && !q && <ClearCookie />}
|
||||
</div>
|
||||
</PageTransition>
|
||||
|
||||
Reference in New Issue
Block a user