feat(auth): create routes to change locale and set initial locale
This commit is contained in:
parent
242d475ba6
commit
4c78a157b8
5 changed files with 42 additions and 3 deletions
|
@ -1,8 +1,10 @@
|
||||||
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 { Locale } from '@/shared/internationalization/locales';
|
||||||
|
|
||||||
type Session = {
|
type Session = {
|
||||||
userId?: number;
|
userId?: number;
|
||||||
|
locale?: Locale;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CreateContextOptions = {
|
type CreateContextOptions = {
|
||||||
|
|
|
@ -40,7 +40,7 @@ export class AuthQueries {
|
||||||
*/
|
*/
|
||||||
public async getUserDtoById(id: number) {
|
public async getUserDtoById(id: number) {
|
||||||
const users = await this.db
|
const users = await this.db
|
||||||
.select({ id: userTable.id, username: userTable.username, totpEnabled: userTable.totpEnabled })
|
.select({ id: userTable.id, username: userTable.username, totpEnabled: userTable.totpEnabled, locale: userTable.locale })
|
||||||
.from(userTable)
|
.from(userTable)
|
||||||
.where(eq(userTable.id, Number(id)));
|
.where(eq(userTable.id, Number(id)));
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,12 @@ 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)),
|
login: publicProcedure.input(z.object({ username: z.string(), password: z.string() })).mutation(async ({ input, ctx }) => AuthService.login({ ...input }, ctx.req)),
|
||||||
logout: protectedProcedure.mutation(async ({ ctx }) => AuthServiceClass.logout(ctx.req)),
|
logout: protectedProcedure.mutation(async ({ ctx }) => AuthServiceClass.logout(ctx.req)),
|
||||||
register: publicProcedure.input(z.object({ username: z.string(), password: z.string() })).mutation(async ({ input, ctx }) => AuthService.register({ ...input }, ctx.req)),
|
register: publicProcedure.input(z.object({ username: z.string(), password: z.string(), locale: z.string() })).mutation(async ({ input, ctx }) => AuthService.register({ ...input }, ctx.req)),
|
||||||
me: publicProcedure.query(async ({ ctx }) => AuthService.me(ctx.req.session?.userId)),
|
me: publicProcedure.query(async ({ ctx }) => AuthService.me(ctx.req.session?.userId)),
|
||||||
isConfigured: publicProcedure.query(async () => AuthService.isConfigured()),
|
isConfigured: publicProcedure.query(async () => AuthService.isConfigured()),
|
||||||
|
changeLocale: protectedProcedure
|
||||||
|
.input(z.object({ locale: z.string() }))
|
||||||
|
.mutation(async ({ input, ctx }) => AuthService.changeLocale({ userId: Number(ctx.req.session.userId), locale: input.locale })),
|
||||||
// Password
|
// Password
|
||||||
checkPasswordChangeRequest: publicProcedure.query(AuthServiceClass.checkPasswordChangeRequest),
|
checkPasswordChangeRequest: publicProcedure.query(AuthServiceClass.checkPasswordChangeRequest),
|
||||||
changeOperatorPassword: publicProcedure.input(z.object({ newPassword: z.string() })).mutation(({ input }) => AuthService.changeOperatorPassword({ newPassword: input.newPassword })),
|
changeOperatorPassword: publicProcedure.input(z.object({ newPassword: z.string() })).mutation(({ input }) => AuthService.changeOperatorPassword({ newPassword: input.newPassword })),
|
||||||
|
|
|
@ -9,10 +9,12 @@ 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';
|
||||||
|
import { Locales, getLocaleFromString } from '@/shared/internationalization/locales';
|
||||||
|
|
||||||
type UsernamePasswordInput = {
|
type UsernamePasswordInput = {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
locale?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class AuthServiceClass {
|
export class AuthServiceClass {
|
||||||
|
@ -225,7 +227,7 @@ export class AuthServiceClass {
|
||||||
|
|
||||||
const hash = await argon2.hash(password);
|
const hash = await argon2.hash(password);
|
||||||
|
|
||||||
const newUser = await this.queries.createUser({ username: email, password: hash, operator: true });
|
const newUser = await this.queries.createUser({ username: email, password: hash, operator: true, locale: getLocaleFromString(input.locale) });
|
||||||
|
|
||||||
if (!newUser) {
|
if (!newUser) {
|
||||||
throw new Error('Error creating user');
|
throw new Error('Error creating user');
|
||||||
|
@ -384,4 +386,31 @@ export class AuthServiceClass {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a userId and a locale, change the user's locale
|
||||||
|
*
|
||||||
|
* @param {object} params - An object containing the user ID and the new locale
|
||||||
|
* @param {string} params.locale - The new locale
|
||||||
|
* @param {number} params.userId - The user ID
|
||||||
|
*/
|
||||||
|
public changeLocale = async (params: { locale: string; userId: number }) => {
|
||||||
|
const { locale, userId } = params;
|
||||||
|
|
||||||
|
const isLocaleValid = Locales.includes(locale);
|
||||||
|
|
||||||
|
if (!isLocaleValid) {
|
||||||
|
throw new Error('Invalid locale');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.queries.getUserById(userId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.queries.updateUser(user.id, { locale });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { typeToFlattenedError, ZodError } from 'zod';
|
||||||
import { type Context } from './context';
|
import { type Context } from './context';
|
||||||
import { AuthQueries } from './queries/auth/auth.queries';
|
import { AuthQueries } from './queries/auth/auth.queries';
|
||||||
import { db } from './db';
|
import { db } from './db';
|
||||||
|
import { Locale } from '@/shared/internationalization/locales';
|
||||||
|
|
||||||
const authQueries = new AuthQueries(db);
|
const authQueries = new AuthQueries(db);
|
||||||
|
|
||||||
|
@ -56,6 +57,10 @@ const isAuthed = t.middleware(async ({ ctx, next }) => {
|
||||||
|
|
||||||
const user = await authQueries.getUserById(userId);
|
const user = await authQueries.getUserById(userId);
|
||||||
|
|
||||||
|
if (user?.locale) {
|
||||||
|
ctx.req.session.locale = user.locale as Locale;
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
ctx.req.session.destroy(() => {});
|
ctx.req.session.destroy(() => {});
|
||||||
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'You need to be logged in to perform this action' });
|
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'You need to be logged in to perform this action' });
|
||||||
|
|
Loading…
Add table
Reference in a new issue