6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (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 / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
383 lines
12 KiB
TypeScript
383 lines
12 KiB
TypeScript
import { env, envBoolean } from '@/lib/core/config/env'
|
|
import { getConfiguredStorageProviderId } from '@/lib/core/config/env-capabilities.server'
|
|
import type { StorageConfig, StorageContext } from '@/lib/uploads/shared/types'
|
|
|
|
export type { StorageConfig, StorageContext } from '@/lib/uploads/shared/types'
|
|
|
|
export const UPLOAD_DIR = '/uploads'
|
|
|
|
const storageProvider = getConfiguredStorageProviderId()
|
|
|
|
export const hasBlobConfig = storageProvider === 'azure'
|
|
export const USE_BLOB_STORAGE = storageProvider === 'azure'
|
|
export const USE_S3_STORAGE = storageProvider === 's3'
|
|
export const USE_GCS_STORAGE = storageProvider === 'gcs'
|
|
|
|
export const S3_CONFIG = {
|
|
bucket: env.S3_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
/**
|
|
* Custom endpoint for S3-compatible providers (Cloudflare R2, MinIO, Backblaze B2).
|
|
* Unset means the AWS SDK derives the host from `region`, targeting AWS S3.
|
|
* This is trusted operator configuration (not user input), so it is passed
|
|
* through verbatim — `http://` and private hosts are allowed for on-prem MinIO.
|
|
*/
|
|
endpoint: env.S3_ENDPOINT || undefined,
|
|
/** Path-style addressing — required by MinIO/Ceph RGW; AWS S3 and R2 use the default `false`. */
|
|
forcePathStyle: envBoolean(env.S3_FORCE_PATH_STYLE) ?? false,
|
|
}
|
|
|
|
export const BLOB_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const GCS_CONFIG = {
|
|
bucket: env.GCS_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_KB_CONFIG = {
|
|
bucket: env.GCS_KB_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_EXECUTION_FILES_CONFIG = {
|
|
bucket: env.GCS_EXECUTION_FILES_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_CHAT_CONFIG = {
|
|
bucket: env.GCS_CHAT_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_COPILOT_CONFIG = {
|
|
bucket: env.GCS_COPILOT_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_PROFILE_PICTURES_CONFIG = {
|
|
bucket: env.GCS_PROFILE_PICTURES_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_OG_IMAGES_CONFIG = {
|
|
bucket: env.GCS_OG_IMAGES_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const GCS_WORKSPACE_LOGOS_CONFIG = {
|
|
bucket: env.GCS_WORKSPACE_LOGOS_BUCKET_NAME || '',
|
|
}
|
|
|
|
export const S3_KB_CONFIG = {
|
|
bucket: env.S3_KB_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const S3_EXECUTION_FILES_CONFIG = {
|
|
bucket: env.S3_EXECUTION_FILES_BUCKET_NAME || 'sim-execution-files',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_KB_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_KB_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const BLOB_EXECUTION_FILES_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_EXECUTION_FILES_CONTAINER_NAME || 'sim-execution-files',
|
|
}
|
|
|
|
export const S3_CHAT_CONFIG = {
|
|
bucket: env.S3_CHAT_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_CHAT_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_CHAT_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const S3_COPILOT_CONFIG = {
|
|
bucket: env.S3_COPILOT_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_COPILOT_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_COPILOT_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const S3_PROFILE_PICTURES_CONFIG = {
|
|
bucket: env.S3_PROFILE_PICTURES_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_PROFILE_PICTURES_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_PROFILE_PICTURES_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const S3_OG_IMAGES_CONFIG = {
|
|
bucket: env.S3_OG_IMAGES_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_OG_IMAGES_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_OG_IMAGES_CONTAINER_NAME || '',
|
|
}
|
|
|
|
export const S3_WORKSPACE_LOGOS_CONFIG = {
|
|
bucket: env.S3_WORKSPACE_LOGOS_BUCKET_NAME || '',
|
|
region: env.AWS_REGION || '',
|
|
}
|
|
|
|
export const BLOB_WORKSPACE_LOGOS_CONFIG = {
|
|
accountName: env.AZURE_ACCOUNT_NAME || '',
|
|
accountKey: env.AZURE_ACCOUNT_KEY || '',
|
|
connectionString: env.AZURE_CONNECTION_STRING || '',
|
|
containerName: env.AZURE_STORAGE_WORKSPACE_LOGOS_CONTAINER_NAME || '',
|
|
}
|
|
|
|
/**
|
|
* Get the current storage provider as a human-readable string
|
|
*/
|
|
export function getStorageProvider(): 'Azure Blob' | 'S3' | 'GCS' | 'Local' {
|
|
if (USE_BLOB_STORAGE) return 'Azure Blob'
|
|
if (USE_S3_STORAGE) return 'S3'
|
|
if (USE_GCS_STORAGE) return 'GCS'
|
|
return 'Local'
|
|
}
|
|
|
|
/**
|
|
* Check if we're using any cloud storage (S3, Blob, or GCS)
|
|
*/
|
|
export function isUsingCloudStorage(): boolean {
|
|
return USE_S3_STORAGE || USE_BLOB_STORAGE || USE_GCS_STORAGE
|
|
}
|
|
|
|
/**
|
|
* Provider segment used in `/api/files/serve/<prefix>/<key>` paths returned to
|
|
* clients after a direct upload. Defaults to `s3` to preserve historical paths.
|
|
*/
|
|
export function getServeStoragePrefix(): 'blob' | 's3' | 'gcs' {
|
|
if (USE_BLOB_STORAGE) return 'blob'
|
|
if (USE_GCS_STORAGE) return 'gcs'
|
|
return 's3'
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate storage configuration for a given context
|
|
*/
|
|
export function getStorageConfig(context: StorageContext): StorageConfig {
|
|
if (USE_BLOB_STORAGE) {
|
|
return getBlobConfig(context)
|
|
}
|
|
|
|
if (USE_S3_STORAGE) {
|
|
return getS3Config(context)
|
|
}
|
|
|
|
if (USE_GCS_STORAGE) {
|
|
return getGcsConfig(context)
|
|
}
|
|
|
|
return {}
|
|
}
|
|
|
|
/**
|
|
* Get S3 configuration for a given context
|
|
*/
|
|
function getS3Config(context: StorageContext): StorageConfig {
|
|
switch (context) {
|
|
case 'knowledge-base':
|
|
return {
|
|
bucket: S3_KB_CONFIG.bucket,
|
|
region: S3_KB_CONFIG.region,
|
|
}
|
|
case 'chat':
|
|
return {
|
|
bucket: S3_CHAT_CONFIG.bucket,
|
|
region: S3_CHAT_CONFIG.region,
|
|
}
|
|
case 'copilot':
|
|
return {
|
|
bucket: S3_COPILOT_CONFIG.bucket,
|
|
region: S3_COPILOT_CONFIG.region,
|
|
}
|
|
case 'execution':
|
|
return {
|
|
bucket: S3_EXECUTION_FILES_CONFIG.bucket,
|
|
region: S3_EXECUTION_FILES_CONFIG.region,
|
|
}
|
|
case 'mothership':
|
|
case 'workspace':
|
|
return {
|
|
bucket: S3_CONFIG.bucket,
|
|
region: S3_CONFIG.region,
|
|
}
|
|
case 'profile-pictures':
|
|
return {
|
|
bucket: S3_PROFILE_PICTURES_CONFIG.bucket,
|
|
region: S3_PROFILE_PICTURES_CONFIG.region,
|
|
}
|
|
case 'og-images':
|
|
return {
|
|
bucket: S3_OG_IMAGES_CONFIG.bucket || S3_CONFIG.bucket,
|
|
region: S3_OG_IMAGES_CONFIG.region || S3_CONFIG.region,
|
|
}
|
|
case 'workspace-logos':
|
|
return {
|
|
bucket: S3_WORKSPACE_LOGOS_CONFIG.bucket || S3_CONFIG.bucket,
|
|
region: S3_WORKSPACE_LOGOS_CONFIG.region || S3_CONFIG.region,
|
|
}
|
|
default:
|
|
return {
|
|
bucket: S3_CONFIG.bucket,
|
|
region: S3_CONFIG.region,
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Azure Blob configuration for a given context
|
|
*/
|
|
function getBlobConfig(context: StorageContext): StorageConfig {
|
|
switch (context) {
|
|
case 'knowledge-base':
|
|
return {
|
|
accountName: BLOB_KB_CONFIG.accountName,
|
|
accountKey: BLOB_KB_CONFIG.accountKey,
|
|
connectionString: BLOB_KB_CONFIG.connectionString,
|
|
containerName: BLOB_KB_CONFIG.containerName,
|
|
}
|
|
case 'chat':
|
|
return {
|
|
accountName: BLOB_CHAT_CONFIG.accountName,
|
|
accountKey: BLOB_CHAT_CONFIG.accountKey,
|
|
connectionString: BLOB_CHAT_CONFIG.connectionString,
|
|
containerName: BLOB_CHAT_CONFIG.containerName,
|
|
}
|
|
case 'copilot':
|
|
return {
|
|
accountName: BLOB_COPILOT_CONFIG.accountName,
|
|
accountKey: BLOB_COPILOT_CONFIG.accountKey,
|
|
connectionString: BLOB_COPILOT_CONFIG.connectionString,
|
|
containerName: BLOB_COPILOT_CONFIG.containerName,
|
|
}
|
|
case 'execution':
|
|
return {
|
|
accountName: BLOB_EXECUTION_FILES_CONFIG.accountName,
|
|
accountKey: BLOB_EXECUTION_FILES_CONFIG.accountKey,
|
|
connectionString: BLOB_EXECUTION_FILES_CONFIG.connectionString,
|
|
containerName: BLOB_EXECUTION_FILES_CONFIG.containerName,
|
|
}
|
|
case 'mothership':
|
|
case 'workspace':
|
|
return {
|
|
accountName: BLOB_CONFIG.accountName,
|
|
accountKey: BLOB_CONFIG.accountKey,
|
|
connectionString: BLOB_CONFIG.connectionString,
|
|
containerName: BLOB_CONFIG.containerName,
|
|
}
|
|
case 'profile-pictures':
|
|
return {
|
|
accountName: BLOB_PROFILE_PICTURES_CONFIG.accountName,
|
|
accountKey: BLOB_PROFILE_PICTURES_CONFIG.accountKey,
|
|
connectionString: BLOB_PROFILE_PICTURES_CONFIG.connectionString,
|
|
containerName: BLOB_PROFILE_PICTURES_CONFIG.containerName,
|
|
}
|
|
case 'og-images':
|
|
return {
|
|
accountName: BLOB_OG_IMAGES_CONFIG.accountName || BLOB_CONFIG.accountName,
|
|
accountKey: BLOB_OG_IMAGES_CONFIG.accountKey || BLOB_CONFIG.accountKey,
|
|
connectionString: BLOB_OG_IMAGES_CONFIG.connectionString || BLOB_CONFIG.connectionString,
|
|
containerName: BLOB_OG_IMAGES_CONFIG.containerName || BLOB_CONFIG.containerName,
|
|
}
|
|
case 'workspace-logos':
|
|
return {
|
|
accountName: BLOB_WORKSPACE_LOGOS_CONFIG.accountName || BLOB_CONFIG.accountName,
|
|
accountKey: BLOB_WORKSPACE_LOGOS_CONFIG.accountKey || BLOB_CONFIG.accountKey,
|
|
connectionString:
|
|
BLOB_WORKSPACE_LOGOS_CONFIG.connectionString || BLOB_CONFIG.connectionString,
|
|
containerName: BLOB_WORKSPACE_LOGOS_CONFIG.containerName || BLOB_CONFIG.containerName,
|
|
}
|
|
default:
|
|
return {
|
|
accountName: BLOB_CONFIG.accountName,
|
|
accountKey: BLOB_CONFIG.accountKey,
|
|
connectionString: BLOB_CONFIG.connectionString,
|
|
containerName: BLOB_CONFIG.containerName,
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get GCS configuration for a given context.
|
|
*
|
|
* Every context falls back to the general bucket when its dedicated bucket is
|
|
* unset. GCS bucket names are globally unique, so an S3-style literal default
|
|
* (e.g. `sim-execution-files`) would point at a bucket the operator does not
|
|
* own; falling back keeps uploads, downloads, and serving consistent when only
|
|
* `GCS_BUCKET_NAME` is configured.
|
|
*/
|
|
function getGcsConfig(context: StorageContext): StorageConfig {
|
|
switch (context) {
|
|
case 'knowledge-base':
|
|
return { bucket: GCS_KB_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'chat':
|
|
return { bucket: GCS_CHAT_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'copilot':
|
|
return { bucket: GCS_COPILOT_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'execution':
|
|
return { bucket: GCS_EXECUTION_FILES_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'mothership':
|
|
case 'workspace':
|
|
return { bucket: GCS_CONFIG.bucket }
|
|
case 'profile-pictures':
|
|
return { bucket: GCS_PROFILE_PICTURES_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'og-images':
|
|
return { bucket: GCS_OG_IMAGES_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
case 'workspace-logos':
|
|
return { bucket: GCS_WORKSPACE_LOGOS_CONFIG.bucket || GCS_CONFIG.bucket }
|
|
default:
|
|
return { bucket: GCS_CONFIG.bucket }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a specific storage context is configured
|
|
* Returns false if the context would fall back to general config but general isn't configured
|
|
*/
|
|
export function isStorageContextConfigured(context: StorageContext): boolean {
|
|
const config = getStorageConfig(context)
|
|
|
|
if (USE_BLOB_STORAGE) {
|
|
return !!(
|
|
config.containerName &&
|
|
(config.connectionString || (config.accountName && config.accountKey))
|
|
)
|
|
}
|
|
|
|
if (USE_S3_STORAGE) {
|
|
return !!(config.bucket && config.region)
|
|
}
|
|
|
|
if (USE_GCS_STORAGE) {
|
|
return !!config.bucket
|
|
}
|
|
|
|
return true
|
|
}
|