diff --git a/ansible/roles/podman/defaults/main.yml b/ansible/roles/podman/defaults/main.yml index 011c388..cfd1e54 100644 --- a/ansible/roles/podman/defaults/main.yml +++ b/ansible/roles/podman/defaults/main.yml @@ -9,6 +9,18 @@ factorio_path: "{{ podman_volumes }}/factorio" fulfillr_path: "{{ podman_volumes }}/fulfillr" fulfillr_cases_table: "debyltech-cases-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" searxng_path: "{{ podman_volumes }}/searxng" hass_path: "{{ podman_volumes }}/hass" diff --git a/ansible/roles/podman/tasks/containers/base/awsddns.yml b/ansible/roles/podman/tasks/containers/base/awsddns.yml index 146340f..4970b0f 100644 --- a/ansible/roles/podman/tasks/containers/base/awsddns.yml +++ b/ansible/roles/podman/tasks/containers/base/awsddns.yml @@ -80,6 +80,35 @@ vars: 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 vars: container_name: awsddns-debyl diff --git a/ansible/roles/podman/tasks/containers/base/caddy.yml b/ansible/roles/podman/tasks/containers/base/caddy.yml index 2b7981f..51878a8 100644 --- a/ansible/roles/podman/tasks/containers/base/caddy.yml +++ b/ansible/roles/podman/tasks/containers/base/caddy.yml @@ -25,6 +25,7 @@ # Legacy volume mounts removed - Caddy manages certificates automatically # Mount static site directories - "/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" env: CADDY_ADMIN: "0.0.0.0:2019" diff --git a/ansible/roles/podman/tasks/containers/debyltech/fulfillr-dev.yml b/ansible/roles/podman/tasks/containers/debyltech/fulfillr-dev.yml new file mode 100644 index 0000000..d009108 --- /dev/null +++ b/ansible/roles/podman/tasks/containers/debyltech/fulfillr-dev.yml @@ -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 diff --git a/ansible/roles/podman/templates/caddy/Caddyfile.j2 b/ansible/roles/podman/templates/caddy/Caddyfile.j2 index 7161dac..048696f 100644 --- a/ansible/roles/podman/templates/caddy/Caddyfile.j2 +++ b/ansible/roles/podman/templates/caddy/Caddyfile.j2 @@ -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 # ============================================================================ diff --git a/ansible/roles/podman/templates/fulfillr/dev.json.j2 b/ansible/roles/podman/templates/fulfillr/dev.json.j2 new file mode 100644 index 0000000..e839704 --- /dev/null +++ b/ansible/roles/podman/templates/fulfillr/dev.json.j2 @@ -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" + } +} diff --git a/ansible/roles/podman/templates/fulfillr/production.json.j2 b/ansible/roles/podman/templates/fulfillr/production.json.j2 index 5cbae00..d4ed0cb 100644 --- a/ansible/roles/podman/templates/fulfillr/production.json.j2 +++ b/ansible/roles/podman/templates/fulfillr/production.json.j2 @@ -1,10 +1,12 @@ { "snipcart_api_key": "{{ snipcart_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 }}", "cases_table": "{{ fulfillr_cases_table }}", "tickets_table": "{{ fulfillr_tickets_table }}", + "store_database_url": "{{ fulfillr_store_database_url }}", + "store_auth_token": "{{ fulfillr_store_auth_token }}", "aws": { "access_key": "{{ fulfillr_access_key }}", "secret_key": "{{ fulfillr_secret_key }}", diff --git a/ansible/vars/vault.yml b/ansible/vars/vault.yml index b17760f..b0bf750 100644 Binary files a/ansible/vars/vault.yml and b/ansible/vars/vault.yml differ