Improve getting started with project experience

This commit is contained in:
Kalle 2022-07-06 11:02:23 +03:00
parent 027adf3bb0
commit 67c8b2c449
4 changed files with 45 additions and 18 deletions

View File

@ -1,10 +1,9 @@
PORT=5800
BASE_URL=http://localhost:5800
DB_PATH=db.sqlite3
// auth
LOHI_TOKEN=salmon
SESSION_SECRET=secret
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
LOHI_TOKEN=salmon
// needed for testing real auth only - values from https://discord.com/developers
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=

View File

@ -2,21 +2,32 @@ Note: This is the WIP Splatoon 3 version of the site. To see the current live ve
## Running locally
### sendou.ink
Prerequisites: [nvm](https://github.com/nvm-sh/nvm)
There is a sequence of commands you need to run:
1. `nvm use` to switch to the correct Node version.
2. `npm i` to install the dependencies.
3. Make a copy of `.env.example` that's called `.env` and fill it with values.
3. Make a copy of `.env.example` that's called `.env`. See below for note about environment variables.
4. `npm run migrate` to set up the database tables.
5. `npm run dev` to run both the server and frontend.
5. `npm run seed` to fill database with test data.
6. `npm run dev` to run the project in development mode.
And if you want to run the E2E tests:
6. Make a copy of the `db.sqlite3` file created by migration and name it `db-cypress.sqlite3`.
7. `npm run dev:cypress` and `npm run cy:open` can be used to run the E2E tests.
#### Environment variables
You don't need to fill the missing values from `.env.example` to get started. Instead of using real auth via Discord you can "impersonate" the admin (=Sendou#0043) or any other use in the /admin page once the project has started up. `LOHI_TOKEN` is only needed for bot + sendou.ink interoperability.
### Lohi
TODO: instructions on how to develop Lohi locally
## Project structure
```
@ -31,6 +42,7 @@ sendou.ink/
│ ├── utils/ -- Random helper functions used in many places
│ └── permissions.ts / -- What actions are allowed. Separated by frontend and backend as frontend has constraints based on what user sees.
├── cypress/ -- see: https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Folder-structure
├── discord-bot/ -- Lohi Discord bot that works together with sendou.ink
├── migrations/ -- Database migrations
├── public/ -- Images, built assets etc. static files to be served as is
└── scripts/ -- Stand-alone scripts to be run outside of the app

View File

@ -41,20 +41,15 @@ export class DiscordStrategy extends OAuth2Strategy<
scope: string;
constructor() {
invariant(process.env["DISCORD_CLIENT_ID"]);
invariant(process.env["DISCORD_CLIENT_SECRET"]);
invariant(process.env["BASE_URL"]);
const envVars = authEnvVars();
super(
{
authorizationURL: "https://discord.com/api/oauth2/authorize",
tokenURL: "https://discord.com/api/oauth2/token",
clientID: process.env["DISCORD_CLIENT_ID"],
clientSecret: process.env["DISCORD_CLIENT_SECRET"],
callbackURL: new URL(
"/auth/callback",
process.env["BASE_URL"]
).toString(),
clientID: envVars.DISCORD_CLIENT_ID,
clientSecret: envVars.DISCORD_CLIENT_SECRET,
callbackURL: new URL("/auth/callback", envVars.BASE_URL).toString(),
},
async ({ accessToken }) => {
const authHeader = ["Authorization", `Bearer ${accessToken}`];
@ -133,3 +128,24 @@ export class DiscordStrategy extends OAuth2Strategy<
return new URLSearchParams(urlSearchParams);
}
}
function authEnvVars() {
if (process.env.NODE_ENV === "production") {
invariant(process.env["DISCORD_CLIENT_ID"]);
invariant(process.env["DISCORD_CLIENT_SECRET"]);
invariant(process.env["BASE_URL"]);
return {
DISCORD_CLIENT_ID: process.env["DISCORD_CLIENT_ID"],
DISCORD_CLIENT_SECRET: process.env["DISCORD_CLIENT_SECRET"],
BASE_URL: process.env["BASE_URL"],
};
}
// allow running the project in development without setting auth env vars
return {
DISCORD_CLIENT_ID: process.env["DISCORD_CLIENT_ID"] ?? "",
DISCORD_CLIENT_SECRET: process.env["DISCORD_CLIENT_SECRET"] ?? "",
BASE_URL: process.env["BASE_URL"] ?? "",
};
}

View File

@ -11,7 +11,7 @@ import { Catcher } from "~/components/Catcher";
import { UserCombobox } from "~/components/Combobox";
import { Main } from "~/components/Main";
import { requireUser } from "~/modules/auth";
import { isImpersonating } from "~/modules/auth/user.server";
import { getUser, isImpersonating } from "~/modules/auth/user.server";
import { canPerformAdminActions } from "~/permissions";
import { makeTitle, parseRequestFormData, validate } from "~/utils/remix";
import { impersonateUrl, STOP_IMPERSONATING_URL } from "~/utils/urls";
@ -52,7 +52,7 @@ interface AdminPageLoaderData {
}
export const loader: LoaderFunction = async ({ request }) => {
const user = await requireUser(request);
const user = await getUser(request);
if (!canPerformAdminActions(user)) {
return redirect("/");