/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ /** @jsxRuntime automatic */ /** @jsxImportSource hono/jsx */ import {Layout} from '@fluxer/admin/src/components/Layout'; import {Heading, Text} from '@fluxer/admin/src/components/ui/Typography'; import type {Session} from '@fluxer/admin/src/types/App'; import type {AdminConfig as Config} from '@fluxer/admin/src/types/Config'; import type {Flash} from '@fluxer/hono/src/Flash'; import type {UserAdminResponse} from '@fluxer/schema/src/domains/admin/AdminUserSchemas'; import {Button} from '@fluxer/ui/src/components/Button'; import {Card} from '@fluxer/ui/src/components/Card'; import {CsrfInput} from '@fluxer/ui/src/components/CsrfInput'; import {Input} from '@fluxer/ui/src/components/Form'; import {Grid, Stack} from '@fluxer/ui/src/components/Layout'; import type {FC} from 'hono/jsx'; export type BanType = 'ip' | 'email' | 'phone'; interface BanConfig { title: string; route: string; inputLabel: string; inputName: string; inputType: 'text' | 'email' | 'tel'; placeholder: string; entityName: string; activePage: string; } export function getBanConfig(banType: BanType): BanConfig { switch (banType) { case 'ip': return { title: 'IP Bans', route: '/ip-bans', inputLabel: 'IP Address or CIDR', inputName: 'ip', inputType: 'text', placeholder: '192.168.1.1 or 192.168.0.0/16', entityName: 'IP/CIDR', activePage: 'ip-bans', }; case 'email': return { title: 'Email Bans', route: '/email-bans', inputLabel: 'Email Address', inputName: 'email', inputType: 'email', placeholder: 'user@example.com', entityName: 'Email', activePage: 'email-bans', }; case 'phone': return { title: 'Phone Bans', route: '/phone-bans', inputLabel: 'Phone Number', inputName: 'phone', inputType: 'tel', placeholder: '+1234567890', entityName: 'Phone', activePage: 'phone-bans', }; } } export interface BanManagementPageProps { config: Config; session: Session; currentAdmin: UserAdminResponse | undefined; flash: Flash | undefined; assetVersion: string; banType: BanType; csrfToken: string; listResult?: {ok: true; bans: Array<{value: string; reverseDns?: string | null}>} | {ok: false; errorMessage: string}; } const BanCard: FC<{config: Config; banConfig: BanConfig; csrfToken: string}> = ({config, banConfig, csrfToken}) => { return ( Ban {banConfig.inputLabel}
); }; const CheckBanCard: FC<{config: Config; banConfig: BanConfig; csrfToken: string}> = ({ config, banConfig, csrfToken, }) => { return ( Check {banConfig.inputLabel} Ban Status
); }; const UnbanCard: FC<{config: Config; banConfig: BanConfig; csrfToken: string}> = ({config, banConfig, csrfToken}) => { return ( Remove {banConfig.inputLabel} Ban
); }; const BanListCard: FC<{ config: Config; banType: BanType; banConfig: BanConfig; listResult: BanManagementPageProps['listResult']; csrfToken: string; }> = ({config, banType, banConfig, listResult, csrfToken}) => { if (!listResult) return null; return ( Current bans {!listResult.ok ? ( Failed to load bans list: {listResult.errorMessage} ) : listResult.bans.length === 0 ? ( No {banConfig.entityName.toLowerCase()} bans found ) : (
{banType === 'ip' && } {listResult.bans.map((ban) => ( {banType === 'ip' && } ))}
{banConfig.inputLabel}Reverse DNSActions
{ban.value} Search users {ban.reverseDns ?? '—'}
)}
); }; export const BanManagementPage: FC = ({ config, session, currentAdmin, flash, assetVersion, banType, csrfToken, listResult, }) => { const banConfig = getBanConfig(banType); return ( {banConfig.title} ); };