fix: parse kiosk qr pass urls

This commit is contained in:
unknown
2026-07-22 19:22:32 +09:00
parent ed972a318d
commit 7c579608a0
2 changed files with 26 additions and 5 deletions

View File

@@ -149,12 +149,12 @@ export const getPass = (id: number) => request<VisitRequestView>(`/passes/${id}`
export const passQrUrl = (id: number) => `/api/passes/${id}/qr.png`; export const passQrUrl = (id: number) => `/api/passes/${id}/qr.png`;
// ===== Public pass + self-service kiosk (no login, token-based) ===== // ===== Public pass + self-service kiosk (no login, token-based) =====
export const getPublicPass = (token: string) => request<PublicPass>(`/public/passes/${token}`); export const getPublicPass = (token: string) => request<PublicPass>(`/public/passes/${encodeURIComponent(token)}`);
export const publicPassQrUrl = (token: string) => `/api/public/passes/${token}/qr.png`; export const publicPassQrUrl = (token: string) => `/api/public/passes/${encodeURIComponent(token)}/qr.png`;
export const publicCheckIn = (token: string) => export const publicCheckIn = (token: string) =>
request<AccessAction>(`/public/passes/${token}/check-in`, { method: 'POST' }); request<AccessAction>(`/public/passes/${encodeURIComponent(token)}/check-in`, { method: 'POST' });
export const publicCheckOut = (token: string) => export const publicCheckOut = (token: string) =>
request<AccessAction>(`/public/passes/${token}/check-out`, { method: 'POST' }); request<AccessAction>(`/public/passes/${encodeURIComponent(token)}/check-out`, { method: 'POST' });
// ===== Stats ===== // ===== Stats =====
export const getStatsSummary = () => request<StatsSummary>('/stats/summary'); export const getStatsSummary = () => request<StatsSummary>('/stats/summary');

View File

@@ -5,6 +5,27 @@ import { formatVisitRange } from '../status';
import { useQrScanner } from '../useQrScanner'; import { useQrScanner } from '../useQrScanner';
import { playChime } from '../chime'; import { playChime } from '../chime';
function extractQrToken(raw: string): string {
const text = raw.trim();
if (!text) return '';
try {
const url = new URL(text);
const passMatch = url.pathname.match(/\/pass\/([^/?#]+)/);
if (passMatch?.[1]) return decodeURIComponent(passMatch[1]);
const apiMatch = url.pathname.match(/\/public\/passes\/([^/?#]+)/);
if (apiMatch?.[1]) return decodeURIComponent(apiMatch[1]);
} catch {
// Not a URL; fall through to path/token parsing.
}
const pathMatch = text.match(/\/pass\/([^/?#]+)/) ?? text.match(/\/public\/passes\/([^/?#]+)/);
if (pathMatch?.[1]) return decodeURIComponent(pathMatch[1]);
return text;
}
/** /**
* Public entrance kiosk (no login). The visitor scans their phone QR, the * Public entrance kiosk (no login). The visitor scans their phone QR, the
* approved pass is shown, and they tap 입장/퇴장 to self check-in/out. * approved pass is shown, and they tap 입장/퇴장 to self check-in/out.
@@ -19,7 +40,7 @@ export const KioskPage: React.FC = () => {
const [manual, setManual] = useState(''); const [manual, setManual] = useState('');
const resolveToken = async (raw: string) => { const resolveToken = async (raw: string) => {
const t = raw.trim(); const t = extractQrToken(raw);
if (!t || busy || token) return; if (!t || busy || token) return;
setError(null); setError(null);
scanner.stop(); scanner.stop();