belt 0.2.17 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +226 -0
- data/README.md +42 -3
- data/lib/belt/action_router.rb +34 -19
- data/lib/belt/cli/app_detection.rb +30 -3
- data/lib/belt/cli/auth_command.rb +395 -0
- data/lib/belt/cli/console_command.rb +17 -2
- data/lib/belt/cli/deploy_command.rb +51 -17
- data/lib/belt/cli/destroy_command.rb +62 -25
- data/lib/belt/cli/doctor_command.rb +166 -22
- data/lib/belt/cli/environment_command.rb +12 -0
- data/lib/belt/cli/explain_command.rb +112 -0
- data/lib/belt/cli/frontend_command.rb +31 -10
- data/lib/belt/cli/frontend_deploy_command.rb +110 -21
- data/lib/belt/cli/frontend_env_command.rb +56 -19
- data/lib/belt/cli/frontend_env_map.rb +30 -10
- data/lib/belt/cli/frontend_registry.rb +373 -0
- data/lib/belt/cli/frontend_setup_command.rb +71 -5
- data/lib/belt/cli/generate_command.rb +61 -42
- data/lib/belt/cli/index_command.rb +192 -0
- data/lib/belt/cli/lambda_config_command.rb +9 -3
- data/lib/belt/cli/routes_command/request_model_inference.rb +131 -0
- data/lib/belt/cli/routes_command/route_inference.rb +4 -3
- data/lib/belt/cli/routes_command.rb +31 -19
- data/lib/belt/cli/server_command.rb +28 -15
- data/lib/belt/cli/tables_command.rb +3 -1
- data/lib/belt/cli/terraform_command.rb +9 -0
- data/lib/belt/cli/views_command.rb +15 -15
- data/lib/belt/cli/zip_artifact_builder.rb +199 -0
- data/lib/belt/cli.rb +17 -9
- data/lib/belt/docs/backups.md +101 -0
- data/lib/belt/docs/console.md +65 -0
- data/lib/belt/docs/controllers.md +155 -0
- data/lib/belt/docs/deployment.md +166 -0
- data/lib/belt/docs/frontend.md +103 -0
- data/lib/belt/docs/generators.md +137 -0
- data/lib/belt/docs/lambda_handler.md +105 -0
- data/lib/belt/docs/models.md +160 -0
- data/lib/belt/docs/observability.md +94 -0
- data/lib/belt/docs/plugins.md +94 -0
- data/lib/belt/docs/routing.md +138 -0
- data/lib/belt/docs/structure.md +96 -0
- data/lib/belt/inflector.rb +8 -0
- data/lib/belt/route_dsl.rb +318 -100
- data/lib/belt/version.rb +1 -1
- data/lib/templates/frontend/react/env.yml.example +2 -2
- data/lib/templates/generate/auth/cognito.tf.erb +71 -0
- data/lib/templates/generate/auth/cognito_outputs.tf.erb +19 -0
- data/lib/templates/generate/auth/frontend/ConfirmEmail.jsx +55 -0
- data/lib/templates/generate/auth/frontend/Login.jsx +79 -0
- data/lib/templates/generate/auth/frontend/ProtectedRoute.jsx +9 -0
- data/lib/templates/generate/auth/frontend/SignUp.jsx +58 -0
- data/lib/templates/generate/auth/frontend/apiClient.js +34 -0
- data/lib/templates/generate/auth/frontend/auth.css +157 -0
- data/lib/templates/generate/auth/frontend/auth.js +121 -0
- data/lib/templates/generate/model.rb.erb +4 -6
- data/lib/templates/module/frontend.tf.erb +49 -43
- data/lib/templates/module/outputs.tf.erb +1 -1
- data/lib/templates/new_app/AGENTS.md.erb +170 -20
- data/lib/templates/new_app/config/lambda/api.yml.erb +1 -0
- data/lib/templates/new_app/config/routes.rb.erb +8 -3
- data/lib/templates/new_app/lambda/api.rb.erb +1 -1
- metadata +56 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Backups
|
|
2
|
+
|
|
3
|
+
Belt integrates automated pre-deploy backups into the deploy lifecycle.
|
|
4
|
+
When configured, `belt deploy` creates recovery points before applying changes.
|
|
5
|
+
|
|
6
|
+
## Quick Setup
|
|
7
|
+
|
|
8
|
+
Create `infrastructure/<env>/belt.rb`:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
Belt.configure do |config|
|
|
12
|
+
config.backups do
|
|
13
|
+
dynamodb :all
|
|
14
|
+
retention snapshots: 90
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then deploy normally — backups run automatically:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
belt deploy prod
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Simple Mode
|
|
26
|
+
|
|
27
|
+
For DynamoDB-only backups with defaults (all tables, 90-day retention):
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
Belt.configure do |config|
|
|
31
|
+
config.backups = true
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Full Configuration
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
Belt.configure do |config|
|
|
39
|
+
config.backups do
|
|
40
|
+
dynamodb :all # All tables: PITR check + on-demand snapshot
|
|
41
|
+
dynamodb :posts, :users # Or specific tables only
|
|
42
|
+
cognito :users, :pool_config # Export user list + pool settings to S3
|
|
43
|
+
s3 :legal_documents # Sync bucket to backup bucket
|
|
44
|
+
retention snapshots: 90, cognito: 10, s3: 10
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Backup Types
|
|
50
|
+
|
|
51
|
+
| Type | What It Does | Default Retention |
|
|
52
|
+
|------|-------------|-------------------|
|
|
53
|
+
| `dynamodb :all` | PITR verification + on-demand snapshot per table | 90 days |
|
|
54
|
+
| `dynamodb :table1, :table2` | Same, specific tables only | 90 days |
|
|
55
|
+
| `cognito :users` | Paginated user export → JSON in backup bucket | 10 copies |
|
|
56
|
+
| `cognito :pool_config` | Pool configuration export → JSON | 10 copies |
|
|
57
|
+
| `s3 :bucket_name` | Full sync to backup bucket | 10 copies |
|
|
58
|
+
|
|
59
|
+
## CLI Flags
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
belt deploy prod # normal deploy (runs backups first)
|
|
63
|
+
belt deploy prod --skip-backup # skip backup phase
|
|
64
|
+
belt deploy prod --backup-only # just create recovery point, don't deploy
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## How It Works
|
|
68
|
+
|
|
69
|
+
1. Creates backup bucket `<app-name>-backups-<env>` on first run (versioned, public access blocked)
|
|
70
|
+
2. Reads table names from `terraform output`
|
|
71
|
+
3. Verifies PITR is enabled on each DynamoDB table
|
|
72
|
+
4. Creates on-demand backup named `<table>-<timestamp>`
|
|
73
|
+
5. For Cognito/S3: exports to backup bucket under timestamped prefixes
|
|
74
|
+
6. Cleans up expired snapshots/copies beyond retention
|
|
75
|
+
|
|
76
|
+
## First Deploy
|
|
77
|
+
|
|
78
|
+
On a brand-new environment with no prior deploys, there are no Terraform outputs
|
|
79
|
+
to read table names from. Belt warns and skips the backup phase gracefully.
|
|
80
|
+
After the first successful deploy, backups run normally.
|
|
81
|
+
|
|
82
|
+
## DynamoDB Protection Defaults
|
|
83
|
+
|
|
84
|
+
All Belt-generated DynamoDB tables include:
|
|
85
|
+
- **PITR** (Point-in-Time Recovery) — enabled by default (35 days continuous)
|
|
86
|
+
- **Deletion protection** — enabled in prod, disabled in dev
|
|
87
|
+
|
|
88
|
+
## Skipping Backups in Dev
|
|
89
|
+
|
|
90
|
+
Don't create `infrastructure/dev01/belt.rb`, or omit the backups block:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Belt.configure do |config|
|
|
94
|
+
# No backups block = no backups during deploy
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## See Also
|
|
99
|
+
|
|
100
|
+
- `belt explain deployment` — the full deploy lifecycle
|
|
101
|
+
- `belt deploy --help` — all deploy options
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Console
|
|
2
|
+
|
|
3
|
+
`belt console` (alias: `belt c`) starts an interactive Ruby session with your
|
|
4
|
+
application fully loaded — models, configuration, AWS clients, everything.
|
|
5
|
+
|
|
6
|
+
## Basic Usage
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
belt console # uses BELT_ENV or defaults to 'dev'
|
|
10
|
+
belt c prod # specify environment
|
|
11
|
+
belt c dev01 # any environment name
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## What Gets Loaded
|
|
15
|
+
|
|
16
|
+
1. `lambda/config/environment.rb` — your app's boot file (AWS setup, models, libs)
|
|
17
|
+
2. IRB starts with `reload!` available
|
|
18
|
+
3. `.irbrc` from project root (optional console customization)
|
|
19
|
+
|
|
20
|
+
## Runner Mode
|
|
21
|
+
|
|
22
|
+
Execute a command and exit (useful for scripts/CI):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
belt c dev01 --run "Customer.first"
|
|
26
|
+
belt c prod --run "Post.count"
|
|
27
|
+
belt c dev01 --run "User.where(status: 'active', index: 'StatusIndex').count"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Production Safety
|
|
31
|
+
|
|
32
|
+
When the environment is `prod`, Belt shows a confirmation prompt before
|
|
33
|
+
starting the console. This prevents accidentally running destructive commands
|
|
34
|
+
against production data.
|
|
35
|
+
|
|
36
|
+
## Common Tasks
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
# Find a record
|
|
40
|
+
post = Post.find("post-id-123")
|
|
41
|
+
|
|
42
|
+
# Query with index
|
|
43
|
+
users = User.where(status: "active", index: "StatusIndex")
|
|
44
|
+
|
|
45
|
+
# Create a record
|
|
46
|
+
Post.create!(title: "Test", body: "Hello", user_id: "u-123")
|
|
47
|
+
|
|
48
|
+
# Count records
|
|
49
|
+
Order.count
|
|
50
|
+
|
|
51
|
+
# Reload code changes
|
|
52
|
+
reload!
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Environment Resolution
|
|
56
|
+
|
|
57
|
+
Priority order:
|
|
58
|
+
1. Explicit argument: `belt c prod`
|
|
59
|
+
2. `BELT_ENV` environment variable
|
|
60
|
+
3. Default: `dev`
|
|
61
|
+
|
|
62
|
+
## See Also
|
|
63
|
+
|
|
64
|
+
- `belt explain models` — ActiveItem query methods
|
|
65
|
+
- `belt explain structure` — where environment.rb lives
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Controllers
|
|
2
|
+
|
|
3
|
+
Belt controllers inherit from `BeltController::Base` and handle HTTP requests
|
|
4
|
+
dispatched by `Belt::ActionRouter`. They provide callbacks, strong parameters,
|
|
5
|
+
response helpers, and error handling — similar to Rails ActionController.
|
|
6
|
+
|
|
7
|
+
## Basic Controller
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
module MyApp
|
|
11
|
+
class PostsController < ApplicationController
|
|
12
|
+
def index
|
|
13
|
+
@posts = Post.all
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def show
|
|
17
|
+
@post = Post.find(params["id"])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create
|
|
21
|
+
attrs = params.require(:post).permit(:title, :body).to_h
|
|
22
|
+
@post = Post.create!(attrs.merge(user_id: current_user_id))
|
|
23
|
+
response_status :created
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy
|
|
27
|
+
Post.find(params["id"]).destroy
|
|
28
|
+
head :no_content
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Response Behavior
|
|
35
|
+
|
|
36
|
+
### Implicit Responses (default: JSON)
|
|
37
|
+
|
|
38
|
+
When an action sets instance variables and returns without calling a response
|
|
39
|
+
helper, Belt auto-serializes assigns into a JSON response:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
def index
|
|
43
|
+
@posts = Post.all # → { "posts": [...] }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def show
|
|
47
|
+
@post = Post.find(params["id"]) # → { "post": {...} }
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Explicit Response Helpers
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
success_response({ id: "123", name: "Example" }) # 200 JSON
|
|
55
|
+
success_response({ id: "123" }, :created) # 201 JSON
|
|
56
|
+
error_response("Not found", :not_found) # 404 JSON
|
|
57
|
+
error_response("Nope", :unprocessable_entity) # 422 JSON
|
|
58
|
+
html_response("<h1>Hello</h1>") # 200 HTML
|
|
59
|
+
head :no_content # 204 empty
|
|
60
|
+
head :created # 201 empty
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Non-200 with Implicit Assigns
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
def create
|
|
67
|
+
@post = Post.create!(...)
|
|
68
|
+
response_status :created # → 201 + { "post": {...} }
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Default Format
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# Global (in config/environment.rb)
|
|
76
|
+
Belt.configure do |config|
|
|
77
|
+
config.default_format = :json # or :html
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Per-controller
|
|
81
|
+
class PagesController < ApplicationController
|
|
82
|
+
self.default_format = :html
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- `:json` — assigns become JSON body
|
|
87
|
+
- `:html` — renders `views/<controller>/<action>.html.erb`
|
|
88
|
+
|
|
89
|
+
## Callbacks
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
class ApplicationController < BeltController::Base
|
|
93
|
+
before_action :authenticate!
|
|
94
|
+
before_action :require_admin!, except: [:health]
|
|
95
|
+
skip_before_action :authenticate!, only: [:health]
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Callbacks run in definition order. `before_action` can halt the request by
|
|
100
|
+
calling a response helper (e.g., `error_response`).
|
|
101
|
+
|
|
102
|
+
## Strong Parameters
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
params.require(:user).permit(:name, :email, address: [:street, :city])
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- `params` — merged hash of path parameters + parsed JSON body
|
|
109
|
+
- `require(:key)` — raises if key missing
|
|
110
|
+
- `permit(:field1, :field2)` — whitelists allowed fields
|
|
111
|
+
- Nested: `permit(:name, address: [:street, :city])`
|
|
112
|
+
|
|
113
|
+
## Error Handling
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
class ApplicationController < BeltController::Base
|
|
117
|
+
rescue_from ActiveItem::RecordNotFound, with: :not_found
|
|
118
|
+
rescue_from MyCustomError, with: :handle_custom
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def not_found(exception, _context = {})
|
|
123
|
+
error_response(exception.message, :not_found)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def handle_custom(exception, _context = {})
|
|
127
|
+
error_response(exception.message, :unprocessable_entity)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Controller Discovery
|
|
133
|
+
|
|
134
|
+
Belt resolves controllers by:
|
|
135
|
+
1. Checking the app's namespace module (e.g., `MyApp::PostsController`)
|
|
136
|
+
2. Searching `Belt.all_controller_paths`
|
|
137
|
+
|
|
138
|
+
No manual registration required. Controllers are auto-discovered from the
|
|
139
|
+
`lambda/controllers/` directory.
|
|
140
|
+
|
|
141
|
+
## CORS
|
|
142
|
+
|
|
143
|
+
CORS headers are handled automatically by `Belt::LambdaHandler`. Controllers
|
|
144
|
+
don't need to set them manually. Configure allowed origins via environment
|
|
145
|
+
variables:
|
|
146
|
+
|
|
147
|
+
- `CORS_ALLOWED_ORIGINS` — comma-separated origins
|
|
148
|
+
- `CUSTOMER_APP_DOMAIN` — primary app domain
|
|
149
|
+
- `OPS_APP_DOMAIN` — internal tools domain
|
|
150
|
+
|
|
151
|
+
## See Also
|
|
152
|
+
|
|
153
|
+
- `belt explain routing` — how requests reach controllers
|
|
154
|
+
- `belt explain models` — ActiveItem ORM
|
|
155
|
+
- `belt explain parameters` — strong parameters in detail
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
Belt deploys serverless applications to AWS using Terraform. The CLI wraps
|
|
4
|
+
`terraform init/plan/apply` with conventions for environment management,
|
|
5
|
+
Lambda packaging, and pre-deploy backups.
|
|
6
|
+
|
|
7
|
+
## Quick Deploy
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
belt deploy <env> # init → plan → apply (interactive)
|
|
11
|
+
belt deploy prod --auto # skip confirmation prompt
|
|
12
|
+
belt deploy prod --skip-backup # skip pre-deploy backup
|
|
13
|
+
belt deploy --backup-only # create recovery point without deploying
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## First-Time Setup
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 1. Create S3 bucket for Terraform state
|
|
20
|
+
belt setup state
|
|
21
|
+
|
|
22
|
+
# 2. Scaffold an environment
|
|
23
|
+
belt generate environment dev01
|
|
24
|
+
|
|
25
|
+
# 3. Generate DynamoDB table definitions from schema
|
|
26
|
+
belt setup tables dev01
|
|
27
|
+
|
|
28
|
+
# 4. Initialize and deploy
|
|
29
|
+
belt deploy dev01
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Environment Structure
|
|
33
|
+
|
|
34
|
+
Each environment lives in `infrastructure/<env>/`:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
infrastructure/
|
|
38
|
+
├── modules/
|
|
39
|
+
│ └── main/ # Shared Terraform module
|
|
40
|
+
├── dev01/
|
|
41
|
+
│ ├── main.tf # Module reference + provider
|
|
42
|
+
│ ├── variables.tf # Variable declarations
|
|
43
|
+
│ ├── terraform.tfvars # Environment-specific values
|
|
44
|
+
│ ├── backend.tf # S3 state backend
|
|
45
|
+
│ └── outputs.tf # Exported values
|
|
46
|
+
└── prod/
|
|
47
|
+
└── ...
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## What Gets Deployed
|
|
51
|
+
|
|
52
|
+
The Conveyor Belt Terraform provider reads your Ruby DSL and creates:
|
|
53
|
+
|
|
54
|
+
1. **API Gateway** — HTTP API with routes matching your DSL
|
|
55
|
+
2. **Lambda functions** — packaged Ruby code (one per `gateway`/`function` block)
|
|
56
|
+
3. **IAM roles** — least-privilege policies for DynamoDB table access
|
|
57
|
+
4. **CloudWatch logs** — log groups for each Lambda
|
|
58
|
+
5. **DynamoDB tables** — from your schema definition
|
|
59
|
+
6. **Custom domain** — if configured (Route53 + ACM certificate)
|
|
60
|
+
|
|
61
|
+
## Lambda Packaging
|
|
62
|
+
|
|
63
|
+
Belt packages the `lambda/` directory plus vendored gems. For `path:` gems
|
|
64
|
+
(local development), Belt materializes them into `vendor/cache` automatically.
|
|
65
|
+
|
|
66
|
+
The Lambda entry point is specified in `config/lambda/<name>.yml`:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
handler: lambda/<name>.lambda_handler
|
|
70
|
+
runtime: ruby3.3
|
|
71
|
+
timeout: 30
|
|
72
|
+
memory: 256
|
|
73
|
+
environment:
|
|
74
|
+
ENVIRONMENT: ${var.environment}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Route Manifests
|
|
78
|
+
|
|
79
|
+
Before deploying, generate the route manifest used at runtime:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
belt routes --namespace api
|
|
83
|
+
# → writes lambda/lib/routes/api_routes.rb
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This is typically done automatically by `belt deploy`.
|
|
87
|
+
|
|
88
|
+
## Sidecar Lambda Zips
|
|
89
|
+
|
|
90
|
+
Conveyor Belt packages Ruby lambdas. Some apps also have standalone
|
|
91
|
+
`aws_lambda_function` resources (Node image processors, Cognito triggers)
|
|
92
|
+
whose Terraform uses `filename` + `filebase64sha256` pointing at a zip on disk.
|
|
93
|
+
|
|
94
|
+
`belt deploy`, `belt plan`, and `belt apply` scan `infrastructure/**/*.tf` for
|
|
95
|
+
those zip paths and build any that are missing (or whose source hash changed):
|
|
96
|
+
|
|
97
|
+
- Directory with `package.json` — `npm ci` in Docker (`linux/amd64`, Lambda
|
|
98
|
+
Node image) so native addons like `sharp` match Lambda, then zip
|
|
99
|
+
- Plain JS directory — zip the `.js` / `.mjs` files
|
|
100
|
+
|
|
101
|
+
The zip lives next to the source (`image-processor/image-processor.zip`). This
|
|
102
|
+
is the same pre-terraform step Stowzilla's `scripts/deploy.sh` does for the
|
|
103
|
+
image processor. Existing zips with no hash file are left alone.
|
|
104
|
+
|
|
105
|
+
Docker must be running for Node packages.
|
|
106
|
+
|
|
107
|
+
## Terraform Commands
|
|
108
|
+
|
|
109
|
+
Belt wraps Terraform with environment awareness:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
belt init <env> # terraform init with correct backend
|
|
113
|
+
belt plan <env> # terraform plan
|
|
114
|
+
belt apply <env> # terraform apply
|
|
115
|
+
belt destroy <env> # terraform destroy (careful!)
|
|
116
|
+
belt output <env> # terraform output
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or use `belt deploy <env>` which runs init → plan → apply in sequence.
|
|
120
|
+
|
|
121
|
+
## Pre-Deploy Backups
|
|
122
|
+
|
|
123
|
+
Configure in `infrastructure/<env>/belt.rb`:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
Belt.configure do |config|
|
|
127
|
+
config.backups do
|
|
128
|
+
dynamodb :all
|
|
129
|
+
retention snapshots: 90
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Backups run automatically before each deploy (DynamoDB snapshots, Cognito
|
|
135
|
+
exports, S3 syncs). See `belt explain backups` for full documentation.
|
|
136
|
+
|
|
137
|
+
## Environment Variables
|
|
138
|
+
|
|
139
|
+
Set `BELT_ENV` to avoid typing the environment every time:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
export BELT_ENV=dev01
|
|
143
|
+
belt deploy # uses BELT_ENV
|
|
144
|
+
belt deploy prod # explicit arg wins
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Frontend Deployment
|
|
148
|
+
|
|
149
|
+
For apps with a frontend:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
belt deploy frontend <env> # build + deploy all frontends
|
|
153
|
+
belt deploy frontend <env> --frontend ops # deploy one named frontend
|
|
154
|
+
belt frontend env <env> # generate .env from Terraform outputs
|
|
155
|
+
belt frontend list # show configured frontends
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The directory is `frontend/` by default. Multiple SPAs are declared in
|
|
159
|
+
`config/frontends.yml` — see `belt explain frontend`. Full `belt deploy <env>`
|
|
160
|
+
deploys every configured frontend after terraform apply.
|
|
161
|
+
|
|
162
|
+
## See Also
|
|
163
|
+
|
|
164
|
+
- `belt explain routing` — how routes map to infrastructure
|
|
165
|
+
- `belt explain backups` — pre-deploy backup configuration
|
|
166
|
+
- `belt doctor` — check system dependencies before deploying
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Frontends
|
|
2
|
+
|
|
3
|
+
Belt can host one or more JavaScript SPAs (React, Vue, or Svelte) next to the
|
|
4
|
+
Lambda API. A typical app has a single `frontend/` directory. Some apps — Stowzilla
|
|
5
|
+
is the example that drove this — have several independent SPAs (customer, ops,
|
|
6
|
+
partners) that share the same models and API.
|
|
7
|
+
|
|
8
|
+
## Single frontend (default)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
belt new blog --frontend react
|
|
12
|
+
# or, in an existing app:
|
|
13
|
+
belt generate frontend react
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
That creates `frontend/`, S3 + CloudFront infrastructure, and wires CORS so the
|
|
17
|
+
SPA can call the API. No config file is required.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
belt server # local Vite dev server
|
|
21
|
+
belt frontend env dev01 # write frontend/.env from terraform outputs
|
|
22
|
+
belt deploy frontend dev01 # npm ci → build → S3 sync → CloudFront invalidation
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Multiple frontends
|
|
26
|
+
|
|
27
|
+
Declare them in `config/frontends.yml` (or `.belt/frontends.yml`):
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
frontends:
|
|
31
|
+
customer:
|
|
32
|
+
path: app
|
|
33
|
+
dist: build
|
|
34
|
+
default: true
|
|
35
|
+
bucket_output: web_app_bucket_name
|
|
36
|
+
url_output: web_app_url
|
|
37
|
+
cloudfront_domain_output: web_app_cloudfront_domain
|
|
38
|
+
ops:
|
|
39
|
+
path: ops-app
|
|
40
|
+
dist: build
|
|
41
|
+
bucket_output: ops_app_bucket_name
|
|
42
|
+
url_output: ops_app_url
|
|
43
|
+
cloudfront_domain_output: ops_app_cloudfront_domain
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`path` is the directory. `dist` is the build output (`dist` by default; belt
|
|
47
|
+
also auto-detects `build/` after `npm run build`). Terraform output names default
|
|
48
|
+
to `frontend_*` for the `frontend` app and `{name}_frontend_*` for others — override
|
|
49
|
+
them when existing infrastructure uses different names.
|
|
50
|
+
|
|
51
|
+
If terraform exports a CloudFront **domain** instead of a distribution ID, set
|
|
52
|
+
`cloudfront_domain_output` and skip `distribution_output`. Belt looks the ID up
|
|
53
|
+
via AWS and will not probe `{name}_frontend_distribution_id`.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
belt frontend list
|
|
57
|
+
belt generate frontend react --name ops --path ops-app
|
|
58
|
+
belt generate views bag --frontend ops
|
|
59
|
+
belt generate scaffold order --frontend customer
|
|
60
|
+
belt server --frontend ops
|
|
61
|
+
belt frontend env dev01 --frontend customer
|
|
62
|
+
belt deploy frontend dev01 # all configured frontends
|
|
63
|
+
belt deploy frontend dev01 --frontend ops # just ops
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If several frontends exist and you omit `--frontend`, generators and `belt server`
|
|
67
|
+
use the one marked `default: true`. If none is default, they ask you to pick.
|
|
68
|
+
|
|
69
|
+
`belt deploy frontend <env>` with no `--frontend` deploys every frontend that
|
|
70
|
+
has a `package.json`. Full `belt deploy <env>` does the same after terraform apply.
|
|
71
|
+
|
|
72
|
+
## Env maps
|
|
73
|
+
|
|
74
|
+
Each frontend can have its own `env.yml` mapping process env names to terraform
|
|
75
|
+
outputs:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
# app/env.yml
|
|
79
|
+
VITE_API_URL: api_url
|
|
80
|
+
VITE_COGNITO_USER_POOL_ID: cognito_user_pool_id
|
|
81
|
+
VITE_COGNITO_CLIENT_ID: cognito_client_id
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The default `frontend/` directory also accepts `.belt/frontend_env.yml` as a
|
|
85
|
+
fallback. See the existing env-map behavior: only mapped keys are written into
|
|
86
|
+
`.env`; missing terraform outputs warn and do not clobber local values.
|
|
87
|
+
|
|
88
|
+
## Infrastructure
|
|
89
|
+
|
|
90
|
+
`belt setup frontend` (and `belt generate frontend`) writes S3 + CloudFront into
|
|
91
|
+
`infrastructure/modules/app/frontend.tf`. Additional named frontends get
|
|
92
|
+
`{name}_frontend.tf` with unique resource names and outputs
|
|
93
|
+
(`ops_frontend_bucket_name`, …). Extra frontends get a CloudFront URL only —
|
|
94
|
+
custom DNS stays on the default frontend unless you add records yourself.
|
|
95
|
+
|
|
96
|
+
CORS: each CloudFront domain is added to `frontend_urls` on the conveyor-belt
|
|
97
|
+
resource so SPA → API calls work.
|
|
98
|
+
|
|
99
|
+
## See Also
|
|
100
|
+
|
|
101
|
+
- `belt explain deployment` — how frontend deploy fits into `belt deploy`
|
|
102
|
+
- `belt explain generators` — `belt generate frontend` / views
|
|
103
|
+
- `belt explain structure` — where frontend directories live
|