mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-12 22:26:12 -05:00
Validate image upload page
This commit is contained in:
21
app/features/img-upload/queries/oneUnvalidatedImage.ts
Normal file
21
app/features/img-upload/queries/oneUnvalidatedImage.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { UserSubmittedImage } from "~/db/types";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
select
|
||||
"id",
|
||||
"url",
|
||||
"submitterUserId"
|
||||
from "UnvalidatedUserSubmittedImage"
|
||||
where "validatedAt" is null
|
||||
limit 1
|
||||
`);
|
||||
|
||||
type UnvalidatedImage = Pick<
|
||||
UserSubmittedImage,
|
||||
"id" | "url" | "submitterUserId"
|
||||
>;
|
||||
|
||||
export function oneUnvalidatedImage(): UnvalidatedImage | null {
|
||||
return stm.get();
|
||||
}
|
||||
12
app/features/img-upload/queries/validateImage.ts
Normal file
12
app/features/img-upload/queries/validateImage.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
update "UnvalidatedUserSubmittedImage"
|
||||
set "validatedAt" = @validatedAt
|
||||
where "id" = @id
|
||||
`);
|
||||
|
||||
export function validateImage(id: number) {
|
||||
stm.run({ validatedAt: dateToDatabaseTimestamp(new Date()), id });
|
||||
}
|
||||
62
app/features/img-upload/routes/upload.admin.tsx
Normal file
62
app/features/img-upload/routes/upload.admin.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { ActionFunction, LoaderArgs } from "@remix-run/node";
|
||||
import { Form, useLoaderData } from "@remix-run/react";
|
||||
import { Main } from "~/components/Main";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { requireUser } from "~/modules/auth";
|
||||
import { isAdmin } from "~/permissions";
|
||||
import { notFoundIfFalsy, parseRequestFormData, validate } from "~/utils/remix";
|
||||
import { userSubmittedImage } from "~/utils/urls";
|
||||
import { oneUnvalidatedImage } from "../queries/oneUnvalidatedImage";
|
||||
import { validateImage } from "../queries/validateImage";
|
||||
import { validateImageSchema } from "../upload-schemas.server";
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
const user = await requireUser(request);
|
||||
const data = await parseRequestFormData({
|
||||
schema: validateImageSchema,
|
||||
request,
|
||||
});
|
||||
|
||||
validate(isAdmin(user));
|
||||
|
||||
validateImage(data.imageId);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({ request }: LoaderArgs) => {
|
||||
const user = await requireUser(request);
|
||||
|
||||
notFoundIfFalsy(isAdmin(user));
|
||||
|
||||
return {
|
||||
image: oneUnvalidatedImage(),
|
||||
};
|
||||
};
|
||||
|
||||
export default function ImageUploadAdminPage() {
|
||||
return (
|
||||
<Main>
|
||||
<ImageValidator />
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
|
||||
function ImageValidator() {
|
||||
const data = useLoaderData<typeof loader>();
|
||||
|
||||
if (!data.image) {
|
||||
return <>All validated!</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<img src={userSubmittedImage(data.image.url)} alt="" />
|
||||
<Form method="post">
|
||||
<input type="hidden" name="imageId" value={data.image.id} />
|
||||
<SubmitButton>Ok</SubmitButton>
|
||||
</Form>
|
||||
<div>From: {data.image.submitterUserId}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
6
app/features/img-upload/upload-schemas.server.ts
Normal file
6
app/features/img-upload/upload-schemas.server.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { id } from "~/utils/zod";
|
||||
|
||||
export const validateImageSchema = z.object({
|
||||
imageId: id,
|
||||
});
|
||||
@@ -7,6 +7,7 @@ module.exports = {
|
||||
routes: async (defineRoutes) => {
|
||||
return defineRoutes((route) => {
|
||||
route("/upload", "features/img-upload/routes/upload.tsx");
|
||||
route("/upload/admin", "features/img-upload/routes/upload.admin.tsx");
|
||||
route("/plans", "features/map-planner/routes/plans.tsx");
|
||||
route("/analyzer", "features/build-analyzer/routes/analyzer.tsx");
|
||||
route(
|
||||
|
||||
Reference in New Issue
Block a user