-
Notifications
You must be signed in to change notification settings - Fork 0
Add admin VPS console, tips persistence, HTTPS health, safe path handling and admin UX improvements #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add admin VPS console, tips persistence, HTTPS health, safe path handling and admin UX improvements #74
Changes from all commits
8bda869
d177515
12e7aa4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -199,12 +199,6 @@ mkdir -p "$PROJECT_DIR/data" | |||||||||||||
| mkdir -p "$PROJECT_DIR/data/backups" | ||||||||||||||
| touch "$MAIN_LOG" "$ERROR_LOG" "$ADMIN_EVENTS_LOG" "$SSHV_SESSIONS_FILE" # create if missing; safe on existing files | ||||||||||||||
|
|
||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The data directory and JSON/log files are created by this script (often run as root), but the Node service typically runs under a non-root service user; without correcting ownership, the service user cannot write to Severity Level: Critical 🚨- ❌ /sshv admin console sessions fail to persist across restarts.
- ❌ Admin events log writes fail or crash admin commands.
- ⚠️ God-mode heal diagnostics see stale admin-events.log data.
- ⚠️ Future JSON persistence features under data/ may break.
Suggested change
Steps of Reproduction ✅1. On a VPS host, create a dedicated system user `runewager` and group (as implied by the
systemd unit user selection in `prod-run.sh:141-143`, where `svc_user="$APP_NAME"` and
`APP_NAME="runewager"` at `prod-run.sh:25`).
2. As root, run `prod-run.sh` from the project root; it resolves `PROJECT_DIR` and creates
runtime paths at `prod-run.sh:59-62`:
- `mkdir -p "$PROJECT_DIR/data"` and `"$PROJECT_DIR/data/backups"`
- `touch "$PROJECT_DIR/data/admin-events.log"` (via `ADMIN_EVENTS_LOG` at
`prod-run.sh:29`)
- `touch "$PROJECT_DIR/data/sshv-sessions.json"` (via `SSHV_SESSIONS_FILE` at
`prod-run.sh:30`)
These directories and files are owned by root:root with default permissions (typically
755 for dirs, 644 for files).
3. The same script writes the systemd unit with `User=runewager` and `Group=runewager` in
`ensure_systemd_service()` at `prod-run.sh:133-170` and enables it; systemd then starts
`index.js` as the non-root `runewager` user from `WorkingDirectory=${PROJECT_DIR}`.
4. An admin uses Telegram admin features (e.g., `/sshv` or other admin commands) handled
in `index.js`, which:
- Defines `const sshvSessionsFile = path.join(dataDir, 'sshv-sessions.json');` at
`index.js:206` and later calls `saveJson(sshvSessionsFile, entries);` in
`persistSshvSessions()` at `index.js:969`.
- Defines `const adminEventsLogFile = path.join(dataDir, 'admin-events.log');` at
`index.js:2141` and appends events with `fs.appendFileSync(adminEventsLogFile,
JSON.stringify(entry) + '\n');` at `index.js:2152`.
- Uses `function saveJson(filePath, data) { ... }` declared at `index.js:2271` to write
JSON files under `data/`.
When these writes execute under the `runewager` user, the process lacks write
permission to the root-owned `data/` directory and files, causing `EACCES` filesystem
errors. As a result, admin events are not logged, SSHV sessions cannot be persisted,
and depending on error handling, the admin flows may throw or silently fail to persist
data.Prompt for AI Agent 🤖This is a comment left during a code review.
**Path:** prod-run.sh
**Line:** 58:58
**Comment:**
*Logic Error: The data directory and JSON/log files are created by this script (often run as root), but the Node service typically runs under a non-root service user; without correcting ownership, the service user cannot write to `data/admin-events.log`, `sshv-sessions.json`, or other files under `data`, breaking persistence of admin events and SSHV sessions.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. |
||||||||||||||
| if id -u "$SERVICE_USER" >/dev/null 2>&1; then | ||||||||||||||
| chown -R "$SERVICE_USER:$SERVICE_GROUP" "$LOG_DIR" "$PROJECT_DIR/data" | ||||||||||||||
| fi | ||||||||||||||
| chmod 0750 "$PROJECT_DIR/data" "$PROJECT_DIR/data/backups" "$LOG_DIR" | ||||||||||||||
| chmod 0640 "$MAIN_LOG" "$ERROR_LOG" "$ADMIN_EVENTS_LOG" "$SSHV_SESSIONS_FILE" | ||||||||||||||
|
|
||||||||||||||
| say "Ensured runtime directories and log files exist" | ||||||||||||||
|
|
||||||||||||||
| # --------------------------------------------------------- | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Serialize ephemeral prompt state mutations through the per-user queue.
Lines 2111-2112, 2128-2129, and 2134-2137 mutate
userstate across async and timer boundaries withoutrunUserMutation, which can race with concurrent handlers for the same user.🔧 Proposed fix
async function deleteEphemeralBonusPrompt(ctx, user) { if (!user.ephemeralBonusMsgId || !user.ephemeralBonusChatId) return; + const msgId = user.ephemeralBonusMsgId; + const chatId = user.ephemeralBonusChatId; try { - await ctx.telegram.deleteMessage(user.ephemeralBonusChatId, user.ephemeralBonusMsgId); + await ctx.telegram.deleteMessage(chatId, msgId); } catch (_) { /* message may already be gone */ } - user.ephemeralBonusMsgId = null; - user.ephemeralBonusChatId = null; + await runUserMutation(user.id, async () => { + if (user.ephemeralBonusMsgId === msgId && user.ephemeralBonusChatId === chatId) { + user.ephemeralBonusMsgId = null; + user.ephemeralBonusChatId = null; + } + }); } async function sendEphemeralBonusPrompt(ctx, user) { + await deleteEphemeralBonusPrompt(ctx, user); const sent = await ctx.reply( @@ - user.ephemeralBonusMsgId = sent.message_id; - user.ephemeralBonusChatId = sent.chat.id; + await runUserMutation(user.id, async () => { + user.ephemeralBonusMsgId = sent.message_id; + user.ephemeralBonusChatId = sent.chat.id; + }); setTimeout(async () => { try { await ctx.telegram.deleteMessage(sent.chat.id, sent.message_id); } catch (_) { /* best effort */ } - if (user.ephemeralBonusMsgId === sent.message_id && user.ephemeralBonusChatId === sent.chat.id) { - user.ephemeralBonusMsgId = null; - user.ephemeralBonusChatId = null; - } + await runUserMutation(user.id, async () => { + if (user.ephemeralBonusMsgId === sent.message_id && user.ephemeralBonusChatId === sent.chat.id) { + user.ephemeralBonusMsgId = null; + user.ephemeralBonusChatId = null; + } + }); }, 15 * 1000); }As per coding guidelines
Use per-user mutation queues to prevent race conditions on concurrent bonus/state mutations.🤖 Prompt for AI Agents