Skip to content

Commit 79c4b4a

Browse files
committed
achieve prod env && email auth
1 parent c39b94a commit 79c4b4a

File tree

6 files changed

+91
-66
lines changed

6 files changed

+91
-66
lines changed

deploy/cloudflare/dev/wrangler.jsonc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
"compatibility_date": "2025-09-06",
44
"compatibility_flags": ["nodejs_compat"],
55

6-
"pages_build_output_dir": "../../frontend/.svelte-kit/cloudflare",
6+
"pages_build_output_dir": "../../../frontend/.svelte-kit/cloudflare",
77

88
"env": {
99
"preview": {
1010
"d1_databases": [
1111
{
1212
"binding": "DB",
1313
"database_name": "unitn-oj-dev",
14-
"database_id": "84a77c0f-9983-43da-bd6c-35982cb191bb",
15-
"migrations_dir": "../../frontend/d1/migrations"
14+
"database_id": "7eed840d-0135-441a-a24d-d4ec7133a1f1",
15+
"migrations_dir": "../../../frontend/d1/migrations"
1616
}
1717
],
1818
"queues": {
@@ -28,16 +28,16 @@
2828
"AUTH_ALLOWED_DOMAIN": "studenti.unitn.it",
2929
"AUTH_TOKEN_TTL_SECONDS": "300",
3030
"AUTH_SESSION_TTL_SECONDS": "604800",
31-
"AUTH_SESSION_SECRET": "remote-dev-secret"
31+
"AUTH_SESSION_SECRET": "remote-dev-preview-secret"
3232
}
3333
},
3434
"production": {
3535
"d1_databases": [
3636
{
3737
"binding": "DB",
3838
"database_name": "unitn-oj-dev",
39-
"database_id": "84a77c0f-9983-43da-bd6c-35982cb191bb",
40-
"migrations_dir": "../../frontend/d1/migrations"
39+
"database_id": "7eed840d-0135-441a-a24d-d4ec7133a1f1",
40+
"migrations_dir": "../../../frontend/d1/migrations"
4141
}
4242
],
4343
"queues": {
@@ -53,7 +53,7 @@
5353
"AUTH_ALLOWED_DOMAIN": "studenti.unitn.it",
5454
"AUTH_TOKEN_TTL_SECONDS": "300",
5555
"AUTH_SESSION_TTL_SECONDS": "604800",
56-
"AUTH_SESSION_SECRET": "remote-dev-secret"
56+
"AUTH_SESSION_SECRET": "remote-dev-prod-secret"
5757
}
5858
}
5959
}

deploy/cloudflare/prod/wrangler.jsonc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
"name": "unitn-oj",
33
"compatibility_date": "2025-09-06",
44
"compatibility_flags": ["nodejs_compat"],
5-
"pages_build_output_dir": "../../frontend/.svelte-kit/cloudflare",
5+
"pages_build_output_dir": "../../../frontend/.svelte-kit/cloudflare",
66
"env": {
77
"preview": {
88
"d1_databases": [
99
{
1010
"binding": "DB",
1111
"database_name": "unitn-oj",
12-
"database_id": "<PROD_D1_UUID>",
13-
"migrations_dir": "../../frontend/d1/migrations"
12+
"database_id": "48fda8e4-a30a-4e75-acd5-f7ecc22a4665",
13+
"migrations_dir": "../../../frontend/d1/migrations"
1414
}
1515
],
1616
"queues": {
@@ -19,16 +19,19 @@
1919
]
2020
},
2121
"vars": {
22-
"APP_ENV": "prod"
22+
"APP_ENV": "prod",
23+
"AUTH_ALLOWED_DOMAIN": "studenti.unitn.it",
24+
"AUTH_TOKEN_TTL_SECONDS": "300",
25+
"AUTH_SESSION_TTL_SECONDS": "604800"
2326
}
2427
},
2528
"production": {
2629
"d1_databases": [
2730
{
2831
"binding": "DB",
2932
"database_name": "unitn-oj",
30-
"database_id": "<PROD_D1_UUID>",
31-
"migrations_dir": "../../frontend/d1/migrations"
33+
"database_id": "48fda8e4-a30a-4e75-acd5-f7ecc22a4665",
34+
"migrations_dir": "../../../frontend/d1/migrations"
3235
}
3336
],
3437
"queues": {
@@ -37,7 +40,10 @@
3740
]
3841
},
3942
"vars": {
40-
"APP_ENV": "prod"
43+
"APP_ENV": "prod",
44+
"AUTH_ALLOWED_DOMAIN": "studenti.unitn.it",
45+
"AUTH_TOKEN_TTL_SECONDS": "300",
46+
"AUTH_SESSION_TTL_SECONDS": "604800"
4147
}
4248
}
4349
}

frontend/d1/seed/prod_seed.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
INSERT OR IGNORE INTO schools (school_id, name) VALUES ('unitn', 'University of Trento');

frontend/src/routes/login/+page.svelte

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,62 @@
11
<script lang="ts">
2-
const email = "";
3-
let turnstileToken = "";
4-
let sending = false,
5-
message = "",
6-
magicUrl: string | null = null;
7-
8-
async function requestLink() {
9-
sending = true;
10-
message = "";
11-
magicUrl = null;
12-
13-
const body = { email, turnstileToken };
14-
15-
const res = await fetch("/api/v1/auth/requestLink", {
16-
method: "POST",
17-
headers: { "content-type": "application/json" },
18-
credentials: "same-origin",
19-
body: JSON.stringify(body),
20-
});
21-
const j = await res.json().catch(() => ({}));
22-
if (!res.ok) {
23-
message = j?.error?.message || "request failed";
24-
sending = false;
25-
return;
26-
}
27-
if (j?.data?.magicUrl) {
28-
magicUrl = j.data.magicUrl;
29-
message = "Dev mode: Magic link is ready below.";
30-
} else {
31-
message = "Check your email for the magic link.";
32-
}
33-
sending = false;
34-
}
35-
36-
function onTurnstileSuccess(token: string) {
37-
turnstileToken = token;
38-
}
2+
import { onMount } from 'svelte';
3+
4+
let email = '';
5+
let turnstileToken = '';
6+
let sending = false;
7+
let message = '';
8+
let magicUrl: string | null = null;
9+
10+
async function requestLink() {
11+
sending = true;
12+
message = '';
13+
magicUrl = null;
14+
const res = await fetch('/api/v1/auth/requestLink', {
15+
method: 'POST',
16+
headers: { 'content-type': 'application/json' },
17+
credentials: 'same-origin',
18+
body: JSON.stringify({ email, turnstileToken })
19+
});
20+
const j = await res.json().catch(() => ({} as any));
21+
if (!res.ok) {
22+
message = j?.error?.message || 'request failed';
23+
sending = false;
24+
return;
25+
}
26+
if (j?.data?.magicUrl) {
27+
magicUrl = j.data.magicUrl;
28+
message = 'Dev mode: Magic link is ready below.';
29+
} else {
30+
message = 'Check your email for the magic link.';
31+
}
32+
sending = false;
33+
}
34+
35+
onMount(() => {
36+
const render = () => {
37+
(window as any).turnstile?.render('#turnstile-widget', {
38+
sitekey: '0x4AAAAAABubm1BqOdDseIMZ',
39+
callback: (t: string) => { turnstileToken = t; }
40+
});
41+
};
42+
if ((window as any).turnstile) {
43+
render();
44+
} else {
45+
const s = document.createElement('script');
46+
s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
47+
s.defer = true;
48+
s.onload = render;
49+
document.head.appendChild(s);
50+
}
51+
});
3952
</script>
4053

41-
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
42-
4354
<h1>Sign in</h1>
44-
<div class="login-form">
55+
<div style="display:flex;flex-direction:column;gap:8px;max-width:420px">
4556
<input type="email" bind:value={email} placeholder="[email protected]" required />
4657
<div id="turnstile-widget"></div>
47-
<button on:click|preventDefault={requestLink} disabled={sending}>Request Link</button>
58+
<button on:click|preventDefault={requestLink} disabled={sending || !turnstileToken}>Request Link</button>
4859
</div>
4960

5061
{#if message}<p>{message}</p>{/if}
5162
{#if magicUrl}<p><a href={magicUrl}>Open magic link</a></p>{/if}
52-
53-
<svelte:window on:load={() => {
54-
window.turnstile?.render('#turnstile-widget', {
55-
sitekey: 'YOUR_TURNSTILE_SITE_KEY',
56-
callback: onTurnstileSuccess
57-
});
58-
}} />

package.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,38 @@
99
"gen:wrangler:prod": "node scripts/update-d1-id.mjs prod",
1010

1111
"tf:init": "terraform -chdir=infra/terraform init",
12+
1213
"tf:new:dev": "terraform -chdir=infra/terraform workspace new dev",
1314
"tf:select:dev": "terraform -chdir=infra/terraform workspace select dev",
1415
"tf:plan:dev": "pnpm tf:select:dev || pnpm tf:new:dev && terraform -chdir=infra/terraform plan -var-file=dev.tfvars",
1516
"tf:apply:dev": "pnpm tf:select:dev && terraform -chdir=infra/terraform apply -var-file=dev.tfvars -auto-approve",
1617
"tf:destroy:dev": "pnpm tf:select:dev && terraform -chdir=infra/terraform destroy -var-file=dev.tfvars -auto-approve",
1718
"tf:destroy:dev:db": "pnpm terraform -chdir=infra/terraform destroy -target=cloudflare_d1_database.db -var-file=dev.tfvars -auto-approve",
18-
"tf:output:d1": "pnpm tf:select:dev && terraform -chdir=infra/terraform output -raw d1_db_id",
19+
"tf:output:dev:d1": "pnpm tf:select:dev && terraform -chdir=infra/terraform output -raw d1_db_id",
20+
21+
"tf:new:prod": "terraform -chdir=infra/terraform workspace new prod",
22+
"tf:select:prod": "terraform -chdir=infra/terraform workspace select prod",
23+
"tf:plan:prod": "pnpm tf:select:prod || pnpm tf:new:prod && terraform -chdir=infra/terraform plan -var-file=prod.tfvars",
24+
"tf:apply:prod": "pnpm tf:select:prod && terraform -chdir=infra/terraform apply -var-file=prod.tfvars",
25+
"tf:destroy:prod": "pnpm tf:select:prod && terraform -chdir=infra/terraform destroy -var-file=prod.tfvars",
26+
"tf:destroy:prod:db": "pnpm terraform -chdir=infra/terraform destroy -target=cloudflare_d1_database.db -var-file=prod.tfvars",
27+
"tf:output:prod:d1": "pnpm tf:select:prod && terraform -chdir=infra/terraform output -raw d1_db_id",
1928

2029
"db:reset:local": "pnpm --filter ./frontend db:reset",
2130
"db:migrate:local": "pnpm --filter ./frontend db:migrate",
2231
"db:seed:local": "pnpm --filter ./frontend db:seed",
2332
"db:init:local": "pnpm --filter ./frontend db:init",
2433

25-
"db:reset:dev": "pnpm tf:select:dev && pnpm tf:destroy:dev:db && pnpm tf:apply:dev && pnpm tf:output:d1",
34+
"db:reset:dev": "pnpm tf:select:dev && pnpm tf:destroy:dev:db && pnpm tf:apply:dev && pnpm tf:output:dev:d1",
2635
"db:migrate:dev": "pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/dev d1 migrations apply DB --env production --remote",
27-
"db:seed:dev": "pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/dev d1 execute DB --env production --remote --file ../../frontend/d1/seed/dev_seed.sql",
36+
"db:seed:dev": "pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/dev d1 execute DB --env production --remote --file ../../../frontend/d1/seed/dev_seed.sql",
2837
"db:init:dev": "pnpm db:reset:dev && pnpm gen:wrangler:dev && pnpm db:migrate:dev && pnpm db:seed:dev",
2938

39+
"db:reset:prod": "pnpm tf:select:prod && pnpm tf:destroy:prod:db && pnpm tf:apply:prod && pnpm tf:output:prod:d1",
40+
"db:migrate:prod": "pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/prod d1 migrations apply DB --env production --remote",
41+
"db:seed:prod": "pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/prod d1 execute DB --env production --remote --file ../../../frontend/d1/seed/prod_seed.sql",
42+
"db:init:prod": "pnpm db:reset:prod && pnpm gen:wrangler:prod && pnpm db:migrate:prod && pnpm db:seed:prod",
43+
3044
"pages:deploy:dev:prod": "pnpm --filter ./frontend build && pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/dev pages deploy --project-name unitn-oj-dev --branch main",
3145
"pages:deploy:dev:preview": "pnpm --filter ./frontend build && pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/dev pages deploy --project-name unitn-oj-dev --branch feature/demo",
3246
"pages:deploy:prod:prod": "pnpm --filter ./frontend build && pnpm --filter ./frontend exec wrangler --cwd ../deploy/cloudflare/prod pages deploy --project-name unitn-oj --branch main",

scripts/update-d1-id.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if (!ENV || !["dev", "prod"].includes(ENV)) {
1010
}
1111

1212
const TF_DIR = resolve("infra/terraform");
13-
const WRANGLER_PATH = resolve(`deploy/${ENV}/wrangler.jsonc`);
13+
const WRANGLER_PATH = resolve(`deploy/cloudflare/${ENV}/wrangler.jsonc`);
1414

1515
function sh(cmd, opts = {}) {
1616
return execSync(cmd, { stdio: ["pipe", "pipe", "inherit"], ...opts })

0 commit comments

Comments
 (0)