Merge pull request #190 from cita-777/codex/site-announcement-timezone-fix
[codex] fix site announcement timezone
This commit is contained in:
@@ -122,8 +122,10 @@ describe('settings and auth events', () => {
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const body = response.json() as { currentAdminIp?: string };
|
||||
const body = response.json() as { currentAdminIp?: string; serverTimeZone?: string };
|
||||
expect(body.currentAdminIp).toBe('203.0.113.5');
|
||||
expect(typeof body.serverTimeZone).toBe('string');
|
||||
expect((body.serverTimeZone || '').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('rejects proxy token that does not start with sk-', async () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
testDatabaseConnection,
|
||||
type MigrationDialect,
|
||||
} from '../../services/databaseMigrationService.js';
|
||||
import { formatUtcSqlDateTime } from '../../services/localTimeService.js';
|
||||
import { formatUtcSqlDateTime, getResolvedTimeZone } from '../../services/localTimeService.js';
|
||||
import { extractClientIp, isIpAllowed } from '../../middleware/auth.js';
|
||||
import { invalidateSiteProxyCache, normalizeSiteProxyUrl } from '../../services/siteProxy.js';
|
||||
import { performFactoryReset } from '../../services/factoryResetService.js';
|
||||
@@ -442,6 +442,7 @@ function getRuntimeSettingsResponse(currentAdminIp = '') {
|
||||
notifyCooldownSec: config.notifyCooldownSec,
|
||||
adminIpAllowlist: config.adminIpAllowlist,
|
||||
currentAdminIp,
|
||||
serverTimeZone: getResolvedTimeZone(),
|
||||
systemProxyUrl: config.systemProxyUrl,
|
||||
proxyErrorKeywords: config.proxyErrorKeywords,
|
||||
proxyEmptyContentFailEnabled: config.proxyEmptyContentFailEnabled,
|
||||
|
||||
@@ -39,10 +39,12 @@ export default function SiteAnnouncements() {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [clearing, setClearing] = useState(false);
|
||||
const [markingAll, setMarkingAll] = useState(false);
|
||||
const [serverTimeZone, setServerTimeZone] = useState<string | undefined>(undefined);
|
||||
const [highlightAnnouncementId, setHighlightAnnouncementId] = useState<number | null>(null);
|
||||
const rowRefs = useRef<Map<number, HTMLDivElement>>(new Map());
|
||||
const highlightTimerRef = useRef<number | null>(null);
|
||||
const viewerTimeZone = useMemo(() => readClientTimeZone(), []);
|
||||
const displayTimeZone = serverTimeZone || viewerTimeZone;
|
||||
|
||||
const siteNameById = useMemo(() => {
|
||||
const map = new Map<number, string>();
|
||||
@@ -56,12 +58,17 @@ export default function SiteAnnouncements() {
|
||||
if (silent) setRefreshing(true);
|
||||
else setLoading(true);
|
||||
try {
|
||||
const [announcementRows, siteRows] = await Promise.all([
|
||||
const [announcementRows, siteRows, runtimeInfo] = await Promise.all([
|
||||
api.getSiteAnnouncements(),
|
||||
api.getSites(),
|
||||
api.getRuntimeSettings().catch(() => null),
|
||||
]);
|
||||
setRows(Array.isArray(announcementRows) ? announcementRows : []);
|
||||
setSites(Array.isArray(siteRows) ? siteRows : []);
|
||||
const nextServerTimeZone = typeof runtimeInfo?.serverTimeZone === 'string'
|
||||
? runtimeInfo.serverTimeZone.trim()
|
||||
: '';
|
||||
setServerTimeZone(nextServerTimeZone || undefined);
|
||||
} catch (error: any) {
|
||||
toast.error(error?.message || '加载站点公告失败');
|
||||
} finally {
|
||||
@@ -219,9 +226,9 @@ export default function SiteAnnouncements() {
|
||||
<SiteAnnouncementContent content={row.content} />
|
||||
<div
|
||||
style={{ marginTop: 8, fontSize: 12, color: 'var(--color-text-muted)' }}
|
||||
title={viewerTimeZone ? `本地时区:${viewerTimeZone}` : undefined}
|
||||
title={displayTimeZone ? `本地时区:${displayTimeZone}` : undefined}
|
||||
>
|
||||
首次发现:{formatSiteAnnouncementSeenAt(row.firstSeenAt || row.lastSeenAt || '', viewerTimeZone)}
|
||||
首次发现:{formatSiteAnnouncementSeenAt(row.firstSeenAt || row.lastSeenAt || '', displayTimeZone)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -9,6 +9,7 @@ const { apiMock } = vi.hoisted(() => ({
|
||||
apiMock: {
|
||||
getSiteAnnouncements: vi.fn(),
|
||||
getSites: vi.fn(),
|
||||
getRuntimeSettings: vi.fn(),
|
||||
markSiteAnnouncementRead: vi.fn(),
|
||||
markAllSiteAnnouncementsRead: vi.fn(),
|
||||
clearSiteAnnouncements: vi.fn(),
|
||||
@@ -41,6 +42,9 @@ describe('SiteAnnouncements page', () => {
|
||||
apiMock.getSites.mockResolvedValue([
|
||||
{ id: 9, name: 'Sub Site', platform: 'sub2api' },
|
||||
]);
|
||||
apiMock.getRuntimeSettings.mockResolvedValue({
|
||||
serverTimeZone: 'UTC',
|
||||
});
|
||||
apiMock.getSiteAnnouncements.mockResolvedValue([
|
||||
{
|
||||
id: 12,
|
||||
@@ -50,8 +54,8 @@ describe('SiteAnnouncements page', () => {
|
||||
title: 'Maintenance',
|
||||
content: 'Window starts at 10:00',
|
||||
level: 'info',
|
||||
firstSeenAt: '2026-03-20 10:00:00',
|
||||
lastSeenAt: '2026-03-20 10:00:00',
|
||||
firstSeenAt: '2026-03-20 04:23:27',
|
||||
lastSeenAt: '2026-03-20 04:23:27',
|
||||
readAt: null,
|
||||
},
|
||||
]);
|
||||
@@ -100,8 +104,10 @@ describe('SiteAnnouncements page', () => {
|
||||
|
||||
expect(apiMock.getSiteAnnouncements).toHaveBeenCalled();
|
||||
expect(apiMock.getSites).toHaveBeenCalled();
|
||||
expect(apiMock.getRuntimeSettings).toHaveBeenCalled();
|
||||
expect(JSON.stringify(root!.toJSON())).toContain('站点公告');
|
||||
expect(JSON.stringify(root!.toJSON())).toContain('Maintenance');
|
||||
expect(JSON.stringify(root!.toJSON())).toContain('2026/03/20 04:23:27');
|
||||
|
||||
const highlightedRows = root!.root.findAll((node) => {
|
||||
const className = typeof node.props.className === 'string' ? node.props.className : '';
|
||||
|
||||
Reference in New Issue
Block a user