core&ui: fix #973

This commit is contained in:
undefined committed 2025-04-04 20:40:23 +08:00
1 parent c061bc3b5b
commit 5d5f6c69d7
4 files changed
+60 -46

No files matched your search

+1 -2
View File
@@ -4,7 +4,7 @@ import moment from 'moment-timezone';
import type { Context } from '../context'; import type { Context } from '../context';
import { import {
CannotDeleteSystemDomainError, DomainJoinAlreadyMemberError, DomainJoinForbiddenError, ForbiddenError, CannotDeleteSystemDomainError, DomainJoinAlreadyMemberError, DomainJoinForbiddenError, ForbiddenError,
InvalidJoinInvitationCodeError, NotFoundError, OnlyOwnerCanDeleteDomainError, PermissionError, RoleAlreadyExistError, ValidationError, InvalidJoinInvitationCodeError, OnlyOwnerCanDeleteDomainError, PermissionError, RoleAlreadyExistError, ValidationError,
} from '../error'; } from '../error';
import type { DomainDoc } from '../interface'; import type { DomainDoc } from '../interface';
import avatar from '../lib/avatar'; import avatar from '../lib/avatar';
@@ -337,7 +337,6 @@ class DomainJoinHandler extends Handler {
domain.get(target), domain.get(target),
domain.collUser.findOne({ domainId: target, uid: this.user._id }), domain.collUser.findOne({ domainId: target, uid: this.user._id }),
]); ]);
if (!dudoc) throw new NotFoundError(target);
const assignedRole = this.user.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN) const assignedRole = this.user.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN)
? 'root' ? 'root'
: dudoc?.role || 'default'; : dudoc?.role || 'default';
+1 -1
View File
@@ -5,7 +5,7 @@ import {
// from https://github.com/logos // from https://github.com/logos
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const icon = '<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>'; const icon = '<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" /></svg>';
export default class LoginWithGithubService extends Service { export default class LoginWithGithubService extends Service {
static inject = ['oauth']; static inject = ['oauth'];
+6 -1
View File
@@ -24,7 +24,12 @@ export default class TelegramService extends Service {
icon, icon,
canRegister: config.canRegister, canRegister: config.canRegister,
callback: async function callback({ payload }) { callback: async function callback({ payload }) {
const parsed = JSON.parse(payload); let parsed;
try {
parsed = JSON.parse(payload);
} catch (e) {
throw new ForbiddenError('Invalid payload');
}
const hash = parsed.hash; const hash = parsed.hash;
delete parsed.hash; delete parsed.hash;
const dataCheckString = Object.keys(parsed).sort().map((key) => (`${key}=${parsed[key]}`)).join('\n'); const dataCheckString = Object.keys(parsed).sort().map((key) => (`${key}=${parsed[key]}`)).join('\n');
@@ -9,34 +9,31 @@ import {
delay, i18n, request, secureRandomString, tpl, delay, i18n, request, secureRandomString, tpl,
} from 'vj/utils'; } from 'vj/utils';
const t = (s) => escape(i18n(s));
async function changeMail() { async function changeMail() {
const changeMailDialog = new ActionDialog({ const changeMailDialog = new ActionDialog({
$body: tpl` $body: tpl(
<div class="typo"> <div className="typo" id="change-mail-dialog">
<p>${i18n('Please enter your current password and new email address:')}</p> <label>{i18n('Current Password')}
<label>${i18n('Current Password')} <div className="textbox-container">
<div class="textbox-container"> <input className="textbox" type="password" name="password" data-autofocus required></input>
<input class="textbox" type="password" name="password" data-autofocus required></input>
</div> </div>
</label> </label>
<label>${i18n('Current Email')} <label>{i18n('Current Email')}
<div class="textbox-container"> <div className="textbox-container">
<input class="textbox" type="text" name="currentEmail" value="${UserContext.mail}" disabled></input> <input className="textbox" type="text" name="currentEmail" value={UserContext.mail} disabled></input>
</div> </div>
</label> </label>
<label>${i18n('New Email')} <label>{i18n('New Email')}
<div class="textbox-container"> <div className="textbox-container">
<input class="textbox" type="text" name="mail" required></input> <input className="textbox" type="text" name="mail" required></input>
</div> </div>
</label> </label>
</div> </div>
`, ),
onDispatch(action) { onDispatch(action) {
if (action === 'ok') { if (action === 'ok') {
const $password = $('[name="password"]'); const $password = $('#change-mail-dialog [name="password"]');
const $mail = $('[name="mail"]'); const $mail = $('#change-mail-dialog [name="mail"]');
if (!$password.val() || !$mail.val()) { if (!$password.val() || !$mail.val()) {
if (!$password.val()) $password.focus(); if (!$password.val()) $password.focus();
else $mail.focus(); else $mail.focus();
@@ -162,35 +159,48 @@ async function enableAuthn(type: string) {
} }
export default new NamedPage('home_security', () => { export default new NamedPage('home_security', () => {
const menuLink = (inner: string, action?: string) => ` const MenuLink = ({ children, action, icon }: { children: React.ReactNode, action?: string, icon?: string }) => (
<li class="menu__item" ${action ? '' : 'disabled'}> <li className={`menu__item ${action ? '' : 'disabled'}`}>
<a class="menu__link" ${action ? `data-action="${action}"` : 'disabled'}>${inner}</a> <a className={`menu__link ${action ? '' : 'disabled'}`} data-action={action}>
{icon && <span className={`icon icon-${icon}`} />}
{children}
</a>
</li> </li>
`; );
const fingerprint = '<span class="icon icon-fingerprint"></span>';
$(document).on('click', '[name="auth_enable"]', async () => { $(document).on('click', '[name="auth_enable"]', async () => {
let $body = ` const platformAvailable = browserSupportsWebAuthn() && await platformAuthenticatorIsAvailable();
const $body = tpl(
<div> <div>
<h3>${t('Choose Authenticator Type')}</h3> <h3>{i18n('Choose Authenticator Type')}</h3>
<ol class="menu"> <ol className="menu">
${menuLink(`<span class="icon icon-platform--mobile"></span>${t('Two Factor Authentication')}`, 'tfa')} <MenuLink action="tfa" icon="platform--mobile">
<li class="menu__seperator"></li> {i18n('Two Factor Authentication')}
`; </MenuLink>
if (!window.isSecureContext || !browserSupportsWebAuthn()) { <li className="menu__seperator"></li>
const message = window.isSecureContext {(!window.isSecureContext || !browserSupportsWebAuthn()) ? (
? "Your browser doesn't support WebAuthn." <MenuLink icon="fingerprint">
: 'Webauthn is not available in insecure context.'; {window.isSecureContext
$body += menuLink(`${fingerprint}${t(message)}`); ? i18n("Your browser doesn't support WebAuthn.")
} else { : i18n('Webauthn is not available in insecure context.')}
if (!await platformAuthenticatorIsAvailable()) { </MenuLink>
$body += menuLink(`${fingerprint}${t("Your browser doesn't support platform authenticator.")}`); ) : (<>
} else { {platformAvailable ? (
$body += menuLink(`${fingerprint}${t('Your Device')}`, 'platform'); <MenuLink action="platform" icon="fingerprint">
} {i18n('Your Device')}
$body += menuLink(`<span class="icon icon-usb"></span>${t('Multi Platform Authenticator')}`, 'cross-platform'); </MenuLink>
} ) : (
$body += '</ol></div>'; <MenuLink icon="fingerprint">
{i18n("Your browser doesn't support platform authenticator.")}
</MenuLink>
)}
<MenuLink action="cross-platform" icon="usb">
{i18n('Multi Platform Authenticator')}
</MenuLink>
</>)}
</ol>
</div>
);
const action = await new ActionDialog({ $body, $action: [] }).open(); const action = await new ActionDialog({ $body, $action: [] }).open();
if (!action || action === 'cancel') return; if (!action || action === 'cancel') return;
if (action === 'tfa') enableTfa(); if (action === 'tfa') enableTfa();