mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 18:26:42 +02:00
feat(manacore/web): unified time model — timeBlocks for all time data
Introduces a central `timeBlocks` table that owns the time dimension (start, end, recurrence, live status) for all modules. Calendar, times, habits, and todo modules keep only domain-specific data with a timeBlockId reference. The calendar becomes a universal time view showing events, tasks, habits, and time entries from all modules. Key changes: - New `$lib/data/time-blocks/` module (types, service, queries, collections) - Dexie schema v3 with timeBlocks table + migration from existing data - Calendar events store creates TimeBlock + LocalEvent pairs - Times timer uses TimeBlock.isLive instead of LocalTimeEntry.isRunning - Habits logHabit creates point-event TimeBlocks (with optional duration) - Todo scheduled tasks create TimeBlock via scheduledBlockId - Calendar views filter by blockType, show items from all modules - All calendar views use getItemColor() for cross-module color support Also includes mukke → music module rename. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5fd9c1d11e
commit
0aa0d7b135
55 changed files with 1334 additions and 331 deletions
91
apps/api/src/modules/music/routes.ts
Normal file
91
apps/api/src/modules/music/routes.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Mukke module — Audio upload, presigned URLs, cover art
|
||||
* Ported from apps/mukke/apps/server
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const routes = new Hono();
|
||||
|
||||
// ─── Song Upload (presigned URL) ────────────────────────────
|
||||
|
||||
routes.post('/songs/upload', async (c) => {
|
||||
const userId = c.get('userId');
|
||||
const { filename } = await c.req.json();
|
||||
if (!filename) return c.json({ error: 'filename required' }, 400);
|
||||
|
||||
const songId = crypto.randomUUID();
|
||||
const key = `users/${userId}/songs/${songId}/${filename}`;
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const uploadUrl = await storage.getUploadUrl(key, { expiresIn: 3600 });
|
||||
|
||||
return c.json({
|
||||
song: { id: songId, title: filename.replace(/\.[^/.]+$/, ''), storagePath: key },
|
||||
uploadUrl,
|
||||
});
|
||||
} catch {
|
||||
return c.json({ error: 'Failed to generate upload URL' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Download URL ───────────────────────────────────────────
|
||||
|
||||
routes.get('/songs/:id/download-url', async (c) => {
|
||||
const { storagePath } = c.req.query();
|
||||
if (!storagePath) return c.json({ error: 'storagePath required' }, 400);
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const url = await storage.getDownloadUrl(storagePath, { expiresIn: 3600 });
|
||||
return c.json({ url });
|
||||
} catch {
|
||||
return c.json({ error: 'Failed to generate download URL' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Cover Art URL ──────────────────────────────────────────
|
||||
|
||||
routes.get('/songs/:id/cover-url', async (c) => {
|
||||
const { coverArtPath } = c.req.query();
|
||||
if (!coverArtPath) return c.json({ url: null });
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const url = await storage.getDownloadUrl(coverArtPath, { expiresIn: 3600 });
|
||||
return c.json({ url });
|
||||
} catch {
|
||||
return c.json({ url: null });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Batch Cover URLs ───────────────────────────────────────
|
||||
|
||||
routes.post('/library/cover-urls', async (c) => {
|
||||
const { paths } = await c.req.json();
|
||||
if (!paths?.length) return c.json({ urls: {} });
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const urls: Record<string, string> = {};
|
||||
|
||||
for (const path of paths.slice(0, 50)) {
|
||||
try {
|
||||
urls[path] = await storage.getDownloadUrl(path, { expiresIn: 3600 });
|
||||
} catch {
|
||||
// Skip failed URLs
|
||||
}
|
||||
}
|
||||
|
||||
return c.json({ urls });
|
||||
} catch {
|
||||
return c.json({ urls: {} });
|
||||
}
|
||||
});
|
||||
|
||||
export { routes as mukkeRoutes };
|
||||
Loading…
Add table
Add a link
Reference in a new issue