From 92e59790fb8f55175020d1850962e9c2956538c5 Mon Sep 17 00:00:00 2001 From: Pankaj Jangid Date: Sat, 4 Jul 2026 22:06:12 +0530 Subject: [PATCH 1/3] fix(docker): use repo-root-relative COPY paths for shared build context The shared metarepo build workflow builds with repo-root context (context: .), so the bot-dir-relative COPY paths no longer resolve. Copy package*.json and bot.ts by their repo-root-relative paths. Single-stage image and all other directives unchanged; tsconfig intentionally not copied (tsx needs none, tsc absent under npm ci --production). --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ba1addd..d5ed2a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ LABEL org.opencontainers.image.source="https://github.com/AlphaFiTech/alphafi-be WORKDIR /app -COPY package*.json ./ +COPY alphafi-betterstack/package*.json ./ RUN npm ci --production -COPY bot.ts ./ +COPY alphafi-betterstack/bot.ts ./ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD kill -0 1 || exit 1 From a5be64c34011a3d64b1f7ca58e933cebbf212699 Mon Sep 17 00:00:00 2001 From: Pankaj Jangid Date: Sat, 4 Jul 2026 22:54:30 +0530 Subject: [PATCH 2/3] style: apply prettier formatting to satisfy ci fmt:check --- CLAUDE.md | 12 +++++++----- README.md | 17 +++++++++-------- bot.ts | 29 +++++++++++++++++++++++------ eslint.config.mjs | 16 ++++++++-------- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c396065..6559ea8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,13 +9,14 @@ production so future changes are fast and safe. Authorized users are **not** in code. `ALLOWED_USER_IDS` (comma-separated numeric Telegram user IDs) is stored in AWS Secrets Manager and injected into the ECS task. -| | Staging | Production | -|---|---|---| -| Secret | `alphafi-betterstack-allowed-user-ids-staging` | `alphafi-betterstack-allowed-user-ids-production` | -| ECS service | `alphafi-betterstack-staging` | `alphafi-betterstack-production` | -| Bot | @AlphafiAsirStagingBot | @AlphafiAsirBot | +| | Staging | Production | +| ----------- | ---------------------------------------------- | ------------------------------------------------- | +| Secret | `alphafi-betterstack-allowed-user-ids-staging` | `alphafi-betterstack-allowed-user-ids-production` | +| ECS service | `alphafi-betterstack-staging` | `alphafi-betterstack-production` | +| Bot | @AlphafiAsirStagingBot | @AlphafiAsirBot | **Account / access (both environments):** + - AWS account: **v3** — `705393004398` - AWS profile: `v3-mgmt-admin` (SSO session `alphafi`) - Region: `us-east-1` @@ -73,6 +74,7 @@ To **remove** an ID, do the same read-then-write but build the new string withou that ID (e.g. `tr ',' '\n' | grep -vx "$ID" | paste -sd,`), then redeploy. ## Caveats learned the hard way + - The README's "Adding authorized users" snippet is outdated: it overwrites instead of appending and uses the cluster name `alphafi-production` (the real cluster is `AlphafiCluster-production`). Prefer the recipe above. diff --git a/README.md b/README.md index 9f38952..3ff7ff2 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ AlphaFi Security and Incident Response (ASIR) Bot — bridges Telegram `/alert` ## Environment variables -| Variable | Required | Description | -|---|---|---| -| `TELEGRAM_BOT_TOKEN` | Yes | Telegram bot token from @BotFather | -| `BETTER_STACK_API_TOKEN` | Yes | Better Stack API token | -| `ESCALATION_POLICY_ID` | Yes | Better Stack escalation policy ID (numeric string) | -| `ALLOWED_USER_IDS` | Yes | Comma-separated numeric Telegram user IDs authorized to trigger alerts | -| `REQUESTER_EMAIL` | No | Email shown in Better Stack incidents (default: `admin@alphafi.xyz`) | -| `LOG_LEVEL` | No | Pino log level: `fatal`, `error`, `warn`, `info`, `debug` (default: `info`) | +| Variable | Required | Description | +| ------------------------ | -------- | --------------------------------------------------------------------------- | +| `TELEGRAM_BOT_TOKEN` | Yes | Telegram bot token from @BotFather | +| `BETTER_STACK_API_TOKEN` | Yes | Better Stack API token | +| `ESCALATION_POLICY_ID` | Yes | Better Stack escalation policy ID (numeric string) | +| `ALLOWED_USER_IDS` | Yes | Comma-separated numeric Telegram user IDs authorized to trigger alerts | +| `REQUESTER_EMAIL` | No | Email shown in Better Stack incidents (default: `admin@alphafi.xyz`) | +| `LOG_LEVEL` | No | Pino log level: `fatal`, `error`, `warn`, `info`, `debug` (default: `info`) | ## Running locally @@ -46,6 +46,7 @@ make ci # typecheck + lint + fmt check ``` Install pre-commit hooks (requires [pre-commit](https://pre-commit.com)): + ```bash pre-commit install ``` diff --git a/bot.ts b/bot.ts index ba2ace1..f53357e 100644 --- a/bot.ts +++ b/bot.ts @@ -11,7 +11,10 @@ function escapeMarkdown(text: string): string { } function sanitizeForApi(text: string): string { - return text.replace(/[\x00-\x1F\x7F]/g, '').replace(/<[^>]*>/g, '').trim(); + return text + .replace(/[\x00-\x1F\x7F]/g, '') + .replace(/<[^>]*>/g, '') + .trim(); } // Startup Guard: Validate critical environment variables @@ -36,7 +39,9 @@ if (!/^\d+$/.test(POLICY_ID)) { // Use numeric User IDs instead of usernames (more secure/immutable) const ALLOWED_USERS: string[] = process.env.ALLOWED_USER_IDS - ? process.env.ALLOWED_USER_IDS.split(',').map((id) => id.trim()).filter(Boolean) + ? process.env.ALLOWED_USER_IDS.split(',') + .map((id) => id.trim()) + .filter(Boolean) : []; if (ALLOWED_USERS.length === 0) { @@ -134,7 +139,10 @@ bot.command('alert', async (ctx: Context) => { { parse_mode: 'Markdown' }, ); - log.info({ incidentId, userId, userLabel: rawUserLabel }, 'ASIR policy triggered successfully'); + log.info( + { incidentId, userId, userLabel: rawUserLabel }, + 'ASIR policy triggered successfully', + ); } else { await ctx.telegram.editMessageText( ctx.chat!.id, @@ -145,10 +153,17 @@ bot.command('alert', async (ctx: Context) => { ); } } catch (error) { - const axiosError = error as { response?: { status?: number; data?: unknown }; message?: string }; + const axiosError = error as { + response?: { status?: number; data?: unknown }; + message?: string; + }; const statusCode = axiosError.response?.status ? `(Status: ${axiosError.response.status})` : ''; log.error( - { userId, status: axiosError.response?.status, detail: axiosError.response?.data ?? axiosError.message }, + { + userId, + status: axiosError.response?.status, + detail: axiosError.response?.data ?? axiosError.message, + }, 'ASIR policy trigger failed', ); @@ -161,7 +176,9 @@ bot.command('alert', async (ctx: Context) => { { parse_mode: 'Markdown' }, ); } else { - await ctx.reply(`❌ ASIR_bot Error: Failed to trigger Escalation Policy. ${statusCode}\nCheck API logs.`); + await ctx.reply( + `❌ ASIR_bot Error: Failed to trigger Escalation Policy. ${statusCode}\nCheck API logs.`, + ); } } }); diff --git a/eslint.config.mjs b/eslint.config.mjs index 119a9e6..dd1a6ac 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,23 +1,23 @@ -import tseslint from "@typescript-eslint/eslint-plugin"; -import tsparser from "@typescript-eslint/parser"; -import prettier from "eslint-config-prettier"; +import tseslint from '@typescript-eslint/eslint-plugin'; +import tsparser from '@typescript-eslint/parser'; +import prettier from 'eslint-config-prettier'; export default [ { - files: ["**/*.ts"], + files: ['**/*.ts'], languageOptions: { parser: tsparser, parserOptions: { - project: "./tsconfig.json", + project: './tsconfig.json', }, }, plugins: { - "@typescript-eslint": tseslint, + '@typescript-eslint': tseslint, }, rules: { ...tseslint.configs.recommended.rules, - "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], - "@typescript-eslint/explicit-function-return-type": "off", + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/explicit-function-return-type': 'off', }, }, prettier, From 42212098b7f9248c82d3dc4249383886c6137ee4 Mon Sep 17 00:00:00 2001 From: Pankaj Jangid Date: Sat, 4 Jul 2026 22:57:24 +0530 Subject: [PATCH 3/3] ci: drop docker build step (image build owned by metarepo pipeline) --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fc536f..46f26d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: - name: Format check run: npm run fmt:check - - - name: Build Docker image - run: docker build -t alphafi-betterstack:ci . + # Docker image build/publish is owned by the alphafi metarepo + # (build-alphafi-betterstack.yml -> shared-docker-build.yml), which builds + # this service's Dockerfile with the metarepo as the build context. The + # Dockerfile's COPY paths are metarepo-root-relative (alphafi-betterstack/...), + # so a standalone `docker build .` here cannot resolve them. CI keeps + # typecheck/lint/format; image build is verified in the metarepo pipeline.