From 73de7ef43c73fb8ba6c8e9e2fca2b11cf820c6de Mon Sep 17 00:00:00 2001 From: Piet van Zoen Date: Sun, 10 Dec 2023 11:49:18 +0100 Subject: [PATCH] Private Gifable features (#101) * Seed admin user on server start. * Optionally disable public user registration. --- .env.example | 7 ++++++ README.md | 4 +++ app/routes/login.tsx | 60 +++++++++++++++++++++++++++++++++----------- package.json | 2 +- seed-admin.mjs | 44 ++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 seed-admin.mjs diff --git a/.env.example b/.env.example index b5174ee..0e3b7d5 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,10 @@ S3_USE_SSL=true # optional SESSION_SECRET= DEBUG="app:*" + +# Seed an admin user on server start. +# ADMIN_USERNAME= +# ADMIN_PASSWORD= + +# Disable public user registration. +# DISABLE_SIGNUP=1 diff --git a/README.md b/README.md index 3233309..43893f8 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,7 @@ docker run -d \ -e DATABASE_URL="file:/data/gifable.db" \ ghcr.io/pietvanzoen/gifable:latest ``` + +## Configuration + +See `.env.example` for all available configuration options. diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 471e973..c0428b6 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,7 +1,8 @@ -import type { ActionArgs } from "@remix-run/node"; +import { json, type ActionArgs } from "@remix-run/node"; import { isRouteErrorResponse, useActionData, + useLoaderData, useRouteError, useSearchParams, } from "@remix-run/react"; @@ -21,6 +22,7 @@ import { UserSchema } from "~/utils/validators"; import { makeTitle } from "~/utils/meta"; import styles from "~/styles/login.css"; import { isRateLimited, rateLimitError } from "~/utils/rate-limiter.server"; +import env from "~/utils/env.server"; const log = debug("app:login"); @@ -84,6 +86,14 @@ export async function action({ request }: ActionArgs) { } case "register": { + if (env.get("DISABLE_SIGNUP")) { + log("Signup is disabled"); + return badRequest({ + repopulateFields: result.submittedData, + message: `Signup is disabled`, + }); + } + log("Registering user %s", username); const resp = await isRateLimited( getClientIPAddress(request) || username, @@ -126,7 +136,14 @@ export async function action({ request }: ActionArgs) { } } +export function loader() { + return json({ + allowSignup: !env.get("DISABLE_SIGNUP"), + }); +} + export default function Login() { + const data = useLoaderData(); const actionData = useActionData(); const [searchParams] = useSearchParams(); const defaultValues = actionData?.repopulateFields || { @@ -134,6 +151,8 @@ export default function Login() { loginType: "login", }; + const title = data.allowSignup ? "Login or Register" : "Login"; + return (
-

Login or Register?

+

{title}

- - + {data.allowSignup ? ( + <> + + + + ) : ( + + )} {actionData?.message} diff --git a/package.json b/package.json index b20a771..546d32c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "remix build", "dev": "DEBUG=app:* remix dev", "predev": "prisma generate && npm run install:css", - "prestart": "prisma migrate deploy", + "prestart": "prisma migrate deploy && node ./seed-admin.mjs", "start": "remix-serve build", "typecheck": "tsc", "test": "jest app", diff --git a/seed-admin.mjs b/seed-admin.mjs new file mode 100644 index 0000000..18ae4ed --- /dev/null +++ b/seed-admin.mjs @@ -0,0 +1,44 @@ +//@ts-check +import { PrismaClient } from "@prisma/client"; +import bycrypt from "bcryptjs"; + +const db = new PrismaClient(); + +const log = (/** @type {string} */ message) => + console.log(`ADMIN SEED: ${message}`); + +async function main() { + const { ADMIN_USERNAME, ADMIN_PASSWORD } = process.env; + + if (!ADMIN_USERNAME || !ADMIN_PASSWORD) { + return; + } + + const admin = await db.user.findUnique({ + where: { + username: ADMIN_USERNAME, + }, + }); + + if (admin) { + log(`Admin user '${ADMIN_USERNAME}' already exists`); + return; + } + + log(`Seeding admin user '${ADMIN_USERNAME}'`); + + await db.user.create({ + data: { + username: ADMIN_USERNAME, + passwordHash: await bycrypt.hash(ADMIN_PASSWORD, 10), + isAdmin: true, + }, + }); + + log(`Admin user '${ADMIN_USERNAME}' created`); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +});