diff --git a/server/__tests__/fileUpload.test.ts b/server/__tests__/fileUpload.test.ts index 6bf0dd4fa..f4cc3070f 100644 --- a/server/__tests__/fileUpload.test.ts +++ b/server/__tests__/fileUpload.test.ts @@ -3,6 +3,7 @@ import fs from 'fs'; import path from 'path'; import { uploadFiles } from '../controllers/filesController'; +import type { User } from '../entities/user'; import { AwsS3 } from '../fileUpload/s3'; import { AppError } from '../middlewares/handleErrors'; import { appDataSource, fakeUser } from './mock'; @@ -12,9 +13,6 @@ beforeAll(() => { }); beforeEach(() => {}); afterAll(() => { - const directory = path.join(__dirname, '../fileUpload/images/1/'); - // cleqning dev images files - fs.rmSync(directory, { recursive: true, force: true }); return appDataSource.destroy(); }); @@ -29,7 +27,7 @@ describe('Upload files', () => { }); const mockRequest: Partial = { - user: fakeUser, + user: fakeUser as User, files: [ { buffer: dummyPdf, @@ -54,7 +52,7 @@ describe('Upload files', () => { }); test('Should throw - files are missing -', async () => { const mockRequest: Partial = { - user: fakeUser, + user: fakeUser as User, files: [], }; @@ -85,7 +83,7 @@ describe('Upload files', () => { }); const mockRequest: Partial = { - user: fakeUser, + user: fakeUser as User, files: [ { buffer: dummyPdf, @@ -114,7 +112,7 @@ describe('Upload files', () => { }); const mockRequest: Partial = { - user: fakeUser, + user: fakeUser as User, files: [ { buffer: dummyPdf, diff --git a/server/__tests__/mock.ts b/server/__tests__/mock.ts index d6c19bf5a..193f68547 100644 --- a/server/__tests__/mock.ts +++ b/server/__tests__/mock.ts @@ -3,7 +3,6 @@ import supertest from 'supertest'; import { DataSource } from 'typeorm'; import { getApp } from '../app'; -import type { User } from '../entities/user'; export const appDataSource = new DataSource({ type: 'sqlite', @@ -54,6 +53,6 @@ export const fakeUser = { firstLogin: 3, type: 0, villageId: 1, - country: { id: 0, isoCode: 'FR', name: 'France' }, + country: { isoCode: 'FR', name: 'France' }, position: { lat: 48.8863442, lng: 2.380321 }, -} as unknown as User; +}; diff --git a/server/app-staging.ts b/server/app-staging.ts index a1ccf9fcb..95ffc3161 100644 --- a/server/app-staging.ts +++ b/server/app-staging.ts @@ -5,7 +5,7 @@ import morgan from 'morgan'; import next from 'next'; import path from 'path'; -import { UserType } from '../types/user.type'; +import { UserType } from './entities/user'; import { handleErrors } from './middlewares/handleErrors'; import { removeTrailingSlash } from './middlewares/trailingSlash'; diff --git a/server/app.ts b/server/app.ts index cebd7d037..92779842b 100644 --- a/server/app.ts +++ b/server/app.ts @@ -8,9 +8,9 @@ import morgan from 'morgan'; import next from 'next'; import path from 'path'; -import { UserType } from '../types/user.type'; import { authRouter } from './authentication'; import { controllerRouter } from './controllers'; +import { UserType } from './entities/user'; import { getH5pRouter } from './h5p'; import { authenticate } from './middlewares/authenticate'; import { crsfProtection } from './middlewares/csrfCheck'; diff --git a/server/authentication/login.ts b/server/authentication/login.ts index 3acf07c1e..b975168fe 100644 --- a/server/authentication/login.ts +++ b/server/authentication/login.ts @@ -2,8 +2,7 @@ import type { JSONSchemaType } from 'ajv'; import * as argon2 from 'argon2'; import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { UserToStudent } from '../entities/userToStudent'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { AppDataSource } from '../utils/data-source'; diff --git a/server/controllers/activity.ts b/server/controllers/activity.ts index 6056f96e2..92aa11dec 100644 --- a/server/controllers/activity.ts +++ b/server/controllers/activity.ts @@ -7,11 +7,11 @@ import { EPhase1Steps, ActivityStatus, ActivityType, EPhase2Steps, EPhase3Steps import type { GameData, GamesData } from '../../types/game.type'; import type { StoriesData, StoryElement } from '../../types/story.type'; import { ImageType } from '../../types/story.type'; -import { UserType } from '../../types/user.type'; -import { VillagePhase } from '../../types/village.type'; import { Activity } from '../entities/activity'; import { Game } from '../entities/game'; import { Image } from '../entities/image'; +import { UserType } from '../entities/user'; +import { VillagePhase } from '../entities/village'; import { getActivities, getActivitiesCommentCount } from '../manager/activity'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { getQueryString } from '../utils'; @@ -25,11 +25,17 @@ const activityController = new Controller('/activities'); // --- Get all activities. --- activityController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Request, res: Response) => { if (!req.user) throw new AppError('Forbidden', ErrorCode.UNKNOWN); + const activities = await getActivities({ limit: req.query.limit ? Number(getQueryString(req.query.limit)) || 200 : undefined, page: req.query.page ? Number(getQueryString(req.query.page)) || 0 : undefined, villageId: req.query.villageId ? Number(getQueryString(req.query.villageId)) || 0 : undefined, - countries: req.query.countries as string[] | undefined, + countries: + req.query.countries !== undefined + ? req.query.countries.length === 0 + ? [] + : (getQueryString(req.query.countries) || '').split(',') + : undefined, pelico: req.query.pelico ? req.query.pelico !== 'false' : undefined, type: req.query.type ? (getQueryString(req.query.type) || '').split(',') : undefined, subType: req.query.subType ? Number(getQueryString(req.query.subType)) || 0 : undefined, diff --git a/server/controllers/analytic.ts b/server/controllers/analytic.ts index bcad3b2f1..69835daeb 100644 --- a/server/controllers/analytic.ts +++ b/server/controllers/analytic.ts @@ -3,8 +3,8 @@ import useragent from 'express-useragent'; import { Between } from 'typeorm'; import type { AnalyticData, NavigationPerf, BrowserPerf } from '../../types/analytics.type'; -import { UserType } from '../../types/user.type'; import { AnalyticSession, AnalyticPageView, AnalyticPerformance } from '../entities/analytic'; +import { UserType } from '../entities/user'; import { AppError, ErrorCode, handleErrors } from '../middlewares/handleErrors'; import { generateTemporaryToken, getQueryString } from '../utils'; import { AppDataSource } from '../utils/data-source'; diff --git a/server/controllers/archive.ts b/server/controllers/archive.ts index d6897f939..0bdaa1e5f 100644 --- a/server/controllers/archive.ts +++ b/server/controllers/archive.ts @@ -1,6 +1,6 @@ import type { Request, Response, NextFunction } from 'express'; -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { streamFile } from '../fileUpload/streamFile'; import { Controller } from './controller'; diff --git a/server/controllers/audio.ts b/server/controllers/audio.ts index 453be1bdb..3a186cdd7 100644 --- a/server/controllers/audio.ts +++ b/server/controllers/audio.ts @@ -3,7 +3,7 @@ import fs from 'fs-extra'; import path from 'path'; import { v4 as uuidv4 } from 'uuid'; -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { deleteFile, uploadFile } from '../fileUpload'; import { streamFile } from '../fileUpload/streamFile'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; diff --git a/server/controllers/classroom.ts b/server/controllers/classroom.ts index d0cbf9887..e78a00193 100644 --- a/server/controllers/classroom.ts +++ b/server/controllers/classroom.ts @@ -1,10 +1,8 @@ import type { JSONSchemaType } from 'ajv'; import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; import { Classroom } from '../entities/classroom'; -import { Country } from '../entities/country'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { AppDataSource } from '../utils/data-source'; import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator'; @@ -26,7 +24,6 @@ classroomController.get({ path: '/:id', userType: UserType.TEACHER }, async (req const classroom = await AppDataSource.getRepository(Classroom).findOne({ relations: { user: true, - country: true, }, where: { user: { id } }, }); @@ -88,13 +85,12 @@ classroomController.post({ path: '', userType: UserType.TEACHER }, async (req: R where: { user: { id: req.user.id } }, }); if (verification.length !== 0) return res.status(303).send('Classroom already exit'); - const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } }); - const classroom = new Classroom(); - classroom.user.id = data.userId; - classroom.country = countryFound; - classroom.id = data.villageId; - await AppDataSource.getRepository(Classroom).save(classroom); + const classroom = await AppDataSource.createQueryBuilder() + .insert() + .into(Classroom) + .values([{ user: { id: data.userId }, village: { id: data.villageId }, countryCode: data.countryCode }]) + .execute(); res.json(classroom); }); diff --git a/server/controllers/comment.ts b/server/controllers/comment.ts index cf76d5cac..22e6d2732 100644 --- a/server/controllers/comment.ts +++ b/server/controllers/comment.ts @@ -1,9 +1,9 @@ import type { JSONSchemaType } from 'ajv'; import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; import { Activity } from '../entities/activity'; import { Comment } from '../entities/comment'; +import { UserType } from '../entities/user'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { AppDataSource } from '../utils/data-source'; import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator'; diff --git a/server/controllers/controller.ts b/server/controllers/controller.ts index 3c3400098..d04b5d067 100644 --- a/server/controllers/controller.ts +++ b/server/controllers/controller.ts @@ -4,7 +4,7 @@ import fs from 'fs-extra'; import multer from 'multer'; import path from 'path'; -import type { UserType } from '../../types/user.type'; +import type { UserType } from '../entities/user'; import { authenticate } from '../middlewares/authenticate'; import { handleErrors } from '../middlewares/handleErrors'; import { diskStorage } from '../middlewares/multer'; diff --git a/server/controllers/countries.ts b/server/controllers/countries.ts index 452662c6e..e43a15174 100644 --- a/server/controllers/countries.ts +++ b/server/controllers/countries.ts @@ -1,13 +1,11 @@ -import { UserType } from '../../types/user.type'; -import { Country } from '../entities/country'; -import { AppDataSource } from '../utils/data-source'; +import { UserType } from '../entities/user'; +import { countries } from '../utils/iso-3166-countries-french'; import { Controller } from './controller'; const countryController = new Controller('/countries'); //--- Get all countries --- countryController.get({ path: '', userType: UserType.TEACHER }, async (_req, res) => { - const countries = await AppDataSource.getRepository(Country).find(); res.sendJSON(countries); }); diff --git a/server/controllers/currencies.ts b/server/controllers/currencies.ts index b29051995..5e07df679 100644 --- a/server/controllers/currencies.ts +++ b/server/controllers/currencies.ts @@ -1,4 +1,4 @@ -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { currencies } from '../utils/iso-4217-currencies-french'; import { Controller } from './controller'; diff --git a/server/controllers/featureFlag.ts b/server/controllers/featureFlag.ts index fe393123d..5a079d3d3 100644 --- a/server/controllers/featureFlag.ts +++ b/server/controllers/featureFlag.ts @@ -2,9 +2,8 @@ import type { NextFunction, Request, Response } from 'express'; import { In } from 'typeorm'; import type { FeatureFlagsNames } from '../../types/featureFlag.constant'; -import { UserType } from '../../types/user.type'; import { FeatureFlag } from '../entities/featureFlag'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { AppDataSource } from '../utils/data-source'; import { Controller } from './controller'; diff --git a/server/controllers/game.ts b/server/controllers/game.ts index bd9f6af5b..6f0f4ca34 100644 --- a/server/controllers/game.ts +++ b/server/controllers/game.ts @@ -1,9 +1,9 @@ import type { JSONSchemaType } from 'ajv'; import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; import { Game } from '../entities/game'; import { GameResponse } from '../entities/gameResponse'; +import { UserType } from '../entities/user'; import { getQueryString } from '../utils'; import { AppDataSource } from '../utils/data-source'; import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator'; diff --git a/server/controllers/image.ts b/server/controllers/image.ts index 8f9eafdda..b123c75c8 100644 --- a/server/controllers/image.ts +++ b/server/controllers/image.ts @@ -4,7 +4,7 @@ import path from 'path'; import sharp from 'sharp'; import { v4 as uuidv4 } from 'uuid'; -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { deleteFile, uploadFile } from '../fileUpload'; import { streamFile } from '../fileUpload/streamFile'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; diff --git a/server/controllers/story.ts b/server/controllers/story.ts index 65da7b86d..d9da026f2 100644 --- a/server/controllers/story.ts +++ b/server/controllers/story.ts @@ -1,8 +1,8 @@ import type { Request, Response, NextFunction } from 'express'; import { ImageType } from '../../types/story.type'; -import { UserType } from '../../types/user.type'; import { Image } from '../entities/image'; +import { UserType } from '../entities/user'; import { getQueryString } from '../utils'; import { AppDataSource } from '../utils/data-source'; import { Controller } from './controller'; diff --git a/server/controllers/student.ts b/server/controllers/student.ts index 2569d10e7..d2c75a893 100644 --- a/server/controllers/student.ts +++ b/server/controllers/student.ts @@ -1,10 +1,8 @@ import type { JSONSchemaType } from 'ajv'; import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; -import { Classroom } from '../entities/classroom'; import { Student } from '../entities/student'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { UserToStudent } from '../entities/userToStudent'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { getQueryString } from '../utils'; @@ -120,10 +118,7 @@ studentController.post({ path: '', userType: UserType.TEACHER }, async (req: Req throw new AppError('Forbidden', ErrorCode.UNKNOWN); } const student = new Student(); - const classroomFound = await AppDataSource.getRepository(Classroom).findOne({ where: { id: data.classroomId } }); - if (classroomFound) { - student.classroom = classroomFound; - } + student.classroom = data.classroomId; student.firstname = data.firstname ?? null; student.lastname = data.lastname ?? null; student.hashedCode = inviteCodeGenerator(10); @@ -181,7 +176,7 @@ studentController.post({ path: '/link-student', userType: UserType.FAMILY }, asy villageId: student.classroom.villageId, hasStudentLinked: true, firstLogin: student.classroom.village?.activePhase, //TEST THIS LINE - country: student.classroom.country, + countryCode: student.classroom.countryCode as string, }) .where('id = :id', { id: req.user.id }) .execute(); diff --git a/server/controllers/teacher.ts b/server/controllers/teacher.ts index c313036d5..db09c9cae 100644 --- a/server/controllers/teacher.ts +++ b/server/controllers/teacher.ts @@ -1,7 +1,7 @@ import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; import { Activity } from '../entities/activity'; +import { UserType } from '../entities/user'; import { UserToStudent } from '../entities/userToStudent'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { AppDataSource } from '../utils/data-source'; diff --git a/server/controllers/user.ts b/server/controllers/user.ts index b989078c3..e9b71c021 100644 --- a/server/controllers/user.ts +++ b/server/controllers/user.ts @@ -5,15 +5,13 @@ import type { FindOperator } from 'typeorm'; import { In, IsNull, LessThan } from 'typeorm'; import { ActivityStatus, ActivityType } from '../../types/activity.type'; -import { UserType } from '../../types/user.type'; import { getAccessToken } from '../authentication/lib/tokens'; import { Email, sendMail } from '../emails'; import { Activity } from '../entities/activity'; import { Classroom } from '../entities/classroom'; -import { Country } from '../entities/country'; import { FeatureFlag } from '../entities/featureFlag'; import { Student } from '../entities/student'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { UserToStudent } from '../entities/userToStudent'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { generateTemporaryToken, valueOrDefault, isPasswordValid, getQueryString } from '../utils'; @@ -41,7 +39,6 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ // eslint-disable-next-line @typescript-eslint/no-explicit-any { villageId: IsNull(), type: LessThan(`${UserType.TEACHER}`) as FindOperator }, ], - relations: { country: true }, }); const ids = users.map((u) => u.id); const mascottes = ( @@ -65,7 +62,6 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ users = await AppDataSource.getRepository(User).find({ relations: { userToStudents: true, - country: true, }, }); const studentsToSchool = ( @@ -91,10 +87,9 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ }); // --- Get one user. --- -userController.get({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => { +userController.get({ path: '/:id(\\d+)', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => { const id = parseInt(req.params.id, 10) || 0; - - const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: { country: true } }); + const user = await AppDataSource.getRepository(User).findOne({ where: { id } }); const isSelfProfile = req.user && req.user.id === id; const isAdmin = req.user && req.user.type <= UserType.ADMIN; if (user === null || (!isSelfProfile && !isAdmin)) { @@ -134,8 +129,7 @@ userController.get({ path: '/pseudo/:pseudo' }, async (req: Request, res: Respon res.sendJSON({ available: true }); } res.sendJSON({ - relations: { country: true }, - available: (await AppDataSource.getRepository(User).count({ where: { pseudo }, relations: { country: true } })) === 0, + available: (await AppDataSource.getRepository(User).count({ where: { pseudo } })) === 0, }); }); @@ -258,10 +252,7 @@ userController.post({ path: '' }, async (req: Request, res: Response) => { user.hasAcceptedNewsletter = data.hasAcceptedNewsletter || false; user.language = data.language || 'français'; user.hasStudentLinked = data.hasStudentLinked || false; - const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } }); - if (countryFound) { - user.country = countryFound; - } + user.countryCode = data.countryCode || ''; user.type = data.type || UserType.TEACHER || UserType.FAMILY; if (user.type === UserType.TEACHER) { user.isVerified = true; @@ -311,8 +302,7 @@ type EditUserData = { type?: UserType; villageId?: number | null; firstLogin?: number; - positionLat?: number; - positionLon?: number; + position?: { lat: number; lng: number }; isVerified?: boolean; accountRegistration?: number; hasAcceptedNewsletter?: boolean; @@ -342,8 +332,16 @@ const EDIT_SCHEMA: JSONSchemaType = { villageId: { type: 'number', nullable: true }, accountRegistration: { type: 'number', nullable: true }, firstLogin: { type: 'number', nullable: true }, - positionLat: { type: 'number', nullable: true }, - positionLon: { type: 'number', nullable: true }, + position: { + type: 'object', + nullable: true, + properties: { + lat: { type: 'number', nullable: false }, + lng: { type: 'number', nullable: false }, + }, + required: ['lat', 'lng'], + additionalProperties: false, + }, hasAcceptedNewsletter: { type: 'boolean', nullable: true }, isVerified: { type: 'boolean', nullable: true }, language: { type: 'string', nullable: true }, @@ -379,10 +377,7 @@ userController.put({ path: '/:id', userType: UserType.OBSERVATOR }, async (req: user.postalCode = valueOrDefault(data.postalCode, user.postalCode); user.level = valueOrDefault(data.level, user.level); user.school = valueOrDefault(data.school, user.school); - const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } }); - if (countryFound) { - user.country = countryFound; - } + user.countryCode = valueOrDefault(data.countryCode, user.countryCode); user.avatar = valueOrDefault(data.avatar, user.avatar) || null; user.displayName = valueOrDefault(data.displayName, user.displayName) || null; user.firstLogin = valueOrDefault(data.firstLogin, user.firstLogin); @@ -390,35 +385,34 @@ userController.put({ path: '/:id', userType: UserType.OBSERVATOR }, async (req: user.type = valueOrDefault(data.type, user.type); user.villageId = valueOrDefault(data.villageId, user.villageId, true); } - if (data.positionLat && data.positionLon) { - user.positionLat = data.positionLat; - user.positionLon = data.positionLon; + if (data.position) { + user.position = data.position; } if (data.villageId) { if (user.type === UserType.TEACHER) { const classroom = await AppDataSource.getRepository(Classroom).findOne({ where: { user: { id: user.id } } }); - if (classroom) { - const students = await AppDataSource.getRepository(Student).find({ where: { classroom: { id: classroom.id } } }); - const studentsId = students.map((student) => student.id); + if (!classroom) return; - const families = await AppDataSource.getRepository(UserToStudent).find({ - where: { student: { id: In(studentsId) } }, - relations: ['user', 'student'], - }); + const students = await AppDataSource.getRepository(Student).find({ where: { classroom: { id: classroom.id } } }); + const studentsId = students.map((student) => student.id); - const familiesId = families.map((family) => family.user.id); + const families = await AppDataSource.getRepository(UserToStudent).find({ + where: { student: { id: In(studentsId) } }, + relations: ['user', 'student'], + }); - const promises = []; + const familiesId = families.map((family) => family.user.id); - promises.push( - AppDataSource.getRepository(Classroom).update({ user: { id: user.id } }, { villageId: data.villageId }), - AppDataSource.getRepository(Activity).update({ userId: user.id }, { villageId: data.villageId }), - AppDataSource.getRepository(User).update({ id: In(familiesId) }, { villageId: data.villageId }), - ); + const promises = []; - await Promise.all(promises); - } + promises.push( + AppDataSource.getRepository(Classroom).update({ user: { id: user.id } }, { villageId: data.villageId }), + AppDataSource.getRepository(Activity).update({ userId: user.id }, { villageId: data.villageId }), + AppDataSource.getRepository(User).update({ id: In(familiesId) }, { villageId: data.villageId }), + ); + + await Promise.all(promises); } } user.hasAcceptedNewsletter = valueOrDefault(data.hasAcceptedNewsletter, user.hasAcceptedNewsletter); @@ -843,7 +837,7 @@ userController.get({ path: '/get-classroom/:id', userType: UserType.OBSERVATOR } firstname: student.firstname, lastname: student.lastname, classroom: { - user: classroom.user.country?.isoCode, + user: classroom.user.countryCode, }, }, }); @@ -900,7 +894,7 @@ userController.get({ path: '/:id/visibility-params/', userType: UserType.FAMILY throw new AppError('Forbidden', ErrorCode.UNKNOWN); } const id = parseInt(req.params.id, 10) || 0; - const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: ['userToStudents', 'userToStudents.student', 'country'] }); + const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: ['userToStudents', 'userToStudents.student'] }); if (user && user.type === UserType.TEACHER) { const classroom = []; diff --git a/server/controllers/video.ts b/server/controllers/video.ts index 38291ec2d..511743d75 100644 --- a/server/controllers/video.ts +++ b/server/controllers/video.ts @@ -1,4 +1,4 @@ -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { Video } from '../entities/video'; import { deleteVideo, getVideoLink, uploadVideo, getPictureForVideo } from '../fileUpload'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; diff --git a/server/controllers/village.ts b/server/controllers/village.ts index d9cfd6526..9361d954d 100644 --- a/server/controllers/village.ts +++ b/server/controllers/village.ts @@ -1,9 +1,7 @@ import type { JSONSchemaType } from 'ajv'; import type { NextFunction, Request, Response } from 'express'; -import { In } from 'typeorm'; -import { UserType } from '../../types/user.type'; -import { Country } from '../entities/country'; +import { UserType } from '../entities/user'; import { Village } from '../entities/village'; import { createVillagesFromPLM } from '../legacy-plm/api'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; @@ -16,7 +14,7 @@ const villageController = new Controller('/villages'); //--- Get all villages --- villageController.get({ path: '', userType: UserType.OBSERVATOR }, async (_req: Request, res: Response) => { - const villages = await AppDataSource.getRepository(Village).find({ relations: { countries: true } }); + const villages = await AppDataSource.getRepository(Village).find(); res.sendJSON(villages); }); @@ -27,8 +25,7 @@ villageController.get({ path: '/:id', userType: UserType.OBSERVATOR }, async (re return; } const id = parseInt(req.params.id, 10) || 0; - const village = await AppDataSource.getRepository(Village).findOne({ where: { id }, relations: { countries: true } }); - + const village = await AppDataSource.getRepository(Village).findOne({ where: { id } }); if (!village || (req.user.type === UserType.TEACHER && req.user.villageId !== village.id)) { next(); return; @@ -59,12 +56,7 @@ villageController.post({ path: '', userType: UserType.ADMIN }, async (req: Reque } const village = new Village(); village.name = data.name; - const countries = await AppDataSource.getRepository(Country).find({ - where: { isoCode: In(data.countries) }, - }); - if (countries.length) { - village.countries = countries; - } + village.countryCodes = data.countries; await AppDataSource.getRepository(Village).save(village); res.sendJSON(village); }); @@ -95,26 +87,17 @@ villageController.put({ path: '/:id', userType: UserType.ADMIN }, async (req: Re return; } const id = parseInt(req.params.id, 10) || 0; - const village = await AppDataSource.getRepository(Village).findOne({ - where: { id }, - relations: { - countries: true, - }, - }); + const village = await AppDataSource.getRepository(Village).findOne({ where: { id } }); if (!village) { next(); return; } + village.name = valueOrDefault(data.name, village.name); + village.countryCodes = valueOrDefault(data.countries, village.countryCodes); village.activePhase = valueOrDefault(data.activePhase, village.activePhase); village.anthemId = valueOrDefault(data.anthemId, village.anthemId); - const countries = await AppDataSource.getRepository(Country).find({ - where: { isoCode: In(data.countries ?? village.countries.map((country) => country.isoCode)) }, - }); - if (countries.length) { - village.countries = countries; - } await AppDataSource.getRepository(Village).save(village); res.sendJSON(village); }); diff --git a/server/controllers/weather.ts b/server/controllers/weather.ts index e64ccce95..64edbf8e7 100644 --- a/server/controllers/weather.ts +++ b/server/controllers/weather.ts @@ -1,8 +1,8 @@ /* eslint-disable camelcase */ import axios from 'axios'; -import { UserType } from '../../types/user.type'; import type { Weather } from '../../types/weather.type'; +import { UserType } from '../entities/user'; import { AppError, ErrorCode } from '../middlewares/handleErrors'; import { getQueryString, serializeToQueryUrl } from '../utils'; import { logger } from '../utils/logger'; diff --git a/server/entities/classroom.ts b/server/entities/classroom.ts index e7c654560..83fa591d0 100644 --- a/server/entities/classroom.ts +++ b/server/entities/classroom.ts @@ -1,6 +1,7 @@ import { Entity, PrimaryGeneratedColumn, Column, OneToOne, OneToMany, ManyToOne, JoinColumn } from 'typeorm'; -import { Country } from './country'; +import type { Country } from '../../types/country.type'; +import { countriesMap } from '../utils/countries-map'; import { Student } from './student'; import { User } from './user'; import { Village } from './village'; @@ -19,8 +20,14 @@ export class Classroom { @Column({ nullable: true, default: 0 }) public delayedDays: number; - @ManyToOne(() => Country, (country: Country) => country) - public country: Country | null; + @Column({ type: 'varchar', length: 2, nullable: true }) + set countryCode(newCountryCode: string) { + this.country = countriesMap[newCountryCode] || countriesMap['FR']; + } + get countryCode() { + return this.country?.isoCode; + } + public country: Country; @Column({ type: 'boolean', diff --git a/server/entities/user.ts b/server/entities/user.ts index b0c9e8f2c..f6c7c5376 100644 --- a/server/entities/user.ts +++ b/server/entities/user.ts @@ -1,8 +1,10 @@ import { Column, Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn, OneToMany, ManyToMany, JoinTable } from 'typeorm'; +import type { Country } from '../../types/country.type'; import { UserType } from '../../types/user.type'; +import type { User as UserInterface } from '../../types/user.type'; +import { countriesMap } from '../utils/countries-map'; import { Activity } from './activity'; -import { Country } from './country'; import { FeatureFlag } from './featureFlag'; import { Game } from './game'; import { GameResponse } from './gameResponse'; @@ -10,8 +12,10 @@ import { Image } from './image'; import { UserToStudent } from './userToStudent'; import { Village } from './village'; +export { UserType }; + @Entity() -export class User { +export class User implements UserInterface { @PrimaryGeneratedColumn() public id: number; @@ -78,14 +82,38 @@ export class User { @Column({ nullable: true }) public villageId: number | null; - @ManyToOne(() => Country, (country: Country) => country) - public country: Country | null; + @Column({ type: 'varchar', length: 2, nullable: true }) + set countryCode(newCountryCode: string) { + this.country = countriesMap[newCountryCode] || countriesMap['FR']; + } + get countryCode() { + return this.country?.isoCode; + } + public country: Country; @Column({ type: 'decimal', precision: 11, scale: 8, nullable: false, default: 0 }) - public positionLat: number; + set positionLat(newLat: string) { + if (!this.position) { + this.position = { lat: 0, lng: 0 }; + } + this.position.lat = parseFloat(newLat) || 0; + } + get positionLat() { + return `${this.position?.lat || 0}`; + } @Column({ type: 'decimal', precision: 11, scale: 8, nullable: false, default: 0 }) - public positionLon: number; + set positionLon(newLon: string) { + if (!this.position) { + this.position = { lat: 0, lng: 0 }; + } + this.position.lng = parseFloat(newLon) || 0; + } + get positionLon() { + return `${this.position?.lng || 0}`; + } + + public position: { lat: number; lng: number }; public mascotteId?: number; diff --git a/server/entities/userToStudent.ts b/server/entities/userToStudent.ts index d9b693ee0..c5391ced6 100644 --- a/server/entities/userToStudent.ts +++ b/server/entities/userToStudent.ts @@ -53,7 +53,7 @@ export class UserToStudent { remainingUserToStudent.student.classroom.village ) { userToStudent.user.villageId = remainingUserToStudent.student.classroom.village.id; - userToStudent.user.country = remainingUserToStudent.student.classroom.country; + userToStudent.user.countryCode = remainingUserToStudent.student.classroom.countryCode; await entityManager.save(userToStudent.user); } } diff --git a/server/entities/village.ts b/server/entities/village.ts index 7d2154200..9081e9f51 100644 --- a/server/entities/village.ts +++ b/server/entities/village.ts @@ -1,10 +1,11 @@ -import { Column, Entity, JoinColumn, PrimaryGeneratedColumn, OneToMany, OneToOne, ManyToMany, JoinTable } from 'typeorm'; +import { Column, Entity, JoinColumn, PrimaryGeneratedColumn, OneToMany, OneToOne } from 'typeorm'; +import type { Country } from '../../types/country.type'; import type { Village as VillageInterface } from '../../types/village.type'; import { VillagePhase } from '../../types/village.type'; +import { countriesMap } from '../utils/countries-map'; import { Activity } from './activity'; import { Classroom } from './classroom'; -import { Country } from './country'; import { Game } from './game'; import { GameResponse } from './gameResponse'; import { Image } from './image'; @@ -23,8 +24,13 @@ export class Village implements VillageInterface { @Column({ type: 'varchar', length: 80, nullable: false }) public name: string; - @ManyToMany(() => Country) - @JoinTable() + @Column('simple-array') + set countryCodes(newCountryCodes: string[]) { + this.countries = newCountryCodes.map((isoCode) => countriesMap[isoCode]).filter((c) => c !== undefined); + } + get countryCodes() { + return this.countries.map((c) => c.isoCode); + } public countries: Country[]; @Column({ diff --git a/server/legacy-plm/user.ts b/server/legacy-plm/user.ts index f2b0f04d5..f402186b9 100644 --- a/server/legacy-plm/user.ts +++ b/server/legacy-plm/user.ts @@ -2,9 +2,7 @@ import stringSimilarity from 'string-similarity'; import { In } from 'typeorm'; -import { UserType } from '../../types/user.type'; -import { Country } from '../entities/country'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { Village } from '../entities/village'; import { AppDataSource } from '../utils/data-source'; import { setUserPosition } from '../utils/get-pos'; @@ -73,8 +71,8 @@ export async function createPLMUserToDB(plmUser: PLM_User): Promise { countryCode = matchs[c.bestMatchIndex].isoCode; } } - if (village !== null && !village.countries.map((e) => e.isoCode).includes(countryCode)) { - countryCode = village.countries[0].isoCode; + if (village !== null && !village.countryCodes.includes(countryCode)) { + countryCode = village.countryCodes[0]; } // 3- Add user @@ -87,14 +85,12 @@ export async function createPLMUserToDB(plmUser: PLM_User): Promise { user.postalCode = plmUser.postalCode || ''; user.address = plmUser.address || ''; user.villageId = village?.id || null; - const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: countryCode } }); - user.country = countryFound ?? null; + user.countryCode = countryCode; user.type = userType; user.passwordHash = ''; user.verificationHash = ''; user.accountRegistration = 10; - user.positionLat = 0; - user.positionLon = 0; + user.position = { lat: 0, lng: 0 }; await setUserPosition(user); await AppDataSource.getRepository(User).save(user); diff --git a/server/legacy-plm/village.ts b/server/legacy-plm/village.ts index e54623311..99424f02c 100644 --- a/server/legacy-plm/village.ts +++ b/server/legacy-plm/village.ts @@ -1,9 +1,9 @@ /* eslint-disable camelcase */ import stringSimilarity from 'string-similarity'; -import { Country } from '../entities/country'; import { Village } from '../entities/village'; import { AppDataSource } from '../utils/data-source'; +import { countries } from '../utils/iso-3166-countries-french'; import { logger } from '../utils/logger'; export type PLM_Village = { @@ -38,7 +38,6 @@ async function createVillage(plmVillage: PLM_Village): Promise { // get countries and create village logger.info(`Try to create village with slug: ${slug}`); - const countries = await AppDataSource.getRepository(Country).find(); const villageCountries = (slug.startsWith('village-monde-') ? slug.slice(14) : slug).split(/[-–]/).filter((s) => s.length > 0); if (villageCountries.length === 2) { const c1 = stringSimilarity.findBestMatch( @@ -53,7 +52,7 @@ async function createVillage(plmVillage: PLM_Village): Promise { logger.info(`c2: ${JSON.stringify(c2.bestMatch)}`); if (c1.bestMatch.rating > 0.88 && c2.bestMatch.rating > 0.88) { const newVillage = new Village(); - newVillage.countries = [countries[c1.bestMatchIndex], countries[c2.bestMatchIndex]]; + newVillage.countryCodes = [countries[c1.bestMatchIndex].isoCode, countries[c2.bestMatchIndex].isoCode]; newVillage.name = name; newVillage.plmId = plmId; await AppDataSource.getRepository(Village).save(newVillage); @@ -62,8 +61,7 @@ async function createVillage(plmVillage: PLM_Village): Promise { } const newVillage = new Village(); - const frCountry = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: 'FR' } }); - if (frCountry) newVillage.countries = [frCountry, frCountry]; + newVillage.countryCodes = ['FR', 'FR']; newVillage.name = name; newVillage.plmId = plmId; await AppDataSource.getRepository(Village).save(newVillage); diff --git a/server/manager/activity.ts b/server/manager/activity.ts index d6e218dda..3ea43e393 100644 --- a/server/manager/activity.ts +++ b/server/manager/activity.ts @@ -1,9 +1,8 @@ import { In } from 'typeorm'; -import { UserType } from '../../types/user.type'; import { Activity } from '../entities/activity'; import { Comment } from '../entities/comment'; -import { Country } from '../entities/country'; +import { UserType } from '../entities/user'; import { AppDataSource } from '../utils/data-source'; type ActivityGetter = { @@ -79,15 +78,10 @@ export const getActivities = async ({ userId, }); } else if (pelico && countries !== undefined && countries.length > 0) { - const countriesId = await AppDataSource.getRepository(Country).find({ - where: { - isoCode: In(countries), - }, - }); subQueryBuilder = subQueryBuilder .innerJoin('activity.user', 'user') - .andWhere('((user.countryId IN (:countriesId) AND user.type >= :userType) OR user.type <= :userType2)', { - countriesId, + .andWhere('((user.countryCode IN (:countries) AND user.type >= :userType) OR user.type <= :userType2)', { + countries, userType: UserType.TEACHER, userType2: UserType.MEDIATOR, }); @@ -96,13 +90,8 @@ export const getActivities = async ({ userType2: UserType.MEDIATOR, }); } else if (!pelico && countries !== undefined && countries.length > 0) { - const countriesId = await AppDataSource.getRepository(Country).find({ - where: { - isoCode: In(countries), - }, - }); - subQueryBuilder = subQueryBuilder.innerJoin('activity.user', 'user').andWhere('user.countryCode IN (:countriesId) AND user.type >= :userType', { - countriesId, + subQueryBuilder = subQueryBuilder.innerJoin('activity.user', 'user').andWhere('user.countryCode IN (:countries) AND user.type >= :userType', { + countries, userType: UserType.TEACHER, }); } else if (!pelico && countries !== undefined) { diff --git a/server/middlewares/authenticate.ts b/server/middlewares/authenticate.ts index 057e8ce60..758938c33 100644 --- a/server/middlewares/authenticate.ts +++ b/server/middlewares/authenticate.ts @@ -1,8 +1,8 @@ import type { NextFunction, Request, RequestHandler, Response } from 'express'; import jwt from 'jsonwebtoken'; -import type { UserType } from '../../types/user.type'; import { getNewAccessToken } from '../authentication/lib/tokens'; +import type { UserType } from '../entities/user'; import { User } from '../entities/user'; import { getHeader } from '../utils'; import { AppDataSource } from '../utils/data-source'; diff --git a/server/middlewares/setVillage.ts b/server/middlewares/setVillage.ts index 2295171ca..68a50f047 100644 --- a/server/middlewares/setVillage.ts +++ b/server/middlewares/setVillage.ts @@ -1,6 +1,6 @@ import type { NextFunction, Request, Response } from 'express'; -import { UserType } from '../../types/user.type'; +import { UserType } from '../entities/user'; import { Village } from '../entities/village'; import { AppDataSource } from '../utils/data-source'; @@ -13,9 +13,7 @@ export async function setVillage(req: Request, res: Response, next: NextFunction villageId = req.cookies?.['village-id'] || -1; } if (villageId !== -1 || (user && user.type !== UserType.TEACHER)) { - const villages = await AppDataSource.getRepository(Village).find( - villageId !== -1 ? { where: { id: villageId }, relations: { countries: true } } : { order: { id: 'ASC' } }, - ); + const villages = await AppDataSource.getRepository(Village).find(villageId !== -1 ? { where: { id: villageId } } : { order: { id: 'ASC' } }); req.village = villages[0]; } if (villageId === -1 && req.village !== undefined) { diff --git a/server/migrations/1712732757287-New-Country-Table.ts b/server/migrations/1712732757287-New-Country-Table.ts index 9e8fbccf5..267ae846e 100644 --- a/server/migrations/1712732757287-New-Country-Table.ts +++ b/server/migrations/1712732757287-New-Country-Table.ts @@ -13,7 +13,7 @@ export class NewCountryTable1712732757287 implements MigrationInterface { for (const country of countries) { // try catch here to prevent if country is already set in db for some reason try { - await queryRunner.query(`INSERT INTO country (isoCode, name) VALUES ('${country.isoCode}', "${country.name}");`); + await queryRunner.query(`INSERT INTO country (isoCode, name) VALUES ("${country.isoCode}", "${country.name}");`); } catch (error) { console.error(error); } diff --git a/server/migrations/1713446226165-user-village-country-relation.ts b/server/migrations/1713446226165-user-village-country-relation.ts deleted file mode 100644 index 301427085..000000000 --- a/server/migrations/1713446226165-user-village-country-relation.ts +++ /dev/null @@ -1,172 +0,0 @@ -import type { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserVillageCountryRelation1713446226165 implements MigrationInterface { - name = 'UserVillageCountryRelation1713446226165'; - - public async up(queryRunner: QueryRunner): Promise { - // VILLAGE county relation - const villageCountriesCountryTable = await queryRunner.getTable('village_countries_country'); - if (!villageCountriesCountryTable) { - await queryRunner.query( - `CREATE TABLE village_countries_country ( - villageId int NOT NULL, - countryId int NOT NULL, - INDEX IDX_df9756b7b0058adb584d3a8c75 (villageId), - INDEX IDX_870cdc22399cffb6327c914b79 (countryId), - CONSTRAINT FK_df9756b7b0058adb584d3a8c75e FOREIGN KEY (villageId) REFERENCES village(id) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT FK_870cdc22399cffb6327c914b790 FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE CASCADE ON UPDATE CASCADE, - PRIMARY KEY (villageId, countryId)) - ENGINE=InnoDB;`, - ); - } - - // save data stored - const villageTable = await queryRunner.getTable('village'); - const villageCountryCodesColumn = villageTable?.columns.find((c) => c.name === 'countryCodes'); - if (villageCountryCodesColumn) { - const allVillages: { id: number; countryCodes: string }[] = await queryRunner.query(`SELECT id, countryCodes FROM village;`); - for (const village of allVillages) { - if (village.countryCodes) { - const countryCodes = village.countryCodes - .split(',') - .map((cc) => `'${cc}'`) - .join(','); - const countryIds: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode IN(${countryCodes})`); - for (const countryId of countryIds) { - await queryRunner.query(`INSERT INTO village_countries_country (villageId, countryId) VALUES (${village.id}, ${countryId.id});`); - } - } - } - await queryRunner.query(`ALTER TABLE village DROP COLUMN countryCodes;`); - } - - // USER country relation - const userTable = await queryRunner.getTable('user'); - const userCountryId = userTable?.columns.find((c) => c.name === 'countryId'); - const userCountryConstraint = userTable?.foreignKeys.find((fk) => fk.name === 'FK_4aaf6d02199282eb8d3931bff31'); - if (!userCountryId && !userCountryConstraint) { - await queryRunner.query(`ALTER TABLE user - ADD COLUMN countryId int, - ADD CONSTRAINT FK_4aaf6d02199282eb8d3931bff31 FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - const userCountryCode = userTable?.columns.find((c) => c.name === 'countryCode'); - if (userCountryCode) { - const usersCountry: { id: number; countryCode: string | null }[] = await queryRunner.query(`SELECT id, countryCode FROM user;`); - // save data stored - for (const userCountry of usersCountry) { - const countryId: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode='${userCountry.countryCode}';`); - if (countryId.length && countryId[0].id) { - await queryRunner.query(`UPDATE user SET countryId=${countryId[0].id} WHERE id=${userCountry.id};`); - } - } - await queryRunner.query(`ALTER TABLE user DROP COLUMN countryCode;`); - } - - // CLASSROOM relation - const classroomTable = await queryRunner.getTable('classroom'); - const classroomCountryId = classroomTable?.columns.find((c) => c.name === 'countryId'); - const classroomCountryConstraint = classroomTable?.foreignKeys.find((fk) => fk.name === 'FK_650c574da06eaf08695ed7a7bcd'); - if (!classroomCountryId && !classroomCountryConstraint) { - await queryRunner.query( - `ALTER TABLE classroom - ADD countryId int NULL, - ADD CONSTRAINT FK_650c574da06eaf08695ed7a7bcd FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE NO ACTION ON UPDATE NO ACTION;`, - ); - } - const classroomCountryCode = classroomTable?.columns.find((c) => c.name === 'countryCode'); - if (classroomCountryCode) { - const classroomCountryCodes: { id: number; countryCode: string | null }[] = await queryRunner.query(`SELECT id, countryCode FROM classroom`); - for (const classroom of classroomCountryCodes) { - if (classroom.countryCode) { - const country: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode='${classroom.countryCode}';`); - await queryRunner.query(`UPDATE classroom SET countryId=${country[0].id} WHERE id=${classroom.id};`); - } - } - await queryRunner.query(`ALTER TABLE classroom DROP COLUMN countryCode`); - } - } - - public async down(queryRunner: QueryRunner): Promise { - // recreate VILLAGE data - const villageTable = await queryRunner.getTable('village'); - const villageCountryCodes = villageTable?.columns.find((c) => c.name === 'countryCodes'); - if (!villageCountryCodes) { - await queryRunner.query(`ALTER TABLE village ADD COLUMN countryCodes TINYTEXT;`); - } - const villageCountriesCountryTable = await queryRunner.getTable('village_countries_country'); - if (villageCountriesCountryTable) { - const villageCountryRelation: { villageId: number; countryId: number }[] = await queryRunner.query(`SELECT * FROM village_countries_country;`); - // create countryCodes column - const allVillages: { id: number }[] = await queryRunner.query(`SELECT id FROM village;`); - for (const village of allVillages) { - // build string query from country id - const countryIds = villageCountryRelation - .reduce((acc, relation) => { - if (relation.villageId === village.id) { - acc += `${relation.countryId}, `; - } - return acc; - }, '') - .slice(0, -2); - - // get isoCodes from country table - const countryIsoCodes: { isoCode: string }[] = await queryRunner.query(`SELECT isoCode FROM country WHERE id IN(${countryIds});`); - // build original countryCodes and set them in village.countryCodes - const countryCodes = countryIsoCodes - .reduce((acc, countryIso) => { - acc += `${countryIso.isoCode},`; - return acc; - }, '') - .slice(0, -1); - await queryRunner.query(`UPDATE village SET countryCodes = '${countryCodes}' WHERE id = ${village.id};`); - } - // removing relation village countries table - if (villageTable?.foreignKeys.find((fk) => fk.name === 'FK_870cdc22399cffb6327c914b790')) { - await queryRunner.query(`ALTER TABLE village_countries_country DROP FOREIGN KEY FK_870cdc22399cffb6327c914b790`); - } - if (villageTable?.foreignKeys.find((fk) => fk.name === 'FK_df9756b7b0058adb584d3a8c75e')) { - await queryRunner.query(`ALTER TABLE village_countries_country DROP FOREIGN KEY FK_df9756b7b0058adb584d3a8c75e`); - } - await queryRunner.dropTable('village_countries_country'); - } - - // recreate USER data - const userTable = await queryRunner.getTable('user'); - const userCountryId = userTable?.columns.find((c) => c.name === 'countryId'); - if (userCountryId) { - const usersCountryId: { id: number; countryId: number }[] = await queryRunner.query(`SELECT id, countryId FROM user;`); - const userCountryCode = userTable?.columns.find((c) => c.name === 'countryCode'); - if (!userCountryCode) { - await queryRunner.query(`ALTER TABLE user ADD COLUMN countryCode TINYTEXT;`); - for (const userData of usersCountryId) { - const isoCode: [{ isoCode: string }] = await queryRunner.query(`SELECT isoCode FROM country WHERE id=${userData.countryId}`); - if (isoCode.length) { - await queryRunner.query(`UPDATE user SET countryCode = '${isoCode[0].isoCode}' WHERE id = ${userData.id};`); - } - } - } - // drop key constraint + column - await queryRunner.query(`ALTER TABLE user DROP FOREIGN KEY FK_4aaf6d02199282eb8d3931bff31`); - await queryRunner.query(`ALTER TABLE user DROP COLUMN countryId`); - } - - // recreate CLASSROOM data - const classroomTable = await queryRunner.getTable('classroom'); - const classroomCountryId = classroomTable?.columns.find((c) => c.name === 'countryId'); - if (classroomCountryId) { - const classroomsCountryId: { id: number; countryId: number }[] = await queryRunner.query(`SELECT id, countryId FROM classroom;`); - if (!classroomTable?.columns.find((c) => c.name === 'countryCode')) { - await queryRunner.query(`ALTER TABLE classroom ADD COLUMN countryCode TINYTEXT;`); - for (const classroom of classroomsCountryId) { - const isoCode: [{ isoCode: string }] = await queryRunner.query(`SELECT isoCode FROM country WHERE id=${classroom.countryId}`); - if (isoCode.length) { - await queryRunner.query(`UPDATE classroom SET countryCode = '${isoCode[0].isoCode}' WHERE id = ${classroom.id};`); - } - } - } - // drop key contraint and relation table - await queryRunner.query(`ALTER TABLE classroom DROP FOREIGN KEY FK_650c574da06eaf08695ed7a7bcd`); - await queryRunner.query(`ALTER TABLE classroom DROP COLUMN countryId`); - } - } -} diff --git a/server/utils/countries-map.ts b/server/utils/countries-map.ts new file mode 100644 index 000000000..43789d4aa --- /dev/null +++ b/server/utils/countries-map.ts @@ -0,0 +1,7 @@ +import type { Country } from '../../types/country.type'; +import { countries } from './iso-3166-countries-french'; + +export const countriesMap = countries.reduce>((acc, c) => { + acc[c.isoCode] = c; + return acc; +}, {}); diff --git a/server/utils/database.ts b/server/utils/database.ts index 8b943567d..2ca60eb65 100644 --- a/server/utils/database.ts +++ b/server/utils/database.ts @@ -1,9 +1,7 @@ import * as argon2 from 'argon2'; import mysql from 'mysql2'; -import { UserType } from '../../types/user.type'; -import { Country } from '../entities/country'; -import { User } from '../entities/user'; +import { User, UserType } from '../entities/user'; import { AppDataSource, DEFAULT_NAME } from './data-source'; import { sleep } from './index'; import { logger } from './logger'; @@ -68,10 +66,9 @@ async function createSuperAdminUser(): Promise { user.type = UserType.SUPER_ADMIN; user.passwordHash = await argon2.hash(adminPassword); user.accountRegistration = 0; - const frCountry = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: 'FR' } }); - if (frCountry) user.country = frCountry; - user.positionLat = 0; - user.positionLon = 0; + user.countryCode = 'fr'; + user.positionLat = '0'; + user.positionLon = '0'; await AppDataSource.getRepository(User).save(user); logger.info('Super user Admin created!'); } diff --git a/server/utils/get-pos.ts b/server/utils/get-pos.ts index c275a78e4..c5d6f1135 100644 --- a/server/utils/get-pos.ts +++ b/server/utils/get-pos.ts @@ -38,11 +38,10 @@ export async function setUserPosition(user: User): Promise { const pos = (await getPosition({ q: query })) || - (await getPosition({ city: user.city, country: user.country?.name ?? 'France' })) || - (await getPosition({ country: user.country?.name ?? 'France' })); + (await getPosition({ city: user.city, country: user.country?.name })) || + (await getPosition({ country: user.country?.name })); if (pos !== null) { - user.positionLat = pos.lat; - user.positionLon = pos.lng; + user.position = pos; return; } } diff --git a/server/utils/iso-3166-countries-french.ts b/server/utils/iso-3166-countries-french.ts index d81937c8b..026ae1640 100644 --- a/server/utils/iso-3166-countries-french.ts +++ b/server/utils/iso-3166-countries-french.ts @@ -1,6 +1,6 @@ import type { Country } from '../../types/country.type'; -export const countries: Omit[] = [ +export const countries: Country[] = [ { isoCode: 'AF', name: 'Afghanistan', diff --git a/src/api/countries/countries.get.ts b/src/api/countries/countries.get.ts deleted file mode 100644 index 9c123f92a..000000000 --- a/src/api/countries/countries.get.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useQuery } from 'react-query'; -import type { Country } from 'server/entities/country'; - -import { axiosRequest } from 'src/utils/axiosRequest'; - -async function getCountries(): Promise { - return ( - await axiosRequest({ - method: 'GET', - baseURL: '/api', - url: '/countries', - }) - ).data; -} - -export const useGetCountries = () => { - return useQuery(['countries'], () => getCountries()); -}; diff --git a/src/api/user/user.get.ts b/src/api/user/user.get.ts index ef4b99aa2..9fd078f38 100644 --- a/src/api/user/user.get.ts +++ b/src/api/user/user.get.ts @@ -34,6 +34,7 @@ export const getUserVisibilityFamilyParams = async (user: User) => { url: `/users/${user.id}/visibility-params`, }); if (response.error) return null; + // console.log('User visibility params: ', response.data); return response.data; } return []; diff --git a/src/components/Flag.tsx b/src/components/Flag.tsx index 0c3e2dbbe..8c23ca027 100644 --- a/src/components/Flag.tsx +++ b/src/components/Flag.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type { Country } from 'server/entities/country'; import MysteryFlag from 'src/svg/mystery-flag.svg'; @@ -10,7 +9,7 @@ const sizes = { interface FlagProps { isMistery?: boolean; - country?: Country; + country?: string; size?: 'small' | 'medium'; style?: React.CSSProperties; } @@ -21,9 +20,6 @@ export const Flag = ({ country, isMistery = false, size = 'medium', style = {} } return ( // Small SVG, no need of improvments // eslint-disable-next-line @next/next/no-img-element - + ); }; diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 5f66aca36..984d654e9 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -257,12 +257,12 @@ export const Navigation = (): JSX.Element => { ); @@ -271,7 +271,7 @@ export const Navigation = (): JSX.Element => { { displayName: newUser.displayName || '', }; if (position !== null) { - updatedValues.positionLat = position.lat; - updatedValues.positionLon = position.lng; + updatedValues.position = position; } const response = await axiosRequest({ method: 'PUT', @@ -227,7 +226,7 @@ export const FirstPhase = () => {

{user ? user.country?.name : ''} - {user && } + {user && }