diff --git a/files/file.go b/files/file.go index 569b0be4..2d96d4c5 100644 --- a/files/file.go +++ b/files/file.go @@ -322,7 +322,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { continue } - isSymlink := false + isSymlink, isInvalidLink := false, false if IsSymlink(f.Mode()) { isSymlink = true // It's a symbolic link. We try to follow it. If it doesn't work, @@ -330,6 +330,8 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { info, err := i.Fs.Stat(fPath) if err == nil { f = info + } else { + isInvalidLink = true } } @@ -350,9 +352,13 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { } else { listing.NumFiles++ - err := file.detectType(true, false, readHeader) - if err != nil { - return err + if isInvalidLink { + file.Type = "invalid_link" + } else { + err := file.detectType(true, false, readHeader) + if err != nil { + return err + } } } diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js index 7494e55b..7b885cfa 100644 --- a/frontend/src/api/files.js +++ b/frontend/src/api/files.js @@ -1,4 +1,4 @@ -import { fetchURL, removePrefix } from "./utils"; +import { fetchURL, removePrefix, createURL } from "./utils"; import { baseURL } from "@/utils/constants"; import store from "@/store"; @@ -7,28 +7,24 @@ export async function fetch(url) { const res = await fetchURL(`/api/resources${url}`, {}); - if (res.status === 200) { - let data = await res.json(); - data.url = `/files${url}`; + let data = await res.json(); + data.url = `/files${url}`; - if (data.isDir) { - if (!data.url.endsWith("/")) data.url += "/"; - data.items = data.items.map((item, index) => { - item.index = index; - item.url = `${data.url}${encodeURIComponent(item.name)}`; + if (data.isDir) { + if (!data.url.endsWith("/")) data.url += "/"; + data.items = data.items.map((item, index) => { + item.index = index; + item.url = `${data.url}${encodeURIComponent(item.name)}`; - if (item.isDir) { - item.url += "/"; - } + if (item.isDir) { + item.url += "/"; + } - return item; - }); - } - - return data; - } else { - throw new Error(res.status); + return item; + }); } + + return data; } async function resourceAction(url, method, content) { @@ -42,11 +38,7 @@ async function resourceAction(url, method, content) { const res = await fetchURL(`/api/resources${url}`, opts); - if (res.status !== 200) { - throw new Error(await res.text()); - } else { - return res; - } + return res; } export async function remove(url) { @@ -119,8 +111,8 @@ export async function post(url, content = "", overwrite = false, onupload) { } }; - request.onerror = (error) => { - reject(error); + request.onerror = () => { + reject(new Error("001 Connection aborted")); }; request.send(bufferContent || content); @@ -154,3 +146,33 @@ export async function checksum(url, algo) { const data = await resourceAction(`${url}?checksum=${algo}`, "GET"); return (await data.json()).checksums[algo]; } + +export function getDownloadURL(file, inline) { + const params = { + ...(inline && { inline: "true" }), + }; + + return createURL("api/raw" + file.path, params); +} + +export function getPreviewURL(file, size) { + const params = { + inline: "true", + key: Date.parse(file.modified), + }; + + return createURL("api/preview/" + size + file.path, params); +} + +export function getSubtitlesURL(file) { + const params = { + inline: "true", + }; + + const subtitles = []; + for (const sub of file.subtitles) { + subtitles.push(createURL("api/raw" + sub, params)); + } + + return subtitles; +} diff --git a/frontend/src/api/pub.js b/frontend/src/api/pub.js index 58eb1eb6..1511143d 100644 --- a/frontend/src/api/pub.js +++ b/frontend/src/api/pub.js @@ -1,35 +1,35 @@ -import { fetchURL, removePrefix } from "./utils"; +import { fetchURL, removePrefix, createURL } from "./utils"; import { baseURL } from "@/utils/constants"; export async function fetch(url, password = "") { url = removePrefix(url); - const res = await fetchURL(`/api/public/share${url}`, { - headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) }, - }); + const res = await fetchURL( + `/api/public/share${url}`, + { + headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) }, + }, + false + ); - if (res.status === 200) { - let data = await res.json(); - data.url = `/share${url}`; + let data = await res.json(); + data.url = `/share${url}`; - if (data.isDir) { - if (!data.url.endsWith("/")) data.url += "/"; - data.items = data.items.map((item, index) => { - item.index = index; - item.url = `${data.url}${encodeURIComponent(item.name)}`; + if (data.isDir) { + if (!data.url.endsWith("/")) data.url += "/"; + data.items = data.items.map((item, index) => { + item.index = index; + item.url = `${data.url}${encodeURIComponent(item.name)}`; - if (item.isDir) { - item.url += "/"; - } + if (item.isDir) { + item.url += "/"; + } - return item; - }); - } - - return data; - } else { - throw new Error(res.status); + return item; + }); } + + return data; } export function download(format, hash, token, ...files) { @@ -59,3 +59,12 @@ export function download(format, hash, token, ...files) { window.open(url); } + +export function getDownloadURL(share, inline = false) { + const params = { + ...(inline && { inline: "true" }), + ...(share.token && { token: share.token }), + }; + + return createURL("api/public/dl/" + share.hash + share.path, params, false); +} diff --git a/frontend/src/api/search.js b/frontend/src/api/search.js index 08be5c1d..42846880 100644 --- a/frontend/src/api/search.js +++ b/frontend/src/api/search.js @@ -11,21 +11,17 @@ export default async function search(base, query) { let res = await fetchURL(`/api/search${base}?query=${query}`, {}); - if (res.status === 200) { - let data = await res.json(); + let data = await res.json(); - data = data.map((item) => { - item.url = `/files${base}` + url.encodePath(item.path); + data = data.map((item) => { + item.url = `/files${base}` + url.encodePath(item.path); - if (item.dir) { - item.url += "/"; - } + if (item.dir) { + item.url += "/"; + } - return item; - }); + return item; + }); - return data; - } else { - throw Error(res.status); - } + return data; } diff --git a/frontend/src/api/settings.js b/frontend/src/api/settings.js index 8abe1f1e..e03b0db1 100644 --- a/frontend/src/api/settings.js +++ b/frontend/src/api/settings.js @@ -5,12 +5,8 @@ export function get() { } export async function update(settings) { - const res = await fetchURL(`/api/settings`, { + await fetchURL(`/api/settings`, { method: "PUT", body: JSON.stringify(settings), }); - - if (res.status !== 200) { - throw new Error(res.status); - } } diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js index 54bbc460..1ac4473a 100644 --- a/frontend/src/api/share.js +++ b/frontend/src/api/share.js @@ -1,4 +1,4 @@ -import { fetchURL, fetchJSON, removePrefix } from "./utils"; +import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils"; export async function list() { return fetchJSON("/api/shares"); @@ -10,13 +10,9 @@ export async function get(url) { } export async function remove(hash) { - const res = await fetchURL(`/api/share/${hash}`, { + await fetchURL(`/api/share/${hash}`, { method: "DELETE", }); - - if (res.status !== 200) { - throw new Error(res.status); - } } export async function create(url, password = "", expires = "", unit = "hours") { @@ -34,3 +30,7 @@ export async function create(url, password = "", expires = "", unit = "hours") { body: body, }); } + +export function getShareURL(share) { + return createURL("share/" + share.hash, {}, false); +} diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js index 7975d66a..105d6cc0 100644 --- a/frontend/src/api/users.js +++ b/frontend/src/api/users.js @@ -20,13 +20,11 @@ export async function create(user) { if (res.status === 201) { return res.headers.get("Location"); - } else { - throw new Error(res.status); } } export async function update(user, which = ["all"]) { - const res = await fetchURL(`/api/users/${user.id}`, { + await fetchURL(`/api/users/${user.id}`, { method: "PUT", body: JSON.stringify({ what: "user", @@ -34,18 +32,10 @@ export async function update(user, which = ["all"]) { data: user, }), }); - - if (res.status !== 200) { - throw new Error(res.status); - } } export async function remove(id) { - const res = await fetchURL(`/api/users/${id}`, { + await fetchURL(`/api/users/${id}`, { method: "DELETE", }); - - if (res.status !== 200) { - throw new Error(res.status); - } } diff --git a/frontend/src/api/utils.js b/frontend/src/api/utils.js index 65c6740a..ddddcfa7 100644 --- a/frontend/src/api/utils.js +++ b/frontend/src/api/utils.js @@ -1,8 +1,9 @@ import store from "@/store"; -import { renew } from "@/utils/auth"; +import { renew, logout } from "@/utils/auth"; import { baseURL } from "@/utils/constants"; +import { encodePath } from "@/utils/url"; -export async function fetchURL(url, opts) { +export async function fetchURL(url, opts, auth = true) { opts = opts || {}; opts.headers = opts.headers || {}; @@ -17,14 +18,28 @@ export async function fetchURL(url, opts) { }, ...rest, }); - } catch (error) { - return { status: 0 }; + } catch { + const error = new Error("000 No connection"); + error.status = 0; + + throw error; } - if (res.headers.get("X-Renew-Token") === "true") { + if (auth && res.headers.get("X-Renew-Token") === "true") { await renew(store.state.jwt); } + if (res.status < 200 || res.status > 299) { + const error = new Error(await res.text()); + error.status = res.status; + + if (auth && res.status == 401) { + logout(); + } + + throw error; + } + return res; } @@ -45,3 +60,18 @@ export function removePrefix(url) { if (url[0] !== "/") url = "/" + url; return url; } + +export function createURL(endpoint, params = {}, auth = true) { + const url = new URL(encodePath(endpoint), origin + baseURL); + + const searchParams = { + ...(auth && { auth: store.state.jwt }), + ...params, + }; + + for (const key in searchParams) { + url.searchParams.set(key, searchParams[key]); + } + + return url.toString(); +} diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index 351fba1f..b2853fcb 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -35,7 +35,7 @@