// `storage.uploadAvatar` — stores a PUBLIC avatar. Public objects are // served direct from the bucket/CDN; the returned `url` is what you'd put // in an . File upload is external I/O → an action. // // For large files prefer a presigned direct-to-bucket upload via the // plugin's built-in `storage.mintUploadUrl` route. import { Schema } from 'effect' import { defineAction } from '@voltro/protocol' export const uploadAvatar = defineAction({ name: 'storage.uploadAvatar', // Writes bytes the caller sent to a PUBLIC object and returns its URL. It // reads nothing and can overwrite nothing — the key is minted server-side per // upload — but an unguarded upload endpoint is free hosting for whatever the // internet feels like storing. `provider: 'memory'` in app.config.ts keeps // that inside this process; the plugin's `maxBytes` / content-type policy is // what bounds it once you point at S3/R2. // // No auth strategy ships in this template, so a scope guard would deny every // caller. Add one, then `guards: [{ scope: 'files:write' }]`. openAccess: 'stores caller-supplied bytes as a new PUBLIC object and returns its URL — reads nothing, ' + 'overwrites nothing (the key is minted server-side). The `memory` provider keeps it ' + 'in-process; guard it before pointing at a real bucket.', input: Schema.Struct({ bytesBase64: Schema.String, contentType: Schema.String, }), output: Schema.Struct({ id: Schema.String, url: Schema.String, }), })