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 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 15:19:20 -07:00
parent 0bdac16250
commit 2c7fcdb5ae
6 changed files with 66 additions and 26 deletions

48
.env.example Normal file
View File

@@ -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=

View File

@@ -18,7 +18,7 @@
## Castopod (Podcast Publishing) ## Castopod (Podcast Publishing)
- **URL**: https://podcast.macneilmediagroup.com - **URL**: https://podcast.macneilmediagroup.com
- **Podcast handle**: `@LukeAtTheRoost` - **Podcast handle**: `@LukeAtTheRoost`
- **API Auth**: Basic auth (admin/podcast2026api) - **API Auth**: Basic auth (credentials in .env: CASTOPOD_USERNAME, CASTOPOD_PASSWORD)
- **Container**: `castopod-castopod-1` - **Container**: `castopod-castopod-1`
- **Database**: `castopod-mariadb-1` (user: castopod, db: castopod) - **Database**: `castopod-mariadb-1` (user: castopod, db: castopod)

View File

@@ -4003,9 +4003,9 @@ def _restore_signalwire_webhook():
# BunnyCDN config for public on-air status # BunnyCDN config for public on-air status
_BUNNY_STORAGE_ZONE = "lukeattheroost" _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_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): def _update_on_air_cdn(on_air: bool):

View File

@@ -42,7 +42,7 @@ echo "podcast-stats: starting hourly loop"
while true; do while true; do
echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') Running stats update..." echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ') Running stats update..."
if python podcast_stats.py --json --upload 2>&1; then 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" echo " ...done, heartbeat sent"
else else
echo " ...failed, will retry next hour" echo " ...failed, will retry next hour"

View File

@@ -22,6 +22,8 @@ import sys
from datetime import datetime, timezone from datetime import datetime, timezone
import requests 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-" YOUTUBE_PLAYLIST = "PLGq4uZyNV1yYH_rcitTTPVysPbC6-7pe-"
APPLE_PODCAST_ID = "1875205848" APPLE_PODCAST_ID = "1875205848"
@@ -33,9 +35,9 @@ DOCKER_BIN = "/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker"
CASTOPOD_DB_CONTAINER = "castopod-mariadb-1" CASTOPOD_DB_CONTAINER = "castopod-mariadb-1"
BUNNY_STORAGE_ZONE = "lukeattheroost" 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_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(): def _find_ytdlp():
@@ -243,13 +245,14 @@ def _run_db_query(sql):
docker_bin = path docker_bin = path
break break
db_pass = os.getenv("CASTOPOD_DB_PASS", "")
if docker_bin: if docker_bin:
cmd = [docker_bin, "exec", "-i", CASTOPOD_DB_CONTAINER, cmd = [docker_bin, "exec", "-i", CASTOPOD_DB_CONTAINER,
"mysql", "-u", "castopod", "-pBYtbFfk3ndeVabb26xb0UyKU", "castopod", "-N"] "mysql", "-u", "castopod", f"-p{db_pass}", "castopod", "-N"]
else: else:
cmd = [ cmd = [
"ssh", "-p", NAS_SSH_PORT, NAS_SSH, "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: try:
proc = subprocess.run(cmd, input=sql, capture_output=True, text=True, timeout=30) proc = subprocess.run(cmd, input=sql, capture_output=True, text=True, timeout=30)

View File

@@ -55,8 +55,8 @@ load_dotenv(Path(__file__).parent / ".env")
# Configuration # Configuration
CASTOPOD_URL = "https://podcast.macneilmediagroup.com" CASTOPOD_URL = "https://podcast.macneilmediagroup.com"
CASTOPOD_USERNAME = "admin" CASTOPOD_USERNAME = os.getenv("CASTOPOD_USERNAME", "admin")
CASTOPOD_PASSWORD = "podcast2026api" CASTOPOD_PASSWORD = os.getenv("CASTOPOD_PASSWORD")
PODCAST_ID = 1 PODCAST_ID = 1
PODCAST_HANDLE = "LukeAtTheRoost" PODCAST_HANDLE = "LukeAtTheRoost"
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY") OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
@@ -74,25 +74,14 @@ YT_PODCAST_PLAYLIST = "PLGq4uZyNV1yYH_rcitTTPVysPbC6-7pe-"
# Postiz (social media posting) # Postiz (social media posting)
POSTIZ_URL = "https://social.lukeattheroost.com" POSTIZ_URL = "https://social.lukeattheroost.com"
POSTIZ_JWT_SECRET = "9d499bab97b303506af6ae18b29a60e6b5a0b1049177f533232ad14dd9729814" POSTIZ_JWT_SECRET = os.getenv("POSTIZ_JWT_SECRET")
POSTIZ_USER_ID = "00c14319-9eac-42c3-a467-68d3c1634fe1" POSTIZ_USER_ID = os.getenv("POSTIZ_USER_ID")
POSTIZ_INTEGRATIONS = { POSTIZ_INTEGRATIONS = json.loads(os.getenv("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"},
}
# NAS Configuration for chapters upload # NAS Configuration for chapters upload
# BunnyCDN Storage # BunnyCDN Storage
BUNNY_STORAGE_ZONE = "lukeattheroost" 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 BUNNY_STORAGE_REGION = "la" # Los Angeles
NAS_HOST = "mmgnas" NAS_HOST = "mmgnas"
@@ -102,7 +91,7 @@ DOCKER_PATH = "/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker"
CASTOPOD_CONTAINER = "castopod-castopod-1" CASTOPOD_CONTAINER = "castopod-castopod-1"
MARIADB_CONTAINER = "castopod-mariadb-1" MARIADB_CONTAINER = "castopod-mariadb-1"
DB_USER = "castopod" DB_USER = "castopod"
DB_PASS = "BYtbFfk3ndeVabb26xb0UyKU" DB_PASS = os.getenv("CASTOPOD_DB_PASS")
DB_NAME = "castopod" DB_NAME = "castopod"
LOCK_FILE = Path(__file__).parent / ".publish.lock" LOCK_FILE = Path(__file__).parent / ".publish.lock"