refactor: export TipiCache as a non-instantiated class
This commit is contained in:
parent
4e6a6b713d
commit
1d41f5de9b
13 changed files with 78 additions and 51 deletions
|
@ -1,6 +1,7 @@
|
||||||
|
const values = new Map();
|
||||||
|
const expirations = new Map();
|
||||||
|
|
||||||
export const createClient = jest.fn(() => {
|
export const createClient = jest.fn(() => {
|
||||||
const values = new Map();
|
|
||||||
const expirations = new Map();
|
|
||||||
return {
|
return {
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
connect: jest.fn(),
|
connect: jest.fn(),
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import merge from 'lodash.merge';
|
import merge from 'lodash.merge';
|
||||||
import { deleteCookie, setCookie } from 'cookies-next';
|
import { deleteCookie, setCookie } from 'cookies-next';
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
import TipiCache from '@/server/core/TipiCache/TipiCache';
|
import { TipiCache } from '@/server/core/TipiCache';
|
||||||
import { getAuthedPageProps, getMessagesPageProps } from '../page-helpers';
|
import { getAuthedPageProps, getMessagesPageProps } from '../page-helpers';
|
||||||
import englishMessages from '../../messages/en.json';
|
import englishMessages from '../../messages/en.json';
|
||||||
import frenchMessages from '../../messages/fr-FR.json';
|
import frenchMessages from '../../messages/fr-FR.json';
|
||||||
|
|
||||||
|
const cache = new TipiCache();
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cache.close();
|
||||||
|
});
|
||||||
|
|
||||||
describe('test: getAuthedPageProps()', () => {
|
describe('test: getAuthedPageProps()', () => {
|
||||||
it('should redirect to /login if there is no user id in session', async () => {
|
it('should redirect to /login if there is no user id in session', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
|
@ -23,7 +29,7 @@ describe('test: getAuthedPageProps()', () => {
|
||||||
it('should return props if there is a user id in session', async () => {
|
it('should return props if there is a user id in session', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const ctx = { req: { headers: { 'x-session-id': '123' } } };
|
const ctx = { req: { headers: { 'x-session-id': '123' } } };
|
||||||
await TipiCache.set('session:123', '456');
|
await cache.set('session:123', '456');
|
||||||
|
|
||||||
// act
|
// act
|
||||||
// @ts-expect-error - we're passing in a partial context
|
// @ts-expect-error - we're passing in a partial context
|
||||||
|
|
|
@ -2,11 +2,13 @@ import { GetServerSideProps } from 'next';
|
||||||
import merge from 'lodash.merge';
|
import merge from 'lodash.merge';
|
||||||
import { getLocaleFromString } from '@/shared/internationalization/locales';
|
import { getLocaleFromString } from '@/shared/internationalization/locales';
|
||||||
import { getCookie } from 'cookies-next';
|
import { getCookie } from 'cookies-next';
|
||||||
import TipiCache from '@/server/core/TipiCache/TipiCache';
|
import { TipiCache } from '@/server/core/TipiCache';
|
||||||
|
|
||||||
export const getAuthedPageProps: GetServerSideProps = async (ctx) => {
|
export const getAuthedPageProps: GetServerSideProps = async (ctx) => {
|
||||||
|
const cache = new TipiCache();
|
||||||
const sessionId = ctx.req.headers['x-session-id'];
|
const sessionId = ctx.req.headers['x-session-id'];
|
||||||
const userId = await TipiCache.get(`session:${sessionId}`);
|
const userId = await cache.get(`session:${sessionId}`);
|
||||||
|
await cache.close();
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { getConfig } from '@/server/core/TipiConfig/TipiConfig';
|
import { getConfig } from '@/server/core/TipiConfig/TipiConfig';
|
||||||
import TipiCache from '@/server/core/TipiCache/TipiCache';
|
import { TipiCache } from '@/server/core/TipiCache/TipiCache';
|
||||||
import { AuthQueries } from '@/server/queries/auth/auth.queries';
|
import { AuthQueries } from '@/server/queries/auth/auth.queries';
|
||||||
import { db } from '@/server/db';
|
import { db } from '@/server/db';
|
||||||
|
|
||||||
|
@ -13,12 +13,16 @@ import fs from 'fs-extra';
|
||||||
* @param {NextApiResponse} res - The response
|
* @param {NextApiResponse} res - The response
|
||||||
*/
|
*/
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const cache = new TipiCache();
|
||||||
|
|
||||||
const authService = new AuthQueries(db);
|
const authService = new AuthQueries(db);
|
||||||
|
|
||||||
const sessionId = req.headers['x-session-id'];
|
const sessionId = req.headers['x-session-id'];
|
||||||
const userId = await TipiCache.get(`session:${sessionId}`);
|
const userId = await cache.get(`session:${sessionId}`);
|
||||||
const user = await authService.getUserById(Number(userId));
|
const user = await authService.getUserById(Number(userId));
|
||||||
|
|
||||||
|
await cache.close();
|
||||||
|
|
||||||
if (user?.operator) {
|
if (user?.operator) {
|
||||||
const filePath = `${getConfig().rootFolder}/traefik/tls/cert.pem`;
|
const filePath = `${getConfig().rootFolder}/traefik/tls/cert.pem`;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { setCookie } from 'cookies-next';
|
import { setCookie } from 'cookies-next';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import TipiCache from '../core/TipiCache/TipiCache';
|
import { TipiCache } from '../core/TipiCache/TipiCache';
|
||||||
|
|
||||||
const COOKIE_MAX_AGE = 60 * 60 * 24; // 1 day
|
const COOKIE_MAX_AGE = 60 * 60 * 24; // 1 day
|
||||||
const COOKIE_NAME = 'tipi.sid';
|
const COOKIE_NAME = 'tipi.sid';
|
||||||
|
@ -11,10 +11,14 @@ export const generateSessionId = (prefix: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setSession = async (sessionId: string, userId: string, req: NextApiRequest, res: NextApiResponse) => {
|
export const setSession = async (sessionId: string, userId: string, req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
const cache = new TipiCache();
|
||||||
|
|
||||||
setCookie(COOKIE_NAME, sessionId, { req, res, maxAge: COOKIE_MAX_AGE, httpOnly: true, secure: true, sameSite: false });
|
setCookie(COOKIE_NAME, sessionId, { req, res, maxAge: COOKIE_MAX_AGE, httpOnly: true, secure: true, sameSite: false });
|
||||||
|
|
||||||
const sessionKey = `session:${sessionId}`;
|
const sessionKey = `session:${sessionId}`;
|
||||||
|
|
||||||
await TipiCache.set(sessionKey, userId);
|
await cache.set(sessionKey, userId);
|
||||||
await TipiCache.set(`session:${userId}:${sessionId}`, sessionKey);
|
await cache.set(`session:${userId}:${sessionId}`, sessionKey);
|
||||||
|
|
||||||
|
await cache.close();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { inferAsyncReturnType } from '@trpc/server';
|
import { inferAsyncReturnType } from '@trpc/server';
|
||||||
import { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
import { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
||||||
import TipiCache from './core/TipiCache/TipiCache';
|
import { TipiCache } from './core/TipiCache/TipiCache';
|
||||||
|
|
||||||
type CreateContextOptions = {
|
type CreateContextOptions = {
|
||||||
req: CreateNextContextOptions['req'];
|
req: CreateNextContextOptions['req'];
|
||||||
|
@ -27,11 +27,14 @@ const createContextInner = async (opts: CreateContextOptions) => ({
|
||||||
* @param {CreateNextContextOptions} opts - options
|
* @param {CreateNextContextOptions} opts - options
|
||||||
*/
|
*/
|
||||||
export const createContext = async (opts: CreateNextContextOptions) => {
|
export const createContext = async (opts: CreateNextContextOptions) => {
|
||||||
|
const cache = new TipiCache();
|
||||||
const { req, res } = opts;
|
const { req, res } = opts;
|
||||||
|
|
||||||
const sessionId = req.headers['x-session-id'] as string;
|
const sessionId = req.headers['x-session-id'] as string;
|
||||||
|
|
||||||
const userId = await TipiCache.get(`session:${sessionId}`);
|
const userId = await cache.get(`session:${sessionId}`);
|
||||||
|
|
||||||
|
await cache.close();
|
||||||
|
|
||||||
return createContextInner({
|
return createContextInner({
|
||||||
req,
|
req,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { getConfig } from '../TipiConfig';
|
||||||
|
|
||||||
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
|
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
|
||||||
|
|
||||||
class TipiCache {
|
export class TipiCache {
|
||||||
private static instance: TipiCache;
|
private static instance: TipiCache;
|
||||||
|
|
||||||
private client: RedisClientType;
|
private client: RedisClientType;
|
||||||
|
@ -78,5 +78,3 @@ class TipiCache {
|
||||||
return client.ttl(key);
|
return client.ttl(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TipiCache.getInstance();
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './TipiCache';
|
export { TipiCache } from './TipiCache';
|
||||||
|
|
|
@ -7,7 +7,7 @@ const AuthService = new AuthServiceClass(db);
|
||||||
|
|
||||||
export const authRouter = router({
|
export const authRouter = router({
|
||||||
login: publicProcedure.input(z.object({ username: z.string(), password: z.string() })).mutation(async ({ input, ctx }) => AuthService.login({ ...input }, ctx.req, ctx.res)),
|
login: publicProcedure.input(z.object({ username: z.string(), password: z.string() })).mutation(async ({ input, ctx }) => AuthService.login({ ...input }, ctx.req, ctx.res)),
|
||||||
logout: protectedProcedure.mutation(async ({ ctx }) => AuthServiceClass.logout(ctx.sessionId)),
|
logout: protectedProcedure.mutation(async ({ ctx }) => AuthService.logout(ctx.sessionId)),
|
||||||
register: publicProcedure
|
register: publicProcedure
|
||||||
.input(z.object({ username: z.string(), password: z.string(), locale: z.string() }))
|
.input(z.object({ username: z.string(), password: z.string(), locale: z.string() }))
|
||||||
.mutation(async ({ input, ctx }) => AuthService.register({ ...input }, ctx.req, ctx.res)),
|
.mutation(async ({ input, ctx }) => AuthService.register({ ...input }, ctx.req, ctx.res)),
|
||||||
|
|
|
@ -11,12 +11,14 @@ import { encrypt } from '../../utils/encryption';
|
||||||
import { setConfig } from '../../core/TipiConfig';
|
import { setConfig } from '../../core/TipiConfig';
|
||||||
import { createUser, getUserByEmail, getUserById } from '../../tests/user.factory';
|
import { createUser, getUserByEmail, getUserById } from '../../tests/user.factory';
|
||||||
import { AuthServiceClass } from './auth.service';
|
import { AuthServiceClass } from './auth.service';
|
||||||
import TipiCache from '../../core/TipiCache';
|
import { TipiCache } from '../../core/TipiCache';
|
||||||
|
|
||||||
let AuthService: AuthServiceClass;
|
let AuthService: AuthServiceClass;
|
||||||
let database: TestDatabase;
|
let database: TestDatabase;
|
||||||
const TEST_SUITE = 'authservice';
|
const TEST_SUITE = 'authservice';
|
||||||
|
|
||||||
|
const cache = new TipiCache();
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
setConfig('jwtSecret', 'test');
|
setConfig('jwtSecret', 'test');
|
||||||
database = await createDatabase(TEST_SUITE);
|
database = await createDatabase(TEST_SUITE);
|
||||||
|
@ -30,6 +32,7 @@ beforeEach(async () => {
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await closeDatabase(database);
|
await closeDatabase(database);
|
||||||
|
await cache.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Login', () => {
|
describe('Login', () => {
|
||||||
|
@ -51,7 +54,7 @@ describe('Login', () => {
|
||||||
|
|
||||||
const sessionId = session.split(';')[0]?.split('=')[1];
|
const sessionId = session.split(';')[0]?.split('=')[1];
|
||||||
const sessionKey = `session:${sessionId}`;
|
const sessionKey = `session:${sessionId}`;
|
||||||
const userId = await TipiCache.get(sessionKey);
|
const userId = await cache.get(sessionKey);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(userId).toBeDefined();
|
expect(userId).toBeDefined();
|
||||||
|
@ -105,12 +108,12 @@ describe('Test: verifyTotp', () => {
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
const otp = TotpAuthenticator.generate(totpSecret);
|
const otp = TotpAuthenticator.generate(totpSecret);
|
||||||
|
|
||||||
await TipiCache.set(totpSessionId, user.id.toString());
|
await cache.set(totpSessionId, user.id.toString());
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const result = await AuthService.verifyTotp({ totpSessionId, totpCode: otp }, fromPartial({}), fromPartial(res));
|
const result = await AuthService.verifyTotp({ totpSessionId, totpCode: otp }, fromPartial({}), fromPartial(res));
|
||||||
const sessionId = session.split(';')[0]?.split('=')[1];
|
const sessionId = session.split(';')[0]?.split('=')[1];
|
||||||
const userId = await TipiCache.get(`session:${sessionId}`);
|
const userId = await cache.get(`session:${sessionId}`);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
|
@ -128,7 +131,7 @@ describe('Test: verifyTotp', () => {
|
||||||
const encryptedTotpSecret = encrypt(totpSecret, salt);
|
const encryptedTotpSecret = encrypt(totpSecret, salt);
|
||||||
const user = await createUser({ email, totpEnabled: true, totpSecret: encryptedTotpSecret, salt }, database);
|
const user = await createUser({ email, totpEnabled: true, totpSecret: encryptedTotpSecret, salt }, database);
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
await TipiCache.set(totpSessionId, user.id.toString());
|
await cache.set(totpSessionId, user.id.toString());
|
||||||
|
|
||||||
// act & assert
|
// act & assert
|
||||||
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: 'wrong' }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-invalid-code');
|
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: 'wrong' }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-invalid-code');
|
||||||
|
@ -144,7 +147,7 @@ describe('Test: verifyTotp', () => {
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
const otp = TotpAuthenticator.generate(totpSecret);
|
const otp = TotpAuthenticator.generate(totpSecret);
|
||||||
|
|
||||||
await TipiCache.set(totpSessionId, user.id.toString());
|
await cache.set(totpSessionId, user.id.toString());
|
||||||
|
|
||||||
// act & assert
|
// act & assert
|
||||||
await expect(AuthService.verifyTotp({ totpSessionId: 'wrong', totpCode: otp }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-session-not-found');
|
await expect(AuthService.verifyTotp({ totpSessionId: 'wrong', totpCode: otp }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-session-not-found');
|
||||||
|
@ -153,7 +156,7 @@ describe('Test: verifyTotp', () => {
|
||||||
it('should throw if the user does not exist', async () => {
|
it('should throw if the user does not exist', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
await TipiCache.set(totpSessionId, '1234');
|
await cache.set(totpSessionId, '1234');
|
||||||
|
|
||||||
// act & assert
|
// act & assert
|
||||||
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: '1234' }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.user-not-found');
|
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: '1234' }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.user-not-found');
|
||||||
|
@ -169,7 +172,7 @@ describe('Test: verifyTotp', () => {
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
const otp = TotpAuthenticator.generate(totpSecret);
|
const otp = TotpAuthenticator.generate(totpSecret);
|
||||||
|
|
||||||
await TipiCache.set(totpSessionId, user.id.toString());
|
await cache.set(totpSessionId, user.id.toString());
|
||||||
|
|
||||||
// act & assert
|
// act & assert
|
||||||
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: otp }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-not-enabled');
|
await expect(AuthService.verifyTotp({ totpSessionId, totpCode: otp }, fromPartial({}), fromPartial({}))).rejects.toThrowError('server-messages.errors.totp-not-enabled');
|
||||||
|
@ -477,7 +480,7 @@ describe('Register', () => {
|
||||||
describe('Test: logout', () => {
|
describe('Test: logout', () => {
|
||||||
it('Should return true if there is no session to delete', async () => {
|
it('Should return true if there is no session to delete', async () => {
|
||||||
// act
|
// act
|
||||||
const result = await AuthServiceClass.logout('session');
|
const result = await AuthService.logout('session');
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
|
@ -487,11 +490,11 @@ describe('Test: logout', () => {
|
||||||
// arrange
|
// arrange
|
||||||
const sessionId = v4();
|
const sessionId = v4();
|
||||||
|
|
||||||
await TipiCache.set(`session:${sessionId}`, '1');
|
await cache.set(`session:${sessionId}`, '1');
|
||||||
|
|
||||||
// act
|
// act
|
||||||
const result = await AuthServiceClass.logout(sessionId);
|
const result = await AuthService.logout(sessionId);
|
||||||
const session = await TipiCache.get(`session:${sessionId}`);
|
const session = await cache.get(`session:${sessionId}`);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
|
@ -715,14 +718,14 @@ describe('Test: changePassword', () => {
|
||||||
const email = faker.internet.email();
|
const email = faker.internet.email();
|
||||||
const user = await createUser({ email }, database);
|
const user = await createUser({ email }, database);
|
||||||
const newPassword = faker.internet.password();
|
const newPassword = faker.internet.password();
|
||||||
await TipiCache.set(`session:${user.id}:${faker.lorem.word()}`, 'test');
|
await cache.set(`session:${user.id}:${faker.lorem.word()}`, 'test');
|
||||||
|
|
||||||
// act
|
// act
|
||||||
await AuthService.changePassword({ userId: user.id, newPassword, currentPassword: 'password' });
|
await AuthService.changePassword({ userId: user.id, newPassword, currentPassword: 'password' });
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
// eslint-disable-next-line testing-library/no-await-sync-query
|
// eslint-disable-next-line testing-library/no-await-sync-query
|
||||||
const sessions = await TipiCache.getByPrefix(`session:${user.id}:`);
|
const sessions = await cache.getByPrefix(`session:${user.id}:`);
|
||||||
expect(sessions).toHaveLength(0);
|
expect(sessions).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { generateSessionId, setSession } from '@/server/common/session.helpers';
|
||||||
import { Database } from '@/server/db';
|
import { Database } from '@/server/db';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { getConfig } from '../../core/TipiConfig';
|
import { getConfig } from '../../core/TipiConfig';
|
||||||
import TipiCache from '../../core/TipiCache';
|
import { TipiCache } from '../../core/TipiCache';
|
||||||
import { fileExists, unlinkFile } from '../../common/fs.helpers';
|
import { fileExists, unlinkFile } from '../../common/fs.helpers';
|
||||||
import { decrypt, encrypt } from '../../utils/encryption';
|
import { decrypt, encrypt } from '../../utils/encryption';
|
||||||
|
|
||||||
|
@ -22,8 +22,11 @@ type UsernamePasswordInput = {
|
||||||
export class AuthServiceClass {
|
export class AuthServiceClass {
|
||||||
private queries;
|
private queries;
|
||||||
|
|
||||||
|
private cache;
|
||||||
|
|
||||||
constructor(p: Database) {
|
constructor(p: Database) {
|
||||||
this.queries = new AuthQueries(p);
|
this.queries = new AuthQueries(p);
|
||||||
|
this.cache = new TipiCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +52,7 @@ export class AuthServiceClass {
|
||||||
|
|
||||||
if (user.totpEnabled) {
|
if (user.totpEnabled) {
|
||||||
const totpSessionId = generateSessionId('otp');
|
const totpSessionId = generateSessionId('otp');
|
||||||
await TipiCache.set(totpSessionId, user.id.toString());
|
await this.cache.set(totpSessionId, user.id.toString());
|
||||||
return { totpSessionId };
|
return { totpSessionId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +73,7 @@ export class AuthServiceClass {
|
||||||
*/
|
*/
|
||||||
public verifyTotp = async (params: { totpSessionId: string; totpCode: string }, req: NextApiRequest, res: NextApiResponse) => {
|
public verifyTotp = async (params: { totpSessionId: string; totpCode: string }, req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { totpSessionId, totpCode } = params;
|
const { totpSessionId, totpCode } = params;
|
||||||
const userId = await TipiCache.get(totpSessionId);
|
const userId = await this.cache.get(totpSessionId);
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new TranslatedError('server-messages.errors.totp-session-not-found');
|
throw new TranslatedError('server-messages.errors.totp-session-not-found');
|
||||||
|
@ -261,8 +264,8 @@ export class AuthServiceClass {
|
||||||
* @param {string} sessionId - The session token to remove
|
* @param {string} sessionId - The session token to remove
|
||||||
* @returns {Promise<boolean>} - Returns true if the session token is removed successfully
|
* @returns {Promise<boolean>} - Returns true if the session token is removed successfully
|
||||||
*/
|
*/
|
||||||
public static logout = async (sessionId: string): Promise<boolean> => {
|
public logout = async (sessionId: string): Promise<boolean> => {
|
||||||
await TipiCache.del(`session:${sessionId}`);
|
await this.cache.del(`session:${sessionId}`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -341,12 +344,12 @@ export class AuthServiceClass {
|
||||||
* @param {number} userId - The user ID
|
* @param {number} userId - The user ID
|
||||||
*/
|
*/
|
||||||
private destroyAllSessionsByUserId = async (userId: number) => {
|
private destroyAllSessionsByUserId = async (userId: number) => {
|
||||||
const sessions = await TipiCache.getByPrefix(`session:${userId}:`);
|
const sessions = await this.cache.getByPrefix(`session:${userId}:`);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
sessions.map(async (session) => {
|
sessions.map(async (session) => {
|
||||||
await TipiCache.del(session.key);
|
await this.cache.del(session.key);
|
||||||
if (session.val) await TipiCache.del(session.val);
|
if (session.val) await this.cache.del(session.val);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import semver from 'semver';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import { EventDispatcher } from '../../core/EventDispatcher';
|
import { EventDispatcher } from '../../core/EventDispatcher';
|
||||||
import { setConfig } from '../../core/TipiConfig';
|
import { setConfig } from '../../core/TipiConfig';
|
||||||
import TipiCache from '../../core/TipiCache';
|
import { TipiCache } from '../../core/TipiCache';
|
||||||
import { SystemServiceClass } from '.';
|
import { SystemServiceClass } from '.';
|
||||||
|
|
||||||
jest.mock('redis');
|
jest.mock('redis');
|
||||||
|
@ -14,6 +14,8 @@ const SystemService = new SystemServiceClass();
|
||||||
|
|
||||||
const server = setupServer();
|
const server = setupServer();
|
||||||
|
|
||||||
|
const cache = new TipiCache();
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await setConfig('demoMode', false);
|
await setConfig('demoMode', false);
|
||||||
|
|
||||||
|
@ -71,14 +73,15 @@ describe('Test: getVersion', () => {
|
||||||
server.listen();
|
server.listen();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
server.resetHandlers();
|
server.resetHandlers();
|
||||||
TipiCache.del('latestVersion');
|
await cache.del('latestVersion');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(async () => {
|
||||||
server.close();
|
server.close();
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
|
await cache.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('It should return version with body', async () => {
|
it('It should return version with body', async () => {
|
||||||
|
@ -163,7 +166,7 @@ describe('Test: update', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
EventDispatcher.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
|
EventDispatcher.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
|
||||||
setConfig('version', '0.0.1');
|
setConfig('version', '0.0.1');
|
||||||
TipiCache.set('latestVersion', '0.0.2');
|
await cache.set('latestVersion', '0.0.2');
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const update = await SystemService.update();
|
const update = await SystemService.update();
|
||||||
|
@ -174,7 +177,7 @@ describe('Test: update', () => {
|
||||||
|
|
||||||
it('Should throw an error if latest version is not set', async () => {
|
it('Should throw an error if latest version is not set', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
TipiCache.del('latestVersion');
|
await cache.del('latestVersion');
|
||||||
server.use(
|
server.use(
|
||||||
rest.get('https://api.github.com/repos/meienberger/runtipi/releases/latest', (_, res, ctx) => {
|
rest.get('https://api.github.com/repos/meienberger/runtipi/releases/latest', (_, res, ctx) => {
|
||||||
return res(ctx.json({ name: null }));
|
return res(ctx.json({ name: null }));
|
||||||
|
@ -189,7 +192,7 @@ describe('Test: update', () => {
|
||||||
it('Should throw if current version is higher than latest', async () => {
|
it('Should throw if current version is higher than latest', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
setConfig('version', '0.0.2');
|
setConfig('version', '0.0.2');
|
||||||
TipiCache.set('latestVersion', '0.0.1');
|
await cache.set('latestVersion', '0.0.1');
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.current-version-is-latest');
|
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.current-version-is-latest');
|
||||||
|
@ -198,7 +201,7 @@ describe('Test: update', () => {
|
||||||
it('Should throw if current version is equal to latest', async () => {
|
it('Should throw if current version is equal to latest', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
setConfig('version', '0.0.1');
|
setConfig('version', '0.0.1');
|
||||||
TipiCache.set('latestVersion', '0.0.1');
|
await cache.set('latestVersion', '0.0.1');
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.current-version-is-latest');
|
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.current-version-is-latest');
|
||||||
|
@ -207,7 +210,7 @@ describe('Test: update', () => {
|
||||||
it('Should throw an error if there is a major version difference', async () => {
|
it('Should throw an error if there is a major version difference', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
setConfig('version', '0.0.1');
|
setConfig('version', '0.0.1');
|
||||||
TipiCache.set('latestVersion', '1.0.0');
|
await cache.set('latestVersion', '1.0.0');
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.major-version-update');
|
await expect(SystemService.update()).rejects.toThrow('server-messages.errors.major-version-update');
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { TranslatedError } from '@/server/utils/errors';
|
||||||
import { readJsonFile } from '../../common/fs.helpers';
|
import { readJsonFile } from '../../common/fs.helpers';
|
||||||
import { EventDispatcher } from '../../core/EventDispatcher';
|
import { EventDispatcher } from '../../core/EventDispatcher';
|
||||||
import { Logger } from '../../core/Logger';
|
import { Logger } from '../../core/Logger';
|
||||||
import TipiCache from '../../core/TipiCache';
|
import { TipiCache } from '../../core/TipiCache';
|
||||||
import * as TipiConfig from '../../core/TipiConfig';
|
import * as TipiConfig from '../../core/TipiConfig';
|
||||||
|
|
||||||
const SYSTEM_STATUS = ['UPDATING', 'RESTARTING', 'RUNNING'] as const;
|
const SYSTEM_STATUS = ['UPDATING', 'RESTARTING', 'RUNNING'] as const;
|
||||||
|
@ -33,7 +33,7 @@ export class SystemServiceClass {
|
||||||
private dispatcher;
|
private dispatcher;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.cache = TipiCache;
|
this.cache = new TipiCache();
|
||||||
this.dispatcher = EventDispatcher;
|
this.dispatcher = EventDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue