feat(settings): добавить Bot API URL и ячейку ошибок уведомлений
Docker / build (push) Failing after 26s

Монитор больше не ставит «Внимание» по скрытому счётчику. Telegram можно слать через локальный telegram-bot-api.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-08-22 02:29:07 +07:00
co-authored by Cursor
parent 9214d6fbab
commit 9e0311b53a
21 changed files with 235 additions and 20 deletions
@@ -30,4 +30,16 @@ describe('settingsRepository', () => {
})
expect(settingsRepository.getRow('settings-main')?.telegramBotToken).toBe('new-token')
})
it('defaults telegramApiUrl to cloud origin and persists a custom URL', () => {
const created = settingsRepository.upsert('settings-main', {
telegramChatId: '-100',
})
expect(created.telegramApiUrl).toBe('https://api.telegram.org')
const updated = settingsRepository.upsert('settings-main', {
telegramApiUrl: 'http://127.0.0.1:8081/',
})
expect(updated.telegramApiUrl).toBe('http://127.0.0.1:8081')
})
})
+7
View File
@@ -3,6 +3,7 @@ import {
appSwitcherConfigSchema,
type AppSwitcherConfig,
} from '@cfdm/shared/contracts/app-switcher'
import { normalizeTelegramApiUrl } from '@cfdm/shared/contracts/settings'
import { getDb, schema } from '../index.js'
import {
getCurrentSpaceId,
@@ -111,6 +112,7 @@ function toDto(row: Row | undefined): SettingsDto | undefined {
webhookEnabled: Boolean(row.webhookEnabled),
integrationEnabled: Boolean(row.integrationEnabled),
showQuickActions: row.showQuickActions == null ? true : Boolean(row.showQuickActions),
telegramApiUrl: normalizeTelegramApiUrl(row.telegramApiUrl),
notifyIntervalMinutes: Number(row.notifyIntervalMinutes) || 60,
uptimeCheckIntervalMinutes: Number(row.uptimeCheckIntervalMinutes) || 5,
customFields: Array.isArray(customFields) ? customFields : [],
@@ -135,6 +137,7 @@ interface SettingsInput {
syncTariffsIntervalMinutes?: number
telegramBotToken?: string
telegramChatId?: string
telegramApiUrl?: string
telegramMessageThreadId?: string
notifyPaymentExpiryEnabled?: boolean
notifyNewTariffsEnabled?: boolean
@@ -179,6 +182,10 @@ function buildValues(id: string, spaceId: string, existing: Row | undefined, r:
: existing?.telegramBotToken ?? '',
telegramChatId:
r.telegramChatId !== undefined ? r.telegramChatId || '' : existing?.telegramChatId ?? '',
telegramApiUrl:
r.telegramApiUrl !== undefined
? normalizeTelegramApiUrl(r.telegramApiUrl)
: normalizeTelegramApiUrl(existing?.telegramApiUrl),
telegramMessageThreadId:
r.telegramMessageThreadId !== undefined
? r.telegramMessageThreadId || ''
+2
View File
@@ -146,6 +146,7 @@ const CORE_TABLE_MIGRATIONS: string[] = [
customFields TEXT,
telegramBotToken TEXT,
telegramChatId TEXT,
telegramApiUrl TEXT,
notifyPaymentExpiryEnabled INTEGER,
notifyNewTariffsEnabled INTEGER,
telegramMessageThreadId TEXT,
@@ -302,6 +303,7 @@ const COLUMN_MIGRATIONS: string[] = [
`ALTER TABLE settings ADD COLUMN showQuickActions INTEGER`,
`ALTER TABLE settings ADD COLUMN telegramBotToken TEXT`,
`ALTER TABLE settings ADD COLUMN telegramChatId TEXT`,
`ALTER TABLE settings ADD COLUMN telegramApiUrl TEXT`,
`ALTER TABLE settings ADD COLUMN notifyPaymentExpiryEnabled INTEGER`,
`ALTER TABLE settings ADD COLUMN notifyNewTariffsEnabled INTEGER`,
`ALTER TABLE settings ADD COLUMN telegramMessageThreadId TEXT`,
+1
View File
@@ -194,6 +194,7 @@ export const settings = sqliteTable('settings', {
customFields: text('customFields'),
telegramBotToken: text('telegramBotToken'),
telegramChatId: text('telegramChatId'),
telegramApiUrl: text('telegramApiUrl'),
notifyPaymentExpiryEnabled: integer('notifyPaymentExpiryEnabled'),
notifyNewTariffsEnabled: integer('notifyNewTariffsEnabled'),
telegramMessageThreadId: text('telegramMessageThreadId'),
+1
View File
@@ -196,6 +196,7 @@ CREATE TABLE IF NOT EXISTS settings (
customFields TEXT,
telegramBotToken TEXT,
telegramChatId TEXT,
telegramApiUrl TEXT,
notifyPaymentExpiryEnabled INTEGER,
notifyNewTariffsEnabled INTEGER,
telegramMessageThreadId TEXT,
+17
View File
@@ -2,6 +2,21 @@ import { z } from 'zod'
import { customFieldsSchema } from './custom-fields.js'
import { appSwitcherConfigSchema } from './app-switcher.js'
/** Cloud Bot API origin. Local telegram-bot-api: http://127.0.0.1:8081 or https via TLS proxy. */
export const DEFAULT_TELEGRAM_API_URL = 'https://api.telegram.org'
export function normalizeTelegramApiUrl(raw?: string | null): string {
const trimmed = String(raw ?? '').trim()
if (!trimmed) return DEFAULT_TELEGRAM_API_URL
return trimmed.replace(/\/+$/, '').replace(/\/bot$/i, '')
}
export const telegramApiUrlSchema = z
.string()
.optional()
.transform((value) => normalizeTelegramApiUrl(value))
.pipe(z.string().url('Невалидный URL'))
export const settingsSchema = z.object({
id: z.string().optional(),
baseCurrency: z.string().optional(),
@@ -15,6 +30,7 @@ export const settingsSchema = z.object({
uptimeCheckIntervalMinutes: z.coerce.number().optional(),
telegramBotToken: z.string().optional(),
telegramChatId: z.string().optional(),
telegramApiUrl: telegramApiUrlSchema,
telegramMessageThreadId: z.string().optional(),
notifyPaymentExpiryEnabled: z.boolean().optional(),
notifyNewTariffsEnabled: z.boolean().optional(),
@@ -36,6 +52,7 @@ export type Settings = z.infer<typeof settingsSchema>
export const telegramTestBodySchema = z.object({
telegramBotToken: z.string().optional(),
telegramChatId: z.string().optional(),
telegramApiUrl: z.string().url('Невалидный URL').or(z.literal('')).optional(),
telegramMessageThreadId: z.string().optional(),
})