import { I18N } from './i18n.js';
import { bytesToBase64, base64ToBytes, concatBytes, pbkdf2, hkdfExpand, encryptBw, decryptBw, decryptStr, extractTotpSecret, calcTotpNow } from './crypto.js';
import { parseFieldType as parseFieldTypeUtil, selectedCount as selectedCountUtil, cipherTypeKey as cipherTypeKeyUtil, firstCipherUri as firstCipherUriUtil, hostFromUri as hostFromUriUtil } from './vault-utils.js';
export function startNodewardenApp(runtimeConfig) {
var app = document.getElementById('app');
var defaultKdfIterations = Number(runtimeConfig && runtimeConfig.defaultKdfIterations) || 600000;
var state = {
phase: 'loading',
lang: (navigator.language || '').toLowerCase().startsWith('zh') ? 'zh' : 'en',
msg: '',
msgType: 'ok',
inviteCode: '',
registerName: '',
registerEmail: '',
registerPassword: '',
registerPassword2: '',
session: null,
profile: null,
tab: 'vault',
ciphers: [],
folders: [],
folderFilterId: '',
vaultQuery: '',
vaultType: 'all',
showSelectedPassword: false,
vaultSearchComposing: false,
vaultSearchTimer: 0,
totpTicking: false,
totpTickBusy: false,
detailMode: 'view',
detailDraft: null,
createMenuOpen: false,
fieldModalOpen: false,
fieldModalType: 'text',
fieldModalLabel: '',
fieldModalValue: '',
selectedCipherId: '',
selectedMap: {},
users: [],
invites: [],
loginEmail: '',
loginPassword: '',
loginTotpToken: '',
loginTotpError: '',
pendingLogin: null,
totpSetupSecret: '',
totpSetupToken: '',
totpDisableOpen: false,
totpDisablePassword: '',
totpDisableError: ''
};
var NO_FOLDER_FILTER = '__none__';
var i18n = I18N;
function t(key) { return i18n[state.lang][key] || key; }
function esc(v) {
return String(v == null ? '' : v).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''');
}
function sessionKey() { return 'nodewarden.web.session.v2'; }
function setMsg(t, ty) { state.msg = t || ''; state.msgType = ty || 'ok'; render(); }
function clearMsg() { state.msg = ''; }
function renderMsg() { return state.msg ? '
' + esc(state.msg) + '
' : ''; }
function saveSession() { if (state.session) localStorage.setItem(sessionKey(), JSON.stringify(state.session)); else localStorage.removeItem(sessionKey()); }
function loadSession() { try { var r = localStorage.getItem(sessionKey()); if (!r) return null; var p = JSON.parse(r); if (!p || !p.accessToken || !p.refreshToken) return null; return p; } catch (e) { return null; } }
async function jsonOrNull(resp){ var t=await resp.text(); if(!t) return null; try{ return JSON.parse(t);} catch(e){ return null; } }
async function decryptVault(){
if(!state.session||!state.session.symEncKey||!state.session.symMacKey) return;
var encKey=base64ToBytes(state.session.symEncKey); var macKey=base64ToBytes(state.session.symMacKey);
for(var i=0;i0) state.selectedCipherId=state.ciphers[0].id; await decryptVault(); }
async function loadAdminData(){ if(!state.profile||state.profile.role!=='admin') return; var u=await authFetch('/api/admin/users',{method:'GET'}); if(u.ok){ var uj=await u.json(); state.users=uj.data||[]; } var i=await authFetch('/api/admin/invites?includeInactive=true',{method:'GET'}); if(i.ok){ var ij=await i.json(); state.invites=ij.data||[]; } }
function selectedCount(){ return selectedCountUtil(state.selectedMap); }
function cipherTypeKey(c){ return cipherTypeKeyUtil(c); }
function cipherTypeLabel(c){
var k=cipherTypeKey(c);
if(k==='login') return t('typeLogin');
if(k==='card') return t('typeCard');
if(k==='identity') return t('typeIdentity');
if(k==='note') return t('typeNote');
return t('typeOther');
}
function folderNameById(id){
for(var i=0;i=64) return { enc: raw.slice(0,32), mac: raw.slice(32,64), key: cipher.key };
}catch(e){}
}
return { enc: user.enc, mac: user.mac, key: null };
}
async function encryptTextValue(v, enc, mac){
var s=String(v==null?'':v);
if(!s) return null;
return encryptBw(new TextEncoder().encode(s), enc, mac);
}
function openCreateDraft(){
state.detailMode='create';
state.showSelectedPassword=true;
state.createMenuOpen=false;
state.detailDraft={
id: '',
type: 1,
name: '',
folderId: state.folderFilterId&&state.folderFilterId!==NO_FOLDER_FILTER?state.folderFilterId:'',
reprompt: false,
loginUsername: '',
loginPassword: '',
loginTotp: '',
websites: [''],
cardholderName: '',
cardNumber: '',
cardBrand: '',
cardExpMonth: '',
cardExpYear: '',
cardCode: '',
identTitle: '',
identFirstName: '',
identMiddleName: '',
identLastName: '',
identUsername: '',
identCompany: '',
identSsn: '',
identPassportNumber: '',
identLicenseNumber: '',
identEmail: '',
identPhone: '',
identAddress1: '',
identAddress2: '',
identAddress3: '',
identCity: '',
identState: '',
identPostalCode: '',
identCountry: '',
sshPrivateKey: '',
sshPublicKey: '',
sshFingerprint: '',
customFields: [],
notes: ''
};
}
function openEditDraft(cipher){
if(!cipher) return;
var login=cipher.login||{};
var uris=Array.isArray(login.uris)?login.uris:[];
var ws=[]; for(var i=0;i'+l+''; }
return opt('text',t('fieldText'))+opt('hidden',t('fieldHidden'))+opt('boolean',t('fieldBoolean'))+opt('linked',t('fieldLinked'));
}
function renderCreateMenu(){
if(!state.createMenuOpen) return '';
return '';
}
function renderFieldModal(){
if(!state.fieldModalOpen) return '';
return ''
+ '
'+t('addField')+'
'
+ ''
+ ''
+ ''
+ '
'
+ '
';
}
function fieldTypeTextByNum(n){
var x=parseFieldType(n);
if(x===1) return t('fieldHidden');
if(x===2) return t('fieldBoolean');
if(x===3) return t('fieldLinked');
return t('fieldText');
}
function renderCardBrandOptions(selected){
var s=String(selected||'').toLowerCase();
var brands=['','visa','mastercard','amex','discover','jcb','unionpay','dinersclub','maestro'];
var labels={ '':'-- Select --', visa:'Visa', mastercard:'Mastercard', amex:'American Express', discover:'Discover', jcb:'JCB', unionpay:'UnionPay', dinersclub:'Diners Club', maestro:'Maestro' };
var out='';
for(var i=0;i'+labels[b]+'';
}
return out;
}
function renderMonthOptions(selected){
var s=String(selected||'');
var out='';
for(var m=1;m<=12;m++){
var mm=m<10?('0'+m):String(m);
out += '';
}
return out;
}
function renderDraftTypeCards(d){
var typeNum=Number(d&&d.type||1);
if(typeNum===3){
return ''
+ '
Card details
'
+ '
Cardholder name
'
+ '
Number
'
+ '
Brand
'
+ '
Exp month
Exp year
'
+ '
Security code (CVV)
'
+ '
';
}
if(typeNum===4){
return ''
+ '
Personal details
'
+ '
Title
'
+ '
First name
'
+ '
Middle name
'
+ '
Last name
'
+ '
Username
'
+ '
Company
'
+ '
'
+ '
Identity
'
+ '
SSN
'
+ '
Passport number
'
+ '
License number
'
+ '
'
+ '
Contact information
'
+ '
Email
'
+ '
Phone
'
+ '
'
+ '
Address
'
+ '
Address 1
'
+ '
Address 2
'
+ '
Address 3
'
+ '
City / Town
'
+ '
State / Province
'
+ '
ZIP / Postal code
'
+ '
Country
'
+ '
';
}
if(typeNum===5){
return ''
+ '
SSH key
'
+ '
Private key
'
+ '
Public key
'
+ '
Fingerprint
'
+ '
';
}
if(typeNum===2){
return '';
}
return ''
+ '
'+t('credentials')+'
'
+ '
Username
'
+ '
Password
'
+ '
TOTP Secret
'
+ '
';
}
function renderReadOnlyCustomFields(cipher){
var fs=Array.isArray(cipher&&cipher.fields)?cipher.fields:[];
if(!fs.length) return '';
var rows='';
for(var i=0;i
';
}
function renderReadOnlyTypeDetails(c0, folderLabel, created, updated){
var typeNum=Number(c0&&c0.type||1);
var notes=c0&&((c0.decNotes||c0.notes)||'');
var baseHead=''
+ '
'
+ renderReadOnlyCustomFields(c0)
+ history;
}
var login=c0.login||{};
var username=login.decUsername||login.username||'';
var rawPwd=login.decPassword||login.password||'';
var masked=rawPwd?new Array(Math.max(rawPwd.length,12)+1).join('•'):'';
var pwdText=state.showSelectedPassword?rawPwd:masked;
var totp=login.decTotp||login.totp||'';
var uri0=firstCipherUri(c0);
return baseHead
+ '
';
}
function renderRegisterScreen(){
return ''
+ '
'
+ '
'+t('langSwitch')+'
'
+ '
'
+ '
'
+ ' '
+ '
'+t('register')+'
'
+ '
'+t('brand')+'
'
+ '
'
+ renderMsg()
+ ' '
+ ' '
+ '
'
+ '
';
}
function renderVaultTab(){
var list=filteredCiphers();
function renderFolderOptions(selectedId){
var html='';
for(var fi=0;fi'+esc(ff.decName||ff.name||ff.id)+'';
}
return html;
}
var rows='';
for(var i=0;i🌐')
: '🌐';
rows += ''
+ '
'
+ ''
+ '
'+icon+'
'+esc(nameText)+'
'+esc(subtitle||'')+'
'
+ '
';
}
if(!rows) rows='
'+t('noItems')+'
';
var c0=selectedCipher();
var detail='
'+t('selectItem')+'
';
if(state.detailMode==='create'){
var dc=state.detailDraft||{};
var wsHtml=''; var cws=Array.isArray(dc.websites)?dc.websites:[''];
for(var wci=0;wci
'+t('website')+' (URI)
'+(cws.length>1?'':'')+'';
}
var cfHtml=''; var cfs=Array.isArray(dc.customFields)?dc.customFields:[];
for(var cfi=0;cfi
';
} else if(c0){
var folderLabel=c0.folderId?folderNameById(c0.folderId):t('noFolder');
var updated=c0.revisionDate||c0.updatedAt||'';
var created=c0.creationDate||c0.createdAt||'';
if(state.detailMode==='edit'){
var de=state.detailDraft||{};
var ewsHtml=''; var ews=Array.isArray(de.websites)?de.websites:[''];
for(var wei=0;wei
'+t('website')+' (URI)
'+(ews.length>1?'':'')+'';
}
var efsHtml=''; var efs=Array.isArray(de.customFields)?de.customFields:[];
for(var efi=0;efi
';
}
function renderSettingsTab(){
var p=state.profile||{};
var secret=currentTotpSecret();
var qr='https://api.qrserver.com/v1/create-qr-code/?size=180x180&data='+encodeURIComponent(buildTotpUri(secret));
return ''
+ renderMsg()
+ '