fulfillr-dev: staging back-office container + Turso store prep

Add a second go-fulfillr container (fulfillr-dev) wired to the staging
Turso store + EasyPost/Stripe test keys via dev.json, served at
fulfillr-dev.debyltech.com (Caddy -> :9055), LAN-restricted like prod.

- fulfillr-dev.yml + dev.json.j2: the staging container, volumes, config
- defaults: fulfillr_dev_* vars; prod store URL stubbed off until cutover
- Caddyfile + caddy.yml: fulfillr-dev site block and static mount
- awsddns.yml: Route53 DDNS for the fulfillr-dev hostname
- production.json.j2: add store_database_url/store_auth, rename stripe key
  var to fulfillr_stripe_api_key
- vault.yml: dev + store/stripe secrets

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bastian de Byl
2026-06-06 00:23:07 -04:00
parent 2640d09cb5
commit e82ace6de3
8 changed files with 207 additions and 1 deletions
+12
View File
@@ -9,6 +9,18 @@ factorio_path: "{{ podman_volumes }}/factorio"
fulfillr_path: "{{ podman_volumes }}/fulfillr" fulfillr_path: "{{ podman_volumes }}/fulfillr"
fulfillr_cases_table: "debyltech-cases-prod" fulfillr_cases_table: "debyltech-cases-prod"
fulfillr_tickets_table: "debyltech-tickets-prod" fulfillr_tickets_table: "debyltech-tickets-prod"
# Turso ecommerce store (self-hosted checkout).
# PROD store is OFF until cutover (empty URL -> store routes not registered). At
# cutover set this to libsql://debyltech-store-prod-debyltech.aws-us-east-1.turso.io
# and add `fulfillr_store_auth_token` (prod RW token) to the vault.
fulfillr_store_database_url: ""
fulfillr_store_auth_token: ""
# Staging back-office (fulfillr-dev.debyltech.com, port 9055) -> staging Turso store.
# Its RW token is `fulfillr_dev_store_auth_token` and EasyPost test key is
# `fulfillr_dev_easypost_api_key`, both in the encrypted vault.
fulfillr_dev_path: "{{ podman_volumes }}/fulfillr-dev"
fulfillr_dev_server_name: fulfillr-dev.debyltech.com
fulfillr_dev_store_database_url: "libsql://debyltech-store-staging-debyltech.aws-us-east-1.turso.io"
gregtime_path: "{{ podman_volumes }}/gregtime" gregtime_path: "{{ podman_volumes }}/gregtime"
searxng_path: "{{ podman_volumes }}/searxng" searxng_path: "{{ podman_volumes }}/searxng"
hass_path: "{{ podman_volumes }}/hass" hass_path: "{{ podman_volumes }}/hass"
@@ -80,6 +80,35 @@
vars: vars:
container_name: awsddns-fulfillr container_name: awsddns-fulfillr
- import_tasks: podman/podman-check.yml
vars:
container_name: awsddns-fulfillr-dev
container_image: "{{ image }}"
# Staging back-office DNS — same Route53 zone + creds as prod fulfillr, just a
# different hostname (-> same host IP; Caddy routes both by Host header).
- name: create fulfillr-dev.debyltech.com awsddns server container
become: true
become_user: "{{ podman_user }}"
diff: false
containers.podman.podman_container:
name: awsddns-fulfillr-dev
image: "{{ image }}"
restart_policy: on-failure:3
log_driver: journald
env:
AWS_ZONE_TTL: 60
AWS_ZONE_ID: "{{ fulfillr_zone_id }}"
AWS_ZONE_HOSTNAME: "{{ fulfillr_dev_server_name }}"
AWS_ACCESS_KEY_ID: "{{ fulfillr_dns_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ fulfillr_dns_secret_key }}"
AWS_DEFAULT_REGION: "{{ fulfillr_region }}"
- name: create systemd startup job for awsddns-fulfillr-dev
include_tasks: podman/systemd-generate.yml
vars:
container_name: awsddns-fulfillr-dev
- import_tasks: podman/podman-check.yml - import_tasks: podman/podman-check.yml
vars: vars:
container_name: awsddns-debyl container_name: awsddns-debyl
@@ -25,6 +25,7 @@
# Legacy volume mounts removed - Caddy manages certificates automatically # Legacy volume mounts removed - Caddy manages certificates automatically
# Mount static site directories # Mount static site directories
- "/usr/local/share/fulfillr-site:/usr/local/share/fulfillr-site:ro" - "/usr/local/share/fulfillr-site:/usr/local/share/fulfillr-site:ro"
- "/usr/local/share/fulfillr-site-dev:/usr/local/share/fulfillr-site-dev:ro"
- "/usr/local/share/test-site:/srv/test-site:ro" - "/usr/local/share/test-site:/srv/test-site:ro"
env: env:
CADDY_ADMIN: "0.0.0.0:2019" CADDY_ADMIN: "0.0.0.0:2019"
@@ -0,0 +1,67 @@
---
# Staging back-office: a second go-fulfillr container (same image as prod) wired to
# the STAGING Turso store + EasyPost test key via dev.json. Served at
# fulfillr-dev.debyltech.com (Caddy -> :9055), LAN-restricted like prod.
- import_tasks: gitea/podman-gitea-login.yml
- name: create nginx fulfillr-site-dev directory
become: true
ansible.builtin.file:
path: /usr/local/share/fulfillr-site-dev
state: directory
owner: "fedora"
group: "wheel"
mode: 0755
- name: create fulfillr-dev host directory volumes
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: 0755
notify: restorecon podman
loop:
- "{{ fulfillr_dev_path }}"
- name: template fulfillr-dev config
become: true
ansible.builtin.template:
src: "templates/fulfillr/{{ item }}.j2"
dest: "{{ fulfillr_dev_path }}/{{ item }}"
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: 0644
loop:
- dev.json
notify:
- restorecon podman
- name: flush handlers
ansible.builtin.meta: flush_handlers
- import_tasks: podman/podman-check.yml
vars:
container_name: fulfillr-dev
container_image: "{{ image }}"
- name: create fulfillr-dev server container
become: true
become_user: "{{ podman_user }}"
containers.podman.podman_container:
name: fulfillr-dev
image: "{{ image }}"
image_strict: true
command: --config /config/dev.json
restart_policy: on-failure:3
log_driver: journald
volumes:
- "{{ fulfillr_dev_path }}:/config"
ports:
- 9055:8080/tcp
- name: create systemd startup job for fulfillr-dev
include_tasks: podman/systemd-generate.yml
vars:
container_name: fulfillr-dev
@@ -389,6 +389,53 @@
} }
} }
# Fulfillr DEV/staging - {{ fulfillr_dev_server_name }} (Static + API with IP restrictions)
{{ fulfillr_dev_server_name }} {
{{ ip_restricted_site() }}
@api {
path /api/*
}
# Handle API requests with CORS for local development
handle @api {
header {
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
Access-Control-Allow-Credentials "true"
}
# Handle preflight requests
@options {
method OPTIONS
}
handle @options {
respond "" 204
}
reverse_proxy localhost:9055
}
# Serve static files with SPA fallback
handle {
root * /usr/local/share/fulfillr-site-dev
try_files {path} {path}/ /index.html
file_server
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "same-origin"
}
log {
output file /var/log/caddy/fulfillr-dev.log
format json
}
}
# ============================================================================ # ============================================================================
# TEST/STAGING SITES # TEST/STAGING SITES
# ============================================================================ # ============================================================================
@@ -0,0 +1,48 @@
{# Staging back-office config (fulfillr-dev). Isolated dev tier:
- ecommerce store -> STAGING Turso (fulfillr_dev_store_*)
- EasyPost + Stripe -> TEST keys (fulfillr_dev_easypost_api_key / fulfillr_dev_stripe_api_key)
- AWS -> FulfillrAPI-Dev key (fulfillr_dev_access_key/secret_key), scoped to the -dev
DynamoDB tables + debyltech.reviewr.dev. Snipcart key + outreach HMAC secret are
reused read-only. Never touches prod data or live payment APIs. #}
{
"snipcart_api_key": "{{ snipcart_api_key }}",
"easypost_api_key": "{{ fulfillr_dev_easypost_api_key }}",
"stripe_api_key": "{{ fulfillr_dev_stripe_api_key }}",
"backinstock_table": "debyltech-backinstock-dev",
"cases_table": "debyltech-cases-dev",
"tickets_table": "debyltech-tickets-dev",
"store_database_url": "{{ fulfillr_dev_store_database_url }}",
"store_auth_token": "{{ fulfillr_dev_store_auth_token }}",
"aws": {
"access_key": "{{ fulfillr_dev_access_key }}",
"secret_key": "{{ fulfillr_dev_secret_key }}",
"region": "{{ fulfillr_region }}",
"bucket": "debyltech.reviewr.dev"
},
"tax": {
"ein": "{{ fulfillr_tax_ein }}",
"ioss": null
},
"sender_address": {
"city": "Newbury",
"country": "US",
"email": "sales@debyltech.com",
"name": "de Byl Technologies LLC",
"phone": "6034160859",
"state": "NH",
"street1": "976 Route 103",
"street2": "Unit 95",
"zip": "03255"
},
"outreach": {
"outreach_table": "debyltech-outreach-dev",
"unsubscribe_table": "debyltech-unsubscribe-dev",
"email_log_table": "debyltech-email-log-dev",
"reviews_table": "debyltech-reviews-dev",
"hmac_secret_arn": "{{ fulfillr_hmac_arn }}",
"ses_from_email": "noreply@debyltech.com",
"ses_reply_to": "support@debyltech.com",
"ses_region": "us-east-1",
"base_url": "https://debyltech.com"
}
}
@@ -1,10 +1,12 @@
{ {
"snipcart_api_key": "{{ snipcart_api_key }}", "snipcart_api_key": "{{ snipcart_api_key }}",
"easypost_api_key": "{{ easypost_api_key }}", "easypost_api_key": "{{ easypost_api_key }}",
"stripe_api_key": "{{ stripe_api_key }}", "stripe_api_key": "{{ fulfillr_stripe_api_key }}",
"backinstock_table": "{{ fulfillr_backinstock_table }}", "backinstock_table": "{{ fulfillr_backinstock_table }}",
"cases_table": "{{ fulfillr_cases_table }}", "cases_table": "{{ fulfillr_cases_table }}",
"tickets_table": "{{ fulfillr_tickets_table }}", "tickets_table": "{{ fulfillr_tickets_table }}",
"store_database_url": "{{ fulfillr_store_database_url }}",
"store_auth_token": "{{ fulfillr_store_auth_token }}",
"aws": { "aws": {
"access_key": "{{ fulfillr_access_key }}", "access_key": "{{ fulfillr_access_key }}",
"secret_key": "{{ fulfillr_secret_key }}", "secret_key": "{{ fulfillr_secret_key }}",
Binary file not shown.