refactor: update dependencies and remove unused code

- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`.
- Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27.
- Removed the `backup.rs` file as it is no longer needed.
- Introduced tests for configuration and credential management.
- Enhanced the `identity` module to generate W3C compliant DID documents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-12 00:19:30 +00:00
co-authored by Claude Opus 4.6
parent 2a867b32a8
commit 6fee6befed
347 changed files with 18703 additions and 46785 deletions
+69 -11
View File
@@ -15,6 +15,26 @@ interface FileBrowserListResponse {
sorting: { by: string; asc: boolean }
}
/**
* Normalize a path: resolve `.` and `..`, reject traversal outside root.
* Always returns a path starting with `/` and never containing `..`.
*/
export function sanitizePath(path: string): string {
const segments = path.split('/').filter(Boolean)
const resolved: string[] = []
for (const seg of segments) {
if (seg === '.') continue
if (seg === '..') {
resolved.pop() // go up one level, but never past root
} else {
resolved.push(seg)
}
}
return '/' + resolved.join('/')
}
class FileBrowserClient {
private token: string | null = null
private baseUrl: string
@@ -38,6 +58,8 @@ class FileBrowserClient {
const text = await res.text()
// FileBrowser returns the JWT as a plain string (possibly quoted)
this.token = text.replace(/^"|"$/g, '')
// Store token as cookie for img/video/audio src requests (avoids token in URL)
document.cookie = `auth=${this.token}; path=/app/filebrowser; SameSite=Strict`
return true
} catch {
return false
@@ -51,7 +73,7 @@ class FileBrowserClient {
}
async listDirectory(path: string): Promise<FileBrowserItem[]> {
const safePath = path.startsWith('/') ? path : `/${path}`
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
headers: this.headers(),
})
@@ -63,14 +85,47 @@ class FileBrowserClient {
}))
}
/**
* @deprecated Use fetchBlobUrl() instead to avoid exposing tokens in URLs.
* Returns a plain URL (no token in query string).
*/
downloadUrl(path: string): string {
const safePath = path.startsWith('/') ? path : `/${path}`
// Token is passed as query param for direct downloads (img src, audio src, etc.)
return `${this.baseUrl}/api/raw${safePath}?auth=${this.token}`
const safePath = sanitizePath(path)
return `${this.baseUrl}/api/raw${safePath}`
}
/**
* Fetch a file as a blob URL using header-based auth (no token in URL).
* Use this for img/video/audio src attributes and download links.
*/
async fetchBlobUrl(path: string): Promise<string> {
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/raw${safePath}`, {
headers: this.headers(),
})
if (!res.ok) throw new Error(`Failed to fetch file: ${res.status}`)
const blob = await res.blob()
return URL.createObjectURL(blob)
}
/**
* Trigger a file download using header-based auth (no token in URL).
*/
async downloadFile(path: string): Promise<void> {
const blobUrl = await this.fetchBlobUrl(path)
const filename = path.split('/').pop() || 'download'
const a = document.createElement('a')
a.href = blobUrl
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(blobUrl)
}
async upload(dirPath: string, file: File): Promise<void> {
const safePath = dirPath.endsWith('/') ? dirPath : `${dirPath}/`
const sanitized = sanitizePath(dirPath)
const safePath = sanitized.endsWith('/') ? sanitized : `${sanitized}/`
const encodedName = encodeURIComponent(file.name)
const res = await fetch(
`${this.baseUrl}/api/resources${safePath}${encodedName}?override=true`,
@@ -87,8 +142,10 @@ class FileBrowserClient {
}
async createFolder(parentPath: string, name: string): Promise<void> {
const safePath = parentPath.endsWith('/') ? parentPath : `${parentPath}/`
const res = await fetch(`${this.baseUrl}/api/resources${safePath}${name}/`, {
const sanitized = sanitizePath(parentPath)
const safePath = sanitized.endsWith('/') ? sanitized : `${sanitized}/`
const sanitizedName = name.replace(/\.\./g, '').replace(/\//g, '')
const res = await fetch(`${this.baseUrl}/api/resources${safePath}${sanitizedName}/`, {
method: 'POST',
headers: this.headers(),
})
@@ -96,7 +153,7 @@ class FileBrowserClient {
}
async deleteItem(path: string): Promise<void> {
const safePath = path.startsWith('/') ? path : `/${path}`
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
method: 'DELETE',
headers: this.headers(),
@@ -142,7 +199,7 @@ class FileBrowserClient {
if (!this.isTextFile(path)) {
throw new Error(`Cannot read binary file: ${path}`)
}
const safePath = path.startsWith('/') ? path : `/${path}`
const safePath = sanitizePath(path)
const res = await fetch(`${this.baseUrl}/api/raw${safePath}`, {
headers: this.headers(),
})
@@ -156,15 +213,16 @@ class FileBrowserClient {
}
async rename(oldPath: string, newName: string): Promise<void> {
const safePath = oldPath.startsWith('/') ? oldPath : `/${oldPath}`
const safePath = sanitizePath(oldPath)
const dir = safePath.substring(0, safePath.lastIndexOf('/') + 1)
const sanitizedName = newName.replace(/\.\./g, '').replace(/\//g, '')
const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
method: 'PATCH',
headers: {
...this.headers(),
'Content-Type': 'application/json',
},
body: JSON.stringify({ destination: `${dir}${newName}` }),
body: JSON.stringify({ destination: `${dir}${sanitizedName}` }),
})
if (!res.ok) throw new Error(`Rename failed: ${res.status}`)
}