tangled
alpha
login
or
join now
flo-bit.dev
/
blento
21
fork
atom
your personal website on atproto - mirror
blento.app
21
fork
atom
overview
issues
pulls
pipelines
ok actually maybe a big refactor ^^
Florian
4 weeks ago
d71c3655
30852a30
+154
-423
22 changed files
expand all
collapse all
unified
split
src
lib
website
EditBar.svelte
SaveModal.svelte
routes
+page.server.ts
+page.svelte
[[actor=actor]]
(pages)
+layout.server.ts
+page.svelte
edit
+page.svelte
p
[[page]]
+page.svelte
copy
+page.svelte
edit
+page.svelte
.well-known
site.standard.publication
+server.ts
api
refresh
+server.ts
og.png
+server.ts
[actor=actor]
(pages)
+layout.server.ts
p
[[page]]
copy
+page.svelte
.well-known
site.standard.publication
+server.ts
api
refresh
+server.ts
edit
+page.server.ts
+page.svelte
p
[[page]]
+layout.server.ts
+page.svelte
edit
+page.svelte
+1
-1
src/lib/website/EditBar.svelte
···
63
63
function getShareUrl() {
64
64
const base = typeof window !== 'undefined' ? window.location.origin : '';
65
65
const pagePath =
66
66
-
data.page && data.page !== 'blento.self' ? `/${data.page.replace('blento.', '')}` : '';
66
66
+
data.page && data.page !== 'blento.self' ? `/p/${data.page.replace('blento.', '')}` : '';
67
67
return `${base}/${data.handle}${pagePath}`;
68
68
}
69
69
+1
-1
src/lib/website/SaveModal.svelte
···
15
15
16
16
function getShareUrl() {
17
17
const base = typeof window !== 'undefined' ? window.location.origin : '';
18
18
-
const pagePath = page && page !== 'blento.self' ? `/${page.replace('blento.', '')}` : '';
18
18
+
const pagePath = page && page !== 'blento.self' ? `/p/${page.replace('blento.', '')}` : '';
19
19
return `${base}/${handle}${pagePath}`;
20
20
}
21
21
-26
src/routes/+page.server.ts
···
1
1
-
import { loadData } from '$lib/website/load';
2
2
-
import { env } from '$env/dynamic/public';
3
3
-
import { env as privateEnv } from '$env/dynamic/private';
4
4
-
import { createCache } from '$lib/cache';
5
5
-
import type { ActorIdentifier } from '@atcute/lexicons';
6
6
-
7
7
-
export async function load({ platform, request }) {
8
8
-
const handle = env.PUBLIC_HANDLE;
9
9
-
10
10
-
const kv = platform?.env?.CUSTOM_DOMAINS;
11
11
-
12
12
-
const cache = createCache(platform);
13
13
-
const customDomain = request.headers.get('X-Custom-Domain')?.toLocaleLowerCase();
14
14
-
15
15
-
if (kv && customDomain) {
16
16
-
try {
17
17
-
const did = await kv.get(customDomain);
18
18
-
19
19
-
if (did) return await loadData(did as ActorIdentifier, cache, false, 'self', privateEnv);
20
20
-
} catch (error) {
21
21
-
console.error('failed to get custom domain kv', error);
22
22
-
}
23
23
-
}
24
24
-
25
25
-
return await loadData(handle as ActorIdentifier, cache, false, 'self', privateEnv);
26
26
-
}
-13
src/routes/+page.svelte
···
1
1
-
<script lang="ts">
2
2
-
import { refreshData } from '$lib/helper.js';
3
3
-
import Website from '$lib/website/Website.svelte';
4
4
-
import { onMount } from 'svelte';
5
5
-
6
6
-
let { data } = $props();
7
7
-
8
8
-
onMount(() => {
9
9
-
refreshData(data);
10
10
-
});
11
11
-
</script>
12
12
-
13
13
-
<Website {data} />
+40
src/routes/[[actor=actor]]/(pages)/+layout.server.ts
···
1
1
+
import { loadData } from '$lib/website/load';
2
2
+
import { env } from '$env/dynamic/private';
3
3
+
import { error } from '@sveltejs/kit';
4
4
+
import { createCache } from '$lib/cache';
5
5
+
import type { ActorIdentifier } from '@atcute/lexicons';
6
6
+
import { env as publicEnv } from '$env/dynamic/public';
7
7
+
8
8
+
export async function load({ params, platform, request }) {
9
9
+
if (env.PUBLIC_IS_SELFHOSTED) error(404);
10
10
+
11
11
+
const cache = createCache(platform);
12
12
+
13
13
+
const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
14
14
+
15
15
+
let actor: ActorIdentifier | undefined = params.actor;
16
16
+
17
17
+
if (!actor) {
18
18
+
const kv = platform?.env?.CUSTOM_DOMAINS;
19
19
+
20
20
+
if (kv && customDomain) {
21
21
+
try {
22
22
+
const did = await kv.get(customDomain);
23
23
+
24
24
+
if (did) actor = did as ActorIdentifier;
25
25
+
} catch (error) {
26
26
+
console.error('failed to get custom domain kv', error);
27
27
+
}
28
28
+
} else {
29
29
+
actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
30
30
+
}
31
31
+
} else if (customDomain && params.actor) {
32
32
+
actor = undefined;
33
33
+
}
34
34
+
35
35
+
if (!actor) {
36
36
+
throw error(404, 'Page not found');
37
37
+
}
38
38
+
39
39
+
return await loadData(actor, cache, false, params.page, env);
40
40
+
}
+44
src/routes/[[actor=actor]]/.well-known/site.standard.publication/+server.ts
···
1
1
+
import { loadData } from '$lib/website/load';
2
2
+
import { createCache } from '$lib/cache';
3
3
+
import { env } from '$env/dynamic/private';
4
4
+
import { env as publicEnv } from '$env/dynamic/public';
5
5
+
import type { ActorIdentifier } from '@atcute/lexicons';
6
6
+
7
7
+
import { error } from '@sveltejs/kit';
8
8
+
import { text } from '@sveltejs/kit';
9
9
+
10
10
+
export async function GET({ params, platform, request }) {
11
11
+
const cache = createCache(platform);
12
12
+
13
13
+
const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
14
14
+
15
15
+
let actor: ActorIdentifier | undefined = params.actor;
16
16
+
17
17
+
if (!actor) {
18
18
+
const kv = platform?.env?.CUSTOM_DOMAINS;
19
19
+
20
20
+
if (kv && customDomain) {
21
21
+
try {
22
22
+
const did = await kv.get(customDomain);
23
23
+
24
24
+
if (did) actor = did as ActorIdentifier;
25
25
+
} catch (error) {
26
26
+
console.error('failed to get custom domain kv', error);
27
27
+
}
28
28
+
} else {
29
29
+
actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
30
30
+
}
31
31
+
} else if (customDomain && params.actor) {
32
32
+
actor = undefined;
33
33
+
}
34
34
+
35
35
+
if (!actor) {
36
36
+
throw error(404, 'Page not found');
37
37
+
}
38
38
+
39
39
+
const data = await loadData(actor, cache, false, params.page, env);
40
40
+
41
41
+
if (!data.publication) throw error(300);
42
42
+
43
43
+
return text(data.did + '/site.standard.publication/blento.self');
44
44
+
}
+37
src/routes/[[actor=actor]]/api/refresh/+server.ts
···
1
1
+
import { createCache } from '$lib/cache';
2
2
+
import { loadData } from '$lib/website/load.js';
3
3
+
import { env } from '$env/dynamic/private';
4
4
+
import { env as publicEnv } from '$env/dynamic/public';
5
5
+
import type { ActorIdentifier } from '@atcute/lexicons';
6
6
+
import { error, json } from '@sveltejs/kit';
7
7
+
8
8
+
export async function GET({ params, platform, request }) {
9
9
+
const cache = createCache(platform);
10
10
+
if (!cache) return json('no cache');
11
11
+
12
12
+
const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
13
13
+
14
14
+
let actor: ActorIdentifier | undefined = params.actor;
15
15
+
16
16
+
if (!actor) {
17
17
+
const kv = platform?.env?.CUSTOM_DOMAINS;
18
18
+
19
19
+
if (kv && customDomain) {
20
20
+
try {
21
21
+
const did = await kv.get(customDomain);
22
22
+
23
23
+
if (did) actor = did as ActorIdentifier;
24
24
+
} catch (error) {
25
25
+
console.error('failed to get custom domain kv', error);
26
26
+
}
27
27
+
} else {
28
28
+
actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
29
29
+
}
30
30
+
}
31
31
+
32
32
+
if (!actor) {
33
33
+
throw error(404, 'Page not found');
34
34
+
}
35
35
+
36
36
+
return json(await loadData(actor, cache, true, 'self', env));
37
37
+
}
-18
src/routes/[actor=actor]/(pages)/+layout.server.ts
···
1
1
-
import { loadData } from '$lib/website/load';
2
2
-
import { env } from '$env/dynamic/private';
3
3
-
import { error } from '@sveltejs/kit';
4
4
-
import { createCache } from '$lib/cache';
5
5
-
6
6
-
export async function load({ params, platform, request }) {
7
7
-
if (env.PUBLIC_IS_SELFHOSTED) error(404);
8
8
-
9
9
-
const cache = createCache(platform);
10
10
-
11
11
-
const customDomain = request.headers.get('X-Custom-Domain');
12
12
-
13
13
-
if (customDomain) {
14
14
-
throw error(404, 'Page not found!');
15
15
-
}
16
16
-
17
17
-
return await loadData(params.actor, cache, false, params.page, env);
18
18
-
}
src/routes/[actor=actor]/(pages)/+page.svelte
src/routes/[[actor=actor]]/(pages)/+page.svelte
src/routes/[actor=actor]/(pages)/edit/+page.svelte
src/routes/[[actor=actor]]/(pages)/edit/+page.svelte
src/routes/[actor=actor]/(pages)/p/[[page]]/+page.svelte
src/routes/[[actor=actor]]/(pages)/p/[[page]]/+page.svelte
-252
src/routes/[actor=actor]/(pages)/p/[[page]]/copy/+page.svelte
···
1
1
-
<script lang="ts">
2
2
-
import {
3
3
-
putRecord,
4
4
-
deleteRecord,
5
5
-
listRecords,
6
6
-
uploadBlob,
7
7
-
getCDNImageBlobUrl
8
8
-
} from '$lib/atproto/methods';
9
9
-
import { user } from '$lib/atproto/auth.svelte';
10
10
-
import { goto } from '$app/navigation';
11
11
-
import * as TID from '@atcute/tid';
12
12
-
import { Button } from '@foxui/core';
13
13
-
import { loginModalState } from '$lib/atproto/UI/LoginModal.svelte';
14
14
-
15
15
-
let { data } = $props();
16
16
-
17
17
-
let destinationPage = $state('');
18
18
-
let copying = $state(false);
19
19
-
let error = $state('');
20
20
-
let success = $state(false);
21
21
-
22
22
-
const sourceHandle = $derived(data.handle);
23
23
-
24
24
-
const sourcePage = $derived(
25
25
-
data.page === 'blento.self' ? 'main' : data.page.replace('blento.', '')
26
26
-
);
27
27
-
const sourceCards = $derived(data.cards);
28
28
-
29
29
-
// Re-upload blobs from source repo to current user's repo
30
30
-
async function reuploadBlobs(obj: any, sourceDid: string): Promise<void> {
31
31
-
if (!obj || typeof obj !== 'object') return;
32
32
-
33
33
-
for (const key of Object.keys(obj)) {
34
34
-
const value = obj[key];
35
35
-
36
36
-
if (value && typeof value === 'object') {
37
37
-
// Check if this is a blob reference
38
38
-
if (value.$type === 'blob' && value.ref?.$link) {
39
39
-
try {
40
40
-
// Get the blob URL from source repo
41
41
-
const blobUrl = getCDNImageBlobUrl({ did: sourceDid, blob: value });
42
42
-
if (!blobUrl) continue;
43
43
-
44
44
-
// Fetch the blob via proxy to avoid CORS
45
45
-
const response = await fetch(`/api/image-proxy?url=${encodeURIComponent(blobUrl)}`);
46
46
-
if (!response.ok) {
47
47
-
console.error('Failed to fetch blob:', blobUrl);
48
48
-
continue;
49
49
-
}
50
50
-
51
51
-
// Upload to current user's repo
52
52
-
const blob = await response.blob();
53
53
-
const newBlobRef = await uploadBlob({ blob });
54
54
-
55
55
-
if (newBlobRef) {
56
56
-
// Replace with new blob reference
57
57
-
obj[key] = newBlobRef;
58
58
-
}
59
59
-
} catch (err) {
60
60
-
console.error('Failed to re-upload blob:', err);
61
61
-
}
62
62
-
} else {
63
63
-
// Recursively check nested objects
64
64
-
await reuploadBlobs(value, sourceDid);
65
65
-
}
66
66
-
}
67
67
-
}
68
68
-
}
69
69
-
70
70
-
async function copyPage() {
71
71
-
if (!user.isLoggedIn || !user.did) {
72
72
-
error = 'You must be logged in to copy pages';
73
73
-
return;
74
74
-
}
75
75
-
76
76
-
copying = true;
77
77
-
error = '';
78
78
-
79
79
-
try {
80
80
-
const targetPage =
81
81
-
destinationPage.trim() === '' ? 'blento.self' : `blento.${destinationPage.trim()}`;
82
82
-
83
83
-
// Fetch existing cards from destination page and delete them
84
84
-
const existingCards = await listRecords({
85
85
-
did: user.did,
86
86
-
collection: 'app.blento.card'
87
87
-
});
88
88
-
89
89
-
const cardsToDelete = existingCards.filter(
90
90
-
(card: { value: { page?: string } }) => card.value.page === targetPage
91
91
-
);
92
92
-
93
93
-
// Delete existing cards from destination page
94
94
-
const deletePromises = cardsToDelete.map((card: { uri: string }) => {
95
95
-
const rkey = card.uri.split('/').pop()!;
96
96
-
return deleteRecord({
97
97
-
collection: 'app.blento.card',
98
98
-
rkey
99
99
-
});
100
100
-
});
101
101
-
102
102
-
await Promise.all(deletePromises);
103
103
-
104
104
-
// Copy each card with a new ID to the destination page
105
105
-
// Re-upload blobs from source repo to current user's repo
106
106
-
for (const card of sourceCards) {
107
107
-
const newCard = {
108
108
-
...structuredClone(card),
109
109
-
id: TID.now(),
110
110
-
page: targetPage,
111
111
-
updatedAt: new Date().toISOString(),
112
112
-
version: 2
113
113
-
};
114
114
-
115
115
-
// Re-upload any blobs in cardData
116
116
-
await reuploadBlobs(newCard.cardData, data.did);
117
117
-
118
118
-
await putRecord({
119
119
-
collection: 'app.blento.card',
120
120
-
rkey: newCard.id,
121
121
-
record: newCard
122
122
-
});
123
123
-
}
124
124
-
125
125
-
const userHandle = user.profile?.handle ?? data.handle;
126
126
-
127
127
-
// Copy publication data if it exists
128
128
-
if (data.publication) {
129
129
-
const publicationCopy = structuredClone(data.publication) as Record<string, unknown>;
130
130
-
131
131
-
// Re-upload any blobs in publication (e.g., icon)
132
132
-
await reuploadBlobs(publicationCopy, data.did);
133
133
-
134
134
-
// Update the URL to point to the user's page
135
135
-
publicationCopy.url = `https://blento.app/${userHandle}`;
136
136
-
if (targetPage !== 'blento.self') {
137
137
-
publicationCopy.url += '/' + targetPage.replace('blento.', '');
138
138
-
}
139
139
-
140
140
-
// Save to appropriate collection based on destination page type
141
141
-
if (targetPage === 'blento.self') {
142
142
-
await putRecord({
143
143
-
collection: 'site.standard.publication',
144
144
-
rkey: targetPage,
145
145
-
record: publicationCopy
146
146
-
});
147
147
-
} else {
148
148
-
await putRecord({
149
149
-
collection: 'app.blento.page',
150
150
-
rkey: targetPage,
151
151
-
record: publicationCopy
152
152
-
});
153
153
-
}
154
154
-
}
155
155
-
156
156
-
// Refresh the logged-in user's cache
157
157
-
await fetch(`/${userHandle}/api/refresh`);
158
158
-
159
159
-
success = true;
160
160
-
161
161
-
// Redirect to the logged-in user's destination page edit
162
162
-
const destPath = destinationPage.trim() === '' ? '' : `/${destinationPage.trim()}`;
163
163
-
setTimeout(() => {
164
164
-
goto(`/${userHandle}${destPath}/edit`);
165
165
-
}, 1000);
166
166
-
} catch (e) {
167
167
-
error = e instanceof Error ? e.message : 'Failed to copy page';
168
168
-
} finally {
169
169
-
copying = false;
170
170
-
}
171
171
-
}
172
172
-
</script>
173
173
-
174
174
-
<div class="bg-base-50 dark:bg-base-900 flex min-h-screen items-center justify-center p-4">
175
175
-
<div class="bg-base-100 dark:bg-base-800 w-full max-w-md rounded-2xl p-6 shadow-lg">
176
176
-
{#if user.isLoggedIn}
177
177
-
<h1 class="text-base-900 dark:text-base-50 mb-6 text-2xl font-bold">Copy Page</h1>
178
178
-
179
179
-
<div class="mb-4">
180
180
-
<div class="text-base-700 dark:text-base-300 mb-1 block text-sm font-medium">
181
181
-
Source Page
182
182
-
</div>
183
183
-
<div
184
184
-
class="bg-base-200 dark:bg-base-700 text-base-900 dark:text-base-100 rounded-lg px-3 py-2"
185
185
-
>
186
186
-
{sourceHandle}/{sourcePage || 'main'}
187
187
-
</div>
188
188
-
<p class="text-base-500 mt-1 text-sm">{sourceCards.length} cards</p>
189
189
-
</div>
190
190
-
191
191
-
<div class="mb-6">
192
192
-
<label
193
193
-
for="destination"
194
194
-
class="text-base-700 dark:text-base-300 mb-1 block text-sm font-medium"
195
195
-
>
196
196
-
Destination Page (on your profile: {user.profile?.handle})
197
197
-
</label>
198
198
-
<input
199
199
-
id="destination"
200
200
-
type="text"
201
201
-
bind:value={destinationPage}
202
202
-
placeholder="Leave empty for main page"
203
203
-
class="bg-base-50 dark:bg-base-700 border-base-300 dark:border-base-600 text-base-900 dark:text-base-100 focus:ring-accent-500 w-full rounded-lg border px-3 py-2 focus:ring-2 focus:outline-none"
204
204
-
/>
205
205
-
</div>
206
206
-
207
207
-
{#if error}
208
208
-
<div
209
209
-
class="mb-4 rounded-lg bg-red-100 p-3 text-red-700 dark:bg-red-900/30 dark:text-red-400"
210
210
-
>
211
211
-
{error}
212
212
-
</div>
213
213
-
{/if}
214
214
-
215
215
-
{#if success}
216
216
-
<div
217
217
-
class="mb-4 rounded-lg bg-green-100 p-3 text-green-700 dark:bg-green-900/30 dark:text-green-400"
218
218
-
>
219
219
-
Page copied successfully! Redirecting...
220
220
-
</div>
221
221
-
{/if}
222
222
-
223
223
-
<div class="flex gap-3">
224
224
-
<a
225
225
-
href="/{data.handle}/{sourcePage === 'main' ? '' : sourcePage}"
226
226
-
class="bg-base-200 hover:bg-base-300 dark:bg-base-700 dark:hover:bg-base-600 text-base-700 dark:text-base-300 flex-1 rounded-lg px-4 py-2 text-center font-medium transition-colors"
227
227
-
>
228
228
-
Cancel
229
229
-
</a>
230
230
-
<button
231
231
-
onclick={copyPage}
232
232
-
disabled={copying || success}
233
233
-
class="bg-accent-500 hover:bg-accent-600 flex-1 rounded-lg px-4 py-2 font-medium text-white transition-colors disabled:cursor-not-allowed disabled:opacity-50"
234
234
-
>
235
235
-
{#if copying}
236
236
-
Copying...
237
237
-
{:else}
238
238
-
Copy {sourceCards.length} cards
239
239
-
{/if}
240
240
-
</button>
241
241
-
</div>
242
242
-
{:else}
243
243
-
<h1 class="text-base-900 dark:text-base-50 mb-6 text-2xl font-bold">
244
244
-
You must be signed in to copy a page!
245
245
-
</h1>
246
246
-
247
247
-
<div class="flex w-full justify-center">
248
248
-
<Button size="lg" onclick={() => loginModalState.show()}>Login</Button>
249
249
-
</div>
250
250
-
{/if}
251
251
-
</div>
252
252
-
</div>
src/routes/[actor=actor]/(pages)/p/[[page]]/edit/+page.svelte
src/routes/[[actor=actor]]/(pages)/p/[[page]]/edit/+page.svelte
-16
src/routes/[actor=actor]/.well-known/site.standard.publication/+server.ts
···
1
1
-
import { loadData } from '$lib/website/load';
2
2
-
import { createCache } from '$lib/cache';
3
3
-
import { env } from '$env/dynamic/private';
4
4
-
5
5
-
import { error } from '@sveltejs/kit';
6
6
-
import { text } from '@sveltejs/kit';
7
7
-
8
8
-
export async function GET({ params, platform }) {
9
9
-
const cache = createCache(platform);
10
10
-
11
11
-
const data = await loadData(params.actor, cache, false, params.page, env);
12
12
-
13
13
-
if (!data.publication) throw error(300);
14
14
-
15
15
-
return text(data.did + '/site.standard.publication/blento.self');
16
16
-
}
-14
src/routes/[actor=actor]/api/refresh/+server.ts
···
1
1
-
import { createCache } from '$lib/cache';
2
2
-
import { loadData } from '$lib/website/load.js';
3
3
-
import { env } from '$env/dynamic/private';
4
4
-
import type { Handle } from '@atcute/lexicons';
5
5
-
import { json } from '@sveltejs/kit';
6
6
-
7
7
-
export async function GET({ params, platform }) {
8
8
-
const cache = createCache(platform);
9
9
-
if (!cache) return json('no cache');
10
10
-
11
11
-
await loadData(params.actor, cache, true, 'self', env);
12
12
-
13
13
-
return json('ok');
14
14
-
}
+30
-3
src/routes/[actor=actor]/og.png/+server.ts
src/routes/[[actor=actor]]/og.png/+server.ts
···
2
2
import { createCache } from '$lib/cache';
3
3
import { loadData } from '$lib/website/load';
4
4
import { env } from '$env/dynamic/private';
5
5
-
import type { Handle } from '@atcute/lexicons';
5
5
+
import { env as publicEnv } from '$env/dynamic/public';
6
6
+
7
7
+
import type { ActorIdentifier } from '@atcute/lexicons';
6
8
import { ImageResponse } from '@ethercorps/sveltekit-og';
9
9
+
import { error } from '@sveltejs/kit';
7
10
8
11
function escapeHtml(str: string): string {
9
12
return str
···
14
17
.replace(/'/g, ''');
15
18
}
16
19
17
17
-
export async function GET({ params, platform }) {
20
20
+
export async function GET({ params, platform, request }) {
18
21
const cache = createCache(platform);
19
22
20
20
-
const data = await loadData(params.actor, cache, false, 'self', env);
23
23
+
const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
24
24
+
25
25
+
let actor: ActorIdentifier | undefined = params.actor;
26
26
+
27
27
+
if (!actor) {
28
28
+
const kv = platform?.env?.CUSTOM_DOMAINS;
29
29
+
30
30
+
if (kv && customDomain) {
31
31
+
try {
32
32
+
const did = await kv.get(customDomain);
33
33
+
34
34
+
if (did) actor = did as ActorIdentifier;
35
35
+
} catch (error) {
36
36
+
console.error('failed to get custom domain kv', error);
37
37
+
}
38
38
+
} else {
39
39
+
actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
40
40
+
}
41
41
+
}
42
42
+
43
43
+
if (!actor) {
44
44
+
throw error(404, 'Page not found');
45
45
+
}
46
46
+
47
47
+
const data = await loadData(actor, cache, false, 'self', env);
21
48
22
49
let image: string | undefined = data.profile.avatar;
23
50
-26
src/routes/edit/+page.server.ts
···
1
1
-
import { loadData } from '$lib/website/load';
2
2
-
import { env } from '$env/dynamic/public';
3
3
-
import { env as privateEnv } from '$env/dynamic/private';
4
4
-
import { createCache } from '$lib/cache';
5
5
-
import type { ActorIdentifier } from '@atcute/lexicons';
6
6
-
7
7
-
export async function load({ platform, request }) {
8
8
-
const handle = env.PUBLIC_HANDLE;
9
9
-
10
10
-
const kv = platform?.env?.CUSTOM_DOMAINS;
11
11
-
12
12
-
const cache = createCache(platform);
13
13
-
const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
14
14
-
15
15
-
if (kv && customDomain) {
16
16
-
try {
17
17
-
const did = await kv.get(customDomain);
18
18
-
19
19
-
if (did) return await loadData(did as ActorIdentifier, cache, false, 'self', privateEnv);
20
20
-
} catch (error) {
21
21
-
console.error('failed to get custom domain kv', error);
22
22
-
}
23
23
-
}
24
24
-
25
25
-
return await loadData(handle as ActorIdentifier, cache, false, 'self', privateEnv);
26
26
-
}
-7
src/routes/edit/+page.svelte
···
1
1
-
<script lang="ts">
2
2
-
import EditableWebsite from '$lib/website/EditableWebsite.svelte';
3
3
-
4
4
-
let { data } = $props();
5
5
-
</script>
6
6
-
7
7
-
<EditableWebsite {data} />
-26
src/routes/p/[[page]]/+layout.server.ts
···
1
1
-
import { loadData } from '$lib/website/load';
2
2
-
import { env } from '$env/dynamic/public';
3
3
-
import { env as privateEnv } from '$env/dynamic/private';
4
4
-
import { createCache } from '$lib/cache';
5
5
-
import type { Did, Handle } from '@atcute/lexicons';
6
6
-
7
7
-
export async function load({ params, platform, request }) {
8
8
-
const cache = createCache(platform);
9
9
-
10
10
-
const handle = env.PUBLIC_HANDLE;
11
11
-
12
12
-
const kv = platform?.env?.CUSTOM_DOMAINS;
13
13
-
14
14
-
const customDomain = request.headers.get('X-Custom-Domain')?.toLocaleLowerCase();
15
15
-
16
16
-
if (kv && customDomain) {
17
17
-
try {
18
18
-
const did = await kv.get(customDomain);
19
19
-
return await loadData(did as Did, cache, false, params.page, privateEnv);
20
20
-
} catch {
21
21
-
console.error('failed');
22
22
-
}
23
23
-
}
24
24
-
25
25
-
return await loadData(handle as Handle, cache, false, params.page, privateEnv);
26
26
-
}
-13
src/routes/p/[[page]]/+page.svelte
···
1
1
-
<script lang="ts">
2
2
-
import { refreshData } from '$lib/helper.js';
3
3
-
import Website from '$lib/website/Website.svelte';
4
4
-
import { onMount } from 'svelte';
5
5
-
6
6
-
let { data } = $props();
7
7
-
8
8
-
onMount(() => {
9
9
-
refreshData(data);
10
10
-
});
11
11
-
</script>
12
12
-
13
13
-
<Website {data} />
+1
-1
src/routes/p/[[page]]/copy/+page.svelte
src/routes/[[actor=actor]]/(pages)/p/[[page]]/copy/+page.svelte
···
159
159
success = true;
160
160
161
161
// Redirect to the logged-in user's destination page edit
162
162
-
const destPath = destinationPage.trim() === '' ? '' : `/${destinationPage.trim()}`;
162
162
+
const destPath = destinationPage.trim() === '' ? '' : `/p/${destinationPage.trim()}`;
163
163
setTimeout(() => {
164
164
goto(`/${userHandle}${destPath}/edit`);
165
165
}, 1000);
-6
src/routes/p/[[page]]/edit/+page.svelte
···
1
1
-
<script lang="ts">
2
2
-
import EditableWebsite from '$lib/website/EditableWebsite.svelte';
3
3
-
let { data } = $props();
4
4
-
</script>
5
5
-
6
6
-
<EditableWebsite {data} />