@mostajs/media-server 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/llms.txt +153 -0
- package/package.json +9 -4
package/llms.txt
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# @mostajs/media-server — fiche LLM
|
|
2
|
+
> Factory + schema + handlers Next.js pour persister les recordings de @mostajs/media : blob via @mostajs/storage, métadonnées via un ORM injecté.
|
|
3
|
+
|
|
4
|
+
- Version: 0.2.2 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
|
|
5
|
+
- Chemin: mostajs/mosta-media-stack/mosta-media-server · Statut audit: complet (dist/)
|
|
6
|
+
|
|
7
|
+
## RÔLE
|
|
8
|
+
Couche serveur de persistance des enregistrements media — PAS une topologie WebRTC.
|
|
9
|
+
Découple les bytes (gérés par `@mostajs/storage` : FS/S3) de la row métadonnée
|
|
10
|
+
(gérée par un `MediaRepository` que le consumer implémente au-dessus de son ORM).
|
|
11
|
+
Fournit : un `MediaService` pur (`createMedia`) et des route handlers Next.js App
|
|
12
|
+
Router prêts à monter (`createRecordingsRoute`, `createMediaByIdRoute`). Le handler
|
|
13
|
+
reste responsable du RBAC (extraction `accountId` tenant) et de la sérialisation HTTP.
|
|
14
|
+
ESM only. Surface storage déclarée localement (`StorageLike`) → pas de hard-dep build.
|
|
15
|
+
|
|
16
|
+
## INSTALLATION
|
|
17
|
+
```
|
|
18
|
+
npm i @mostajs/media-server
|
|
19
|
+
```
|
|
20
|
+
peerDependencies (non optionnelles) : `@mostajs/media ^2.0.4`, `@mostajs/storage ^0.1.2`.
|
|
21
|
+
|
|
22
|
+
## EXPORTS
|
|
23
|
+
- `createMedia(opts: CreateMediaOptions): MediaService`
|
|
24
|
+
- `createRecordingsRoute(opts: RecordingsRouteOptions): RecordingsRouteHandlers`
|
|
25
|
+
- `createMediaByIdRoute(opts: MediaByIdRouteOptions): MediaByIdRouteHandlers`
|
|
26
|
+
- Types : `MediaKind`, `MediaStatus`, `MediaRow`, `MediaListFilter`, `MediaListResult`,
|
|
27
|
+
`MediaRepository`, `CreateRecordingInput`, `CreateRecordingResult`,
|
|
28
|
+
`MediaWithSignedUrl`, `MediaService`, `StorageLike`, `CreateMediaOptions`,
|
|
29
|
+
`RecordingsRouteOptions`, `RecordingsRouteHandlers`, `MediaByIdRouteOptions`,
|
|
30
|
+
`MediaByIdRouteHandlers`, `GetAccountIdFromRequest`, `MimeAcceptor`
|
|
31
|
+
|
|
32
|
+
## EXPORTS PAR SOUS-CHEMIN
|
|
33
|
+
Aucun — un seul point d'entrée `"."` dans `exports`. Tout passe par `@mostajs/media-server`.
|
|
34
|
+
|
|
35
|
+
## API — SIGNATURES
|
|
36
|
+
```ts
|
|
37
|
+
createMedia(opts: CreateMediaOptions): MediaService
|
|
38
|
+
createRecordingsRoute(opts: RecordingsRouteOptions): RecordingsRouteHandlers
|
|
39
|
+
createMediaByIdRoute(opts: MediaByIdRouteOptions): MediaByIdRouteHandlers
|
|
40
|
+
|
|
41
|
+
interface MediaService {
|
|
42
|
+
createRecording(input: CreateRecordingInput): Promise<CreateRecordingResult>
|
|
43
|
+
updateRecording(id, input: UpdateRecordingInput): Promise<CreateRecordingResult | null> // saveOrUpdate
|
|
44
|
+
getMedia(id): Promise<MediaWithSignedUrl | null>
|
|
45
|
+
reissueSignedUrl(id, ttlSec?): Promise<MediaWithSignedUrl | null>
|
|
46
|
+
deleteMedia(id): Promise<void> // soft-delete + purge blob
|
|
47
|
+
listForAccount(accountId, filter?: MediaListFilter): Promise<MediaListResult>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// RecordingsRouteHandlers : POST(req) -> POST /api/media/recordings (multipart)
|
|
51
|
+
// MediaByIdRouteHandlers : GET / PUT / DELETE -> /api/media/[id]
|
|
52
|
+
// GET -> row + signed URL frais (404 absent, 403 tenant mismatch)
|
|
53
|
+
// PUT -> remplace le blob (multipart { blob, mimeType?, durationMs?, metadata? })
|
|
54
|
+
// DELETE -> soft-delete row + purge storage
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## TYPES CLÉS
|
|
58
|
+
```ts
|
|
59
|
+
type MediaKind = 'video' | 'audio' | 'image' | 'data' // 'data' = blob non-média
|
|
60
|
+
type MediaStatus = 'uploading' | 'ready' | 'deleted' | 'failed'
|
|
61
|
+
|
|
62
|
+
interface MediaRow {
|
|
63
|
+
id: string // UUID/ULID, stable cross-restart
|
|
64
|
+
accountId: string // tenant — clé d'isolation multi-tenant
|
|
65
|
+
ownerEmail?: string | null
|
|
66
|
+
bucket: string // bucket @mostajs/storage, défaut 'media-recordings'
|
|
67
|
+
key: string // path relatif, convention '<accountId>/<id>.<ext>'
|
|
68
|
+
mimeType: string; sizeBytes: number; durationMs: number // durationMs 0 pour image
|
|
69
|
+
kind: MediaKind; status: MediaStatus
|
|
70
|
+
sessionId?: string | null; takeIndex?: number | null // groupage multi-take
|
|
71
|
+
checksum?: string | null; metadata?: Record<string,string> | null
|
|
72
|
+
createdAt: Date | string; updatedAt?: Date | string | null
|
|
73
|
+
}
|
|
74
|
+
// MediaRepository (à implémenter par le consumer, au-dessus de @mostajs/orm/Prisma…) :
|
|
75
|
+
// insert, findById, update, delete, list(accountId, filter?)
|
|
76
|
+
// StorageLike (compatible @mostajs/storage FileStore) : put, delete, signedUrl
|
|
77
|
+
|
|
78
|
+
interface CreateMediaOptions {
|
|
79
|
+
storage: StorageLike // adapter storage
|
|
80
|
+
repo: MediaRepository // repository métadonnée ORM
|
|
81
|
+
defaultBucket?: string // override per-request via input.bucket
|
|
82
|
+
signedUrlTtlSec?: number // défaut 1h
|
|
83
|
+
generateId?: () => string // défaut ULID-like
|
|
84
|
+
}
|
|
85
|
+
interface CreateRecordingInput {
|
|
86
|
+
accountId: string; ownerEmail?: string
|
|
87
|
+
body: Uint8Array | Blob | ArrayBuffer
|
|
88
|
+
mimeType: string; durationMs: number
|
|
89
|
+
kind?: MediaKind // sinon dérivé du mimeType
|
|
90
|
+
sessionId?: string; takeIndex?: number; bucket?: string
|
|
91
|
+
metadata?: Record<string, string>
|
|
92
|
+
}
|
|
93
|
+
interface RecordingsRouteOptions {
|
|
94
|
+
mediaService: MediaService
|
|
95
|
+
getAccountIdFromRequest: GetAccountIdFromRequest // (req) => string | null (null => 401)
|
|
96
|
+
acceptMimeType?: MimeAcceptor // défaut video/* audio/* image/*
|
|
97
|
+
maxSizeBytes?: number // défaut 500 MB
|
|
98
|
+
getOwnerEmail?: (req) => Promise<string|null> | string | null
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## PATTERN
|
|
103
|
+
```ts
|
|
104
|
+
// lib/media-service.ts
|
|
105
|
+
import { createMedia } from '@mostajs/media-server'
|
|
106
|
+
export const mediaService = createMedia({
|
|
107
|
+
storage, // @mostajs/storage FileStore
|
|
108
|
+
repo: mediaRepository, // implémente MediaRepository sur l'ORM
|
|
109
|
+
signedUrlTtlSec: 3600,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// app/api/media/recordings/route.ts
|
|
113
|
+
import { createRecordingsRoute } from '@mostajs/media-server'
|
|
114
|
+
export const runtime = 'nodejs'
|
|
115
|
+
const { POST } = createRecordingsRoute({
|
|
116
|
+
mediaService,
|
|
117
|
+
getAccountIdFromRequest: (req) => getAccountIdFromSession(req),
|
|
118
|
+
maxSizeBytes: 200 * 1024 * 1024,
|
|
119
|
+
})
|
|
120
|
+
export { POST }
|
|
121
|
+
|
|
122
|
+
// app/api/media/[id]/route.ts
|
|
123
|
+
import { createMediaByIdRoute } from '@mostajs/media-server'
|
|
124
|
+
const { GET, PUT, DELETE } = createMediaByIdRoute({ mediaService, getAccountIdFromRequest })
|
|
125
|
+
export { GET, PUT, DELETE }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## DÉPEND DE
|
|
129
|
+
- `@mostajs/media` (peer requis, ^2.0.4) — fournit les recordings côté client.
|
|
130
|
+
- `@mostajs/storage` (peer requis, ^0.1.2) — persistance des bytes (FileStore).
|
|
131
|
+
La surface attendue est aussi déclarée localement via `StorageLike` (pas de
|
|
132
|
+
hard-dep build-time).
|
|
133
|
+
|
|
134
|
+
## PIÈGES
|
|
135
|
+
- Le `MediaRepository` n'est PAS fourni : le consumer doit l'implémenter au-dessus
|
|
136
|
+
de son ORM (`@mostajs/orm`, Prisma, repo maison). Il ne manipule que la row.
|
|
137
|
+
- Flux `createRecording` : insert row(status=uploading) → put storage → update
|
|
138
|
+
row(status=ready) → sign URL. Si le put échoue, la row passe à `status=failed`
|
|
139
|
+
(conservée pour audit, blob inexistant/orphelin).
|
|
140
|
+
- RBAC à la charge du handler : `getAccountIdFromRequest` retourne `null` → 401.
|
|
141
|
+
`accountId` ≠ `userId` (clé tenant). GET `/api/media/[id]` renvoie 403 si tenant
|
|
142
|
+
mismatch.
|
|
143
|
+
- `createRecording` (nouveau média) vs `updateRecording` (ré-enregistre, même
|
|
144
|
+
bucket/key, écrase le blob) : pattern saveOrUpdate côté consumer.
|
|
145
|
+
- Build : `tsc` puis script `fix-esm` (ajoute les extensions `.js` aux imports
|
|
146
|
+
relatifs). Les `.d.ts` du dist sont la source de vérité de l'API.
|
|
147
|
+
- `RouteParamsCtx` accepte `params` objet (Next ≤ 14) ET `Promise` (Next ≥ 15).
|
|
148
|
+
- `MediaListFilter` : pagination forward-only par curseur (`cursor` = id dernière
|
|
149
|
+
row), `limit` défaut 50 / max 200, status filtré par défaut sur `ready`.
|
|
150
|
+
|
|
151
|
+
## RÉFÉRENCES
|
|
152
|
+
- README.md (mosta-media-server) — guide d'intégration.
|
|
153
|
+
- docs/ — documentation complémentaire.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/media-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Server-side factory + schema + service contract for @mostajs/media recordings — pairs with @mostajs/storage for blob persistence and an injected ORM repository for row metadata.",
|
|
5
5
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"dist",
|
|
19
19
|
"README.md",
|
|
20
20
|
"LICENSE",
|
|
21
|
-
"docs"
|
|
21
|
+
"docs",
|
|
22
|
+
"llms.txt"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
24
25
|
"build": "tsc && npm run fix-esm",
|
|
@@ -30,8 +31,12 @@
|
|
|
30
31
|
"@mostajs/storage": "^0.1.2"
|
|
31
32
|
},
|
|
32
33
|
"peerDependenciesMeta": {
|
|
33
|
-
"@mostajs/media": {
|
|
34
|
-
|
|
34
|
+
"@mostajs/media": {
|
|
35
|
+
"optional": false
|
|
36
|
+
},
|
|
37
|
+
"@mostajs/storage": {
|
|
38
|
+
"optional": false
|
|
39
|
+
}
|
|
35
40
|
},
|
|
36
41
|
"devDependencies": {
|
|
37
42
|
"typescript": "^5.4.0"
|