npm.io
0.1.0 • Published 2d ago

@classytic/arc-media

Licence
MIT
Version
0.1.0
Deps
0
Size
21 kB
Vulns
0
Weekly
0

@classytic/arc-media

Two-phase direct-to-storage uploads, content-hash dedup, and media registry — @classytic/media-kit as one composable Arc resource.

The module every host otherwise hand-rolls around a blob store. Bytes never stream through your API server: start-write mints a presigned target (or a multipart/resumable session for big files), the client uploads straight to storage, and complete-write registers the asset storage-verified (the kernel re-checks exists/stat/MIME/tenant-bound key — client claims are never trusted). A client that sends a content hash (@classytic/media-transform's sha256Hex) gets the dedup handshake: on a tenant-scoped hash hit, no upload happens at all.

Direct wire create/update are disabled — the verified verbs are the only way bytes enter. DELETE respects the engine's soft-delete + storage cleanup.

Install

npm install @classytic/arc-media @classytic/media-kit @classytic/arc @classytic/mongokit mongoose

Peers: @classytic/arc >=2.21.0 · @classytic/media-kit >=3.6.0 · @classytic/mongokit >=3.21.0 · mongoose >=9.4.1.

Usage

import { createApp } from '@classytic/arc/factory';
import { schedulesPlugin } from '@classytic/arc/plugins';
import { requireAuth, requireRoles } from '@classytic/arc/permissions';
import { createMedia } from '@classytic/media-kit';
import { createS3Provider } from '@classytic/media-kit/providers/s3';
import { createMediaModule, mediaMaintenanceSchedules } from '@classytic/arc-media';

const engine = await createMedia({ connection, driver: createS3Provider({ /* ... */ }) });

const app = await createApp({
  modules: [
    createMediaModule({
      engine,
      permissions: {
        view: requireAuth(),
        upload: requireAuth(),
        manage: requireRoles(['admin']),
      },
    }),
  ],
  plugins: async (f) => {
    await f.register(schedulesPlugin, {
      // lock: createMongoLockAdapter({ connection }) — for multi-replica
      schedules: mediaMaintenanceSchedules(engine),
    });
  },
});

Surface

Route What
POST /media/start-write Dedup short-circuit (sha256) → else presigned PUT → else multipart/resumable session (multipart/partCount/size ≥ threshold)
POST /media/complete-write Confirm a presigned upload; stores client display hints (thumbhash, dominantColor, width, height)
POST /media/sign-parts On-demand part URLs for an open multipart session
POST /media/complete-multipart Assemble parts, register the asset
POST /media/:id/action { action: 'signed-url' } Time-boxed signed read URL (needs engine signing config)
GET /media, GET /media/:id Adapter reads (QueryParser filters: status, mimeType, folder, visibility, hash, filename, tags)
DELETE /media/:id Kernel delete (soft per engine config, storage cleanup)

mediaMaintenanceSchedules(engine) returns ScheduleDefinition[] for arc's schedulesPlugin — stale-pending (hourly), soft-delete purge (daily), and expiry purge (hourly) sweeps; pass a lock for multi-replica leader safety. Abandoned uploads that never reached complete-write leave unregistered storage keys — keep bucket lifecycle rules on as belt-and-braces.

The engine is exported at fastify.arc.modules.media. Host seams (extra routes/actions, cache, field rules) inject via seams: — arc 2.21 mergeResourceConfig semantics.

License

MIT Classytic

Keywords