@axium/server 0.7.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps.d.ts +15 -0
- package/dist/apps.js +20 -0
- package/dist/auth.d.ts +53 -30
- package/dist/auth.js +103 -130
- package/dist/cli.js +176 -42
- package/dist/config.d.ts +57 -312
- package/dist/config.js +65 -31
- package/dist/database.d.ts +31 -40
- package/dist/database.js +165 -62
- package/dist/io.js +6 -2
- package/dist/plugins.d.ts +8 -24
- package/dist/plugins.js +10 -14
- package/dist/routes.d.ts +55 -0
- package/dist/routes.js +54 -0
- package/package.json +11 -16
- package/web/api/index.ts +7 -0
- package/web/api/metadata.ts +35 -0
- package/web/api/passkeys.ts +56 -0
- package/web/api/readme.md +1 -0
- package/web/api/register.ts +83 -0
- package/web/api/schemas.ts +22 -0
- package/web/api/session.ts +33 -0
- package/web/api/users.ts +351 -0
- package/web/api/utils.ts +66 -0
- package/web/auth.ts +1 -5
- package/web/hooks.server.ts +12 -1
- package/web/index.server.ts +0 -1
- package/web/lib/ClipboardCopy.svelte +42 -0
- package/web/lib/Dialog.svelte +3 -6
- package/web/lib/FormDialog.svelte +61 -14
- package/web/lib/Toast.svelte +8 -1
- package/web/lib/UserCard.svelte +1 -1
- package/web/lib/auth.ts +12 -0
- package/web/lib/icons/Icon.svelte +7 -13
- package/web/lib/index.ts +0 -2
- package/web/lib/styles.css +18 -1
- package/web/routes/+layout.svelte +1 -1
- package/web/routes/[...path]/+page.server.ts +13 -0
- package/web/routes/[appId]/[...page]/+page.server.ts +14 -0
- package/web/routes/_axium/default/+page.svelte +11 -0
- package/web/routes/account/+page.svelte +291 -0
- package/web/routes/api/[...path]/+server.ts +49 -0
- package/web/routes/login/+page.svelte +25 -0
- package/web/routes/logout/+page.svelte +13 -0
- package/web/routes/register/+page.svelte +21 -0
- package/web/tsconfig.json +2 -1
- package/web/utils.ts +9 -15
- package/web/actions.ts +0 -58
- package/web/lib/Account.svelte +0 -76
- package/web/lib/SignUp.svelte +0 -20
- package/web/lib/account.css +0 -36
- package/web/routes/+page.server.ts +0 -16
- package/web/routes/+page.svelte +0 -10
- package/web/routes/name/+page.server.ts +0 -5
- package/web/routes/name/+page.svelte +0 -20
- package/web/routes/signup/+page.server.ts +0 -10
- package/web/routes/signup/+page.svelte +0 -15
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import FormDialog from '$lib/FormDialog.svelte';
|
|
3
|
+
import { register } from '@axium/client/user';
|
|
4
|
+
|
|
5
|
+
let { dialog = $bindable(), pageMode = true } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<svelte:head>
|
|
9
|
+
<title>Sign Up</title>
|
|
10
|
+
</svelte:head>
|
|
11
|
+
|
|
12
|
+
<FormDialog bind:dialog submitText="Register" submit={register} {pageMode}>
|
|
13
|
+
<div>
|
|
14
|
+
<label for="name">Display Name</label>
|
|
15
|
+
<input name="name" type="text" required />
|
|
16
|
+
</div>
|
|
17
|
+
<div>
|
|
18
|
+
<label for="email">Email</label>
|
|
19
|
+
<input name="email" type="email" required />
|
|
20
|
+
</div>
|
|
21
|
+
</FormDialog>
|
package/web/tsconfig.json
CHANGED
package/web/utils.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import type { Session } from '@auth/sveltekit';
|
|
2
1
|
import type { ActionFailure, RequestEvent } from '@sveltejs/kit';
|
|
3
|
-
import { fail
|
|
4
|
-
import
|
|
5
|
-
import { fromError } from 'zod-validation-error';
|
|
6
|
-
import config from '../dist/config.js';
|
|
7
|
-
|
|
8
|
-
export async function loadSession(event: RequestEvent): Promise<{ session: Session }> {
|
|
9
|
-
const session = await event.locals.auth();
|
|
10
|
-
if (!session) redirect(307, '/auth/signin');
|
|
11
|
-
if (!session.user.name && event.url.pathname != config.web.prefix + '/name') redirect(307, config.web.prefix + '/name');
|
|
12
|
-
return { session };
|
|
13
|
-
}
|
|
2
|
+
import { fail } from '@sveltejs/kit';
|
|
3
|
+
import z from 'zod/v4';
|
|
14
4
|
|
|
15
5
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
16
|
-
export interface FormFail<S extends z.
|
|
6
|
+
export interface FormFail<S extends z.ZodType, E extends object = object> extends ActionFailure<z.infer<S> & { error: string } & E> {}
|
|
17
7
|
|
|
18
|
-
export async function parseForm<S extends z.
|
|
8
|
+
export async function parseForm<S extends z.ZodObject, E extends object = object>(
|
|
9
|
+
event: RequestEvent,
|
|
10
|
+
schema: S,
|
|
11
|
+
errorData?: E
|
|
12
|
+
): Promise<[z.infer<S>, FormFail<S, E> | null]> {
|
|
19
13
|
const formData = Object.fromEntries(await event.request.formData());
|
|
20
14
|
const { data, error, success } = schema.safeParse(formData);
|
|
21
15
|
|
|
@@ -26,7 +20,7 @@ export async function parseForm<S extends z.AnyZodObject, E extends object = obj
|
|
|
26
20
|
fail(400, {
|
|
27
21
|
...data,
|
|
28
22
|
...errorData,
|
|
29
|
-
error:
|
|
23
|
+
error: z.prettifyError(error),
|
|
30
24
|
}),
|
|
31
25
|
];
|
|
32
26
|
}
|
package/web/actions.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Registration, User } from '@axium/core/schemas';
|
|
2
|
-
import type { RequestEvent } from '@sveltejs/kit';
|
|
3
|
-
import { fail } from '@sveltejs/kit';
|
|
4
|
-
import { adapter, register } from '../dist/auth.js';
|
|
5
|
-
import { parseForm } from './utils.js';
|
|
6
|
-
|
|
7
|
-
export async function editEmail(event: RequestEvent) {
|
|
8
|
-
const session = await event.locals.auth();
|
|
9
|
-
if (!session) return fail(401, { error: 'You are not signed in' });
|
|
10
|
-
|
|
11
|
-
const [{ email } = {}, error] = await parseForm(event, User.pick({ email: true }));
|
|
12
|
-
if (error) return error;
|
|
13
|
-
|
|
14
|
-
const user = await adapter.getUserByEmail(session.user.email);
|
|
15
|
-
if (!user) return fail(401, { email, error: 'You are not signed in' });
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
await adapter.updateUser({ id: user.id, email, image: user.image });
|
|
19
|
-
} catch (error: any) {
|
|
20
|
-
return fail(400, { email, error: typeof error === 'string' ? error : error.message });
|
|
21
|
-
}
|
|
22
|
-
return { success: true };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function editName(event: RequestEvent) {
|
|
26
|
-
const session = await event.locals.auth();
|
|
27
|
-
if (!session) return fail(401, { error: 'You are not signed in' });
|
|
28
|
-
|
|
29
|
-
const [{ name } = {}, error] = await parseForm(event, User.pick({ name: true }));
|
|
30
|
-
if (error) return error;
|
|
31
|
-
|
|
32
|
-
const user = await adapter.getUserByEmail(session.user.email);
|
|
33
|
-
if (!user) return fail(401, { name, error: 'You are not signed in' });
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
await adapter.updateUser({ id: user.id, name, image: user.image });
|
|
37
|
-
} catch (error: any) {
|
|
38
|
-
return fail(400, { name, error: typeof error === 'string' ? error : error.message });
|
|
39
|
-
}
|
|
40
|
-
return { success: true };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function signup(event: RequestEvent) {
|
|
44
|
-
const [data, error] = await parseForm(event, Registration);
|
|
45
|
-
if (error) return error;
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const { session } = await register(data);
|
|
49
|
-
event.cookies.set('session', session.sessionToken, {
|
|
50
|
-
path: '/',
|
|
51
|
-
expires: session.expires,
|
|
52
|
-
httpOnly: true,
|
|
53
|
-
});
|
|
54
|
-
return { ...data, success: true, data: session.sessionToken };
|
|
55
|
-
} catch (error: any) {
|
|
56
|
-
return fail(400, { ...data, error: typeof error === 'string' ? error : error.message });
|
|
57
|
-
}
|
|
58
|
-
}
|
package/web/lib/Account.svelte
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { goto } from '$app/navigation';
|
|
3
|
-
import { getUserImage } from '@axium/core';
|
|
4
|
-
import './account.css';
|
|
5
|
-
import FormDialog from './FormDialog.svelte';
|
|
6
|
-
import Icon from './icons/Icon.svelte';
|
|
7
|
-
import './styles.css';
|
|
8
|
-
|
|
9
|
-
const { data, children = () => {}, form } = $props();
|
|
10
|
-
|
|
11
|
-
const user = $derived(data.user);
|
|
12
|
-
|
|
13
|
-
$effect(() => {
|
|
14
|
-
if (!user) goto('/auth/signin');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const image = $derived(getUserImage(user));
|
|
18
|
-
|
|
19
|
-
let changeEmail = $state(false);
|
|
20
|
-
let changeName = $state(false);
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<div class="Account flex-content">
|
|
24
|
-
<img class="pfp" src={image} alt="User profile" />
|
|
25
|
-
<p class="greeting">Welcome, {user.name}</p>
|
|
26
|
-
<div class="account-section main">
|
|
27
|
-
<div class="account-item">
|
|
28
|
-
<p class="subtle">Name</p>
|
|
29
|
-
<p>{user.name}</p>
|
|
30
|
-
<button style:display="contents" class="change" onclick={() => (changeName = true)}><Icon i="chevron-right" /></button>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="account-item">
|
|
33
|
-
<p class="subtle">Email</p>
|
|
34
|
-
<p>{user.email}</p>
|
|
35
|
-
<button style:display="contents" class="change" onclick={() => (changeEmail = true)}><Icon i="chevron-right" /></button>
|
|
36
|
-
</div>
|
|
37
|
-
<div class="account-item">
|
|
38
|
-
<p class="subtle">User ID <dfn title="This is your UUID."><Icon i="regular/circle-info" /></dfn></p>
|
|
39
|
-
<p>{user.id}</p>
|
|
40
|
-
</div>
|
|
41
|
-
<a class="signout" href="/auth/signout"><button>Sign out</button></a>
|
|
42
|
-
</div>
|
|
43
|
-
{@render children()}
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<FormDialog bind:active={changeEmail} {form} submitText="Change" action="?/email">
|
|
47
|
-
<div>
|
|
48
|
-
<label for="email">Email Address</label>
|
|
49
|
-
<input name="email" type="email" value={form?.email || user.email || ''} required />
|
|
50
|
-
</div>
|
|
51
|
-
</FormDialog>
|
|
52
|
-
|
|
53
|
-
<FormDialog bind:active={changeName} {form} submitText="Change" action="?/name">
|
|
54
|
-
<div>
|
|
55
|
-
<label for="name">What do you want to be called?</label>
|
|
56
|
-
<input name="name" type="text" value={form?.name || user.name || ''} required />
|
|
57
|
-
</div>
|
|
58
|
-
</FormDialog>
|
|
59
|
-
|
|
60
|
-
<style>
|
|
61
|
-
.pfp {
|
|
62
|
-
width: 100px;
|
|
63
|
-
height: 100px;
|
|
64
|
-
border-radius: 50%;
|
|
65
|
-
border: 1px solid #8888;
|
|
66
|
-
margin-top: 3em;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.greeting {
|
|
70
|
-
font-size: 2em;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.signout {
|
|
74
|
-
margin-top: 2em;
|
|
75
|
-
}
|
|
76
|
-
</style>
|
package/web/lib/SignUp.svelte
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import FormDialog from './FormDialog.svelte';
|
|
3
|
-
import './styles.css';
|
|
4
|
-
let { form } = $props();
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<FormDialog pageMode={true} {form} submitText="Sign Up">
|
|
8
|
-
<div>
|
|
9
|
-
<label for="name">Display Name</label>
|
|
10
|
-
<input name="name" type="text" value={form?.name || ''} required />
|
|
11
|
-
</div>
|
|
12
|
-
<div>
|
|
13
|
-
<label for="email">Email</label>
|
|
14
|
-
<input name="email" type="email" value={form?.email || ''} required />
|
|
15
|
-
</div>
|
|
16
|
-
<div>
|
|
17
|
-
<label for="password">Password</label>
|
|
18
|
-
<input name="password" type="password" />
|
|
19
|
-
</div>
|
|
20
|
-
</FormDialog>
|
package/web/lib/account.css
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
.account-section {
|
|
2
|
-
width: 50%;
|
|
3
|
-
padding-top: 4em;
|
|
4
|
-
|
|
5
|
-
> div:has(+ div) {
|
|
6
|
-
border-bottom: 1px solid #8888;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.account-section .account-item {
|
|
11
|
-
display: grid;
|
|
12
|
-
grid-template-columns: 10em 1fr 2em;
|
|
13
|
-
align-items: center;
|
|
14
|
-
width: 100%;
|
|
15
|
-
gap: 1em;
|
|
16
|
-
text-wrap: nowrap;
|
|
17
|
-
padding-bottom: 1em;
|
|
18
|
-
|
|
19
|
-
> :first-child {
|
|
20
|
-
margin: 0 5em 0 1em;
|
|
21
|
-
grid-column: 1;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
> :nth-child(2) {
|
|
25
|
-
margin: 0;
|
|
26
|
-
grid-column: 2;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
> :last-child:nth-child(3) {
|
|
30
|
-
margin: 0;
|
|
31
|
-
display: inline;
|
|
32
|
-
grid-column: 3;
|
|
33
|
-
font-size: 0.75em;
|
|
34
|
-
cursor: pointer;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Actions } from '@sveltejs/kit';
|
|
2
|
-
import { adapter } from '../../dist/auth.js';
|
|
3
|
-
import { editEmail, editName } from '../actions.js';
|
|
4
|
-
import { loadSession } from '../utils.js';
|
|
5
|
-
import type { PageServerLoadEvent } from './$types.js';
|
|
6
|
-
|
|
7
|
-
export async function load(event: PageServerLoadEvent) {
|
|
8
|
-
const { session } = await loadSession(event);
|
|
9
|
-
const user = await adapter.getUserByEmail(session.user.email);
|
|
10
|
-
return { session, user };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const actions = {
|
|
14
|
-
email: editEmail,
|
|
15
|
-
name: editName,
|
|
16
|
-
} satisfies Actions;
|
package/web/routes/+page.svelte
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { goto } from '$app/navigation';
|
|
3
|
-
import FormDialog from '../../lib/FormDialog.svelte';
|
|
4
|
-
import '../../lib/styles.css';
|
|
5
|
-
let { form, data } = $props();
|
|
6
|
-
$effect(() => {
|
|
7
|
-
if (form?.success) goto('/');
|
|
8
|
-
});
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<svelte:head>
|
|
12
|
-
<title>Edit Name</title>
|
|
13
|
-
</svelte:head>
|
|
14
|
-
|
|
15
|
-
<FormDialog pageMode={true} {form} submitText="Change">
|
|
16
|
-
<div>
|
|
17
|
-
<label for="name">What do you want to be called?</label>
|
|
18
|
-
<input name="name" type="text" value={form?.name || data.session.user.name || ''} required />
|
|
19
|
-
</div>
|
|
20
|
-
</FormDialog>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { redirect } from '@sveltejs/kit';
|
|
2
|
-
import config from '../../../dist/config.js';
|
|
3
|
-
import { signup } from '../../actions.js';
|
|
4
|
-
import type { Actions } from './$types.js';
|
|
5
|
-
|
|
6
|
-
export function load() {
|
|
7
|
-
if (!config.auth.credentials) return redirect(307, '/auth/signin');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const actions = { default: signup } satisfies Actions;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { goto } from '$app/navigation';
|
|
3
|
-
import SignUp from '../../lib/SignUp.svelte';
|
|
4
|
-
import '../../lib/styles.css';
|
|
5
|
-
let { form } = $props();
|
|
6
|
-
$effect(() => {
|
|
7
|
-
if (form?.success) goto('/');
|
|
8
|
-
});
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<svelte:head>
|
|
12
|
-
<title>Sign Up</title>
|
|
13
|
-
</svelte:head>
|
|
14
|
-
|
|
15
|
-
<SignUp {form} />
|