feat: Archipelago public channel (Tor), FileBrowser auto-login

Public Channel:
- "Archipelago" channel in Mesh — broadcasts to all federation peers over Tor
- Shows received messages from all peers with pubkey label
- Auto-polls every 15s for new messages
- Orange-branded channel icon with unread badge
- Send handler routes to Tor broadcast when arch channel is active

FileBrowser Auto-Login:
- All filebrowser-client methods now call ensureAuth() before requests
- Auto-authenticates with default credentials if not logged in
- Fixes "files don't work when FileBrowser hasn't been logged into"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-19 22:24:27 +00:00
parent c5417640a2
commit c4853fe746
2 changed files with 103 additions and 4 deletions

View File

@@ -73,7 +73,15 @@ class FileBrowserClient {
return h
}
/** Ensure we're authenticated before making a request. Auto-logins if needed. */
private async ensureAuth(): Promise<void> {
if (this.token) return
const ok = await this.login()
if (!ok) throw new Error('FileBrowser authentication failed — please open Cloud to log in')
}
async listDirectory(path: string): Promise<FileBrowserItem[]> {
await this.ensureAuth()
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
headers: this.headers(),
@@ -100,6 +108,7 @@ class FileBrowserClient {
* Use this for img/video/audio src attributes and download links.
*/
async fetchBlobUrl(path: string): Promise<string> {
await this.ensureAuth()
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/raw${safePath}`, {
headers: this.headers(),
@@ -125,6 +134,7 @@ class FileBrowserClient {
}
async upload(dirPath: string, file: File): Promise<void> {
await this.ensureAuth()
const sanitized = sanitizePath(dirPath)
const safePath = sanitized.endsWith('/') ? sanitized : `${sanitized}/`
const encodedName = encodeURIComponent(file.name)
@@ -143,6 +153,7 @@ class FileBrowserClient {
}
async createFolder(parentPath: string, name: string): Promise<void> {
await this.ensureAuth()
const sanitized = sanitizePath(parentPath)
const safePath = sanitized.endsWith('/') ? sanitized : `${sanitized}/`
const sanitizedName = name.replace(/\.\./g, '').replace(/\//g, '')
@@ -154,6 +165,7 @@ class FileBrowserClient {
}
async deleteItem(path: string): Promise<void> {
await this.ensureAuth()
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
method: 'DELETE',