Skip to content

Commit

Permalink
Add support for bunker URIs and nsec.app
Browse files Browse the repository at this point in the history
add support for video embeds in markdown
  • Loading branch information
hzrd149 committed Mar 19, 2024
1 parent d2a4195 commit e17ebc0
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"lodash.throttle": "^4.1.1",
"mime": "^4.0.1",
"nanoid": "^5.0.5",
"nostr-tools": "^2.3.1",
"nostr-tools": "^2.3.2",
"path-browserify": "^1.0.1",
"rehype-highlight": "^7.0.0",
"svelte-codemirror-editor": "^1.3.0",
Expand Down
6 changes: 0 additions & 6 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
import UploadDrawer from "./components/UploadDrawer.svelte";
import Login from "./pages/Login.svelte";
let remember = localStorage.getItem("auto-login") === "true";
$: {
localStorage.setItem("auto-login", remember ? "true" : "false");
}
const routes = {
"/files": Files,
"/hosting": Hosting,
Expand Down
1 change: 0 additions & 1 deletion src/blossom-drive-client/Upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export default class Upload extends EventEmitter {
try {
const blob = await BlossomClient.uploadBlob(server, _file, token);
this.progress[upload.id][server] = { blob };
debugger;
this.drive.setFile(joinPath(this.basePath, upload.path), {
sha256: blob.sha256,
size: blob.size,
Expand Down
26 changes: 14 additions & 12 deletions src/components/LoginPage.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { Alert, Button, Checkbox, Heading, Helper, Input, Label, Spinner } from "flowbite-svelte";
import { InfoCircleSolid } from "flowbite-svelte-icons";
import { loginWithExtension, loginWithNostrAddress } from "../services/ndk";
import { Alert, Button, Checkbox, Helper, Input, Label, Spinner } from "flowbite-svelte";
import { loginWithExtension, loginWithNostrAddress, loginWithPrivateKey } from "../services/ndk";
let loading = false;
let remember = localStorage.getItem("auto-login") === "true";
Expand All @@ -24,7 +23,10 @@
e.preventDefault();
try {
loading = true;
if (address) {
if (address.startsWith("ncryptsec") || address.startsWith("nsec")) {
await loginWithPrivateKey(address);
if (remember) localStorage.setItem("auto-login", "nsec");
} else if (address) {
await loginWithNostrAddress(address);
if (remember) localStorage.setItem("auto-login", address);
}
Expand All @@ -36,23 +38,23 @@
</script>

<div class="flex flex-1 flex-col items-center gap-4">
<h1 class="text-4xl" style="margin-bottom: 20vh; margin-top: 10vh;">🌸 Blossom Drive</h1>
<h1 class="text-4xl" style="margin-bottom: 10vh; margin-top: 10vh;">🌸 Blossom Drive</h1>

{#if loading}
<Spinner />
{:else}
<Alert color="red">
<InfoCircleSolid slot="icon" class="h-4 w-4" />
<span class="font-medium">Warning!</span>
Everything on blossom is public, don't upload private files
<Alert color="red" class="block text-center">
<p class="text-2xl font-medium">Warning!</p>
<p class="text-xl">Everything on blossom drive is public.</p>
<p class="text-xl">DO NOT upload any files you wish to keep private!</p>
</Alert>

<Button size="xl" on:click={extension}>Login with extension</Button>
<p class="text-lg">Or</p>
<form class="min-w-96" on:submit={login}>
<Label for="address" class="mb-2">Nostr Address</Label>
<form on:submit={login}>
<Label for="address" class="mb-2">Nostr Address / Bunker URI</Label>
<div class="flex gap-2">
<Input type="text" id="address" required bind:value={address} />
<Input class="min-w-96" type="text" id="address" required bind:value={address} />
<Button type="submit">Login</Button>
</div>
<Helper color="red" class="mt-1">
Expand Down
6 changes: 3 additions & 3 deletions src/components/TopNav.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts">
import { Button, DarkMode, Avatar, Search, Dropdown, DropdownItem } from "flowbite-svelte";
import { activeUser, logout } from "../services/ndk";
import _throttle from "lodash.throttle";
import { FileSolid } from "flowbite-svelte-icons";
import { searchForFiles, type FileResult } from "../services/search";
import { Name } from "@nostr-dev-kit/ndk-svelte-components";
import { Button, DarkMode, Avatar, Search, Dropdown, DropdownItem } from "flowbite-svelte";
import { activeUser, logout } from "../services/ndk";
import { searchForFiles, type FileResult } from "../services/search";
let search = "";
Expand Down
5 changes: 4 additions & 1 deletion src/pages/Drive/DrivePage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@
let folderInput: HTMLInputElement;
let filesInput: HTMLInputElement;
let showInfoHeader = false;
let showInfoHeader = localStorage.getItem("show-info-header") !== "false";
$: {
localStorage.setItem("show-info-header", String(showInfoHeader));
}
</script>

<main
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Drive/markdown/plugins/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export function createAutocompleteFromDrivePath(getFolder: (path: string) => Tre
try {
let options: string[] = [];

const folder = getFolder(fullPathBefore.replace("%20", " "));
const folder = getFolder(fullPathBefore.replaceAll("%20", " "));
for (const child of folder) options.push(child.name);

return {
from: pathBefore ? nodeBefore.from + pathBefore.index : context.pos,
options: options.map((p) => {
const path = ((pathBefore?.[1] ?? "/") + p).replace(" ", "%20");
const path = ((pathBefore?.[1] ?? "/") + p).replaceAll(" ", "%20");
return { label: path };
}),
validFor: /^(\/.*)?$/,
Expand Down
10 changes: 8 additions & 2 deletions src/pages/Drive/markdown/plugins/components/Img.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { getContext } from "svelte";
import { Img, Spinner } from "flowbite-svelte";
import { Img, Spinner, Video } from "flowbite-svelte";
import mime from "mime";
import { joinPath } from "../../../../../blossom-drive-client/FileTree/methods";
import { servers } from "../../../../../services/servers";
import type Drive from "../../../../../blossom-drive-client/Drive";
Expand All @@ -9,6 +10,7 @@
export let src: string;
export let alt: string;
$: type = mime.getType(src);
let loading = false;
let resolved = src;
Expand All @@ -19,7 +21,9 @@
loading = true;
try {
const subPath = getContext<string>("path");
const fullPath = src.startsWith("/") ? src : joinPath(subPath, src.replace(/^\.\//, ""));
const fullPath = src.startsWith("/")
? src.replaceAll("%20", " ")
: joinPath(subPath, src.replaceAll("%20", " ").replace(/^\.\//, ""));
getLocalFileURL(drive, fullPath, $servers)
.then((url) => (resolved = url))
Expand All @@ -35,6 +39,8 @@

{#if loading}
<Spinner />
{:else if type?.startsWith("video/")}
<Video src={resolved || ""} class="max-h-96" controls />
{:else}
<Img src={resolved || undefined} {alt} class="max-h-96" />
{/if}
4 changes: 2 additions & 2 deletions src/pages/Login.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts">
import { push, params } from "svelte-spa-router";
import { replace, params } from "svelte-spa-router";
import LoginPage from "../components/LoginPage.svelte";
import { activeUser } from "../services/ndk";
$: {
if ($activeUser) push($params?.next || "/");
if ($activeUser) replace($params?.next || "/");
}
</script>

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Servers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@
</TableBody>
</Table>
{:else}
<Alert color="yellow">
<Alert color="yellow" class="text-lg">
<span class="font-medium">Warning!</span>
You need at least one server to upload files. try adding
<a
href="https://cdn.satellite.earth"
class="font-bold hover:underline"
class="font-bold underline"
on:click={(e) => {
e.preventDefault();
server = "https://cdn.satellite.earth";
Expand Down
75 changes: 59 additions & 16 deletions src/services/ndk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import NDKCacheAdapterDexie from "@nostr-dev-kit/ndk-cache-dexie";
import { writable } from "svelte/store";
import type { EventTemplate, SignedEvent } from "blossom-client";
import { NDKEvent, NDKNip07Signer, NDKNip46Signer, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { generateSecretKey } from "nostr-tools";
import { generateSecretKey, nip19 } from "nostr-tools";
import { decrypt } from "nostr-tools/nip49";
import { bytesToHex } from "@noble/hashes/utils";

const cacheAdapter = new NDKCacheAdapterDexie({ dbName: "ndk-cache" });
Expand Down Expand Up @@ -35,19 +36,34 @@ export async function loginWithExtension() {
await fetchUserData();
}

export async function loginWithNostrAddress(address: string) {
const user = await ndk.getUserFromNip05(address);
if (!user?.pubkey) throw new Error("Cant find user");
console.log("Found user", user);

const localKey = localStorage.getItem(user.pubkey + "-local-signer") || bytesToHex(generateSecretKey());
export async function loginWithNostrAddress(connectionString: string) {
const localKey = localStorage.getItem("local-signer") || bytesToHex(generateSecretKey());
const localSigner = new NDKPrivateKeySigner(localKey);

const signer: NDKNip46Signer = new NDKNip46Signer(ndk, address, localSigner);
let signer: NDKNip46Signer;

// manually set remote user and pubkey if using NIP05
if (connectionString.includes("@")) {
const user = await ndk.getUserFromNip05(connectionString);
if (!user?.pubkey) throw new Error("Cant find user");
console.log("Found user", user);

signer = new NDKNip46Signer(ndk, connectionString, localSigner);

signer.remoteUser = user;
signer.remotePubkey = user.pubkey;
} else if (connectionString.startsWith("bunker://")) {
const uri = new URL(connectionString);

// manually set remote user and pubkey
signer.remoteUser = user;
signer.remotePubkey = user.pubkey;
const pubkey = uri.host || uri.pathname.replace("//", "");
const relays = uri.searchParams.getAll("relay");
for (let relay of relays) ndk.addExplicitRelay(relay);
if (relays.length === 0) throw new Error("Missing relays");
signer = new NDKNip46Signer(ndk, pubkey, localSigner);
signer.relayUrls = relays;
} else {
signer = new NDKNip46Signer(ndk, connectionString, localSigner);
}

signer.rpc.on("authUrl", (url: string) => {
window.open(url, "_blank");
Expand All @@ -56,7 +72,27 @@ export async function loginWithNostrAddress(address: string) {
await signer.blockUntilReady();
await signer.user();
ndk.signer = signer;
localStorage.setItem(user.pubkey + "-local-signer", localKey);
localStorage.setItem("local-signer", localSigner.privateKey ?? "");

await fetchUserData();
}

export async function loginWithPrivateKey(key: string) {
if (key.startsWith("ncryptsec")) {
const password = prompt("Enter your private key password");
if (password === null) throw new Error("No password provided");
const plaintext = bytesToHex(decrypt(key, password));
console.log(plaintext);

ndk.signer = new NDKPrivateKeySigner(plaintext);
await ndk.signer.blockUntilReady();
localStorage.setItem("private-key", key);
} else if (key.startsWith("nsec")) {
const decoded = nip19.decode(key);
if (decoded.type !== "nsec") throw new Error("Not nsec");
ndk.signer = new NDKPrivateKeySigner(bytesToHex(decoded.data));
await ndk.signer.blockUntilReady();
} else throw new Error("Unknown private format");

await fetchUserData();
}
Expand Down Expand Up @@ -96,8 +132,15 @@ if (import.meta.env.DEV) {
}

const autoLogin = localStorage.getItem("auto-login");
if (autoLogin === "nip07") {
await loginWithExtension().catch(() => {});
} else if (autoLogin?.includes("@")) {
await loginWithNostrAddress(autoLogin).catch(() => {});
if (autoLogin) {
try {
if (autoLogin === "nip07") {
await loginWithExtension().catch(() => {});
} else if (autoLogin === "nsec") {
const key = localStorage.getItem("private-key");
if (key) await loginWithPrivateKey(key);
} else {
await loginWithNostrAddress(autoLogin).catch(() => {});
}
} catch (e) {}
}
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3866,10 +3866,10 @@ nostr-tools@^1.14.0, nostr-tools@^1.15.0:
"@scure/bip32" "1.3.1"
"@scure/bip39" "1.2.1"

nostr-tools@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.3.1.tgz#348d3c4aab0ab00716f93d6e2a72333d8c7da982"
integrity sha512-qjKx2C3EzwiQOe2LPSPyCnp07pGz1pWaWjDXcm+L2y2c8iTECbvlzujDANm3nJUjWL5+LVRUVDovTZ1a/DC4Bg==
nostr-tools@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.3.2.tgz#74e92898b000413661b091c5829f762cb4420e66"
integrity sha512-8ceZ2ItkAGjR5b9+QOkkV9KWBOK0WPlpFrPPXmbWnNMcnlj9zB7rjdYPK2sV/OK4Ty9J3xL6+bvYKY77gup5EQ==
dependencies:
"@noble/ciphers" "^0.5.1"
"@noble/curves" "1.2.0"
Expand Down

0 comments on commit e17ebc0

Please sign in to comment.