mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5c2ab2b0f | |||
| 9e0908f43c |
@@ -6,7 +6,7 @@ import { t } from '@/lib/i18n';
|
||||
import type { Cipher } from '@/lib/types';
|
||||
import LoadingState from '@/components/LoadingState';
|
||||
import WebsiteIcon from '@/components/vault/WebsiteIcon';
|
||||
import { isCipherVisibleInNormalVault } from '@/components/vault/vault-page-helpers';
|
||||
import { formatTotp, isCipherVisibleInNormalVault } from '@/components/vault/vault-page-helpers';
|
||||
|
||||
interface TotpCodesPageProps {
|
||||
ciphers: Cipher[];
|
||||
@@ -26,13 +26,6 @@ function getTotpTimeState(): { windowId: number; remain: number } {
|
||||
};
|
||||
}
|
||||
|
||||
function formatTotp(code: string): string {
|
||||
if (!code) return code;
|
||||
if (code.length === 5) return `${code.slice(0, 2)} ${code.slice(2)}`;
|
||||
if (code.length < 6) return code;
|
||||
return `${code.slice(0, 3)} ${code.slice(3, 6)}`;
|
||||
}
|
||||
|
||||
function TotpListIcon({ cipher }: { cipher: Cipher }) {
|
||||
return <WebsiteIcon cipher={cipher} fallback={<Globe size={18} />} />;
|
||||
}
|
||||
|
||||
@@ -507,8 +507,9 @@ export function maskSecret(value: string): string {
|
||||
export function formatTotp(code: string): string {
|
||||
if (!code) return code;
|
||||
if (code.length === 5) return `${code.slice(0, 2)} ${code.slice(2)}`;
|
||||
if (code.length < 6) return code;
|
||||
return `${code.slice(0, 3)} ${code.slice(3, 6)}`;
|
||||
if (code.length <= 4) return code;
|
||||
if (code.length === 8) return `${code.slice(0, 4)} ${code.slice(4)}`;
|
||||
return code.replace(/(.{3})(?=.)/g, '$1 ');
|
||||
}
|
||||
|
||||
export function formatHistoryTime(value: string | null | undefined): string {
|
||||
|
||||
@@ -220,6 +220,25 @@ function normalizeTotpSecret(secret: string): string {
|
||||
return secret.toUpperCase().replace(/[\s-]/g, '').replace(/=+$/g, '');
|
||||
}
|
||||
|
||||
function readOtpAuthParam(raw: string, name: string): string {
|
||||
const queryStart = raw.indexOf('?');
|
||||
if (queryStart < 0) return '';
|
||||
const fragmentStart = raw.indexOf('#', queryStart + 1);
|
||||
const query = raw.slice(queryStart + 1, fragmentStart > queryStart ? fragmentStart : undefined);
|
||||
for (const part of query.split('&')) {
|
||||
const eq = part.indexOf('=');
|
||||
const key = eq >= 0 ? part.slice(0, eq) : part;
|
||||
if (key.trim().toLowerCase() !== name.toLowerCase()) continue;
|
||||
const value = eq >= 0 ? part.slice(eq + 1) : '';
|
||||
try {
|
||||
return decodeURIComponent(value.replace(/\+/g, ' '));
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function parseSteamSecret(raw: string): string {
|
||||
const match = raw.trim().match(/^steam:\/\/([^/?#]+)(?:[/?#].*)?$/i);
|
||||
if (!match?.[1]) return '';
|
||||
@@ -276,7 +295,8 @@ function parseTotpConfig(raw: string): TotpConfig {
|
||||
if (/^otpauth:\/\//i.test(s)) {
|
||||
try {
|
||||
const u = new URL(s);
|
||||
if (u.hostname.toLowerCase() !== 'totp') {
|
||||
const otpType = u.hostname.toLowerCase();
|
||||
if (otpType !== 'totp') {
|
||||
return { secret: '', steam: false, ...DEFAULT_TOTP_CONFIG };
|
||||
}
|
||||
const label = decodeURIComponent((u.pathname || '').replace(/^\/+/, '')).toLowerCase();
|
||||
@@ -291,7 +311,16 @@ function parseTotpConfig(raw: string): TotpConfig {
|
||||
period: parseTotpPositiveInt(u.searchParams.get('period'), DEFAULT_TOTP_CONFIG.period, 1, 3600),
|
||||
};
|
||||
} catch {
|
||||
return { secret: '', steam: false, ...DEFAULT_TOTP_CONFIG };
|
||||
const issuer = readOtpAuthParam(s, 'issuer').trim().toLowerCase();
|
||||
const algorithm = readOtpAuthParam(s, 'algorithm').trim().toLowerCase();
|
||||
const steam = issuer === 'steam' || algorithm === 'steam';
|
||||
return {
|
||||
secret: normalizeTotpSecret(readOtpAuthParam(s, 'secret')),
|
||||
steam,
|
||||
algorithm: steam ? 'SHA-1' : parseTotpHashAlgorithm(algorithm),
|
||||
digits: steam ? 5 : parseTotpPositiveInt(readOtpAuthParam(s, 'digits'), DEFAULT_TOTP_CONFIG.digits, 1, 10),
|
||||
period: parseTotpPositiveInt(readOtpAuthParam(s, 'period'), DEFAULT_TOTP_CONFIG.period, 1, 3600),
|
||||
};
|
||||
}
|
||||
}
|
||||
return { secret: normalizeTotpSecret(s), steam: false, ...DEFAULT_TOTP_CONFIG };
|
||||
|
||||
@@ -892,6 +892,10 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.totp-code-main strong {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.settings-module .field,
|
||||
.auth-card .field {
|
||||
margin-bottom: 8px;
|
||||
|
||||
@@ -952,7 +952,7 @@ select.input.duplicate-mode-toolbar-select {
|
||||
|
||||
.totp-codes-list {
|
||||
@apply grid w-full items-start gap-2.5;
|
||||
grid-template-columns: repeat(var(--totp-columns, 1), minmax(300px, 1fr));
|
||||
grid-template-columns: repeat(var(--totp-columns, 1), minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.totp-code-row {
|
||||
@@ -977,7 +977,7 @@ select.input.duplicate-mode-toolbar-select {
|
||||
}
|
||||
|
||||
.totp-code-main strong {
|
||||
@apply whitespace-nowrap text-[22px] leading-none;
|
||||
@apply min-w-0 whitespace-nowrap text-[22px] leading-none;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user