fix: make logging in not mandatory
All checks were successful
deploy to cloudflare pages / deploy (push) Successful in 32s
All checks were successful
deploy to cloudflare pages / deploy (push) Successful in 32s
This commit is contained in:
parent
d1a3772f6c
commit
67d2358a37
12 changed files with 99 additions and 24 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
44
migrations/0004_fix_tables.sql
Normal file
44
migrations/0004_fix_tables.sql
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
-- Migration number: 0004 2024-11-25T09:05:22.577Z
|
||||||
|
CREATE TABLE user_tmp (
|
||||||
|
`id` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`name` TEXT NOT NULL,
|
||||||
|
`avatar_hash` TEXT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE session_tmp (
|
||||||
|
`id` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`user_id` TEXT NOT NULL REFERENCES user_tmp(id),
|
||||||
|
`expires_at` INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE game_tmp (
|
||||||
|
`id` TEXT NOT NULL PRIMARY KEY, -- uuid
|
||||||
|
`user_id` TEXT NULL REFERENCES user_tmp(id),
|
||||||
|
`mode` TEXT NOT NULL,
|
||||||
|
`time` INTEGER NOT NULL,
|
||||||
|
`total_score` INTEGER NOT NULL,
|
||||||
|
`stops_type` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE round_tmp (
|
||||||
|
`id` INTEGER PRIMARY KEY ASC, -- rowid alias, should be automatically assigned
|
||||||
|
`game_id` TEXT NOT NULL REFERENCES game_tmp(id),
|
||||||
|
`points` INTEGER NOT NULL,
|
||||||
|
`distance` INTEGER NOT NULL,
|
||||||
|
`stop_name` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO user_tmp(id, name, avatar_hash) SELECT id, name, avatar_hash FROM user;
|
||||||
|
INSERT INTO session_tmp(id, user_id, expires_at) SELECT id, user_id, expires_at FROM session;
|
||||||
|
INSERT INTO game_tmp(id, user_id, mode, time, total_score, stops_type) SELECT id, user_id, mode, time, total_score, stops_type FROM game;
|
||||||
|
INSERT INTO round_tmp(id, game_id, points, distance, stop_name) SELECT id, game_id, points, distance, stop_name FROM round;
|
||||||
|
|
||||||
|
DROP TABLE round;
|
||||||
|
DROP TABLE game;
|
||||||
|
DROP TABLE session;
|
||||||
|
DROP TABLE user;
|
||||||
|
|
||||||
|
ALTER TABLE user_tmp RENAME TO user;
|
||||||
|
ALTER TABLE session_tmp RENAME TO session;
|
||||||
|
ALTER TABLE game_tmp RENAME TO game;
|
||||||
|
ALTER TABLE round_tmp RENAME TO round;
|
|
@ -15,6 +15,7 @@
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@oslojs/crypto": "^1.0.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
"@oslojs/encoding": "^1.1.0",
|
||||||
"arctic": "^2.2.2",
|
"arctic": "^2.2.2",
|
||||||
|
"destr": "^2.0.3",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-defaulticon-compatibility": "^0.1.2"
|
"leaflet-defaulticon-compatibility": "^0.1.2"
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,3 +13,8 @@ export interface User {
|
||||||
name: string;
|
name: string;
|
||||||
avatarHash: string;
|
avatarHash: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CookieData {
|
||||||
|
state: string;
|
||||||
|
next: string;
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ let lazyTram: [GeoJSON.Feature, string][] | null = null;
|
||||||
let lazyStops: GeoJSON.Feature[] | null = null;
|
let lazyStops: GeoJSON.Feature[] | null = null;
|
||||||
|
|
||||||
export async function createGame(
|
export async function createGame(
|
||||||
userId: string,
|
userId: string | null,
|
||||||
mode: string,
|
mode: string,
|
||||||
stopsType: string,
|
stopsType: string,
|
||||||
stops: GeoJSON.Feature[],
|
stops: GeoJSON.Feature[],
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { redirect } from "@sveltejs/kit";
|
|
||||||
import type { PageServerLoad } from "./$types";
|
import type { PageServerLoad } from "./$types";
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals }) => {
|
export const load: PageServerLoad = async ({ locals }) => {
|
||||||
if (locals.user === null) {
|
|
||||||
redirect(302, "/login");
|
|
||||||
} else {
|
|
||||||
return { user: locals.user };
|
return { user: locals.user };
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,9 +6,7 @@ export const GET: RequestHandler = async ({ url, platform, locals }) => {
|
||||||
const db = platform?.env?.TCL_GUESSR_D1 ?? null;
|
const db = platform?.env?.TCL_GUESSR_D1 ?? null;
|
||||||
if (db === null) error(500, "could not connect to d1");
|
if (db === null) error(500, "could not connect to d1");
|
||||||
|
|
||||||
const user = locals.user;
|
const userId = locals.user?.id ?? null;
|
||||||
if (user === null) error(401, "not logged in");
|
|
||||||
|
|
||||||
const options = parseOptions(url);
|
const options = parseOptions(url);
|
||||||
|
|
||||||
const crossingStops = await getMergedStops(options.stopsType);
|
const crossingStops = await getMergedStops(options.stopsType);
|
||||||
|
@ -18,7 +16,7 @@ export const GET: RequestHandler = async ({ url, platform, locals }) => {
|
||||||
error(400, "could not select random stop");
|
error(400, "could not select random stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
const gameData = await createGame(user.id, options.mode, options.stopsType, randomStops, db);
|
const gameData = await createGame(userId, options.mode, options.stopsType, randomStops, db);
|
||||||
|
|
||||||
return Response.json(gameData);
|
return Response.json(gameData);
|
||||||
};
|
};
|
||||||
|
|
16
src/routes/api/save/+server.ts
Normal file
16
src/routes/api/save/+server.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { error, redirect } from "@sveltejs/kit";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ locals, platform, url }) => {
|
||||||
|
const db = platform?.env?.TCL_GUESSR_D1 ?? null;
|
||||||
|
if (db === null) error(500, "could not connect to d1");
|
||||||
|
|
||||||
|
const gameId = url.searchParams.get("id") ?? null;
|
||||||
|
if (gameId === null) error(400, "no game id specified");
|
||||||
|
|
||||||
|
if (locals.user === null) error(401);
|
||||||
|
|
||||||
|
await db.prepare("UPDATE game SET user_id = ? WHERE id = ?").bind(locals.user.id, gameId).run();
|
||||||
|
|
||||||
|
redirect(302, `/results?gameId=${gameId}`);
|
||||||
|
};
|
|
@ -2,18 +2,20 @@ import { redirect } from "@sveltejs/kit";
|
||||||
import { generateState } from "arctic";
|
import { generateState } from "arctic";
|
||||||
import { discord } from "$lib/auth/discord";
|
import { discord } from "$lib/auth/discord";
|
||||||
import type { RequestHandler } from "./$types";
|
import type { RequestHandler } from "./$types";
|
||||||
|
import type { CookieData } from "$lib/auth";
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ cookies, url }) => {
|
||||||
|
const cookie: CookieData = { state: generateState(), next: url.searchParams.get("next") ?? "/" };
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ cookies }) => {
|
|
||||||
const state = generateState();
|
|
||||||
const scopes = ["identify"];
|
const scopes = ["identify"];
|
||||||
const url = discord.createAuthorizationURL(state, scopes);
|
const authUrl = discord.createAuthorizationURL(cookie.state, scopes);
|
||||||
|
|
||||||
cookies.set("discord_oauth_state", state, {
|
cookies.set("discord_oauth_state", JSON.stringify(cookie), {
|
||||||
path: "/",
|
path: "/",
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
maxAge: 60 * 10,
|
maxAge: 60 * 10,
|
||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
});
|
});
|
||||||
|
|
||||||
redirect(302, url);
|
redirect(302, authUrl);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { error, redirect } from "@sveltejs/kit";
|
import { error, redirect } from "@sveltejs/kit";
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
import { discord, type DiscordUser } from "$lib/auth/discord";
|
import { discord, type DiscordUser } from "$lib/auth/discord";
|
||||||
import { OAuth2RequestError, type OAuth2Tokens } from "arctic";
|
import { OAuth2RequestError, type OAuth2Tokens } from "arctic";
|
||||||
import { createSession, generateSessionToken } from "$lib/auth/session";
|
import { createSession, generateSessionToken } from "$lib/auth/session";
|
||||||
import { setSessionTokenCookie } from "$lib/auth/cookie";
|
import { setSessionTokenCookie } from "$lib/auth/cookie";
|
||||||
|
import { destr } from "destr";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
import type { CookieData } from "$lib/auth";
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ platform, url, cookies, fetch }) => {
|
export const GET: RequestHandler = async ({ platform, url, cookies, fetch }) => {
|
||||||
const db = platform?.env?.TCL_GUESSR_D1 ?? null;
|
const db = platform?.env?.TCL_GUESSR_D1 ?? null;
|
||||||
|
@ -11,13 +13,13 @@ export const GET: RequestHandler = async ({ platform, url, cookies, fetch }) =>
|
||||||
|
|
||||||
const code = url.searchParams.get("code");
|
const code = url.searchParams.get("code");
|
||||||
const state = url.searchParams.get("state");
|
const state = url.searchParams.get("state");
|
||||||
const storedState = cookies.get("discord_oauth_state") ?? null;
|
const cookie = destr<CookieData>(cookies.get("discord_oauth_state")) ?? null;
|
||||||
|
|
||||||
if (code === null || state === null || storedState === null) {
|
if (code === null || state === null || cookie === null) {
|
||||||
error(400, "missing things from oauth2 response");
|
error(400, "missing things from oauth2 response");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state !== storedState) {
|
if (state !== cookie.state) {
|
||||||
error(400, "state does not match");
|
error(400, "state does not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,5 +51,5 @@ export const GET: RequestHandler = async ({ platform, url, cookies, fetch }) =>
|
||||||
const session = await createSession(sessionToken, discordUser.id, db);
|
const session = await createSession(sessionToken, discordUser.id, db);
|
||||||
setSessionTokenCookie(cookies, sessionToken, session.expiresAt);
|
setSessionTokenCookie(cookies, sessionToken, session.expiresAt);
|
||||||
|
|
||||||
redirect(302, "/");
|
redirect(302, cookie.next);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,12 +4,13 @@ import { error } from "@sveltejs/kit";
|
||||||
interface JoinedRoundScore {
|
interface JoinedRoundScore {
|
||||||
mode: string;
|
mode: string;
|
||||||
total_score: number;
|
total_score: number;
|
||||||
|
name: string;
|
||||||
points: number;
|
points: number;
|
||||||
distance: number;
|
distance: number;
|
||||||
stop_name: string;
|
stop_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url, platform }) => {
|
export const load: PageServerLoad = async ({ locals, url, platform }) => {
|
||||||
const db = platform?.env?.TCL_GUESSR_D1;
|
const db = platform?.env?.TCL_GUESSR_D1;
|
||||||
if (!db) error(500, "could not connect to d1");
|
if (!db) error(500, "could not connect to d1");
|
||||||
|
|
||||||
|
@ -18,10 +19,10 @@ export const load: PageServerLoad = async ({ url, platform }) => {
|
||||||
|
|
||||||
const { results } = await db
|
const { results } = await db
|
||||||
.prepare(
|
.prepare(
|
||||||
"SELECT game.mode, game.total_score, round.points, round.distance, round.stop_name FROM game INNER JOIN round ON round.game_id = game.id WHERE game.id = ?;",
|
"SELECT game.mode, game.total_score, user.name, round.points, round.distance, round.stop_name FROM game INNER JOIN round ON round.game_id = game.id LEFT JOIN user ON user.id = game.user_id WHERE game.id = ?;",
|
||||||
)
|
)
|
||||||
.bind(gameId)
|
.bind(gameId)
|
||||||
.all<JoinedRoundScore>();
|
.all<JoinedRoundScore>();
|
||||||
|
|
||||||
return { rounds: results, gameId };
|
return { rounds: results, gameId, loggedIn: locals.user !== null };
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
const totalScore = props.data.rounds[0].total_score;
|
const totalScore = props.data.rounds[0].total_score;
|
||||||
const mode = props.data.rounds[0].mode;
|
const mode = props.data.rounds[0].mode;
|
||||||
|
const name = props.data.rounds[0].name;
|
||||||
|
|
||||||
|
const saveParams = new URLSearchParams({ next: `/api/save?id=${props.data.gameId}` });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -19,6 +22,14 @@
|
||||||
|
|
||||||
<span>mode: {mode}</span>
|
<span>mode: {mode}</span>
|
||||||
|
|
||||||
|
{#if name !== null}
|
||||||
|
<span>joué par {name}</span>
|
||||||
|
{:else if props.data.loggedIn}
|
||||||
|
<span>joué par <i>un inconnu...</i></span>
|
||||||
|
{:else}
|
||||||
|
<span><a href="/login?{saveParams}">connectez vous</a> pour sauvegarder votre score!</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each props.data.rounds as round}
|
{#each props.data.rounds as round}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue