Initial commit: Music Hub collaboration platform
Full-stack music production collaboration tool with: - SvelteKit frontend with Design System (CSS vars, 8 shared components) - Hono API with auth, projects, tracks, versions, comments - PostgreSQL + Drizzle ORM (8 tables, roles, permissions) - S3-compatible storage with presigned upload URLs - wavesurfer.js audio player with waveform visualization - A/B version comparison with synchronized playback - Timestamped comments with threading and resolve workflow - Magic Link authentication with Resend email integration - Background audio processing (ffmpeg transcode + waveform peaks) - Role-based access control (Owner, Engineers, Artist, Label, Management, Viewer) - Toast notifications, skeleton loading, responsive layout - Docker deployment setup (API + Web + Postgres) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
packages/shared/src/constants/audio.ts
Normal file
24
packages/shared/src/constants/audio.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export const SUPPORTED_AUDIO_FORMATS = [
|
||||
'audio/wav',
|
||||
'audio/x-wav',
|
||||
'audio/mp3',
|
||||
'audio/mpeg',
|
||||
'audio/flac',
|
||||
'audio/x-flac',
|
||||
'audio/aiff',
|
||||
'audio/x-aiff',
|
||||
] as const;
|
||||
|
||||
export const SUPPORTED_EXTENSIONS = ['.wav', '.mp3', '.flac', '.aiff', '.aif'] as const;
|
||||
|
||||
export const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
export const VERSION_STATUSES = [
|
||||
'uploaded',
|
||||
'processing',
|
||||
'ready',
|
||||
'approved',
|
||||
'rejected',
|
||||
] as const;
|
||||
|
||||
export type VersionStatus = (typeof VERSION_STATUSES)[number];
|
||||
3
packages/shared/src/constants/index.ts
Normal file
3
packages/shared/src/constants/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './roles.js';
|
||||
export * from './permissions.js';
|
||||
export * from './audio.js';
|
||||
69
packages/shared/src/constants/permissions.ts
Normal file
69
packages/shared/src/constants/permissions.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type { ProjectRole } from './roles.js';
|
||||
|
||||
export type Permission =
|
||||
| 'project.edit'
|
||||
| 'project.invite'
|
||||
| 'track.upload'
|
||||
| 'track.listen'
|
||||
| 'track.download'
|
||||
| 'version.comment'
|
||||
| 'version.approve';
|
||||
|
||||
const PERMISSIONS: Record<ProjectRole, Permission[]> = {
|
||||
owner: [
|
||||
'project.edit',
|
||||
'project.invite',
|
||||
'track.upload',
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
'version.approve',
|
||||
],
|
||||
recording_engineer: [
|
||||
'track.upload',
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
],
|
||||
mixing_engineer: [
|
||||
'track.upload',
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
],
|
||||
mastering_engineer: [
|
||||
'track.upload',
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
],
|
||||
artist: [
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
'version.approve',
|
||||
],
|
||||
label: [
|
||||
'track.listen',
|
||||
'version.comment',
|
||||
'version.approve',
|
||||
],
|
||||
management: [
|
||||
'project.invite',
|
||||
'track.listen',
|
||||
'track.download',
|
||||
'version.comment',
|
||||
'version.approve',
|
||||
],
|
||||
viewer: [
|
||||
'track.listen',
|
||||
],
|
||||
};
|
||||
|
||||
export function hasPermission(role: ProjectRole, permission: Permission): boolean {
|
||||
return PERMISSIONS[role].includes(permission);
|
||||
}
|
||||
|
||||
export function getPermissions(role: ProjectRole): Permission[] {
|
||||
return PERMISSIONS[role];
|
||||
}
|
||||
29
packages/shared/src/constants/roles.ts
Normal file
29
packages/shared/src/constants/roles.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export const PROJECT_ROLES = [
|
||||
'owner',
|
||||
'recording_engineer',
|
||||
'mixing_engineer',
|
||||
'mastering_engineer',
|
||||
'artist',
|
||||
'label',
|
||||
'management',
|
||||
'viewer',
|
||||
] as const;
|
||||
|
||||
export type ProjectRole = (typeof PROJECT_ROLES)[number];
|
||||
|
||||
export const ROLE_LABELS: Record<ProjectRole, string> = {
|
||||
owner: 'Owner',
|
||||
recording_engineer: 'Recording Engineer',
|
||||
mixing_engineer: 'Mixing Engineer',
|
||||
mastering_engineer: 'Mastering Engineer',
|
||||
artist: 'Artist',
|
||||
label: 'Label',
|
||||
management: 'Management',
|
||||
viewer: 'Viewer',
|
||||
};
|
||||
|
||||
export const ENGINEER_ROLES: ProjectRole[] = [
|
||||
'recording_engineer',
|
||||
'mixing_engineer',
|
||||
'mastering_engineer',
|
||||
];
|
||||
Reference in New Issue
Block a user