From 2c7fcdb5aecbb0d3bf039abab9e723d62bc8fe1a Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Thu, 5 Mar 2026 15:19:20 -0700 Subject: [PATCH] Move hardcoded secrets to .env, add .env.example Castopod password, DB password, BunnyCDN keys, Postiz JWT/IDs, and monitoring token all moved to environment variables. Co-Authored-By: Claude Opus 4.6 --- .env.example | 48 ++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 2 +- backend/main.py | 4 ++-- deploy_stats_cron.sh | 2 +- podcast_stats.py | 11 ++++++---- publish_episode.py | 25 +++++++---------------- 6 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..905ad42 --- /dev/null +++ b/.env.example @@ -0,0 +1,48 @@ +# API Keys +OPENROUTER_API_KEY= +ELEVENLABS_API_KEY= +INWORLD_API_KEY= +OPENAI_API_KEY= + +# SignalWire (real callers) +SIGNALWIRE_PROJECT_ID= +SIGNALWIRE_SPACE= +SIGNALWIRE_TOKEN= +SIGNALWIRE_PHONE= +SIGNALWIRE_STREAM_URL= + +# Social media +TWITTER_API_KEY= +TWITTER_API_SECRET= +TWITTER_ACCESS_TOKEN= +TWITTER_ACCESS_TOKEN_SECRET= +CLIENT_SECRET_ID= +CLIENT_SECRET= +POSTIZ_URL= +POSTIZ_API_KEY= +BSKY_APP_PASSWORD= + +# Castopod +CASTOPOD_USERNAME=admin +CASTOPOD_PASSWORD= +CASTOPOD_DB_PASS= + +# Postiz publishing +POSTIZ_JWT_SECRET= +POSTIZ_USER_ID= +POSTIZ_INTEGRATIONS={} + +# BunnyCDN +BUNNY_STORAGE_KEY= +BUNNY_ACCOUNT_KEY= + +# Monitoring +HEARTBEAT_URL= + +# Google AI +GOOGLE_API_KEY= + +# Email (IMAP) +SUBMISSIONS_IMAP_HOST= +SUBMISSIONS_IMAP_USER= +SUBMISSIONS_IMAP_PASS= diff --git a/CLAUDE.md b/CLAUDE.md index f18a258..98812b2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ ## Castopod (Podcast Publishing) - **URL**: https://podcast.macneilmediagroup.com - **Podcast handle**: `@LukeAtTheRoost` -- **API Auth**: Basic auth (admin/podcast2026api) +- **API Auth**: Basic auth (credentials in .env: CASTOPOD_USERNAME, CASTOPOD_PASSWORD) - **Container**: `castopod-castopod-1` - **Database**: `castopod-mariadb-1` (user: castopod, db: castopod) diff --git a/backend/main.py b/backend/main.py index bba2a73..e7ba691 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4003,9 +4003,9 @@ def _restore_signalwire_webhook(): # BunnyCDN config for public on-air status _BUNNY_STORAGE_ZONE = "lukeattheroost" -_BUNNY_STORAGE_KEY = "92749cd3-85df-4cff-938fe35eb994-30f8-4cf2" +_BUNNY_STORAGE_KEY = os.getenv("BUNNY_STORAGE_KEY", "") _BUNNY_STORAGE_REGION = "la" -_BUNNY_ACCOUNT_KEY = "2865f279-297b-431a-ad18-0ccf1f8e4fa8cf636cea-3222-415a-84ed-56ee195c0530" +_BUNNY_ACCOUNT_KEY = os.getenv("BUNNY_ACCOUNT_KEY", "") def _update_on_air_cdn(on_air: bool): diff --git a/deploy_stats_cron.sh b/deploy_stats_cron.sh index ae9d985..5d15e49 100755 --- a/deploy_stats_cron.sh +++ b/deploy_stats_cron.sh @@ -42,7 +42,7 @@ echo "podcast-stats: starting hourly loop" while true; do echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') Running stats update..." if python podcast_stats.py --json --upload 2>&1; then - curl -s "https://monitoring.macneilmediagroup.com/api/push/yk9tjJVUGVXhu4zjol2EvpepIlBTfFoD?status=up&msg=OK" > /dev/null + [ -n "$HEARTBEAT_URL" ] && curl -s "${HEARTBEAT_URL}?status=up&msg=OK" > /dev/null echo " ...done, heartbeat sent" else echo " ...failed, will retry next hour" diff --git a/podcast_stats.py b/podcast_stats.py index 1ff5fc3..550b826 100644 --- a/podcast_stats.py +++ b/podcast_stats.py @@ -22,6 +22,8 @@ import sys from datetime import datetime, timezone import requests +from dotenv import load_dotenv +load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")) YOUTUBE_PLAYLIST = "PLGq4uZyNV1yYH_rcitTTPVysPbC6-7pe-" APPLE_PODCAST_ID = "1875205848" @@ -33,9 +35,9 @@ DOCKER_BIN = "/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker" CASTOPOD_DB_CONTAINER = "castopod-mariadb-1" BUNNY_STORAGE_ZONE = "lukeattheroost" -BUNNY_STORAGE_KEY = "92749cd3-85df-4cff-938fe35eb994-30f8-4cf2" +BUNNY_STORAGE_KEY = os.getenv("BUNNY_STORAGE_KEY", "") BUNNY_STORAGE_REGION = "la" -BUNNY_ACCOUNT_KEY = "2865f279-297b-431a-ad18-0ccf1f8e4fa8cf636cea-3222-415a-84ed-56ee195c0530" +BUNNY_ACCOUNT_KEY = os.getenv("BUNNY_ACCOUNT_KEY", "") def _find_ytdlp(): @@ -243,13 +245,14 @@ def _run_db_query(sql): docker_bin = path break + db_pass = os.getenv("CASTOPOD_DB_PASS", "") if docker_bin: cmd = [docker_bin, "exec", "-i", CASTOPOD_DB_CONTAINER, - "mysql", "-u", "castopod", "-pBYtbFfk3ndeVabb26xb0UyKU", "castopod", "-N"] + "mysql", "-u", "castopod", f"-p{db_pass}", "castopod", "-N"] else: cmd = [ "ssh", "-p", NAS_SSH_PORT, NAS_SSH, - f"{DOCKER_BIN} exec -i {CASTOPOD_DB_CONTAINER} mysql -u castopod -pBYtbFfk3ndeVabb26xb0UyKU castopod -N" + f"{DOCKER_BIN} exec -i {CASTOPOD_DB_CONTAINER} mysql -u castopod -p{db_pass} castopod -N" ] try: proc = subprocess.run(cmd, input=sql, capture_output=True, text=True, timeout=30) diff --git a/publish_episode.py b/publish_episode.py index 87b92bf..29efc10 100755 --- a/publish_episode.py +++ b/publish_episode.py @@ -55,8 +55,8 @@ load_dotenv(Path(__file__).parent / ".env") # Configuration CASTOPOD_URL = "https://podcast.macneilmediagroup.com" -CASTOPOD_USERNAME = "admin" -CASTOPOD_PASSWORD = "podcast2026api" +CASTOPOD_USERNAME = os.getenv("CASTOPOD_USERNAME", "admin") +CASTOPOD_PASSWORD = os.getenv("CASTOPOD_PASSWORD") PODCAST_ID = 1 PODCAST_HANDLE = "LukeAtTheRoost" OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY") @@ -74,25 +74,14 @@ YT_PODCAST_PLAYLIST = "PLGq4uZyNV1yYH_rcitTTPVysPbC6-7pe-" # Postiz (social media posting) POSTIZ_URL = "https://social.lukeattheroost.com" -POSTIZ_JWT_SECRET = "9d499bab97b303506af6ae18b29a60e6b5a0b1049177f533232ad14dd9729814" -POSTIZ_USER_ID = "00c14319-9eac-42c3-a467-68d3c1634fe1" -POSTIZ_INTEGRATIONS = { - "facebook": {"id": "cmll9hwqj0001mt6xnas2f17w"}, - "instagram": {"id": "cmlljn8920001pk6qqzutqwik"}, - "discord": {"id": "cmllkprk90001uc6v6fwd5y9p", "channel": "1471386314447519754"}, - "bluesky": {"id": "cmlk29h780001p76qa7sstp5h"}, - "mastodon": {"id": "cmlk2r3mf0001le6vx9ey0k5a"}, - "nostr": {"id": "cmlll3y78000cuc6vh8dcpl2w"}, - "linkedin": {"id": "cmluar6cn0004o46x5a1u07vc"}, - "threads": {"id": "cmm13sxhq001mo46x24com5p7"}, - # TikTok excluded — requires video, not image posts. Use upload_clips.py instead. - # "tiktok": {"id": "cmm2ggsno0001md7134cam9t9"}, -} +POSTIZ_JWT_SECRET = os.getenv("POSTIZ_JWT_SECRET") +POSTIZ_USER_ID = os.getenv("POSTIZ_USER_ID") +POSTIZ_INTEGRATIONS = json.loads(os.getenv("POSTIZ_INTEGRATIONS", "{}")) # NAS Configuration for chapters upload # BunnyCDN Storage BUNNY_STORAGE_ZONE = "lukeattheroost" -BUNNY_STORAGE_KEY = "92749cd3-85df-4cff-938fe35eb994-30f8-4cf2" +BUNNY_STORAGE_KEY = os.getenv("BUNNY_STORAGE_KEY") BUNNY_STORAGE_REGION = "la" # Los Angeles NAS_HOST = "mmgnas" @@ -102,7 +91,7 @@ DOCKER_PATH = "/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker" CASTOPOD_CONTAINER = "castopod-castopod-1" MARIADB_CONTAINER = "castopod-mariadb-1" DB_USER = "castopod" -DB_PASS = "BYtbFfk3ndeVabb26xb0UyKU" +DB_PASS = os.getenv("CASTOPOD_DB_PASS") DB_NAME = "castopod" LOCK_FILE = Path(__file__).parent / ".publish.lock"