feat(files): API tokens in Settings, the Hub folder in Files, a notice before a token runs out
Settings gains an API tokens tab: a row per token (name and prefix; each folder with its rights; last use and expiry, "expires soon" marked), revoked from its menu behind a confirm. A new token is the Hub exchange in one click, or folders and rights picked per folder (no Write where an upload cannot land); expiry 30/90 days, 1 year or never (warned). The token shows once in the same dialog, with Copy and a ready curl line. The Hub folder closes the Sources in the Files rail, takes uploads and moved-in files. A token about to run out warns its user once, a week before (the Files GC's sixth pass); the notice opens the tokens tab.
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { Film, Folder, Globe, Upload, type LucideIcon } from "lucide-react";
|
||||
import { Film, Folder, Globe, Server, Upload, type LucideIcon } from "lucide-react";
|
||||
import { YouTubeIcon } from "../lib/brandIcons";
|
||||
import type { FileFolder } from "../lib/api";
|
||||
|
||||
// A library folder's and a file's origin, named and drawn. The backend sends a source folder's KEY
|
||||
// (`youtube`, `plex`, `upload`, `site:<host>`, see backend downloads/folders.py) and leaves
|
||||
// its name to the client, in the reader's language; an own folder carries the name its user gave it.
|
||||
// its name to the client, in the reader's language; an own folder carries the name its user gave it;
|
||||
// the Hub folder (one per user, made by a hub token) is named by the client too.
|
||||
|
||||
const SITE_PREFIX = "site:";
|
||||
|
||||
@@ -24,10 +25,20 @@ export function sourceIcon(key: string): LucideIcon | typeof YouTubeIcon {
|
||||
return Globe;
|
||||
}
|
||||
|
||||
export function folderLabel(folder: FileFolder, t: T): string {
|
||||
type Folderish = Pick<FileFolder, "kind" | "source_key" | "name">;
|
||||
|
||||
export function folderLabel(folder: Folderish, t: T): string {
|
||||
if (folder.kind === "hub") return t("files.folder.hub");
|
||||
return folder.kind === "own" ? (folder.name ?? "") : sourceLabel(folder.source_key ?? "", t);
|
||||
}
|
||||
|
||||
export function folderIcon(folder: FileFolder): LucideIcon | typeof YouTubeIcon {
|
||||
export function folderIcon(folder: Folderish): LucideIcon | typeof YouTubeIcon {
|
||||
if (folder.kind === "hub") return Server;
|
||||
return folder.kind === "own" ? Folder : sourceIcon(folder.source_key ?? "");
|
||||
}
|
||||
|
||||
/** Whether an upload may land in `folder` (the backend's `folders.upload_target`): an own folder,
|
||||
* the Hub folder, or Uploads — a file put in a site's folder would claim an origin it does not have. */
|
||||
export function takesUploads(folder: Folderish): boolean {
|
||||
return folder.kind !== "source" || folder.source_key === "upload";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user