feat: add SMS diagnostics admin check
This commit is contained in:
@@ -5,6 +5,7 @@ import ExcelJS from 'exceljs';
|
||||
import { ok } from '../http/apiResponse.js';
|
||||
import { ApiError } from '../http/errors.js';
|
||||
import { query } from '../db/pool.js';
|
||||
import { diagnoseSmsConnectivity } from '../services/sms.js';
|
||||
import { parseRole, parseRoles, requireAdmin, requireCurrentUser, type QueryFn, type UserRow } from './security.js';
|
||||
|
||||
type Row = Record<string, unknown>;
|
||||
@@ -469,6 +470,11 @@ export function createAdminRouter(deps: AdminRouterDeps = { query }): Router {
|
||||
ok(res, await watcher1(dbQuery));
|
||||
}));
|
||||
|
||||
router.post('/sms/diagnostics', asyncRoute(async (req, res) => {
|
||||
await requireAdmin(dbQuery, req);
|
||||
ok(res, await diagnoseSmsConnectivity());
|
||||
}));
|
||||
|
||||
router.get('/teams', asyncRoute(async (req, res) => {
|
||||
await requireAdmin(dbQuery, req);
|
||||
ok(res, await listTeams(dbQuery));
|
||||
|
||||
@@ -12,6 +12,17 @@ export interface SmsMessage {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface SmsDiagnosticsResult {
|
||||
provider: string;
|
||||
url: string;
|
||||
ok: boolean;
|
||||
reachable: boolean;
|
||||
httpStatus: number | null;
|
||||
elapsedMs: number;
|
||||
checkedAt: string;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
type SmsApiResponse = {
|
||||
requestId?: string;
|
||||
requestTime?: string;
|
||||
@@ -120,6 +131,62 @@ function resolveHanbankSmsUrl(): string {
|
||||
return `${baseUrl.replace(/\/+$/, '')}/sens/sms`;
|
||||
}
|
||||
|
||||
export async function diagnoseSmsConnectivity(): Promise<SmsDiagnosticsResult> {
|
||||
const provider = smsProvider();
|
||||
const url = provider.toLowerCase() === 'hanbank' || provider.toLowerCase() === 'bok'
|
||||
? resolveHanbankSmsUrl()
|
||||
: smsApiUrl() ?? '';
|
||||
const startedAt = Date.now();
|
||||
|
||||
if (!url) {
|
||||
return {
|
||||
provider,
|
||||
url,
|
||||
ok: false,
|
||||
reachable: false,
|
||||
httpStatus: null,
|
||||
elapsedMs: 0,
|
||||
checkedAt: new Date().toISOString(),
|
||||
error: 'ACS_SMS_API_URL이 설정되지 않았습니다.',
|
||||
};
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), smsTimeoutMs());
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { accept: 'application/json' },
|
||||
signal: controller.signal,
|
||||
});
|
||||
return {
|
||||
provider,
|
||||
url,
|
||||
ok: true,
|
||||
reachable: true,
|
||||
httpStatus: response.status,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
checkedAt: new Date().toISOString(),
|
||||
error: null,
|
||||
};
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
provider,
|
||||
url,
|
||||
ok: false,
|
||||
reachable: false,
|
||||
httpStatus: null,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
checkedAt: new Date().toISOString(),
|
||||
error: message.slice(0, 500),
|
||||
};
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function isSuccessResponse(response: SmsApiResponse): boolean {
|
||||
const statusCode = String(response.statusCode ?? '').trim();
|
||||
const statusName = String(response.statusName ?? '').trim().toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user