Compare commits
	
		
			7 Commits
		
	
	
		
			d08dcb9720
			...
			45a1cebcf9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 45a1cebcf9 | |||
| 376c297eda | |||
| 4486e99225 | |||
| 9b3fe93f4f | |||
| 15e0263559 | |||
| 2ad2063339 | |||
| ab180a12ce | 
							
								
								
									
										15
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| { | ||||
|     "sqltools.connections": [ | ||||
|         { | ||||
|             "previewLimit": 50, | ||||
|             "server": "localhost", | ||||
|             "port": 5432, | ||||
|             "driver": "PostgreSQL", | ||||
|             "name": "Traintrape-moi", | ||||
|             "database": "traintrape-moi", | ||||
|             "username": "traintrapemoi", | ||||
|             "socketPath": "/run/postgresql/.s.PGSQL.5432" | ||||
|         } | ||||
|     ], | ||||
|     "sqltools.useNodeRuntime": true | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { ScrollView } from 'react-native'; | ||||
| import { ScrollView } from 'react-native' | ||||
|  | ||||
| import { ThemedText } from '@/components/ThemedText'; | ||||
| import { ThemedView } from '@/components/ThemedView'; | ||||
| import { ThemedText } from '@/components/ThemedText' | ||||
| import { ThemedView } from '@/components/ThemedView' | ||||
|  | ||||
| export default function ChallengesScreen() { | ||||
|   return ( | ||||
| @@ -10,5 +10,5 @@ export default function ChallengesScreen() { | ||||
|         <ThemedText>Ici on aura la gestion des challenges</ThemedText> | ||||
|       </ThemedView> | ||||
|     </ScrollView> | ||||
|   ); | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import { ScrollView } from 'react-native'; | ||||
|  | ||||
| import { ThemedText } from '@/components/ThemedText'; | ||||
| import { ThemedView } from '@/components/ThemedView'; | ||||
| import { ScrollView } from 'react-native' | ||||
| import { ThemedText } from '@/components/ThemedText' | ||||
| import { ThemedView } from '@/components/ThemedView' | ||||
|  | ||||
| export default function HistoryScreen() { | ||||
|   return ( | ||||
| @@ -10,5 +9,5 @@ export default function HistoryScreen() { | ||||
|         <ThemedText>Ici on aura la gestion de l'historique des trains empruntés et des challenges effectués</ThemedText> | ||||
|       </ThemedView> | ||||
|     </ScrollView> | ||||
|   ); | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -27,4 +27,4 @@ const styles = StyleSheet.create({ | ||||
|     flex: 1, | ||||
|     alignSelf: 'stretch', | ||||
|   }, | ||||
| }); | ||||
| }) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { ScrollView } from 'react-native'; | ||||
| import { ScrollView } from 'react-native' | ||||
|  | ||||
| import { ThemedText } from '@/components/ThemedText'; | ||||
| import { ThemedView } from '@/components/ThemedView'; | ||||
| import { ThemedText } from '@/components/ThemedText' | ||||
| import { ThemedView } from '@/components/ThemedView' | ||||
|  | ||||
| export default function TrainScreen() { | ||||
|   return ( | ||||
| @@ -10,5 +10,5 @@ export default function TrainScreen() { | ||||
|         <ThemedText>Ici on aura la page pour ajouter un trajet en train depuis Rail Planner</ThemedText> | ||||
|       </ThemedView> | ||||
|     </ScrollView> | ||||
|   ); | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,11 @@ | ||||
| import { Text, type TextProps, StyleSheet } from 'react-native'; | ||||
|  | ||||
| import { useThemeColor } from '@/hooks/useThemeColor'; | ||||
| import { Text, type TextProps, StyleSheet } from 'react-native' | ||||
| import { useThemeColor } from '@/hooks/useThemeColor' | ||||
|  | ||||
| export type ThemedTextProps = TextProps & { | ||||
|   lightColor?: string; | ||||
|   darkColor?: string; | ||||
|   type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; | ||||
| }; | ||||
|   lightColor?: string | ||||
|   darkColor?: string | ||||
|   type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link' | ||||
| } | ||||
|  | ||||
| export function ThemedText({ | ||||
|   style, | ||||
| @@ -15,7 +14,7 @@ export function ThemedText({ | ||||
|   type = 'default', | ||||
|   ...rest | ||||
| }: ThemedTextProps) { | ||||
|   const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); | ||||
|   const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text') | ||||
|  | ||||
|   return ( | ||||
|     <Text | ||||
| @@ -30,7 +29,7 @@ export function ThemedText({ | ||||
|       ]} | ||||
|       {...rest} | ||||
|     /> | ||||
|   ); | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
| @@ -57,4 +56,4 @@ const styles = StyleSheet.create({ | ||||
|     fontSize: 16, | ||||
|     color: '#0a7ea4', | ||||
|   }, | ||||
| }); | ||||
| }) | ||||
|   | ||||
| @@ -1,14 +1,13 @@ | ||||
| import { View, type ViewProps } from 'react-native'; | ||||
|  | ||||
| import { useThemeColor } from '@/hooks/useThemeColor'; | ||||
| import { View, type ViewProps } from 'react-native' | ||||
| import { useThemeColor } from '@/hooks/useThemeColor' | ||||
|  | ||||
| export type ThemedViewProps = ViewProps & { | ||||
|   lightColor?: string; | ||||
|   darkColor?: string; | ||||
| }; | ||||
|   lightColor?: string | ||||
|   darkColor?: string | ||||
| } | ||||
|  | ||||
| export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { | ||||
|   const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); | ||||
|   const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background') | ||||
|  | ||||
|   return <View style={[{ backgroundColor }, style]} {...otherProps} />; | ||||
|   return <View style={[{ backgroundColor }, style]} {...otherProps} /> | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| import * as React from 'react'; | ||||
| import renderer from 'react-test-renderer'; | ||||
| import * as React from 'react' | ||||
| import renderer from 'react-test-renderer' | ||||
|  | ||||
| import { ThemedText } from '../ThemedText'; | ||||
| import { ThemedText } from '../ThemedText' | ||||
|  | ||||
| it(`renders correctly`, () => { | ||||
|   const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON(); | ||||
|   const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON() | ||||
|  | ||||
|   expect(tree).toMatchSnapshot(); | ||||
| }); | ||||
|   expect(tree).toMatchSnapshot() | ||||
| }) | ||||
|   | ||||
| @@ -21,4 +21,4 @@ exports[`renders correctly 1`] = ` | ||||
| > | ||||
|   Snapshot test! | ||||
| </Text> | ||||
| `; | ||||
| ` | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
|  * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. | ||||
|  */ | ||||
|  | ||||
| const tintColorLight = '#0a7ea4'; | ||||
| const tintColorDark = '#fff'; | ||||
| const tintColorLight = '#0a7ea4' | ||||
| const tintColorDark = '#fff' | ||||
|  | ||||
| export const Colors = { | ||||
|   light: { | ||||
| @@ -23,4 +23,4 @@ export const Colors = { | ||||
|     tabIconDefault: '#9BA1A6', | ||||
|     tabIconSelected: tintColorDark, | ||||
|   }, | ||||
| }; | ||||
| } | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| export { useColorScheme } from 'react-native'; | ||||
| export { useColorScheme } from 'react-native' | ||||
|   | ||||
| @@ -1,21 +1,21 @@ | ||||
| import { useEffect, useState } from 'react'; | ||||
| import { useColorScheme as useRNColorScheme } from 'react-native'; | ||||
| import { useEffect, useState } from 'react' | ||||
| import { useColorScheme as useRNColorScheme } from 'react-native' | ||||
|  | ||||
| /** | ||||
|  * To support static rendering, this value needs to be re-calculated on the client side for web | ||||
|  */ | ||||
| export function useColorScheme() { | ||||
|   const [hasHydrated, setHasHydrated] = useState(false); | ||||
|   const [hasHydrated, setHasHydrated] = useState(false) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setHasHydrated(true); | ||||
|   }, []); | ||||
|     setHasHydrated(true) | ||||
|   }, []) | ||||
|  | ||||
|   const colorScheme = useRNColorScheme(); | ||||
|   const colorScheme = useRNColorScheme() | ||||
|  | ||||
|   if (hasHydrated) { | ||||
|     return colorScheme; | ||||
|     return colorScheme | ||||
|   } | ||||
|  | ||||
|   return 'light'; | ||||
|   return 'light' | ||||
| } | ||||
|   | ||||
| @@ -3,19 +3,19 @@ | ||||
|  * https://docs.expo.dev/guides/color-schemes/ | ||||
|  */ | ||||
|  | ||||
| import { Colors } from '@/constants/Colors'; | ||||
| import { useColorScheme } from '@/hooks/useColorScheme'; | ||||
| import { Colors } from '@/constants/Colors' | ||||
| import { useColorScheme } from '@/hooks/useColorScheme' | ||||
|  | ||||
| export function useThemeColor( | ||||
|   props: { light?: string; dark?: string }, | ||||
|   colorName: keyof typeof Colors.light & keyof typeof Colors.dark | ||||
| ) { | ||||
|   const theme = useColorScheme() ?? 'light'; | ||||
|   const colorFromProps = props[theme]; | ||||
|   const theme = useColorScheme() ?? 'light' | ||||
|   const colorFromProps = props[theme] | ||||
|  | ||||
|   if (colorFromProps) { | ||||
|     return colorFromProps; | ||||
|     return colorFromProps | ||||
|   } else { | ||||
|     return Colors[theme][colorName]; | ||||
|     return Colors[theme][colorName] | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								server/.env.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								server/.env.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| DATABASE_URL="postgres://username:password@localhost:5432/traintrape-moi" | ||||
| JWT_SECRET="CHANGE_ME" | ||||
| @@ -21,5 +21,6 @@ module.exports = { | ||||
|     '@typescript-eslint/explicit-function-return-type': 'off', | ||||
|     '@typescript-eslint/explicit-module-boundary-types': 'off', | ||||
|     '@typescript-eslint/no-explicit-any': 'off', | ||||
|     '@typescript-eslint/semi': 'never', | ||||
|   }, | ||||
| }; | ||||
| } | ||||
|   | ||||
							
								
								
									
										9
									
								
								server/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								server/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,10 @@ | ||||
| /node_modules | ||||
| /build | ||||
|  | ||||
| # Databases | ||||
| *.sqlite3 | ||||
| *.sqlite3-journal | ||||
|  | ||||
| # Logs | ||||
| logs | ||||
| *.log | ||||
| @@ -37,10 +41,7 @@ lerna-debug.log* | ||||
|  | ||||
| # dotenv environment variable files | ||||
| .env | ||||
| .env.development.local | ||||
| .env.test.local | ||||
| .env.production.local | ||||
| .env.local | ||||
| .env*.local | ||||
|  | ||||
| # temp directory | ||||
| .temp | ||||
|   | ||||
							
								
								
									
										836
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										836
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -21,18 +21,30 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@nestjs/common": "^10.0.0", | ||||
|     "@nestjs/core": "^10.0.0", | ||||
|     "@nestjs/core": "^10.4.13", | ||||
|     "@nestjs/jwt": "^10.2.0", | ||||
|     "@nestjs/passport": "^10.0.3", | ||||
|     "@nestjs/platform-express": "^10.0.0", | ||||
|     "@nestjs/swagger": "^8.1.0", | ||||
|     "@prisma/client": "^6.0.1", | ||||
|     "bcrypt": "^5.1.1", | ||||
|     "class-validator": "^0.14.1", | ||||
|     "passport": "^0.7.0", | ||||
|     "passport-jwt": "^4.0.1", | ||||
|     "prisma": "^6.0.1", | ||||
|     "reflect-metadata": "^0.2.0", | ||||
|     "rxjs": "^7.8.1" | ||||
|     "rxjs": "^7.8.1", | ||||
|     "swagger-ui-express": "^5.0.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@nestjs/cli": "^10.0.0", | ||||
|     "@nestjs/schematics": "^10.0.0", | ||||
|     "@nestjs/testing": "^10.0.0", | ||||
|     "@nestjs/testing": "^10.4.13", | ||||
|     "@types/bcrypt": "^5.0.2", | ||||
|     "@types/express": "^5.0.0", | ||||
|     "@types/jest": "^29.5.2", | ||||
|     "@types/node": "^20.3.1", | ||||
|     "@types/passport-jwt": "^4.0.1", | ||||
|     "@types/supertest": "^6.0.0", | ||||
|     "@typescript-eslint/eslint-plugin": "^8.0.0", | ||||
|     "@typescript-eslint/parser": "^8.0.0", | ||||
| @@ -65,5 +77,8 @@ | ||||
|     ], | ||||
|     "coverageDirectory": "../coverage", | ||||
|     "testEnvironment": "node" | ||||
|   }, | ||||
|   "prisma": { | ||||
|     "seed": "ts-node prisma/seed.ts" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										109
									
								
								server/prisma/migrations/20241207114558_init/migration.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								server/prisma/migrations/20241207114558_init/migration.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| -- CreateEnum | ||||
| CREATE TYPE "MoneyUpdateType" AS ENUM ('START', 'WIN_CHALLENGE', 'BUY_TRAIN'); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "User" ( | ||||
|     "id" SERIAL NOT NULL, | ||||
|     "name" TEXT NOT NULL, | ||||
|     "password" TEXT NOT NULL, | ||||
|     "money" INTEGER NOT NULL DEFAULT 0, | ||||
|     "currentRunner" BOOLEAN NOT NULL DEFAULT false, | ||||
|  | ||||
|     CONSTRAINT "User_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "Geolocation" ( | ||||
|     "id" SERIAL NOT NULL, | ||||
|     "userId" INTEGER NOT NULL, | ||||
|     "longitude" DOUBLE PRECISION NOT NULL, | ||||
|     "latitude" DOUBLE PRECISION NOT NULL, | ||||
|     "timestamp" TIMESTAMP(3) NOT NULL, | ||||
|  | ||||
|     CONSTRAINT "Geolocation_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "Challenge" ( | ||||
|     "id" SERIAL NOT NULL, | ||||
|     "title" TEXT NOT NULL, | ||||
|     "description" TEXT NOT NULL, | ||||
|     "reward" INTEGER NOT NULL, | ||||
|  | ||||
|     CONSTRAINT "Challenge_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "ChallengeAction" ( | ||||
|     "id" SERIAL NOT NULL, | ||||
|     "userId" INTEGER NOT NULL, | ||||
|     "challengeId" INTEGER NOT NULL, | ||||
|     "active" BOOLEAN NOT NULL DEFAULT false, | ||||
|     "success" BOOLEAN NOT NULL DEFAULT false, | ||||
|  | ||||
|     CONSTRAINT "ChallengeAction_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "TrainTrip" ( | ||||
|     "id" TEXT NOT NULL, | ||||
|     "userId" INTEGER NOT NULL, | ||||
|     "distance" DOUBLE PRECISION NOT NULL, | ||||
|     "from" TEXT NOT NULL, | ||||
|     "to" TEXT NOT NULL, | ||||
|     "departureTime" TIMESTAMP(3) NOT NULL, | ||||
|     "arrivalTime" TIMESTAMP(3) NOT NULL, | ||||
|     "infoJson" JSONB NOT NULL, | ||||
|     "geometry" TEXT NOT NULL, | ||||
|  | ||||
|     CONSTRAINT "TrainTrip_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateTable | ||||
| CREATE TABLE "MoneyUpdate" ( | ||||
|     "id" SERIAL NOT NULL, | ||||
|     "userId" INTEGER NOT NULL, | ||||
|     "before" INTEGER NOT NULL, | ||||
|     "after" INTEGER NOT NULL, | ||||
|     "reason" "MoneyUpdateType" NOT NULL, | ||||
|     "actionId" INTEGER, | ||||
|     "tripId" TEXT, | ||||
|  | ||||
|     CONSTRAINT "MoneyUpdate_pkey" PRIMARY KEY ("id") | ||||
| ); | ||||
|  | ||||
| -- CreateIndex | ||||
| CREATE UNIQUE INDEX "User_name_key" ON "User"("name"); | ||||
|  | ||||
| -- CreateIndex | ||||
| CREATE UNIQUE INDEX "Challenge_title_key" ON "Challenge"("title"); | ||||
|  | ||||
| -- CreateIndex | ||||
| CREATE UNIQUE INDEX "ChallengeAction_challengeId_key" ON "ChallengeAction"("challengeId"); | ||||
|  | ||||
| -- CreateIndex | ||||
| CREATE UNIQUE INDEX "MoneyUpdate_actionId_key" ON "MoneyUpdate"("actionId"); | ||||
|  | ||||
| -- CreateIndex | ||||
| CREATE UNIQUE INDEX "MoneyUpdate_tripId_key" ON "MoneyUpdate"("tripId"); | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "Geolocation" ADD CONSTRAINT "Geolocation_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "ChallengeAction" ADD CONSTRAINT "ChallengeAction_challengeId_fkey" FOREIGN KEY ("challengeId") REFERENCES "Challenge"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "ChallengeAction" ADD CONSTRAINT "ChallengeAction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "TrainTrip" ADD CONSTRAINT "TrainTrip_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "MoneyUpdate" ADD CONSTRAINT "MoneyUpdate_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "ChallengeAction"("id") ON DELETE SET NULL ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "MoneyUpdate" ADD CONSTRAINT "MoneyUpdate_tripId_fkey" FOREIGN KEY ("tripId") REFERENCES "TrainTrip"("id") ON DELETE SET NULL ON UPDATE CASCADE; | ||||
|  | ||||
| -- AddForeignKey | ||||
| ALTER TABLE "MoneyUpdate" ADD CONSTRAINT "MoneyUpdate_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||||
							
								
								
									
										3
									
								
								server/prisma/migrations/migration_lock.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								server/prisma/migrations/migration_lock.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # Please do not edit this file manually | ||||
| # It should be added in your version-control system (i.e. Git) | ||||
| provider = "postgresql" | ||||
							
								
								
									
										81
									
								
								server/prisma/schema.prisma
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								server/prisma/schema.prisma
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| generator client { | ||||
|   provider = "prisma-client-js" | ||||
| } | ||||
|  | ||||
| datasource db { | ||||
|   provider = "postgresql" | ||||
|   url      = env("DATABASE_URL") | ||||
| } | ||||
|  | ||||
| model User { | ||||
|   id            Int               @id @default(autoincrement()) | ||||
|   name          String            @unique | ||||
|   password      String | ||||
|   money         Int               @default(0) | ||||
|   currentRunner Boolean           @default(false) | ||||
|   actions       ChallengeAction[] | ||||
|   geolocations  Geolocation[] | ||||
|   moneyUpdates  MoneyUpdate[] | ||||
|   trips         TrainTrip[] | ||||
| } | ||||
|  | ||||
| model Geolocation { | ||||
|   id        Int      @id @default(autoincrement()) | ||||
|   userId    Int | ||||
|   longitude Float | ||||
|   latitude  Float | ||||
|   timestamp DateTime | ||||
|   user      User     @relation(fields: [userId], references: [id]) | ||||
| } | ||||
|  | ||||
| model Challenge { | ||||
|   id          Int              @id @default(autoincrement()) | ||||
|   title       String           @unique | ||||
|   description String | ||||
|   reward      Int | ||||
|   action      ChallengeAction? | ||||
| } | ||||
|  | ||||
| model ChallengeAction { | ||||
|   id          Int          @id @default(autoincrement()) | ||||
|   userId      Int | ||||
|   challengeId Int          @unique | ||||
|   active      Boolean      @default(false) | ||||
|   success     Boolean      @default(false) | ||||
|   challenge   Challenge    @relation(fields: [challengeId], references: [id]) | ||||
|   user        User         @relation(fields: [userId], references: [id]) | ||||
|   moneyUpdate MoneyUpdate? | ||||
| } | ||||
|  | ||||
| model TrainTrip { | ||||
|   id            String       @id | ||||
|   userId        Int | ||||
|   distance      Float | ||||
|   from          String | ||||
|   to            String | ||||
|   departureTime DateTime | ||||
|   arrivalTime   DateTime | ||||
|   infoJson      Json | ||||
|   geometry      String | ||||
|   moneyUpdate   MoneyUpdate? | ||||
|   user          User         @relation(fields: [userId], references: [id]) | ||||
| } | ||||
|  | ||||
| model MoneyUpdate { | ||||
|   id       Int              @id @default(autoincrement()) | ||||
|   userId   Int | ||||
|   before   Int | ||||
|   after    Int | ||||
|   reason   MoneyUpdateType | ||||
|   actionId Int?             @unique | ||||
|   tripId   String?          @unique | ||||
|   action   ChallengeAction? @relation(fields: [actionId], references: [id]) | ||||
|   trip     TrainTrip?       @relation(fields: [tripId], references: [id]) | ||||
|   user     User             @relation(fields: [userId], references: [id]) | ||||
| } | ||||
|  | ||||
| enum MoneyUpdateType { | ||||
|   START | ||||
|   WIN_CHALLENGE | ||||
|   BUY_TRAIN | ||||
| } | ||||
							
								
								
									
										30
									
								
								server/prisma/seed.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								server/prisma/seed.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import { PrismaClient } from '@prisma/client' | ||||
| import * as bcrypt from 'bcrypt' | ||||
|  | ||||
| const prisma = new PrismaClient() | ||||
|  | ||||
| async function main() { | ||||
|   const emmyPassword = await bcrypt.hash("Emmy", 10) | ||||
|   const emmy = await prisma.user.upsert({ | ||||
|     where: { id: 1 }, | ||||
|     update: { name: 'Emmy' }, | ||||
|     create: { name: 'Emmy', password: emmyPassword }, | ||||
|   }) | ||||
|  | ||||
|   const taminaPassword = await bcrypt.hash("Tamina", 10) | ||||
|   const tamina = await prisma.user.upsert({ | ||||
|     where: { id: 2 }, | ||||
|     update: { name: 'Tamina' }, | ||||
|     create: { name: 'Tamina', password: taminaPassword }, | ||||
|   }) | ||||
|   console.log({ emmy, tamina }) | ||||
| } | ||||
|  | ||||
| main() | ||||
|   .catch((e) => { | ||||
|     console.error(e) | ||||
|     process.exit(1) | ||||
|   }) | ||||
|   .finally(async () => { | ||||
|     await prisma.$disconnect() | ||||
|   }) | ||||
| @@ -1,22 +0,0 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
| import { AppController } from './app.controller'; | ||||
| import { AppService } from './app.service'; | ||||
|  | ||||
| describe('AppController', () => { | ||||
|   let appController: AppController; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const app: TestingModule = await Test.createTestingModule({ | ||||
|       controllers: [AppController], | ||||
|       providers: [AppService], | ||||
|     }).compile(); | ||||
|  | ||||
|     appController = app.get<AppController>(AppController); | ||||
|   }); | ||||
|  | ||||
|   describe('root', () => { | ||||
|     it('should return "Hello World!"', () => { | ||||
|       expect(appController.getHello()).toBe('Hello World!'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,12 +0,0 @@ | ||||
| import { Controller, Get } from '@nestjs/common'; | ||||
| import { AppService } from './app.service'; | ||||
|  | ||||
| @Controller() | ||||
| export class AppController { | ||||
|   constructor(private readonly appService: AppService) {} | ||||
|  | ||||
|   @Get() | ||||
|   getHello(): string { | ||||
|     return this.appService.getHello(); | ||||
|   } | ||||
| } | ||||
| @@ -1,10 +1,11 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
| import { AppController } from './app.controller'; | ||||
| import { AppService } from './app.service'; | ||||
| import { Module } from '@nestjs/common' | ||||
| import { PrismaService } from './prisma/prisma.service' | ||||
| import { PrismaModule } from './prisma/prisma.module' | ||||
| import { UsersModule } from './users/users.module' | ||||
| import { AuthModule } from './auth/auth.module' | ||||
|  | ||||
| @Module({ | ||||
|   imports: [], | ||||
|   controllers: [AppController], | ||||
|   providers: [AppService], | ||||
|   imports: [PrismaModule, UsersModule, AuthModule], | ||||
|   providers: [PrismaService], | ||||
| }) | ||||
| export class AppModule {} | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| import { Injectable } from '@nestjs/common'; | ||||
|  | ||||
| @Injectable() | ||||
| export class AppService { | ||||
|   getHello(): string { | ||||
|     return 'Hello World!'; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										20
									
								
								server/src/auth/auth.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								server/src/auth/auth.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { AuthController } from './auth.controller' | ||||
| import { AuthService } from './auth.service' | ||||
|  | ||||
| describe('AuthController', () => { | ||||
|   let controller: AuthController | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       controllers: [AuthController], | ||||
|       providers: [AuthService], | ||||
|     }).compile() | ||||
|  | ||||
|     controller = module.get<AuthController>(AuthController) | ||||
|   }) | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(controller).toBeDefined() | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										17
									
								
								server/src/auth/auth.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/src/auth/auth.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import { Body, Controller, Post } from '@nestjs/common' | ||||
| import { AuthService } from './auth.service' | ||||
| import { ApiOkResponse, ApiTags } from '@nestjs/swagger' | ||||
| import { AuthEntity } from './entity/auth.entity' | ||||
| import { LoginDto } from './dto/login.dto' | ||||
|  | ||||
| @Controller('auth') | ||||
| @ApiTags('auth') | ||||
| export class AuthController { | ||||
|   constructor(private readonly authService: AuthService) {} | ||||
|  | ||||
|   @Post('login') | ||||
|   @ApiOkResponse({ type: AuthEntity }) | ||||
|   login(@Body() { name, password }: LoginDto) { | ||||
|     return this.authService.login(name, password) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								server/src/auth/auth.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/src/auth/auth.module.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { Module } from '@nestjs/common' | ||||
| import { AuthService } from './auth.service' | ||||
| import { AuthController } from './auth.controller' | ||||
| import { PrismaModule } from 'src/prisma/prisma.module' | ||||
| import { PassportModule } from '@nestjs/passport' | ||||
| import { JwtModule } from '@nestjs/jwt' | ||||
| import { env } from 'process' | ||||
| import { UsersModule } from 'src/users/users.module' | ||||
| import { JwtStrategy } from './jwt.strategy' | ||||
|  | ||||
| export const JWT_SECRET = env.JWT_SECRET | ||||
|  | ||||
| @Module({ | ||||
|   imports: [ | ||||
|     PrismaModule, | ||||
|     PassportModule, | ||||
|     JwtModule.register({ | ||||
|       secret: JWT_SECRET, | ||||
|       signOptions: { expiresIn: '5m' }, | ||||
|     }), | ||||
|     UsersModule, | ||||
|   ], | ||||
|   controllers: [AuthController], | ||||
|   providers: [AuthService, JwtStrategy], | ||||
| }) | ||||
| export class AuthModule {} | ||||
							
								
								
									
										18
									
								
								server/src/auth/auth.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/src/auth/auth.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { AuthService } from './auth.service' | ||||
|  | ||||
| describe('AuthService', () => { | ||||
|   let service: AuthService | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [AuthService], | ||||
|     }).compile() | ||||
|  | ||||
|     service = module.get<AuthService>(AuthService) | ||||
|   }) | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(service).toBeDefined() | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										27
									
								
								server/src/auth/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/src/auth/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common' | ||||
| import { PrismaService } from './../prisma/prisma.service' | ||||
| import { JwtService } from '@nestjs/jwt' | ||||
| import { AuthEntity } from './entity/auth.entity' | ||||
| import * as bcrypt from 'bcrypt' | ||||
|  | ||||
| @Injectable() | ||||
| export class AuthService { | ||||
|   constructor(private prisma: PrismaService, private jwtService: JwtService) {} | ||||
|  | ||||
|   async login(name: string, password: string): Promise<AuthEntity> { | ||||
|     const user = await this.prisma.user.findUnique({ where: { name: name } }) | ||||
|     if (!user) { | ||||
|         throw new NotFoundException(`Aucun⋅e utilisateur⋅rice avec pour nom ${name}`) | ||||
|     } | ||||
|  | ||||
|     const isPasswordValid = await bcrypt.compare(password, user.password) | ||||
|  | ||||
|     if (!isPasswordValid) { | ||||
|         throw new UnauthorizedException('Mot de passe incorrect') | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       accessToken: this.jwtService.sign({ userId: user.id }), | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								server/src/auth/dto/login.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								server/src/auth/dto/login.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| import { ApiProperty } from '@nestjs/swagger' | ||||
| import { IsNotEmpty, IsString, MinLength } from 'class-validator' | ||||
|  | ||||
| export class LoginDto { | ||||
|   @IsString() | ||||
|   @IsNotEmpty() | ||||
|   @ApiProperty() | ||||
|   name: string | ||||
|  | ||||
|   @IsString() | ||||
|   @IsNotEmpty() | ||||
|   @ApiProperty() | ||||
|   password: string | ||||
| } | ||||
							
								
								
									
										6
									
								
								server/src/auth/entity/auth.entity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								server/src/auth/entity/auth.entity.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { ApiProperty } from '@nestjs/swagger' | ||||
|  | ||||
| export class AuthEntity { | ||||
|   @ApiProperty() | ||||
|   accessToken: string | ||||
| } | ||||
							
								
								
									
										5
									
								
								server/src/auth/jwt-auth.guard.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/src/auth/jwt-auth.guard.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import { Injectable } from '@nestjs/common' | ||||
| import { AuthGuard } from '@nestjs/passport' | ||||
|  | ||||
| @Injectable() | ||||
| export class JwtAuthGuard extends AuthGuard('jwt') {} | ||||
							
								
								
									
										23
									
								
								server/src/auth/jwt.strategy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								server/src/auth/jwt.strategy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import { Injectable, UnauthorizedException } from '@nestjs/common' | ||||
| import { PassportStrategy } from '@nestjs/passport' | ||||
| import { ExtractJwt, Strategy } from 'passport-jwt' | ||||
| import { JWT_SECRET } from './auth.module' | ||||
| import { UsersService } from 'src/users/users.service' | ||||
|  | ||||
| @Injectable() | ||||
| export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { | ||||
|   constructor(private usersService: UsersService) { | ||||
|     super({ | ||||
|       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||||
|       secretOrKey: JWT_SECRET, | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   async validate(payload: { userId: number }) { | ||||
|     const user = await this.usersService.findOne(payload.userId) | ||||
|     if (!user) { | ||||
|       throw new UnauthorizedException() | ||||
|     } | ||||
|     return user | ||||
|   } | ||||
| } | ||||
| @@ -1,8 +1,23 @@ | ||||
| import { NestFactory } from '@nestjs/core'; | ||||
| import { AppModule } from './app.module'; | ||||
| import { NestFactory, Reflector } from '@nestjs/core' | ||||
| import { AppModule } from './app.module' | ||||
| import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' | ||||
| import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common' | ||||
|  | ||||
| async function bootstrap() { | ||||
|   const app = await NestFactory.create(AppModule); | ||||
|   await app.listen(process.env.PORT ?? 3000); | ||||
|   const app = await NestFactory.create(AppModule) | ||||
|  | ||||
|   app.useGlobalPipes(new ValidationPipe()) | ||||
|   app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))) | ||||
|  | ||||
|   const config = new DocumentBuilder() | ||||
|     .setTitle('Traintrape-moi') | ||||
|     .setDescription('API permettant de stocker les données de Traintrape-moi') | ||||
|     .setVersion('1.0') | ||||
|     .addBearerAuth() | ||||
|     .build() | ||||
|   const document = SwaggerModule.createDocument(app, config) | ||||
|  | ||||
|   SwaggerModule.setup('', app, document) | ||||
|   await app.listen(process.env.PORT ?? 3000) | ||||
| } | ||||
| bootstrap(); | ||||
| bootstrap() | ||||
|   | ||||
							
								
								
									
										8
									
								
								server/src/prisma/prisma.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								server/src/prisma/prisma.module.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import { Module } from '@nestjs/common' | ||||
| import { PrismaService } from './prisma.service' | ||||
|  | ||||
| @Module({ | ||||
|     providers: [PrismaService], | ||||
|     exports: [PrismaService], | ||||
|   }) | ||||
| export class PrismaModule {} | ||||
							
								
								
									
										18
									
								
								server/src/prisma/prisma.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/src/prisma/prisma.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { PrismaService } from './prisma.service' | ||||
|  | ||||
| describe('PrismaService', () => { | ||||
|   let service: PrismaService | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [PrismaService], | ||||
|     }).compile() | ||||
|  | ||||
|     service = module.get<PrismaService>(PrismaService) | ||||
|   }) | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(service).toBeDefined() | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										5
									
								
								server/src/prisma/prisma.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								server/src/prisma/prisma.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import { Injectable } from '@nestjs/common' | ||||
| import { PrismaClient } from '@prisma/client' | ||||
|  | ||||
| @Injectable() | ||||
| export class PrismaService extends PrismaClient {} | ||||
							
								
								
									
										24
									
								
								server/src/users/entities/user.entity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/src/users/entities/user.entity.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import { ApiProperty } from "@nestjs/swagger" | ||||
| import { User } from "@prisma/client" | ||||
| import { Exclude } from 'class-transformer' | ||||
|  | ||||
| export class UserEntity implements User { | ||||
|   constructor(partial: Partial<UserEntity>) { | ||||
|     Object.assign(this, partial) | ||||
|   } | ||||
|  | ||||
|   @ApiProperty({description: "Identifiant unique"}) | ||||
|   id: number | ||||
|  | ||||
|   @ApiProperty({description: "Nom de læ joueur⋅se"}) | ||||
|   name: string | ||||
|  | ||||
|   @Exclude() | ||||
|   password: string | ||||
|  | ||||
|   @ApiProperty({description: "Nombre de jetons dont dispose actuellement læ joueur⋅se"}) | ||||
|   money: number | ||||
|  | ||||
|   @ApiProperty({description: "Est-ce que cet⋅te joueur⋅se est cellui actuellement en course"}) | ||||
|   currentRunner: boolean | ||||
| } | ||||
							
								
								
									
										20
									
								
								server/src/users/users.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								server/src/users/users.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { UsersController } from './users.controller' | ||||
| import { UsersService } from './users.service' | ||||
|  | ||||
| describe('UsersController', () => { | ||||
|   let controller: UsersController | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       controllers: [UsersController], | ||||
|       providers: [UsersService], | ||||
|     }).compile() | ||||
|  | ||||
|     controller = module.get<UsersController>(UsersController) | ||||
|   }) | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(controller).toBeDefined() | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										31
									
								
								server/src/users/users.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/src/users/users.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from '@nestjs/common' | ||||
| import { UsersService } from './users.service' | ||||
| import { ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse } from '@nestjs/swagger' | ||||
| import { UserEntity } from './entities/user.entity' | ||||
| import { JwtAuthGuard } from 'src/auth/jwt-auth.guard' | ||||
|  | ||||
| @Controller('users') | ||||
| export class UsersController { | ||||
|   constructor(private readonly usersService: UsersService) {} | ||||
|  | ||||
|   @Get() | ||||
|   @UseGuards(JwtAuthGuard) | ||||
|   @ApiBearerAuth() | ||||
|   @ApiOkResponse({ type: UserEntity, isArray: true }) | ||||
|   async findAll() { | ||||
|     const users = await this.usersService.findAll() | ||||
|     return users.map(user => new UserEntity(user)) | ||||
|   } | ||||
|  | ||||
|   @Get(':id') | ||||
|   @UseGuards(JwtAuthGuard) | ||||
|   @ApiBearerAuth() | ||||
|   @ApiOkResponse({ type: UserEntity }) | ||||
|   @ApiNotFoundResponse({ description: "Utilisateur⋅rice non trouvé⋅e" }) | ||||
|   async findOne(@Param('id', ParseIntPipe) id: number) { | ||||
|     const user = await this.usersService.findOne(id) | ||||
|     if (!user) | ||||
|       throw new NotFoundException(`L'utilisateur⋅rice avec l'identifiant ${id} n'existe pas`) | ||||
|     return new UserEntity(user) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										12
									
								
								server/src/users/users.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								server/src/users/users.module.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import { Module } from '@nestjs/common' | ||||
| import { UsersService } from './users.service' | ||||
| import { UsersController } from './users.controller' | ||||
| import { PrismaModule } from 'src/prisma/prisma.module' | ||||
|  | ||||
| @Module({ | ||||
|   controllers: [UsersController], | ||||
|   providers: [UsersService], | ||||
|   imports: [PrismaModule], | ||||
|   exports: [UsersService], | ||||
| }) | ||||
| export class UsersModule {} | ||||
							
								
								
									
										18
									
								
								server/src/users/users.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/src/users/users.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { UsersService } from './users.service' | ||||
|  | ||||
| describe('UsersService', () => { | ||||
|   let service: UsersService | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const module: TestingModule = await Test.createTestingModule({ | ||||
|       providers: [UsersService], | ||||
|     }).compile() | ||||
|  | ||||
|     service = module.get<UsersService>(UsersService) | ||||
|   }) | ||||
|  | ||||
|   it('should be defined', () => { | ||||
|     expect(service).toBeDefined() | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										15
									
								
								server/src/users/users.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								server/src/users/users.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| import { Injectable } from '@nestjs/common' | ||||
| import { PrismaService } from 'src/prisma/prisma.service' | ||||
|  | ||||
| @Injectable() | ||||
| export class UsersService { | ||||
|   constructor(private prisma: PrismaService) {} | ||||
|  | ||||
|   async findAll() { | ||||
|     return await this.prisma.user.findMany() | ||||
|   } | ||||
|  | ||||
|   async findOne(id: number) { | ||||
|     return await this.prisma.user.findUnique({ where: { id } }) | ||||
|   } | ||||
| } | ||||
| @@ -1,24 +1,24 @@ | ||||
| import { Test, TestingModule } from '@nestjs/testing'; | ||||
| import { INestApplication } from '@nestjs/common'; | ||||
| import * as request from 'supertest'; | ||||
| import { AppModule } from './../src/app.module'; | ||||
| import { Test, TestingModule } from '@nestjs/testing' | ||||
| import { INestApplication } from '@nestjs/common' | ||||
| import * as request from 'supertest' | ||||
| import { AppModule } from './../src/app.module' | ||||
|  | ||||
| describe('AppController (e2e)', () => { | ||||
|   let app: INestApplication; | ||||
|   let app: INestApplication | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     const moduleFixture: TestingModule = await Test.createTestingModule({ | ||||
|       imports: [AppModule], | ||||
|     }).compile(); | ||||
|     }).compile() | ||||
|  | ||||
|     app = moduleFixture.createNestApplication(); | ||||
|     await app.init(); | ||||
|   }); | ||||
|     app = moduleFixture.createNestApplication() | ||||
|     await app.init() | ||||
|   }) | ||||
|  | ||||
|   it('/ (GET)', () => { | ||||
|     return request(app.getHttpServer()) | ||||
|       .get('/') | ||||
|       .expect(200) | ||||
|       .expect('Hello World!'); | ||||
|   }); | ||||
| }); | ||||
|       .expect('Hello World!') | ||||
|   }) | ||||
| }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user