瀏覽代碼

feat(auth): create routes to change locale and set initial locale

Nicolas Meienberger 2 年之前
父節點
當前提交
4c78a157b8

+ 2 - 0
src/server/context.ts

@@ -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 = {

+ 1 - 1
src/server/queries/auth/auth.queries.ts

@@ -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)));
 
 

+ 4 - 1
src/server/routers/auth/auth.router.ts

@@ -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 })),

+ 30 - 1
src/server/services/auth/auth.service.ts

@@ -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;
+  };
 }
 }

+ 5 - 0
src/server/trpc.ts

@@ -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' });