+13
-6
src/lib/atproto.ts
+13
-6
src/lib/atproto.ts
···
6
export interface Bundle {
7
description?: string;
8
assets: Record<string, { $type: "blob"; ref: { $link: string }; mimeType: string; size: number }>;
9
createdAt: string;
10
}
11
···
55
});
56
57
if (isXRPCErrorPayload(data)) throw new Error("couldn't load records");
58
-
return data;
59
}
60
61
-
async updateBundle(rkey: string, description: string, files: File[]) {
62
-
const bundle: Bundle = { description, assets: {}, createdAt: new Date().toISOString() };
63
-
64
for (const file of files) {
65
if (!(file instanceof File)) continue;
66
···
68
if (isXRPCErrorPayload(data)) throw new Error("couldn't upload file");
69
70
const filepath = file.webkitRelativePath?.replace(/^.+\//, "") ?? file.name;
71
-
bundle.assets[filepath] = {
72
$type: "blob",
73
ref: data.blob.ref,
74
mimeType: file.type,
···
76
};
77
}
78
79
const { data } = await this.#client.post("com.atproto.repo.putRecord", {
80
input: {
81
repo: this.#did,
82
collection: "com.jakelazaroff.test",
83
rkey,
84
-
record: bundle as any,
85
},
86
});
87
if (isXRPCErrorPayload(data)) throw new Error("couldn't deploy");
···
6
export interface Bundle {
7
description?: string;
8
assets: Record<string, { $type: "blob"; ref: { $link: string }; mimeType: string; size: number }>;
9
+
fallback?: {
10
+
path: string;
11
+
status?: number;
12
+
};
13
createdAt: string;
14
}
15
···
59
});
60
61
if (isXRPCErrorPayload(data)) throw new Error("couldn't load records");
62
+
return data as any as Omit<typeof data, "value"> & { value: Bundle };
63
}
64
65
+
async uploadFiles(files: File[]) {
66
+
const assets: Bundle["assets"] = {};
67
for (const file of files) {
68
if (!(file instanceof File)) continue;
69
···
71
if (isXRPCErrorPayload(data)) throw new Error("couldn't upload file");
72
73
const filepath = file.webkitRelativePath?.replace(/^.+\//, "") ?? file.name;
74
+
assets[filepath] = {
75
$type: "blob",
76
ref: data.blob.ref,
77
mimeType: file.type,
···
79
};
80
}
81
82
+
return assets;
83
+
}
84
+
85
+
async updateBundle(rkey: string, bundle: Bundle) {
86
const { data } = await this.#client.post("com.atproto.repo.putRecord", {
87
input: {
88
repo: this.#did,
89
collection: "com.jakelazaroff.test",
90
rkey,
91
+
record: { ...(bundle as any), createdAt: new Date().toISOString() },
92
},
93
});
94
if (isXRPCErrorPayload(data)) throw new Error("couldn't deploy");
+27
-9
src/routes/~/sites/[name]/+page.svelte
+27
-9
src/routes/~/sites/[name]/+page.svelte
···
7
8
let { params, data } = $props();
9
10
-
const atp = client(data.session);
11
12
const defaultDescription = "Uploaded from website";
13
···
28
const form = e.currentTarget;
29
const formdata = new FormData(form);
30
31
-
const rkey = formdata.get("rkey");
32
-
if (typeof rkey !== "string") throw new Error("invalid rkey");
33
-
34
let description = formdata.get("description");
35
if (typeof description !== "string" || !description) description = defaultDescription;
36
37
const files = formdata.getAll("files").filter(entry => entry instanceof File);
38
-
await atp.updateBundle(rkey, description, files);
39
invalidate(`rkey:${rkey}`);
40
form.reset();
41
}
42
</script>
43
···
93
<Icon name="toggle" />
94
<span>Settings</span>
95
</h3>
96
-
<form>
97
<fieldset>
98
<legend>Fallback</legend>
99
<label>
100
<span>path</span>
101
-
<input name="fallback_path" />
102
</label>
103
<label>
104
<span>200</span>
105
-
<input type="radio" name="fallback_status" value="200" />
106
</label>
107
<label>
108
<span>404</span>
109
-
<input type="radio" name="fallback_status" value="404" />
110
</label>
111
</fieldset>
112
<button>save</button>
···
7
8
let { params, data } = $props();
9
10
+
let rkey = $derived(data.record.uri.split("/").at(-1) ?? "");
11
+
const atp = $derived(client(data.session));
12
13
const defaultDescription = "Uploaded from website";
14
···
29
const form = e.currentTarget;
30
const formdata = new FormData(form);
31
32
let description = formdata.get("description");
33
if (typeof description !== "string" || !description) description = defaultDescription;
34
35
const files = formdata.getAll("files").filter(entry => entry instanceof File);
36
+
const assets = await atp.uploadFiles(files);
37
+
38
+
await atp.updateBundle(rkey, { ...(data.record.value as any), assets });
39
invalidate(`rkey:${rkey}`);
40
form.reset();
41
+
}
42
+
43
+
async function updateBundle(e: SubmitEvent & { currentTarget: HTMLFormElement }) {
44
+
e.preventDefault();
45
+
const form = e.currentTarget;
46
+
const formdata = new FormData(form);
47
+
48
+
// todo: implement me
49
}
50
</script>
51
···
101
<Icon name="toggle" />
102
<span>Settings</span>
103
</h3>
104
+
<form onsubmit={updateBundle}>
105
<fieldset>
106
<legend>Fallback</legend>
107
<label>
108
<span>path</span>
109
+
<input name="fallback_path" group={data.record.value.fallback?.path} />
110
</label>
111
<label>
112
<span>200</span>
113
+
<input
114
+
type="radio"
115
+
name="fallback_status"
116
+
value="200"
117
+
group={data.record.value.fallback?.status}
118
+
/>
119
</label>
120
<label>
121
<span>404</span>
122
+
<input
123
+
type="radio"
124
+
name="fallback_status"
125
+
value="404"
126
+
group={data.record.value.fallback?.status}
127
+
/>
128
</label>
129
</fieldset>
130
<button>save</button>