function mayMigrateLegacyPassword(target) { return !!target && target.get('legacyPasswordMigrationAllowed') === true && target.get('passwordResetRequired') !== true && !target.get('isDisabled') && !target.get('isDeleted'); } async function migrateLegacyLogin(username,password) { const original=await migratedLoginTarget(username); if(!mayMigrateLegacyPassword(original)) fail(401,'账号或密码错误;如首次登录旧账号,请联系管理员重置密码'); const revision=Number(original.get('credentialRevision')||0); const verified=await verifyLegacyPassword(username,password),target=await migratedLoginTarget(username,verified); if(!target || target.id!==original.id || !mayMigrateLegacyPassword(target)) fail(401,'账号验证未通过'); target.set('credentialExpectedRevision',revision);target.set('credentialMigrationAttempt',true); target.setPassword(password);await target.save(null,{useMasterKey:true}); return Parse.User.logIn(String(target.get('username')),password); } async function changeAppPassword(input,current) { const oldPassword=String(input.currentPassword||''),password=String(input.newPassword||''); if(!oldPassword || password.length<8 || password.length>72)fail(400,'请输入当前密码及 8 至 72 位新密码'); if(oldPassword===password)fail(400,'新密码不能与当前密码相同'); const revision=Number(current.get('credentialRevision')||0); let verified;try{verified=await Parse.User.logIn(String(current.get('username')),oldPassword);}catch(error){if(Number(error.code)===101)fail(400,'当前密码不正确');throw error;} if(verified.id!==current.id)fail(403,'账号验证未通过'); current.set('credentialExpectedRevision',revision);current.setPassword(password);current.set('passwordResetRequired',false); try{await current.save(null,{useMasterKey:true});await revokeUserSessions(current);}finally{ // Reauthentication may have issued a temporary session even when saving failed. const q=new Parse.Query('_Session');q.equalTo('sessionToken',verified.getSessionToken());await Parse.Object.destroyAll(await q.find({useMasterKey:true}),{useMasterKey:true}); } return {changed:true,loginRequired:true}; } async function logoutAppSession(request,current) { const token=String(request.body?.token||''); if(token){const q=new Parse.Query('_Session');q.equalTo('user',current);q.equalTo('sessionToken',token);await Parse.Object.destroyAll(await q.find({useMasterKey:true}),{useMasterKey:true});} return {loggedOut:true}; }