1. Architecture (where secrets live)
| Layer | Role | Credentials |
|---|---|---|
| Browser / SDK | UI, fetch to your APIs | Never embed AWS keys. Public URLs + optional Bearer headers only. |
| Vite demo client | Local demo | Only DEMO_* (see vite envPrefix). |
| example-server / your API | S3 ListObjects, PutObject, … | AWS_ACCESS_KEY_ID, secret, IAM role, etc. |
2. Install, dev, and production build
cd "/path/to/File Manager"
yarn
yarn dev # S3 proxy (3847) + Vite UI (3000)
yarn build # dist/file-storage.min.js + .css | Command | Purpose |
|---|---|
yarn dev | Proxy + UI using root .env |
yarn build | IIFE, ESM, and CSS under dist/ |
yarn marketing:dev | This site on port 3001 |
Copy env.example → .env in the project root before running the S3 demo.
3. Vite demo: DEMO_*
Only DEMO_* keys are exposed to the browser bundle.
S3 via reference proxy (local)
DEMO_STORE=s3
DEMO_S3_PROXY_BASE_URL=http://localhost:3847
DEMO_S3_PUBLIC_BASE_URL=https://your-bucket.s3.us-east-2.amazonaws.com HTTP REST API
DEMO_STORE=http
DEMO_HTTP_API_BASE_URL=https://api.example.com Mock (no network)
DEMO_STORE=mock | Variable | Description |
|---|---|
DEMO_STORE | mock | http | s3 |
DEMO_S3_PROXY_BASE_URL | Origin of S3 proxy (POST /list, /upload, …) |
DEMO_S3_PUBLIC_BASE_URL | Public HTTPS origin for object URLs |
DEMO_APP_AWS_UPLOAD_URL | Optional full URL for multipart upload to your app |
DEMO_FM_MAX_CONCURRENT_FETCHES | Max parallel fetches from UI (default 30) |
4. Example S3 proxy: server env
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-2
AWS_BUCKET=
REKOGNITION_REGION=us-east-2
S3_ROOT_PREFIX=
S3_PUBLIC_BASE_URL=
PORT=3847 5. editableOptions
File: src/config/editableOptions.ts. Brand mark, demo URL fallbacks, color mode, logos,
subscription tier, flat page size, recents limit, virtual trash folder, and more. Restart Vite after changes.
6. Embedding: FileStorageSDK.mount()
HTML (IIFE)
<link rel="stylesheet" href="https://cdn.example.com/file-storage.css" />
<div id="fm" style="height: 70vh"></div>
<script src="https://cdn.example.com/file-storage.min.js"></script>
<script>
FileStorageSDK.mount({
el: '#fm',
apiKey: 'fs_live_…',
s3: {
proxyBaseUrl: 'https://api.example.com/file-manager/s3',
publicObjectBaseUrl: 'https://bucket.s3.us-east-2.amazonaws.com',
headers: () => ({
Authorization: 'Bearer ' + (localStorage.getItem('token') || ''),
}),
connection: {
bucket: 'my-bucket',
region: 'us-east-2',
rootPrefix: 'uploads/app/',
},
},
})
</script> HTTP store
FileStorageSDK.mount({
el: '#fm',
apiKey: 'fs_live_…',
http: {
baseUrl: 'https://api.example.com',
headers: () => ({ Authorization: 'Bearer ' + token }),
endpoints: {
list: '/api/files/list',
upload: '/api/files/upload',
},
},
}) Custom FileStore
FileStorageSDK.mount({
el: '#fm',
apiKey: 'fs_live_…',
store: myCustomStore, // implements FileStore
})
Returns { unmount: () => void }. Pass rekognitionApi: null to disable image recognition wiring when using s3.
7. HTTP backend contract
Default paths relative to baseUrl:
| Path | Body |
|---|---|
POST /api/files/list | { "path": "/" } → { "items": FileEntry[] } |
POST /api/files/folder | { "parentPath", "name" } |
POST /api/files/upload | multipart: path, files[] |
POST /api/files/rename | { "path", "newName" } |
POST /api/files/move | { "path", "destinationFolderPath" } |
POST /api/files/remove | { "path" } |
POST /api/files/search | { "query" } → items |
POST /api/files/listByType (optional) | { "type", "limit?", "cursor?" } → items + nextCursor |
{
path: string
name: string
kind: 'file' | 'folder'
mime?: string
size?: number
updatedAt: number // epoch ms
createdAt?: number
url?: string // preview URL
}
8. S3 proxy contract
JSON routes relative to proxyBaseUrl (no /api/files prefix by default):
Path Purpose POST /listList folder POST /listByTypePaginated type filter POST /folderCreate folder POST /uploadMultipart upload POST /rename / /move / /removeMutations POST /searchSearch
Optional body field connection: { bucket?, region?, rootPrefix? }. Multipart upload fields: parentPath, files, optional bucket/region/rootPrefix.
9. Uploads via your app (appAwsUploadUrl)
When set on s3, uploads POST multipart to that full URL; all other operations still use
proxyBaseUrl. Use this for auth, validation, virus scan, or metadata before writing to S3. Your
endpoint should accept the same multipart shape and write keys compatible with list + publicObjectBaseUrl.
10. Framework snippets
Vue 3
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue'
const host = ref<HTMLElement | null>(null)
let unmount: (() => void) | undefined
onMounted(() => {
if (!host.value) return
unmount = window.FileStorageSDK.mount({
el: host.value,
apiKey: import.meta.env.VITE_FILESTORAGE_API_KEY,
s3: { proxyBaseUrl: import.meta.env.VITE_FM_PROXY_URL },
}).unmount
})
onBeforeUnmount(() => unmount?.())
</script>
<template>
<div ref="host" class="h-[70vh]" />
</template>
React
useEffect(() => {
const el = ref.current
if (!el || !window.FileStorageSDK) return
const { unmount } = window.FileStorageSDK.mount({
el,
apiKey: process.env.NEXT_PUBLIC_FILESTORAGE_API_KEY!,
s3: { proxyBaseUrl: process.env.NEXT_PUBLIC_FM_PROXY_URL! },
})
return unmount
}, [])
Angular
Call FileStorageSDK.mount({ el: this.host.nativeElement, … }) in
ngAfterViewInit and unmount() in ngOnDestroy.
11. CORS, authentication, and troubleshooting
- CORS: Allow your file-manager origin on the proxy and app upload APIs
(POST, Content-Type / multipart headers).
- Auth: Pass
headers: () => ({ Authorization: 'Bearer …' }) on
http or s3.
- 404 on /listByType: Implement the route or restart the example proxy; the SDK falls back to folder walking.
- Images without URLs: Set
publicObjectBaseUrl (and optional
connection.rootPrefix) so the client can build preview URLs.