Appearance
AWS S3 storage
This project uses Amazon S3 for server-side file storage via @aws-sdk/client-s3. Uploads run in uploadToStorage (src/lib/s3.ts). Authenticated features call uploadFileToStorage and removeFileFromStorage (src/actions/file-actions.ts), which require a signed-in user.
Typical use in the app: profile / general settings image upload (src/components/forms/general-setting/use-general-setting-form.ts).
How it works in this codebase
| Concern | Behavior |
|---|---|
| Client | Sends a File to a Server Action (uploadFileToStorage). |
| Auth | Unauthenticated requests return an error from file-actions.ts. |
| Object key | Default prefix uploads/; filenames are sanitized and prefixed with a timestamp (src/lib/s3.ts). |
| Upload API | PutObjectCommand (no ACL set by default — see Object access). |
| Returned URL | Virtual-hosted style: https://{BUCKET}.s3.{REGION}.amazonaws.com/{key} |
| Delete | DeleteObjectCommand; removeFromStorage only deletes if the URL contains the configured bucket name. |
Environment variables
Values are read through src/config.ts → appConfig.s3:
env
AWS_S3_REGION=
AWS_S3_BUCKET_NAME=
AWS_S3_ACCESS_KEY=
AWS_S3_SECRET_KEY=All four should be set in any environment where uploads or deletes must run (! assertions are used when constructing the SDK client).
AWS setup
1. Create a bucket
- In AWS Console → S3 → Create bucket.
- Choose a globally unique name (example:
nexusorbit-production-uploads). - Pick a Region and use the same value as
AWS_S3_REGION.
2. Create an IAM user (access keys)
- IAM → Users → create a user intended for application use (no console access required).
- Attach a policy that allows this app’s operations on your bucket only.
Minimal example (replace placeholders):
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "NexusOrbitS3BucketReadWriteDelete",
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}TIP
Prefer scoped policies (Resource limited to one bucket prefix) over s3:* on *.
3. Generate access keys
IAM → your user → Security credentials → Create access key (use case: application running outside AWS, or per your org’s policy). Map them to AWS_S3_ACCESS_KEY and AWS_S3_SECRET_KEY.
Object access (public vs private)
The upload helper returns a plain HTTPS URL to the object. Browsers can only load or display that URL if the object is readable without signing.
Option A — Public read on objects (simplest for avatars/static files)
- Use a bucket policy allowing
s3:GetObjectforPrincipal: "*"onarn:aws:s3:::YOUR_BUCKET_NAME/*, or enableACL: "public-read"onPutObjectonly if your bucket/account still supports ACL-based public reads (modern defaults often enforce “Bucket owner enforced” and block ACLs).
Example bucket policy snippet (adjust bucket name):
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}WARNING
Public GetObject makes every object URL guessable unless you restrict prefixes, use random keys only, or add WAF/other controls. Prefer least privilege for anything sensitive — this pattern fits non-sensitive public assets (e.g. profile images).
Option B — Private bucket (recommended for sensitive files)
- Keep Block Public Access on and do not expose public
GetObject. - You would need signed URLs (
GetObjectCommand+ presigner) or CloudFront with appropriate auth — that requires code changes beyond the currentuploadToStoragereturn value.
There is an inline comment in src/lib/s3.ts for optional ACL: "public-read" on PutObject; use it only if it matches your bucket’s ACL settings and security model.
File map
| Role | Path |
|---|---|
| S3 client, upload/delete, URL building | src/lib/s3.ts |
| Authenticated server actions | src/actions/file-actions.ts |
| Config keys | src/config.ts → appConfig.s3 |
| Example consumer (avatar upload) | src/components/forms/general-setting/use-general-setting-form.ts |
Troubleshooting
Failed to upload file to aws s3— Check region matches the bucket, credentials are correct, and IAM policy includess3:PutObject.- Upload succeeds but image does not load — Object likely private; add controlled public read policy, or implement signed URLs.
removeFromStoragereturnsfalse— URL string does not include the configuredAWS_S3_BUCKET_NAME(src/lib/s3.ts).