diff --git a/svelte/src/filesystem/ErrorPage.svelte b/svelte/src/filesystem/ErrorPage.svelte index 07fe19c..2a76ccc 100644 --- a/svelte/src/filesystem/ErrorPage.svelte +++ b/svelte/src/filesystem/ErrorPage.svelte @@ -1,5 +1,6 @@ {#snippet breadcrumbs()} @@ -29,6 +40,23 @@ onMount(() => {
This page could not be found.
+ {:else if $nav.navigation_error === "password_required"} ++ This directory is protected with a password. +
+ + {#if wrong_password} +This password is not correct.
+ {/if} {:else if $nav.navigation_error === "permission_denied" || $nav.navigation_error === "forbidden"}
diff --git a/svelte/src/filesystem/FSNavigator.ts b/svelte/src/filesystem/FSNavigator.ts
index 728c22d..76b9dea 100644
--- a/svelte/src/filesystem/FSNavigator.ts
+++ b/svelte/src/filesystem/FSNavigator.ts
@@ -45,8 +45,8 @@ export class FSNavigator {
last_requested_path: string = ""
navigation_error: string = ""
- navigate = async (path: string, push_history: boolean) => {
- if (path === this.last_requested_path) {
+ navigate = async (path: string, push_history: boolean, password?: string) => {
+ if (path === this.last_requested_path && password === undefined) {
console.debug("FSNavigator: Requested path ", path, " is equal to current path. Debouncing")
return
}
@@ -56,6 +56,18 @@ export class FSNavigator {
path = "/" + path
}
+ // The password of a shared directory is entered by the visitor, or it's
+ // in the link they opened. It's sent in a cookie, which is the only way
+ // to get it into the requests for images, videos and downloads. The
+ // cookie's path is the directory, so each directory has its own password
+ if (password === undefined && this.history_enabled) {
+ password = new URLSearchParams(window.location.search).get("password")
+ }
+ if (password) {
+ document.cookie = "nova_fs_password=" + encodeURIComponent(password) +
+ "; path=" + fs_path_url(path.split("/")[1]) + "; samesite=lax"
+ }
+
console.debug("FSNavigator: Navigating to path", path, push_history)
try {
@@ -70,9 +82,9 @@ export class FSNavigator {
this.open_node(resp, push_history)
} catch (err: any) {
if (err.value !== undefined && err.value === "path_not_found") {
- if (path !== this.path[0].path && path !== "/" && path !== "") {
+ if (path !== this.path[0]?.path && path !== "/" && path !== "") {
console.debug("Path", path, "was not found, trying to navigate to parent")
- this.navigate(fs_split_path(path).parent, push_history)
+ this.navigate(fs_split_path(path).parent, push_history, password)
}
} else if (err.value !== undefined) {
this.navigation_error = err.value
diff --git a/svelte/src/filesystem/Filesystem.svelte b/svelte/src/filesystem/Filesystem.svelte
index 6cec240..9cf8046 100644
--- a/svelte/src/filesystem/Filesystem.svelte
+++ b/svelte/src/filesystem/Filesystem.svelte
@@ -58,13 +58,7 @@ onMount(() => {
const keydown = (e: KeyboardEvent) => {
if (e.ctrlKey || e.altKey || e.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts
- } else if (
- (document.activeElement as any).type !== undefined &&
- (
- (document.activeElement as any).type === "text" ||
- (document.activeElement as any).type === "textarea"
- )
- ) {
+ } else if (["text", "password", "textarea"].includes((document.activeElement as any).type)) {
return // Prevent shortcuts from interfering with input fields
}
diff --git a/svelte/src/user_home/AccountSettings.svelte b/svelte/src/user_home/AccountSettings.svelte
index 1a63b64..423d58e 100644
--- a/svelte/src/user_home/AccountSettings.svelte
+++ b/svelte/src/user_home/AccountSettings.svelte
@@ -9,7 +9,7 @@ import { user } from "lib/UserStore";
let affiliate_link = $derived(
window.location.protocol+"//"+window.location.host +
- "?ref=" + encodeURIComponent($user.username)
+ "/user?ref=" + encodeURIComponent($user.username)
)
let affiliate_deny = $state(false)
onMount(() => {
diff --git a/svelte/src/user_home/AffiliatePrompt.svelte b/svelte/src/user_home/AffiliatePrompt.svelte
index 1a4308d..e8c3339 100644
--- a/svelte/src/user_home/AffiliatePrompt.svelte
+++ b/svelte/src/user_home/AffiliatePrompt.svelte
@@ -74,10 +74,10 @@ const deny = () => {
- Hi! {referral} wants you to sponsor their Nova account. This
- will give them €0.50 every month in Nova prepaid credit. They
- can use this credit to get a discount on their file storage and
- sharing costs. Here is a short summary of what this entails:
+ Hi! {referral} wants you to sponsor their Nova account. This will
+ give them €0.01 every day in Nova prepaid credit. They can use this
+ credit to get a discount on their file storage and sharing costs.
+ Here is a short summary of what this entails:
If you click 'Accept' then the requested affiliate code will be
diff --git a/webcontroller/filesystem.go b/webcontroller/filesystem.go
index 4ada746..15a98fe 100644
--- a/webcontroller/filesystem.go
+++ b/webcontroller/filesystem.go
@@ -32,8 +32,13 @@ func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request,
td.User = json.RawMessage("null")
}
+ // A shared link can have the password of the directory in it
var path = strings.TrimPrefix(p.ByName("path"), "/")
- var err = pdapi.GetJSON("filesystem/"+url.PathEscape(path)+"?stat", &td.Node)
+ var err = pdapi.GetJSON(
+ "filesystem/"+url.PathEscape(path)+"?stat&password="+
+ url.QueryEscape(r.URL.Query().Get("password")),
+ &td.Node,
+ )
if apiErr, ok := errors.AsType[pixelapi.Error](err); ok {
// Set the proper response code for the error message
switch apiErr.StatusCode {
diff --git a/webcontroller/opengraph.go b/webcontroller/opengraph.go
index cadd9b4..c24f971 100644
--- a/webcontroller/opengraph.go
+++ b/webcontroller/opengraph.go
@@ -124,12 +124,20 @@ func (wc *WebController) metadataFromFilesystem(r *http.Request, f api.Filesyste
}
}
+ // The services which make link previews don't keep cookies, so a password
+ // in the link has to be in every URL they open
+ var query, thumbnail = "", "?thumbnail"
+ if password := r.URL.Query().Get("password"); password != "" {
+ password = "password=" + url.QueryEscape(password)
+ query, thumbnail = "?"+password, thumbnail+"&"+password
+ }
+
return generateOGData(
base.Name,
base.FileType,
- addr+"/d"+filepath,
- addr+"/api/filesystem"+filepath,
- addr+"/api/filesystem"+filepath+"?thumbnail",
+ addr+"/d"+filepath+query,
+ addr+"/api/filesystem"+filepath+query,
+ addr+"/api/filesystem"+filepath+thumbnail,
colour,
)
}
diff --git a/webcontroller/opengraph_test.go b/webcontroller/opengraph_test.go
new file mode 100644
index 0000000..49c8420
--- /dev/null
+++ b/webcontroller/opengraph_test.go
@@ -0,0 +1,24 @@
+package webcontroller
+
+import (
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "fornaxian.tech/nova_server/api"
+)
+
+// The services which make link previews have no cookies, so a password in the
+// link has to be in the URLs they open
+func TestMetadataPassword(t *testing.T) {
+ var r = httptest.NewRequest("GET", "/d/abc/a%20b.png?password=p%26w+1", nil)
+ var og = (&WebController{}).metadataFromFilesystem(r, api.FilesystemPath{
+ Path: []api.FilesystemNode{{Path: "/abc/a b.png", FileType: "image/png"}},
+ })
+ for _, prop := range og.MetaPropRules {
+ if (prop.Key == "og:url" || prop.Key == "og:image") &&
+ !strings.HasSuffix(prop.Value, "/abc/a%20b.png?password=p%26w+1") {
+ t.Errorf("%s is %s", prop.Key, prop.Value)
+ }
+ }
+}