Skip to content

Commit 9e40cf3

Browse files
authored
workshop 3: attachments (#3)
1 parent b14b924 commit 9e40cf3

File tree

8 files changed

+200
-0
lines changed

8 files changed

+200
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ dist
102102

103103
# TernJS port file
104104
.tern-port
105+
106+
# Ignore share data directory from attachments workshop
107+
03-attachments/share-data

03-attachments/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Instructions
2+
3+
1. Start the server using Deno: `deno run --allow-all server.ts`
4+
5+
2. Start the client server e.g. `npx live-server .`

03-attachments/app.js

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as Earthstar from "https://cdn.earthstar-project.org/js/earthstar.web.v10.0.0-rc.1.js";
2+
3+
// Earthstar stuff
4+
const settings = new Earthstar.SharedSettings();
5+
6+
async function setupSettings() {
7+
const author = await Earthstar.Crypto.generateAuthorKeypair("chou");
8+
9+
settings.author = author;
10+
11+
const shareKeypair = await Earthstar.Crypto.generateShareKeypair(
12+
"attachments"
13+
);
14+
15+
settings.addShare(shareKeypair.shareAddress);
16+
const result = await settings.addSecret(
17+
shareKeypair.shareAddress,
18+
shareKeypair.secret
19+
);
20+
21+
console.log("SECRET", result);
22+
}
23+
24+
// await setupSettings();
25+
26+
const shareAddress = settings.shares[0];
27+
28+
const shareSecret = settings.shareSecrets[shareAddress];
29+
30+
const replica = new Earthstar.Replica({
31+
driver: new Earthstar.ReplicaDriverWeb(shareAddress),
32+
shareSecret: settings.shareSecrets[shareAddress],
33+
});
34+
35+
// DOM Stuff
36+
const fileInput = document.querySelector('input[type="file"]');
37+
const uploadMessage = document.getElementById("upload-result");
38+
const downloadUrl = document.getElementById("download-url");
39+
40+
fileInput.addEventListener("change", async (event) => {
41+
event.preventDefault();
42+
43+
const file = event.target.files[0];
44+
45+
const timestamp = Date.now();
46+
47+
const extension = file.name.split(".")[1];
48+
49+
// TODO: use a proper hash ideally
50+
const path = `/uploads/!${timestamp}.${extension}`;
51+
52+
console.log(path);
53+
54+
const result = await replica.set(settings.author, {
55+
path,
56+
text: `This was uploaded by ANDREWWWW`,
57+
attachment: file.stream(),
58+
// 48 hours
59+
deleteAfter: Date.now() * 1000 + 172_800_000_000,
60+
});
61+
62+
if (Earthstar.isErr(result)) {
63+
uploadMessage.textContent = "Failed to upload";
64+
} else {
65+
uploadMessage.textContent = "Successfully uploaded!";
66+
}
67+
68+
// console.log(result);
69+
70+
const peer = new Earthstar.Peer();
71+
72+
peer.addReplica(replica);
73+
74+
const syncer = peer.sync("http://localhost:8000");
75+
76+
// syncer.onStatusChange((newStatus) => {
77+
// console.log("STATUS", newStatus)
78+
// })
79+
80+
await syncer.isDone();
81+
82+
console.log("SYNCED!!!");
83+
84+
const link = `http://localhost:8000/downloads/${timestamp}.${extension}`;
85+
downloadUrl.href = link;
86+
downloadUrl.textContent = `Download image at ${link}`;
87+
});

03-attachments/index.html

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
7+
<title>Earthstar Attachments App Thingy</title>
8+
<meta name="description" content="Fun stuff with Earthstar attachments">
9+
<meta name="viewport" content="width=device-width, initial-scale=1">
10+
<link rel="stylesheet" href="./styles.css">
11+
<style></style>
12+
<!-- <script src="./earthstar.bundle.js" type="module"> -->
13+
</script>
14+
</head>
15+
16+
17+
<body>
18+
19+
<input type="file" />
20+
21+
<p id="upload-result"></p>
22+
<a id="download-url" />
23+
24+
25+
<script src="./app.js" type="module" async defer></script>
26+
</body>
27+
28+
</html>

03-attachments/known-shares.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["+attachments.bww37ungbbclwmnhf3ll7mfvy2vxc6ehv6djokswwugftfu2l3mna"]

03-attachments/server.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { contentType } from "https://deno.land/std@0.167.0/media_types/mod.ts";
2+
import { extname } from "https://deno.land/std@0.154.0/path/mod.ts";
3+
import * as Earthstar from "https://deno.land/x/earthstar@v10.0.0-rc.1/mod.ts";
4+
5+
class AttachmentsViewerExtension implements Earthstar.IServerExtension {
6+
private replica: Earthstar.Replica | null | undefined = null;
7+
8+
register(peer: Earthstar.Peer): Promise<void> {
9+
this.replica = peer.getReplica(
10+
"+attachments.bww37ungbbclwmnhf3ll7mfvy2vxc6ehv6djokswwugftfu2l3mna"
11+
);
12+
13+
return Promise.resolve();
14+
}
15+
16+
async handler(req: Request): Promise<Response | null> {
17+
if (!this.replica) {
18+
return Promise.resolve(null);
19+
}
20+
21+
const pattern = new URLPattern({
22+
pathname: "/downloads/:filename",
23+
});
24+
25+
const match = pattern.exec(req.url);
26+
27+
if (!match) {
28+
return Promise.resolve(null);
29+
}
30+
31+
const { filename } = match.pathname.groups;
32+
33+
const documentPath = `/uploads/!${filename}`;
34+
35+
const doc = await this.replica.getLatestDocAtPath(documentPath);
36+
37+
if (!doc) {
38+
return Promise.resolve(null);
39+
}
40+
41+
const attachment = await this.replica.getAttachment(doc);
42+
43+
if (Earthstar.isErr(attachment) || !attachment) {
44+
return new Response("No attachment found", { status: 404 });
45+
}
46+
47+
const extension = extname(documentPath);
48+
49+
return new Response(await attachment.stream(), {
50+
headers: {
51+
"content-type": contentType(extension) || "plain/text",
52+
},
53+
});
54+
}
55+
}
56+
57+
new Earthstar.Server([
58+
new Earthstar.ExtensionKnownShares({
59+
knownSharesPath: "./known-shares.json",
60+
onCreateReplica: (shareAddress: string) => {
61+
return new Earthstar.Replica({
62+
driver: new Earthstar.ReplicaDriverFs(
63+
shareAddress,
64+
`./share-data/${shareAddress}`
65+
),
66+
});
67+
},
68+
}),
69+
new AttachmentsViewerExtension(),
70+
new Earthstar.ExtensionSyncWeb(),
71+
]);

03-attachments/styles.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* {
2+
box-sizing: border-box;
3+
}

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ Apps and examples derived from Earthstar workshops
55
- 2022-09-30: [Vanilla Chat](./01-vanilla-chat/) - _a basic chat application that doesn't do much_
66

77
- 2022-10-21: [Vanilla Chat (continued)](./02-vanilla-chat-continued/) - _continuation of workshop 1 where we added actual chat features_
8+
9+
- 2022-12-16: [Attachment sharing](./03-attachments/) - _a media sharing application using Earthstar's attachments functionality_

0 commit comments

Comments
 (0)