From 267596a96988f962d99b41112ced5cfcb3063784 Mon Sep 17 00:00:00 2001 From: Terncode Date: Fri, 20 Mar 2026 19:26:44 +0100 Subject: [PATCH] Improve mock login --- .../shared/sign-in-box/sign-in-box.pug | 12 ++-- .../shared/sign-in-box/sign-in-box.ts | 27 +++++++- src/ts/server/routes/auth.ts | 61 ++++++++++++++++++- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/ts/components/shared/sign-in-box/sign-in-box.pug b/src/ts/components/shared/sign-in-box/sign-in-box.pug index fea2342..b301b2f 100644 --- a/src/ts/components/shared/sign-in-box/sign-in-box.pug +++ b/src/ts/components/shared/sign-in-box/sign-in-box.pug @@ -33,11 +33,13 @@ .sr-only Sign in using {{p.name}} div(*ngIf="local") - .btn-group.dropdown(dropdown) + div(*ngIf="mocks === undefined") + fa-icon([icon]="spinnerIcon" [fixedWidth]="true" [spin]="true") + | Loading mock logins + .btn-group.dropdown(dropdown *ngIf="mocks !== undefined") button.btn.btn-lg.btn-dark.dropdown-toggle(dropdownToggle) | Mock login .dropdown-menu(*dropdownMenu) - //- NOTE: change usernames to ids of accounts you want to use for testing - a.dropdown-item(href="/auth/local?password=x&username=57a3dc6f2f0019a161cdebf6" target="_self") Admin - a.dropdown-item(href="/auth/local?password=x&username=57ae2332a67f4dc52e123e14" target="_self") Test - a.dropdown-item(href="/auth/local?password=x&username=57ae2332a67f4dc52e123e15" target="_self") Other + a.dropdown-item(*ngFor="let a of mocks" [href]="'/auth/local?password=x&username=' + a[0]" target="_self") {{ a[1]}} + a.dropdown-item((click)="createNew()") Create new + \ No newline at end of file diff --git a/src/ts/components/shared/sign-in-box/sign-in-box.ts b/src/ts/components/shared/sign-in-box/sign-in-box.ts index e08e79e..070ca23 100644 --- a/src/ts/components/shared/sign-in-box/sign-in-box.ts +++ b/src/ts/components/shared/sign-in-box/sign-in-box.ts @@ -1,7 +1,10 @@ -import { Component, Output, EventEmitter } from '@angular/core'; +import { Component, Output, EventEmitter, OnInit } from '@angular/core'; import { signUpProviders, signInProviders, local } from '../../../client/data'; import { emptyIcon, oauthIcons } from '../../../client/icons'; import { OAuthProvider } from '../../../common/interfaces'; +import { HttpClient } from '@angular/common/http'; +import { noop } from 'rxjs'; +import { observableToPromise } from '../../../common/utils'; export function getProviderIcon(id: string) { return oauthIcons[id] || emptyIcon; @@ -12,11 +15,31 @@ export function getProviderIcon(id: string) { templateUrl: 'sign-in-box.pug', styleUrls: ['sign-in-box.scss'], }) -export class SignInBox { +export class SignInBox implements OnInit { readonly signUpProviders = signUpProviders; readonly signInProviders = signInProviders; readonly local = local || DEVELOPMENT; @Output() signIn = new EventEmitter(); + mocks?: string[][] = undefined; + constructor(private http: HttpClient) {} + + async ngOnInit() { + observableToPromise(this.http.get('/auth/mocks', undefined)) + .then(data => { + this.mocks = data; + }) + .catch(noop) + } + async createNew() { + const name = prompt('Account name'); + if (name) { + const data = await observableToPromise(this.http.post('/auth/create-account', {name })); + if (!this.mocks) { + this.mocks = []; + } + this.mocks.push(data); + } + } icon(id: string) { return getProviderIcon(id); } diff --git a/src/ts/server/routes/auth.ts b/src/ts/server/routes/auth.ts index 577f415..0b48ee2 100644 --- a/src/ts/server/routes/auth.ts +++ b/src/ts/server/routes/auth.ts @@ -3,7 +3,7 @@ import { use, authenticate, AuthenticateOptions } from 'passport'; import { Strategy as LocalStrategy } from 'passport-local'; import { remove } from 'lodash'; import { MINUTE } from '../../common/constants'; -import { fromNow, hasFlag, includes } from '../../common/utils'; +import { fromNow, hasFlag, includes, uniqById } from '../../common/utils'; import { BannedMuted, Settings, ServerConfig, AccountFlags, ServerLiveSettings } from '../../common/adminInterfaces'; import { Account, IAccount, Origin, IOrigin } from '../db'; import { limit, auth as authRequest, wrap } from '../requestUtils'; @@ -290,6 +290,65 @@ export function authRoutes( })); if (mockLogin) { + app.get('/mocks', wrap(server, async () => { + const accounts: IAccount[] = []; + + const mostUsed = await Account.find({ + $or: [ + { roles: { $exists: false } }, + { roles: { $size: 0 } } + ], + _id: { $nin: accounts.map(acc => acc._id) } + }).sort({ lastVisit: -1 }).limit(5); + + accounts.push(...mostUsed); + + const superAdminAccount = await Account.findOne({ roles: { $in: ['superadmin'] } }).sort({ lastVisit: -1 }); + if (superAdminAccount) { + accounts.push(superAdminAccount); + } + + const adminAccount = await Account.findOne({ roles: { $in: ['admin'], $nin: ['superadmin'] } }) + .sort({ lastVisit: -1 }); + if (adminAccount) { + accounts.push(adminAccount); + } + const modAccount = await Account.findOne({ roles: { $in: ['mod'], $nin: ['superadmin', 'admin'] } }) + .sort({ lastVisit: -1 }); + if (modAccount) { + accounts.push(modAccount); + } + + const devAccount = await Account.findOne({ roles: { $in: ['dev'], $nin: ['superadmin', 'admin', 'mod'] } }) + .sort({ lastVisit: -1 }); + if (devAccount) { + accounts.push(devAccount); + } + const emptyRoleAccount = await Account.findOne({ + $or: [ + { roles: { $exists: false } }, + { roles: { $size: 0 } } + ], + _id: { $nin: accounts.map(acc => acc._id) } + }).sort({ lastVisit: -1 }); + if (emptyRoleAccount) { + accounts.push(emptyRoleAccount); + } + + return uniqById(accounts, acc => acc._id.toString()).map(a => [a!._id, `${a!.name} ${a!.roles.length ? `(${a!.roles.join(' ')})`: ''}`]); + })); + + app.post('/create-account', wrap(server, async (data) => { + const accountCount = await Account.countDocuments(); + + const account = await Account.create({ + name: data.body.name, + roles: accountCount === 0 ? ['superadmin'] : undefined + }); + system(account._id.toString(), `Created account by mock login}`); + return [account._id.toString(), account.name]; + })); + use(new LocalStrategy((login, _pass, done) => Account.findById(login, done))); app.get('/local', authenticate('local', { successRedirect: '/', failureRedirect: '/failed-login' })); }