chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext } from 'react'
|
||||
import {
|
||||
type EmbeddedFileRef,
|
||||
extractEmbeddedFileRef,
|
||||
} from '@/lib/uploads/utils/embedded-image-ref'
|
||||
|
||||
export interface FileContentUrlOptions {
|
||||
/** Request the uncompiled source instead of the rendered/compiled bytes. */
|
||||
raw?: boolean
|
||||
/** Content version (e.g. the record's `updatedAt`) — makes the URL cacheable/immutable. */
|
||||
version?: string | number
|
||||
/** Append a timestamp cache-buster when there is no `version`. */
|
||||
bust?: boolean
|
||||
}
|
||||
|
||||
function inlineRefQuery(ref: NonNullable<EmbeddedFileRef>): string {
|
||||
return 'key' in ref
|
||||
? `key=${encodeURIComponent(ref.key)}`
|
||||
: `fileId=${encodeURIComponent(ref.fileId)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Seam for "where do a file's bytes come from". The in-app viewer resolves the
|
||||
* auth-gated workspace serve URL; the public share page swaps in a token-scoped
|
||||
* URL. Renderers and the binary/text query hooks build their fetch URL through
|
||||
* this source so the same components work in both contexts.
|
||||
*/
|
||||
export interface FileContentSource {
|
||||
buildUrl: (key: string, opts?: FileContentUrlOptions) => string
|
||||
/**
|
||||
* Map an embedded image `src` to a display URL scoped to the current context: the in-app source
|
||||
* points at the workspace-scoped inline route, the public source at the token-scoped cascade route.
|
||||
* Non-workspace srcs (external, `data:`, public assets) pass through unchanged.
|
||||
*/
|
||||
resolveImageSrc: (src: string | undefined) => string | undefined
|
||||
}
|
||||
|
||||
function buildServeUrl(key: string, opts?: FileContentUrlOptions): string {
|
||||
const base = `/api/files/serve/${encodeURIComponent(key)}?context=workspace`
|
||||
const params: string[] = []
|
||||
if (opts?.version != null) params.push(`v=${encodeURIComponent(String(opts.version))}`)
|
||||
else if (opts?.bust) params.push(`t=${Date.now()}`)
|
||||
if (opts?.raw) params.push('raw=1')
|
||||
return params.length > 0 ? `${base}&${params.join('&')}` : base
|
||||
}
|
||||
|
||||
/** Build a source whose embeds resolve through `inlineBase` (the workspace- or token-scoped inline route). */
|
||||
function inlineImageSource(
|
||||
buildUrl: FileContentSource['buildUrl'],
|
||||
inlineBase: string
|
||||
): FileContentSource {
|
||||
return {
|
||||
buildUrl,
|
||||
resolveImageSrc: (src) => {
|
||||
if (!src) return src
|
||||
const ref = extractEmbeddedFileRef(src)
|
||||
return ref ? `${inlineBase}?${inlineRefQuery(ref)}` : src
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In-app source scoped to one workspace. Direct file bytes come from the workspace serve URL; embedded
|
||||
* images route through `/api/workspaces/{workspaceId}/files/inline`, which resolves a reference only
|
||||
* within this workspace — a cross-workspace embed 404s and does not render.
|
||||
*/
|
||||
export function createWorkspaceFileContentSource(workspaceId: string): FileContentSource {
|
||||
return inlineImageSource(buildServeUrl, `/api/workspaces/${workspaceId}/files/inline`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Public share source. Direct file bytes come from the token content URL; embedded images route through
|
||||
* `/api/files/public/{token}/inline`, which serves them only when referenced by the shared document and
|
||||
* in its workspace.
|
||||
*/
|
||||
export function createPublicFileContentSource(
|
||||
token: string,
|
||||
contentUrl: string
|
||||
): FileContentSource {
|
||||
return inlineImageSource(() => contentUrl, `/api/files/public/${token}/inline`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Context default for components rendered outside a {@link FileContentSourceProvider}: serve URLs for
|
||||
* direct bytes, embeds passed through unchanged. The file viewer always provides a workspace- or
|
||||
* token-scoped source, so embeds resolve through the scoped inline routes there.
|
||||
*/
|
||||
export const workspaceFileContentSource: FileContentSource = {
|
||||
buildUrl: buildServeUrl,
|
||||
resolveImageSrc: (src) => src,
|
||||
}
|
||||
|
||||
const FileContentSourceContext = createContext<FileContentSource>(workspaceFileContentSource)
|
||||
|
||||
export const FileContentSourceProvider = FileContentSourceContext.Provider
|
||||
|
||||
export function useFileContentSource(): FileContentSource {
|
||||
return useContext(FileContentSourceContext)
|
||||
}
|
||||
Reference in New Issue
Block a user