c5d3ac01ca
- Group polls with option/rollcall modes, edit by creator, auto-settle - Multimedia memories with upload, preview, inline video playback - In-app notifications for poll/team/group events - Points system and group stats dashboard - Group detail tabs with icons (activity/polls/memories/stats) - Fix: nginx file upload size, static cache blocking API, timezone, auto-cancel Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
import { pb } from './pocketbase'
|
|
import type { Memory } from '@/types'
|
|
|
|
export const GROUP_STORAGE_LIMIT = 10 * 1024 * 1024 * 1024
|
|
|
|
export function detectFileType(file: File): 'image' | 'video' | 'audio' | 'document' | 'other' {
|
|
const mime = file.type || ''
|
|
if (mime.startsWith('image/')) return 'image'
|
|
if (mime.startsWith('video/')) return 'video'
|
|
if (mime.startsWith('audio/')) return 'audio'
|
|
if (
|
|
mime.startsWith('application/pdf') ||
|
|
mime.startsWith('application/msword') ||
|
|
mime.startsWith('application/vnd.') ||
|
|
mime.startsWith('text/')
|
|
) return 'document'
|
|
return 'other'
|
|
}
|
|
|
|
export async function uploadMemory(
|
|
groupId: string,
|
|
file: File,
|
|
meta?: { title?: string; description?: string; tags?: string[] }
|
|
) {
|
|
const used = await getGroupStorageUsed(groupId)
|
|
if (used + file.size > GROUP_STORAGE_LIMIT) {
|
|
throw new Error('群组存储空间不足,无法上传')
|
|
}
|
|
|
|
const user = pb.authStore.model
|
|
const formData = new FormData()
|
|
formData.append('group', groupId)
|
|
formData.append('uploader', user?.id || '')
|
|
formData.append('file', file)
|
|
formData.append('fileType', detectFileType(file))
|
|
formData.append('size', file.size.toString())
|
|
formData.append('title', meta?.title || file.name)
|
|
if (meta?.description) formData.append('description', meta.description)
|
|
|
|
return pb.collection('memories').create(formData)
|
|
}
|
|
|
|
export async function listMemories(
|
|
groupId: string,
|
|
options?: {
|
|
page?: number
|
|
limit?: number
|
|
fileType?: string
|
|
}
|
|
): Promise<{ items: Memory[]; total: number }> {
|
|
const { page = 1, limit = 30, fileType } = options || {}
|
|
let filter = `group="${groupId}"`
|
|
if (fileType) filter += ` && fileType="${fileType}"`
|
|
|
|
const result = await pb.collection('memories').getList(page, limit, {
|
|
filter,
|
|
sort: '-created',
|
|
expand: 'uploader'
|
|
})
|
|
return { items: result.items as unknown as Memory[], total: result.totalItems }
|
|
}
|
|
|
|
export async function deleteMemory(memoryId: string) {
|
|
return pb.collection('memories').delete(memoryId)
|
|
}
|
|
|
|
export async function getGroupStorageUsed(groupId: string): Promise<number> {
|
|
let total = 0
|
|
let page = 1
|
|
const batchSize = 500
|
|
let hasMore = true
|
|
|
|
while (hasMore) {
|
|
const result = await pb.collection('memories').getList(page, batchSize, {
|
|
filter: `group="${groupId}"`,
|
|
fields: 'size'
|
|
})
|
|
total += result.items.reduce((sum: number, r: any) => sum + (r.size || 0), 0)
|
|
hasMore = result.items.length === batchSize && page * batchSize < result.totalItems
|
|
page++
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
export function getGroupStorageLimit(): number {
|
|
return GROUP_STORAGE_LIMIT
|
|
}
|
|
|
|
export function subscribeMemories(
|
|
groupId: string,
|
|
callback: (data: any) => void
|
|
) {
|
|
return pb.collection('memories').subscribe('*', (data) => {
|
|
if (data.record?.group === groupId) {
|
|
callback(data)
|
|
}
|
|
})
|
|
}
|