fix(upload): restrict cross-user private file access (UPLOAD-1)

- Add access_mode column to w_uploads table (0 = private, 1 = public) and initialize data in a single migration script
- Enforce strict ownership check for private files during download
- Allow public files to follow whitelisted public-access rules
- Default access_mode to public for avatars and private for generic uploads
- Update frontend service to support optional accessMode parameter
This commit is contained in:
ryan
2026-06-13 10:13:41 +08:00
parent 5412c385dc
commit 7b379863b4
11 changed files with 190 additions and 12 deletions
@@ -46,7 +46,8 @@ export class UploadService extends BaseService {
static async uploadFile(
file: File,
type: string = 'generic',
metadata?: Record<string, unknown>
metadata?: Record<string, unknown>,
accessMode?: number
): Promise<Upload> {
const formData = new FormData()
formData.append('file', file)
@@ -54,6 +55,9 @@ export class UploadService extends BaseService {
if (metadata) {
formData.append('metadata', JSON.stringify(metadata))
}
if (accessMode !== undefined) {
formData.append('access_mode', String(accessMode))
}
return this.post<Upload>('', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
@@ -113,13 +117,14 @@ export class UploadService extends BaseService {
static async uploadBase64Image(
base64: string,
type: string = 'generic',
filename: string = 'image.png'
filename: string = 'image.png',
accessMode?: number
): Promise<UploadImageResponse> {
const response = await fetch(base64)
const blob = await response.blob()
const mimeType = base64.match(/data:([^;]+);/)?.[1] || 'image/png'
const file = new File([blob], filename, { type: mimeType })
const result = await this.uploadFile(file, type)
const result = await this.uploadFile(file, type, undefined, accessMode)
return { id: result.id }
}
}