Skip to content

Commit 389e8eb

Browse files
committed
feat(auth): add Bearer token support for CLI authentication
1 parent 2820a13 commit 389e8eb

File tree

4 files changed

+46
-19
lines changed

4 files changed

+46
-19
lines changed

src/lib/server/auth.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,24 @@ export function getCookie(cookies: Cookies, name: string): string | undefined {
4242
return cookies.get(name);
4343
}
4444

45-
export async function getCurrentUser(cookies: Cookies, db: D1Database, secret: string) {
45+
export async function getCurrentUser(request: Request, cookies: Cookies, db: D1Database, secret: string) {
46+
const authHeader = request.headers.get('Authorization');
47+
if (authHeader && authHeader.startsWith('Bearer obt_')) {
48+
const token = authHeader.slice(7);
49+
const tokenRow = await db.prepare(
50+
'SELECT * FROM api_tokens WHERE token = ? AND expires_at > datetime(\'now\')'
51+
).bind(token).first<{ id: string; user_id: string }>();
52+
53+
if (tokenRow) {
54+
await db.prepare('UPDATE api_tokens SET last_used_at = datetime(\'now\') WHERE id = ?')
55+
.bind(tokenRow.id).run();
56+
57+
const user = await db.prepare('SELECT id, username, email, avatar_url FROM users WHERE id = ?')
58+
.bind(tokenRow.user_id).first();
59+
if (user) return user;
60+
}
61+
}
62+
4663
const token = getCookie(cookies, 'session');
4764
if (!token) return null;
4865

src/routes/api/configs/+server.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,30 @@ import { json } from '@sveltejs/kit';
22
import type { RequestHandler } from './$types';
33
import { getCurrentUser, slugify, generateId } from '$lib/server/auth';
44

5-
export const GET: RequestHandler = async ({ platform, cookies }) => {
5+
export const GET: RequestHandler = async ({ platform, cookies, request }) => {
66
const env = platform?.env;
77
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
88

9-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
9+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
1010
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
1111

12-
const { results } = await env.DB.prepare('SELECT id, slug, name, description, base_preset, is_public, alias, updated_at FROM configs WHERE user_id = ? ORDER BY updated_at DESC')
12+
const { results } = await env.DB.prepare('SELECT id, slug, name, description, base_preset, is_public, alias, updated_at, snapshot, snapshot_at FROM configs WHERE user_id = ? ORDER BY updated_at DESC')
1313
.bind(user.id)
1414
.all();
1515

16-
return json({ configs: results, username: user.username });
16+
const configs = results.map((config: any) => ({
17+
...config,
18+
snapshot: config.snapshot ? JSON.parse(config.snapshot) : null
19+
}));
20+
21+
return json({ configs, username: user.username });
1722
};
1823

1924
export const POST: RequestHandler = async ({ platform, cookies, request }) => {
2025
const env = platform?.env;
2126
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
2227

23-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
28+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
2429
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
2530

2631
let body;
@@ -30,7 +35,7 @@ export const POST: RequestHandler = async ({ platform, cookies, request }) => {
3035
return json({ error: 'Invalid request body' }, { status: 400 });
3136
}
3237

33-
const { name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo } = body;
38+
const { name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo, snapshot, snapshot_at } = body;
3439

3540
if (!name) return json({ error: 'Name is required' }, { status: 400 });
3641

@@ -64,11 +69,11 @@ export const POST: RequestHandler = async ({ platform, cookies, request }) => {
6469
try {
6570
await env.DB.prepare(
6671
`
67-
INSERT INTO configs (id, user_id, slug, name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo)
68-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
72+
INSERT INTO configs (id, user_id, slug, name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo, snapshot, snapshot_at)
73+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6974
`
7075
)
71-
.bind(id, user.id, slug, name, description || '', base_preset || 'developer', JSON.stringify(packages || []), custom_script || '', is_public !== false ? 1 : 0, cleanAlias, dotfiles_repo || '')
76+
.bind(id, user.id, slug, name, description || '', base_preset || 'developer', JSON.stringify(packages || []), custom_script || '', is_public !== false ? 1 : 0, cleanAlias, dotfiles_repo || '', snapshot ? JSON.stringify(snapshot) : null, snapshot_at || null)
7277
.run();
7378
} catch (e) {
7479
return json({ error: 'Database error: ' + (e as Error).message }, { status: 500 });

src/routes/api/configs/[slug]/+server.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { json } from '@sveltejs/kit';
22
import type { RequestHandler } from './$types';
33
import { getCurrentUser, slugify } from '$lib/server/auth';
44

5-
export const GET: RequestHandler = async ({ platform, cookies, params }) => {
5+
export const GET: RequestHandler = async ({ platform, cookies, params, request }) => {
66
const env = platform?.env;
77
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
88

9-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
9+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
1010
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
1111

1212
const config = await env.DB.prepare('SELECT * FROM configs WHERE user_id = ? AND slug = ?').bind(user.id, params.slug).first();
@@ -17,7 +17,8 @@ export const GET: RequestHandler = async ({ platform, cookies, params }) => {
1717
return json({
1818
config: {
1919
...config,
20-
packages: JSON.parse((config.packages as string) || '[]')
20+
packages: JSON.parse((config.packages as string) || '[]'),
21+
snapshot: config.snapshot ? JSON.parse(config.snapshot) : null
2122
},
2223
install_url: installUrl
2324
});
@@ -27,7 +28,7 @@ export const PUT: RequestHandler = async ({ platform, cookies, params, request }
2728
const env = platform?.env;
2829
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
2930

30-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
31+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
3132
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
3233

3334
let body;
@@ -37,7 +38,7 @@ export const PUT: RequestHandler = async ({ platform, cookies, params, request }
3738
return json({ error: 'Invalid request body' }, { status: 400 });
3839
}
3940

40-
const { name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo } = body;
41+
const { name, description, base_preset, packages, custom_script, is_public, alias, dotfiles_repo, snapshot, snapshot_at } = body;
4142
const slug = params.slug;
4243

4344
const existing = await env.DB.prepare('SELECT id, alias FROM configs WHERE user_id = ? AND slug = ?').bind(user.id, slug).first<{ id: string; alias: string | null }>();
@@ -81,6 +82,8 @@ export const PUT: RequestHandler = async ({ platform, cookies, params, request }
8182
is_public = COALESCE(?, is_public),
8283
alias = ?,
8384
dotfiles_repo = COALESCE(?, dotfiles_repo),
85+
snapshot = ?,
86+
snapshot_at = ?,
8487
updated_at = datetime('now')
8588
WHERE user_id = ? AND slug = ?
8689
`
@@ -95,6 +98,8 @@ export const PUT: RequestHandler = async ({ platform, cookies, params, request }
9598
is_public !== undefined ? (is_public ? 1 : 0) : null,
9699
newAlias,
97100
dotfiles_repo !== undefined ? dotfiles_repo : null,
101+
snapshot !== undefined ? (snapshot ? JSON.stringify(snapshot) : null) : null,
102+
snapshot_at !== undefined ? snapshot_at : null,
98103
user.id,
99104
slug
100105
)
@@ -108,11 +113,11 @@ export const PUT: RequestHandler = async ({ platform, cookies, params, request }
108113
return json({ success: true, slug: newSlug, alias: newAlias, install_url: installUrl });
109114
};
110115

111-
export const DELETE: RequestHandler = async ({ platform, cookies, params }) => {
116+
export const DELETE: RequestHandler = async ({ platform, cookies, params, request }) => {
112117
const env = platform?.env;
113118
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
114119

115-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
120+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
116121
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
117122

118123
await env.DB.prepare('DELETE FROM configs WHERE user_id = ? AND slug = ?').bind(user.id, params.slug).run();

src/routes/api/user/+server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { json } from '@sveltejs/kit';
22
import type { RequestHandler } from './$types';
33
import { getCurrentUser } from '$lib/server/auth';
44

5-
export const GET: RequestHandler = async ({ platform, cookies }) => {
5+
export const GET: RequestHandler = async ({ platform, cookies, request }) => {
66
const env = platform?.env;
77
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
88

9-
const user = await getCurrentUser(cookies, env.DB, env.JWT_SECRET);
9+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
1010
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
1111

1212
return json({ user });

0 commit comments

Comments
 (0)