v0.4.0 f6d8405

sessionsInternal.ts RawHistoryFolder

1import { internalQuery, internalMutation } from './_generated/server';
2import { v } from 'convex/values';
3
4// Create a new session (internal)
5export const create = internalMutation({
6	args: {
7		tokenHash: v.string(), // SHA-256 hash of the token
8		userId: v.id('users'),
9		expiresAt: v.number()
10	},
11	handler: async (ctx, args) => {
12		const user = await ctx.db.get(args.userId);
13		if (!user) {
14			throw new Error('User not found');
15		}
16
17		const id = await ctx.db.insert('sessions', {
18			tokenHash: args.tokenHash,
19			userId: args.userId,
20			expiresAt: args.expiresAt,
21			createdAt: Date.now()
22		});
23
24		return { id, tokenHash: args.tokenHash };
25	}
26});
27
28// Validate session and get user (internal)
29export const validateAndGetUser = internalQuery({
30	args: { tokenHash: v.string() },
31	handler: async (ctx, args) => {
32		// Find session by token hash
33		const session = await ctx.db
34			.query('sessions')
35			.withIndex('by_tokenHash', (q) => q.eq('tokenHash', args.tokenHash))
36			.first();
37
38		if (!session) {
39			return { session: null, user: null };
40		}
41
42		// Check if expired
43		if (session.expiresAt < Date.now()) {
44			return { session: null, user: null };
45		}
46
47		// Get the user
48		const user = await ctx.db.get(session.userId);
49		if (!user) {
50			return { session: null, user: null };
51		}
52
53		return { session, user };
54	}
55});
56
57// Delete a session by token hash (internal)
58export const remove = internalMutation({
59	args: { tokenHash: v.string() },
60	handler: async (ctx, args) => {
61		const session = await ctx.db
62			.query('sessions')
63			.withIndex('by_tokenHash', (q) => q.eq('tokenHash', args.tokenHash))
64			.first();
65
66		if (session) {
67			await ctx.db.delete(session._id);
68		}
69	}
70});
71
72// Delete all sessions for a user (internal)
73export const removeAllForUser = internalMutation({
74	args: { userId: v.id('users') },
75	handler: async (ctx, args) => {
76		const sessions = await ctx.db
77			.query('sessions')
78			.withIndex('by_userId', (q) => q.eq('userId', args.userId))
79			.collect();
80
81		for (const session of sessions) {
82			await ctx.db.delete(session._id);
83		}
84
85		return { deleted: sessions.length };
86	}
87});

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close