Harden deploy token and config handling
This commit is contained in:
parent
c415e82500
commit
991ccdbbb1
2 changed files with 24 additions and 0 deletions
14
README.md
14
README.md
|
|
@ -42,6 +42,8 @@ Edit `.env` and set:
|
||||||
- `DASHBOARD_USERNAME`
|
- `DASHBOARD_USERNAME`
|
||||||
- `DASHBOARD_PASSWORD_HASH`
|
- `DASHBOARD_PASSWORD_HASH`
|
||||||
|
|
||||||
|
Use the raw Discord bot token value. Do not include a `Bot ` prefix.
|
||||||
|
|
||||||
Generate the dashboard password hash:
|
Generate the dashboard password hash:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
@ -95,9 +97,21 @@ Sign in with `DASHBOARD_USERNAME` and the password you used when generating `DAS
|
||||||
```sh
|
```sh
|
||||||
cp .env.deploy.example .env
|
cp .env.deploy.example .env
|
||||||
cp services.example.json services.json
|
cp services.example.json services.json
|
||||||
|
python3 status_bot.py --hash-password
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Paste the generated password hash into `.env` before starting the container.
|
||||||
|
|
||||||
|
Make sure Docker can read the service config and write runtime state:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
chmod 600 .env
|
||||||
|
chmod 644 services.json
|
||||||
|
mkdir -p state
|
||||||
|
chmod 755 state
|
||||||
|
```
|
||||||
|
|
||||||
The bot stores the Discord message ID in `state/status-message.json`. Keep that file mounted so the bot edits the same message after restarts.
|
The bot stores the Discord message ID in `state/status-message.json`. Keep that file mounted so the bot edits the same message after restarts.
|
||||||
|
|
||||||
The deploy compose joins your existing reverse-proxy network:
|
The deploy compose joins your existing reverse-proxy network:
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,13 @@ def env(name: str, default: str | None = None) -> str:
|
||||||
return value.strip()
|
return value.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_discord_token(token: str) -> str:
|
||||||
|
cleaned = token.strip().strip("\"'")
|
||||||
|
if cleaned.lower().startswith("bot "):
|
||||||
|
cleaned = cleaned[4:].strip()
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
|
||||||
def load_dotenv(path: Path = Path(".env")) -> None:
|
def load_dotenv(path: Path = Path(".env")) -> None:
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return
|
return
|
||||||
|
|
@ -189,6 +196,8 @@ def load_json(path: Path) -> dict[str, Any]:
|
||||||
data = json.load(handle)
|
data = json.load(handle)
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
raise ValueError(f"Config file not found: {path}") from exc
|
raise ValueError(f"Config file not found: {path}") from exc
|
||||||
|
except PermissionError as exc:
|
||||||
|
raise ValueError(f"Config file is not readable: {path}") from exc
|
||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
raise ValueError(f"Invalid JSON in {path}: {exc}") from exc
|
raise ValueError(f"Invalid JSON in {path}: {exc}") from exc
|
||||||
|
|
||||||
|
|
@ -876,6 +885,7 @@ def main() -> int:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
token = env("DISCORD_BOT_TOKEN")
|
token = env("DISCORD_BOT_TOKEN")
|
||||||
|
token = normalize_discord_token(token)
|
||||||
channel_id = env("DISCORD_CHANNEL_ID")
|
channel_id = env("DISCORD_CHANNEL_ID")
|
||||||
config_path = Path(env("ARCHIVE_STATUS_CONFIG", "services.json"))
|
config_path = Path(env("ARCHIVE_STATUS_CONFIG", "services.json"))
|
||||||
state_path = Path(env("ARCHIVE_STATUS_STATE", "state/status-message.json"))
|
state_path = Path(env("ARCHIVE_STATUS_STATE", "state/status-message.json"))
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue