diff --git a/.env b/.env index 5ded86e1..e81c3999 100644 --- a/.env +++ b/.env @@ -1,3 +1,8 @@ +#nessecarr var +#API_SECRET + + + # POSTGRES_USER=kinou # POSTGRES_PASSWORD=pass # POSTGRES_DB=postgreDB @@ -5,11 +10,35 @@ # POSTGRES_HOST=localhost # POSTGRES_HOST_AUTH_METHOD=trust -POSTGRES_HOST=127.0.0.1 -POSTGRES_PORT=5432 +#URL +NGINX_ENVSUBST_TEMPLATE_SUFFIX=".conf" + +BASE_URL=http://localhost + +#postgres var +# POSTGRES_HOST=127.0.0.1 +# DB_TYPE=postgres +POSTGRES_HOST=postgresql POSTGRES_USER=postgres POSTGRES_PASSWORD=pass POSTGRES_DATABASE=postgres -PORT=3000 MODE=DEV -RUN_MIGRATIONS=true \ No newline at end of file + +#port +API_PORT=3000 +# REACT_PORT=3000 (current = 8080) +NGINX_PORT=80 +PONG_PORT=4000 +CHAT_PORT=4001 +POSTGRES_PORT=5432 + +#???? +RUN_MIGRATIONS=true +REACT_HOST=0.0.0.0 + + +#auth var +JWT_SECRET=secrethere +REDIRECT_URI=http://localhost:80/api/auth/login +API_SECRET=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 +CLIENT_UID=u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41 \ No newline at end of file diff --git a/conf/nginx.conf b/conf/nginx.conf index f4093a53..8af41775 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -2,15 +2,14 @@ server { # listen 443 ssl; # listen 80 ssl; # listen 443 ssl; - listen 80; - + # listen ${NGINX_PORT}; + listen 80; location /{ proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://react_app:8080; } @@ -19,7 +18,6 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://api:3000/api; } } \ No newline at end of file diff --git a/containers/api/package-lock.json b/containers/api/package-lock.json index e925e7b2..d6ede8ee 100644 --- a/containers/api/package-lock.json +++ b/containers/api/package-lock.json @@ -22,6 +22,7 @@ "express-session": "^1.17.3", "hi-base32": "^0.5.1", "nanoid": "^3.3.4", + "otpauth": "^9.1.2", "passport": "^0.6.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", @@ -6099,6 +6100,14 @@ "npm": ">=6" } }, + "node_modules/jssha": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.0.tgz", + "integrity": "sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==", + "engines": { + "node": "*" + } + }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -6685,6 +6694,17 @@ "node": ">=0.10.0" } }, + "node_modules/otpauth": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.1.2.tgz", + "integrity": "sha512-iI5nlVvMFP3aTPdjG/fnC4mhVJ/KZOSnBrvo/VnYHUwlTp9jVLjAe2B3i3pyCH+3/E5jYQRSvuHk/8oas3870g==", + "dependencies": { + "jssha": "~3.3.0" + }, + "funding": { + "url": "https://github.com/hectorm/otpauth?sponsor=1" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", diff --git a/containers/api/package.json b/containers/api/package.json index 74c414b2..d54f1be3 100644 --- a/containers/api/package.json +++ b/containers/api/package.json @@ -33,6 +33,7 @@ "express-session": "^1.17.3", "hi-base32": "^0.5.1", "nanoid": "^3.3.4", + "otpauth": "^9.1.2", "passport": "^0.6.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", diff --git a/containers/api/src/app.controller.ts b/containers/api/src/app.controller.ts index 489c3ad8..e652ec46 100644 --- a/containers/api/src/app.controller.ts +++ b/containers/api/src/app.controller.ts @@ -9,7 +9,15 @@ import { UsersService } from './users/users.service'; import { MatchLog } from './model/user.entity' import { generate } from 'rxjs'; -import { generateQRcode } from './users/2fa'; + +// import { generateQRcode } from './users/2fa'; +import { generateOTP } from './users/2fa'; +import { VerifyOTP } from './users/2fa'; +import { ValidateOTP } from './users/2fa'; + + +//2fa + // import { initStorage, getUser, setUser } from './storage'; @@ -216,20 +224,30 @@ export class AppController { //======================================================================================================== //======================================================================================================== +// import { Prisma } from "@prisma/client"; +// import { Request, Response, NextFunction } from "express"; +// import { prisma } from "../server"; + + + @Redirect('http://localhost/token', 302) @Get('auth/login') async login2(@Req() request: Request) { const url = request.url; const user = await this.loginClass.Login42(url); console.log(`user in auth/login= ${user}`); - const data = this.authService.login(user); + console.log(`user in auth/login= ${user.username}`); + const data = await this.authService.login(user); console.log(`all data in api = ${data}`) const myJSON = JSON.stringify(data); console.log(`all data json version= ${myJSON}`) console.log(`data in api = ${(await data).access_token}`) + // console.log(`data i = ${(await data).access_token}`) const token = (await data).access_token; + // console + await this.userService.save(user); return { url: `http://localhost/token?data=${encodeURIComponent(JSON.stringify(token))}` }; } @@ -238,17 +256,49 @@ export class AppController { async get2fa(@Request() req) { const user = await this.userService.findOne(req.user.username); - return user.doubleAuth; + return user.otp_enabled; } @UseGuards(JwtAuthGuard) - @Get('/QRcode') - async createQrCode(@Request() req) + @Post('/otp') + async createOTP(@Request() req) { - return (await generateQRcode(req)); + const user = await this.userService.findOne(req.user.username); + // const user2 = await this.userService.findOne(req.user.username); + const res = await generateOTP(user); + await this.userService.save(user); + // console.log(user); + return res; } + @UseGuards(JwtAuthGuard) + @Post('/verifyOtp') + async verifyOTP(@Request() req, @Body() data: any) + { + const user = await this.userService.findOne(req.user.username); + const res = await VerifyOTP(user, data.token) + await this.userService.save(user); + return res + } + + @UseGuards(JwtAuthGuard) + @Post('/validateOtp') + async validateOTP(@Request() req, @Body() data: any) + { + const user = await this.userService.findOne(req.user.username); + const res = await ValidateOTP(user, data.token) + // await this.userService.save(user); + return res + } + +// @UseGuards(JwtAuthGuard) +// @Get('/QRcode') +// async createQrCode(@Request() req) +// { +// return (await generateQRcode(req)); +// } + @UseGuards(JwtAuthGuard) @Post('/quit') async setOffline(@Request() req) { @@ -271,6 +321,8 @@ export class AppController { async createConv(@Request() req, @Body() data: any) { ///create conv and return it ? id? console.log(`data post /conv= ${data}`); + console.log(`data post /conv= ${data.members}`); + console.log(`data post /conv= ${data.name}`); // let test = {id: 2, members: "cc"}; return await this.chatService.createConv(data); // res.json(messages); @@ -278,20 +330,10 @@ export class AppController { -// @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard) @Get('/conv') - async getConv(@Request() req, @Body() data: any) { - ///create conv and return it ? id? - // console.log(`data get /conv= ${data}`); - // let test = {id: 2, members: "cc"}; - - // let tab = [data.member, "test"]; - // console.log(`tab= ${tab}`); - return await this.chatService.getConv(data.member); - // return await this.chatService.getConv(req.user.username); - - - // res.json(messages); + async getConv(@Request() req) { + return await this.chatService.getConv(req.user.username); } // @UseGuards(JwtAuthGuard) diff --git a/containers/api/src/auth/auth.module.ts b/containers/api/src/auth/auth.module.ts index 68f7047e..7d5b4695 100644 --- a/containers/api/src/auth/auth.module.ts +++ b/containers/api/src/auth/auth.module.ts @@ -28,7 +28,7 @@ import { JwtStrategy } from './jwt.strategy'; PassportModule, JwtModule.register({ secret: jwtConstants.secret, - signOptions: { expiresIn: '60000s' }, + // signOptions: { expiresIn: '60000s' }, }), ], providers: [AuthService, LocalStrategy, JwtStrategy], diff --git a/containers/api/src/auth/auth.service.ts b/containers/api/src/auth/auth.service.ts index db08d5b2..8f0a2455 100644 --- a/containers/api/src/auth/auth.service.ts +++ b/containers/api/src/auth/auth.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { UsersService } from '../users/users.service'; import { JwtService } from '@nestjs/jwt'; +import { User } from 'src/model/user.entity'; @Injectable() export class AuthService { @@ -19,13 +20,13 @@ export class AuthService { return null; } - async login(user) { - const myJSON = JSON.stringify(user); + async login(user: User) { + // const myJSON = JSON.stringify(user); // console.log(`in login all user= ${myJSON}`) - // console.log(`in login user= ${user.username}`) + console.log(`in login user= ${user.username}`) const payload = { username: user.username, sub: user.userId }; - // console.log(`in login payload name= ${payload.username}`) - // console.log(`in login payload sub= ${payload.sub}`) + console.log(`in login payload name= ${payload.username}`) + console.log(`in login payload sub= ${payload.sub}`) return { access_token: this.jwtService.sign(payload), }; diff --git a/containers/api/src/auth/constants.ts b/containers/api/src/auth/constants.ts index efce4f67..4ab5a56d 100644 --- a/containers/api/src/auth/constants.ts +++ b/containers/api/src/auth/constants.ts @@ -1,3 +1,4 @@ export const jwtConstants = { - secret: 'DO NOT USE THIS VALUE. INSTEAD, CREATE A COMPLEX SECRET AND KEEP IT SAFE OUTSIDE OF THE SOURCE CODE.', + // secret: 'DO NOT USE THIS VALUE. INSTEAD, CREATE A COMPLEX SECRET AND KEEP IT SAFE OUTSIDE OF THE SOURCE CODE.', + secret: process.env.JWT_SECRET, }; \ No newline at end of file diff --git a/containers/api/src/auth/login42.ts b/containers/api/src/auth/login42.ts index 4d0adea4..929afb39 100644 --- a/containers/api/src/auth/login42.ts +++ b/containers/api/src/auth/login42.ts @@ -24,10 +24,11 @@ export class loginClass { const data = { grant_type: 'authorization_code', - client_id: 'u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41', - client_secret: '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', + client_id: process.env.CLIENT_UID || 'u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41', + // client_secret: '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', + client_secret: process.env.API_SECRET, code: code, - redirect_uri: 'http://localhost:80/api/auth/login', + redirect_uri: process.env.REDIRECT_URI || 'http://localhost:80/api/auth/login', }; try { @@ -64,9 +65,12 @@ export class loginClass { loss: 0, rank: 1200, userId: userId, + otp_base32: null, children: null, status: 1, - doubleAuth: 0, + // doubleAuth: 0, + otp_enabled: false, + otp_verified: false, friendRequest: null, friends: null, photo: null, diff --git a/containers/api/src/config/config.service.ts b/containers/api/src/config/config.service.ts index cd1ded6f..efb443aa 100644 --- a/containers/api/src/config/config.service.ts +++ b/containers/api/src/config/config.service.ts @@ -6,7 +6,7 @@ /* By: apommier +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2023/04/09 14:53:49 by apommier #+# #+# */ -/* Updated: 2023/06/01 13:07:12 by apommier ### ########.fr */ +/* Updated: 2023/06/12 14:51:44 by apommier ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,11 +14,11 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm'; export const getTypeOrmConfig = (): TypeOrmModuleOptions => ({ type: 'postgres', - host: 'postgresql', - port: 5432, - username: 'postgres', - password: 'pass', - database: 'postgres', + host: process.env.POSTGRES_HOST || 'postgresql', + port: parseInt(process.env.POSTGRES_PORT, 10) || 5432, + username: process.env.POSTGRES_USER || 'postgres', + password: process.env.POSTGRES_PASSWORD || 'pass', + database: process.env.POSTGRES_DATABASE || 'postgres', entities: ["dist/**/*.entity.js"], // entities: [join(__dirname, '**', '*.entity.{ts,js}')] // entities: ['**/*.entity{.ts,.js}'], //basic diff --git a/containers/api/src/main.ts b/containers/api/src/main.ts index c3a0187d..aafa4d84 100644 --- a/containers/api/src/main.ts +++ b/containers/api/src/main.ts @@ -1,6 +1,10 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as session from 'express-session'; +import * as dotenv from 'dotenv'; + +dotenv.config(); +console.log(process.env); // async function bootstrap() { // const app = await NestFactory.create(AppModule); @@ -27,6 +31,6 @@ async function bootstrap() { saveUninitialized: false, }), ); - await app.listen(3000); + await app.listen(parseInt(process.env.API_PORT) || 3000); } bootstrap(); \ No newline at end of file diff --git a/containers/api/src/model/user.entity.ts b/containers/api/src/model/user.entity.ts index c0413098..1928c9fa 100644 --- a/containers/api/src/model/user.entity.ts +++ b/containers/api/src/model/user.entity.ts @@ -20,6 +20,18 @@ export class User { @PrimaryGeneratedColumn() id: number; + // otp_enabled Boolean @default(false) + // otp_verified Boolean @default(false) + + @Column({ default: false }) + otp_enabled: boolean; + + @Column({ default: false }) + otp_verified: boolean; + + @Column({ nullable: true }) + otp_base32: string; + @Column({ nullable: true }) nickname: string; @@ -47,8 +59,8 @@ export class User { @Column({ default: 0 }) userId: number; - @Column({ default: 0 }) - doubleAuth: number; + // @Column({ default: 0 }) + // doubleAuth: number; @Column('text', { array: true, nullable: true }) friendRequest: string[]; diff --git a/containers/api/src/users/2fa.ts b/containers/api/src/users/2fa.ts index 2f1a3147..8e505cfd 100644 --- a/containers/api/src/users/2fa.ts +++ b/containers/api/src/users/2fa.ts @@ -1,6 +1,109 @@ -import crypto from 'crypto'; +// import crypto from 'crypto'; import base32Decode from 'base32-decode'; +import crypto from "crypto"; +import * as OTPAuth from "otpauth"; +import { encode } from "hi-base32"; + +// [...] Register user + +// [...] Login user + +// [...] Generate OTP + +const generateRandomBase32 = async () => { + const {randomBytes} = await import('crypto'); + const buffer = randomBytes(15); + const base32 = encode(buffer).replace(/=/g, "").substring(0, 24); + return base32; + }; + +export const generateOTP = async (user) => { + try { + const base32_secret = await generateRandomBase32(); + + let totp = new OTPAuth.TOTP({ + issuer: "Localhost", + label: "OnlinePong", + algorithm: "SHA1", + digits: 6, + period: 15, + secret: base32_secret, + }); + + let otpauth_url = totp.toString(); + + const res = { + otpauth_url: otpauth_url, + base32_secret: base32_secret + } + + console.log("res= ", res) + + //update db with otp var + user.otp_enabled = true; + user.otp_base32 = base32_secret; + return (res) + + } catch (error) { + console.log(error) + } + }; + + export const VerifyOTP = async (user, token: string) => { + try { + let totp = new OTPAuth.TOTP({ + issuer: "Localhost", + label: "OnlinePong", + algorithm: "SHA1", + digits: 6, + period: 15, + secret: user.otp_base32, + }); + + let delta = totp.validate({ token }); + + if (delta === null) { + console.log("error verify token") + return ("error verify token") + } + else + { + user.otp_verified = true; + console.log("token verified") + } + } catch (error) { + console.log(error) + } + }; + + export const ValidateOTP = async (user, token: string) => { + try { + let totp = new OTPAuth.TOTP({ + issuer: "Localhost", + label: "OnlinePong", + algorithm: "SHA1", + digits: 6, + period: 15, + secret: user.otp_base32, + }); + + let delta = totp.validate({ token }); + + if (delta === null) { + console.log("error validate token") + return ("error validate token") + } + else + { + // user.otp_verified = true; + console.log("token validated") + } + } catch (error) { + console.log(error) + } + }; + // import { randomBytes} from 'crypto'; // import { promisify } from 'util'; @@ -33,48 +136,48 @@ import base32Decode from 'base32-decode'; // type QRcode = any; -export function generateHOTP(secret, counter) { - const decodedSecret = base32Decode(secret, 'RFC4648'); +// export function generateHOTP(secret, counter) { +// const decodedSecret = base32Decode(secret, 'RFC4648'); - const buffer = Buffer.alloc(8); - for (let i = 0; i < 8; i++) { - buffer[7 - i] = counter & 0xff; - counter = counter >> 8; - } - - // Step 1: Generate an HMAC-SHA-1 value - const hmac = crypto.createHmac('sha1', Buffer.from(decodedSecret)); - hmac.update(buffer); - const hmacResult = hmac.digest(); - - // Step 2: Generate a 4-byte string (Dynamic Truncation) - const offset = hmacResult[hmacResult.length - 1] & 0xf; - const code = - ((hmacResult[offset] & 0x7f) << 24) | - ((hmacResult[offset + 1] & 0xff) << 16) | - ((hmacResult[offset + 2] & 0xff) << 8) | - (hmacResult[offset + 3] & 0xff); - - // Step 3: Compute an HOTP value - return code % 10 ** 6; - } +// const buffer = Buffer.alloc(8); +// for (let i = 0; i < 8; i++) { +// buffer[7 - i] = counter & 0xff; +// counter = counter >> 8; +// } -export function generateTOTP(secret, window = 0) -{ - const counter = Math.floor(Date.now() / 30000); - return generateHOTP(secret, counter + window); -} +// // Step 1: Generate an HMAC-SHA-1 value +// const hmac = crypto.createHmac('sha1', Buffer.from(decodedSecret)); +// hmac.update(buffer); +// const hmacResult = hmac.digest(); -export function verifyTOTP(token, secret, window = 1) -{ - for (let errorWindow = -window; errorWindow <= +window; errorWindow++) - { - const totp = generateTOTP(secret, errorWindow); - if (token === totp) - return true; - } - return false; -} +// // Step 2: Generate a 4-byte string (Dynamic Truncation) +// const offset = hmacResult[hmacResult.length - 1] & 0xf; +// const code = +// ((hmacResult[offset] & 0x7f) << 24) | +// ((hmacResult[offset + 1] & 0xff) << 16) | +// ((hmacResult[offset + 2] & 0xff) << 8) | +// (hmacResult[offset + 3] & 0xff); + +// // Step 3: Compute an HOTP value +// return code % 10 ** 6; +// } + +// export function generateTOTP(secret, window = 0) +// { +// const counter = Math.floor(Date.now() / 30000); +// return generateHOTP(secret, counter + window); +// } + +// export function verifyTOTP(token, secret, window = 1) +// { +// for (let errorWindow = -window; errorWindow <= +window; errorWindow++) +// { +// const totp = generateTOTP(secret, errorWindow); +// if (token === totp) +// return true; +// } +// return false; +// } @@ -94,86 +197,86 @@ export function verifyTOTP(token, secret, window = 1) // import * as base32Encode from 'base32-encode'; // import { base32Encode } from 'base32-encode'; // import base32Encode from 'base32-encode'; -import { encode } from 'thirty-two'; +// import { encode } from 'thirty-two'; -// ... +// // ... -import * as qrcode from 'qrcode'; -import * as fs from 'fs'; +// import * as qrcode from 'qrcode'; +// import * as fs from 'fs'; -import { nanoid } from "nanoid"; -// import * as nanoid from 'nanoid' +// import { nanoid } from "nanoid"; +// // import * as nanoid from 'nanoid' -export async function generateQRcode(req) -{ - // const base32Encode = (await import('base32-encode')); - // const nanoid = (await import('nanoid')); +// export async function generateQRcode(req) +// { +// // const base32Encode = (await import('base32-encode')); +// // const nanoid = (await import('nanoid')); - // const util = (await import('util')); - // const qrcode = (await import('qrcode')); +// // const util = (await import('util')); +// // const qrcode = (await import('qrcode')); - const user = req.user; - let res; - // For security, we no longer show the QR code after is verified - // if (user.mfaEnabled) return res.status(404).end(); +// const user = req.user; +// let res; +// // For security, we no longer show the QR code after is verified +// // if (user.mfaEnabled) return res.status(404).end(); - // if (!user.mfaSecret) { //to do - const buffer = nanoid(14); - // generate unique secret for user - // this secret will be used to check the verification code sent by user - // const buffer = await util.promisify(crypto.randomBytes)(14); - // const buffer = crypto.lib.WordArray.random(32) - user.mfaSecret = encode(buffer).toString('utf8'); - // user.mfaSecret = base32Encoded(buffer, 'RFC4648', { padding: false }); +// // if (!user.mfaSecret) { //to do +// const buffer = nanoid(14); +// // generate unique secret for user +// // this secret will be used to check the verification code sent by user +// // const buffer = await util.promisify(crypto.randomBytes)(14); +// // const buffer = crypto.lib.WordArray.random(32) +// user.mfaSecret = encode(buffer).toString('utf8'); +// // user.mfaSecret = base32Encoded(buffer, 'RFC4648', { padding: false }); - // setUser(user); // to do !! +// // setUser(user); // to do !! - // } +// // } - const issuer = 'Google'; - const algorithm = 'SHA1'; - const digits = '6'; - const period = '30'; - const otpType = 'totp'; - const configUri = `otpauth://${otpType}/${issuer}:${user.username}?algorithm=${algorithm}&digits=${digits}&period=${period}&issuer=${issuer}&secret=${user.mfaSecret}`; +// const issuer = 'Google'; +// const algorithm = 'SHA1'; +// const digits = '6'; +// const period = '30'; +// const otpType = 'totp'; +// const configUri = `otpauth://${otpType}/${issuer}:${user.username}?algorithm=${algorithm}&digits=${digits}&period=${period}&issuer=${issuer}&secret=${user.mfaSecret}`; - // res.setHeader('Content-Type', 'image/png'); - const QRCode = require('qrcode'); - console.log(`before done`); - // QRCode.toFileStream(res, configUri); - // const filePath = 'qrcode.png'; // Specify the file path where the QR code should be saved +// // res.setHeader('Content-Type', 'image/png'); +// const QRCode = require('qrcode'); +// console.log(`before done`); +// // QRCode.toFileStream(res, configUri); +// // const filePath = 'qrcode.png'; // Specify the file path where the QR code should be saved - const qrCodeData = buffer; // Replace with your actual QR code data - const filePath = 'qrcode.png'; // Specify the file path where the QR code should be saved +// const qrCodeData = buffer; // Replace with your actual QR code data +// const filePath = 'qrcode.png'; // Specify the file path where the QR code should be saved - qrcode.toFile(filePath, qrCodeData, (error) => { - if (error) { - console.error(error); - // Handle the error appropriately - return; - } - // QR code image has been generated and saved to the file - // Or, you can create a buffer of the image data directly - }) +// qrcode.toFile(filePath, qrCodeData, (error) => { +// if (error) { +// console.error(error); +// // Handle the error appropriately +// return; +// } +// // QR code image has been generated and saved to the file +// // Or, you can create a buffer of the image data directly +// }) -// qrcode.toFile(filePath, configUri, (error) => { -// if (error) { -// console.error(error); -// // Handle the error appropriately -// return; -// } -// const readableStream = fs.createReadStream(filePath); -// res.data = readableStream; - // Use the readable stream as needed -// }); +// // qrcode.toFile(filePath, configUri, (error) => { +// // if (error) { +// // console.error(error); +// // // Handle the error appropriately +// // return; +// // } +// // const readableStream = fs.createReadStream(filePath); +// // res.data = readableStream; +// // Use the readable stream as needed +// // }); - // qrcode.toFileStream(res, configUri); - console.log(`QRcode done`); - return res; - // return - } \ No newline at end of file +// // qrcode.toFileStream(res, configUri); +// console.log(`QRcode done`); +// return res; +// // return +// } \ No newline at end of file diff --git a/containers/chat/package-lock.json b/containers/chat/package-lock.json index 914abd4d..02a0936d 100644 --- a/containers/chat/package-lock.json +++ b/containers/chat/package-lock.json @@ -15,6 +15,7 @@ "@nestjs/platform-socket.io": "^9.4.0", "@nestjs/websockets": "^9.4.0", "cors": "^2.8.5", + "dotenv": "^16.1.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "socket.io-client": "^4.6.1", @@ -3398,6 +3399,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/containers/chat/package.json b/containers/chat/package.json index ccf405b6..e1c3403c 100644 --- a/containers/chat/package.json +++ b/containers/chat/package.json @@ -26,6 +26,7 @@ "@nestjs/platform-socket.io": "^9.4.0", "@nestjs/websockets": "^9.4.0", "cors": "^2.8.5", + "dotenv": "^16.1.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "socket.io-client": "^4.6.1", diff --git a/containers/chat/src/main.ts b/containers/chat/src/main.ts index c964f081..b81c8ba7 100644 --- a/containers/chat/src/main.ts +++ b/containers/chat/src/main.ts @@ -3,6 +3,10 @@ import { AppModule } from './app.module'; import * as cors from 'cors'; import { Server } from 'socket.io'; import * as socketio from 'socket.io'; +import * as dotenv from 'dotenv'; + +dotenv.config(); +console.log(process.env); async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -36,7 +40,7 @@ async function bootstrap() { }); }); - await app.listen(4001); + await app.listen(parseInt(process.env.CHAT_PORT) || 4001); } bootstrap(); diff --git a/containers/pong/package-lock.json b/containers/pong/package-lock.json index 914abd4d..02a0936d 100644 --- a/containers/pong/package-lock.json +++ b/containers/pong/package-lock.json @@ -15,6 +15,7 @@ "@nestjs/platform-socket.io": "^9.4.0", "@nestjs/websockets": "^9.4.0", "cors": "^2.8.5", + "dotenv": "^16.1.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "socket.io-client": "^4.6.1", @@ -3398,6 +3399,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/containers/pong/package.json b/containers/pong/package.json index ccf405b6..e1c3403c 100644 --- a/containers/pong/package.json +++ b/containers/pong/package.json @@ -26,6 +26,7 @@ "@nestjs/platform-socket.io": "^9.4.0", "@nestjs/websockets": "^9.4.0", "cors": "^2.8.5", + "dotenv": "^16.1.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "socket.io-client": "^4.6.1", diff --git a/containers/pong/src/main.ts b/containers/pong/src/main.ts index 7d85a5af..676fb1ff 100644 --- a/containers/pong/src/main.ts +++ b/containers/pong/src/main.ts @@ -21,6 +21,10 @@ import { AppModule } from './app.module'; import * as cors from 'cors'; import { Server } from 'socket.io'; import * as socketio from 'socket.io'; +import * as dotenv from 'dotenv'; + +dotenv.config(); +console.log(process.env); async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -54,7 +58,7 @@ async function bootstrap() { }); }); - await app.listen(4000); + await app.listen(process.env.PONG_PORT || 4000); } bootstrap(); \ No newline at end of file diff --git a/containers/pong/src/pong/pong.gateway.ts b/containers/pong/src/pong/pong.gateway.ts index 19b81f12..de0e259b 100644 --- a/containers/pong/src/pong/pong.gateway.ts +++ b/containers/pong/src/pong/pong.gateway.ts @@ -68,11 +68,19 @@ export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa // } // // console.log(`from: ${client.id}`); // } + @SubscribeMessage('pong:invite') + createPrivateGame(client: Socket, payload: any): void { + //after invite accepted ? + //set the two user in a game ? + + } + @SubscribeMessage('pong:matchmaking') addMatchmaking(client: Socket, payload: any): void { console.log("matchmaking"); console.log(payload); + console.log(`option= ${payload.option}`); // Add the client to the waitingClients set along with their chosen option this.waitingClients.add({ client, option: payload.option }); console.log("Adding client to waiting list..."); @@ -147,6 +155,21 @@ addMatchmaking(client: Socket, payload: any): void { // console.log("END OF HANDLE"); // } + @SubscribeMessage('pong:power') + sendPower(client: Socket, payload: any): void + { + console.log(`from: ${client.id}`); + console.log(payload); + + const game = this.games.get(payload.gameId); + const playersIds = game.map(socket => socket.id); + if (playersIds[0] === payload.id) + this.clients[playersIds[1]].emit('pong:power', payload); + else if (playersIds[1] === payload.id) + this.clients[playersIds[0]].emit('pong:power', payload); + console.log("END OF HANDLE"); + } + @SubscribeMessage('pong:message') handleMessage(client: Socket, payload: any): void { diff --git a/containers/react/src/components/404.jsx b/containers/react/src/components/404.jsx new file mode 100644 index 00000000..ac563d3d --- /dev/null +++ b/containers/react/src/components/404.jsx @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* 404.jsx :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: apommier +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2023/06/09 23:07:12 by apommier #+# #+# */ +/* Updated: 2023/06/12 17:09:11 by apommier ### ########.fr */ +/* */ +/* ************************************************************************** */ + +function PageNotFound() { + return ( +
+

404 Page not found

+
+ ); +} + +export default PageNotFound \ No newline at end of file diff --git a/containers/react/src/components/App.jsx b/containers/react/src/components/App.jsx index 204d2a28..27d570b3 100644 --- a/containers/react/src/components/App.jsx +++ b/containers/react/src/components/App.jsx @@ -1,5 +1,5 @@ import React from "react"; -import {Routes, Route} from 'react-router-dom'; +import {Routes, Route, Navigate} from 'react-router-dom'; import HomeLogin from "../pages/Home.js"; import Home from "../pages/Home.jsx"; @@ -17,15 +17,32 @@ import SuccessToken from '../script/tokenSuccess' import DoubleAuth from "../pages/2fa.js"; import Game from "../pages/Game.jsx"; import Social from "../components/Social/Social.jsx"; +import PageNotFound from "../components/404.jsx"; import Logout from "../components/Profile/Logout.jsx"; function AnimatedRoute () { const location = useLocation(); + if (!localStorage.getItem('token')) + { + return ( + + + }/> + }/> + + } /> + } /> + + + ) + } + return ( - }/> + {/* }/> */} + }/> }/> }/> @@ -41,6 +58,9 @@ function AnimatedRoute () { }/> }/> }/> + + } /> + } /> ) diff --git a/containers/react/src/components/Game/PlayButton.js b/containers/react/src/components/Game/PlayButton.js index 041af86e..04576ec7 100644 --- a/containers/react/src/components/Game/PlayButton.js +++ b/containers/react/src/components/Game/PlayButton.js @@ -6,10 +6,33 @@ function PlayButton() { const history = useNavigate(); + // const handleButtonClick = () => { + // let path = `play`; + // history(path); + // }; const handleButtonClick = () => { - let path = `play`; + let path = `play?`; + + const superpowerCheckbox = document.querySelector('input[value="superpower"]'); + if (superpowerCheckbox.checked) { + path += 'superpower=true&'; + } + + const obstacleCheckbox = document.querySelector('input[value="obstacle"]'); + if (obstacleCheckbox.checked) { + path += 'obstacle=true&'; + } + + const speedCheckbox = document.querySelector('input[value="speed"]'); + if (speedCheckbox.checked) { + path += 'speed=true&'; + } + + // Remove the trailing '&' character + path = path.slice(0, -1); + console.log(path) history(path); - }; + }; return (
diff --git a/containers/react/src/components/Header.jsx b/containers/react/src/components/Header.jsx index 15c06b7d..484d2756 100644 --- a/containers/react/src/components/Header.jsx +++ b/containers/react/src/components/Header.jsx @@ -1,13 +1,9 @@ import React, {useState, useEffect} from 'react'; import {AiOutlineMenuUnfold} from 'react-icons/ai'; -// import * as AiIcons from 'react-icons/ai'; import {Link} from 'react-router-dom'; -// import { SidebarData } from './Sidebar/SidebarData'; import DefaultPicture from '../assets/profile.jpg' import { motion, AnimatePresence } from 'framer-motion' import Modal from './Sidebar/Modal'; -// import {BiLogOutCircle} from 'react-icons/bi'; -// import AnimatePresence from import '../styles/Header.css'; import api from '../script/axiosApi'; @@ -32,8 +28,8 @@ function Header() { console.error('Error fetching profile picture:', error); } }; - - fetchProfilePicture(); + if (localStorage.getItem('token')) + fetchProfilePicture(); }, []); // console.log(`profile pic= ${profilePicture}`) diff --git a/containers/react/src/components/Messages/Message.jsx b/containers/react/src/components/Messages/Message.jsx index cd10f4e1..e3c8a9ad 100644 --- a/containers/react/src/components/Messages/Message.jsx +++ b/containers/react/src/components/Messages/Message.jsx @@ -6,7 +6,7 @@ /* By: apommier +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2023/06/01 18:24:46 by apommier #+# #+# */ -/* Updated: 2023/06/09 09:00:06 by apommier ### ########.fr */ +/* Updated: 2023/06/12 17:05:08 by apommier ### ########.fr */ /* */ /* ************************************************************************** */ diff --git a/containers/react/src/components/Social/FriendRequest.jsx b/containers/react/src/components/Social/FriendRequest.jsx index bc828167..29020c86 100644 --- a/containers/react/src/components/Social/FriendRequest.jsx +++ b/containers/react/src/components/Social/FriendRequest.jsx @@ -46,28 +46,7 @@ export default function Friend({currentUser}) }; fetchProfilePicture(); - }) - - function getStatus(friend) - { - let status = friend.status - console.log(`status= ${status}`) - let statusColor; - - if (status === 0) - statusColor = 'grey'; - else if (status === 1) - statusColor = 'green'; - else if (status === 2) - statusColor = 'blue'; - return statusColor; - } - - const handleSpectate = (user) => { - //socket connection and add to party with one with username - console.log(`spectate hehe`) - console.log(`user= ${user}`) - }; + }, []) const handleButtonClick = (user) => { let path = `http://localhost/profile/${user.username}`; @@ -79,10 +58,12 @@ export default function Friend({currentUser}) const Accept = (user) => { console.log("accept") + console.log(`request = ${request}`) } const Refuse = (user) => { console.log("refuse") + console.log(`request = ${request}`) } return ( diff --git a/containers/react/src/pages/Field.js b/containers/react/src/pages/Field.js index c937b880..d299145e 100644 --- a/containers/react/src/pages/Field.js +++ b/containers/react/src/pages/Field.js @@ -1,13 +1,48 @@ -import { useEffect } from 'react'; +import { useEffect, useLocation } from 'react'; // import { useState, useRef } from 'react'; -import { drawCanvas } from './canvas.js'; +import DrawCanvas from './canvas.js'; +import queryString from 'query-string'; import '../styles/field.css'; +import { useParams } from "react-router-dom"; + +// import { withRouter } from 'react-router-dom'; + function Field() { useEffect(() => { + // const location = useLocation(); + const queryParams = queryString.parse(window.location.search); + console.log("launch canva hehe") - drawCanvas(); + let Modifiers = 0; + + if (queryParams.superpower === 'true') { + Modifiers += 1; + } + + if (queryParams.obstacle === 'true') { + Modifiers += 2; + } + + if (queryParams.speed === 'true') { + Modifiers += 4; + } + // console.log(`modifiers= ${Modifiers}`) + // DrawCanvas(Modifiers); + // return () => { + // console.log("000000000000000000000000000000000") + // // socketRef.current.disconnect(); + // }; + + // console.log(`modifiers= ${Modifiers}`) + const cleanup = DrawCanvas(Modifiers); + + return () => { + console.log("Cleanup"); + cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks + }; + }, []); // const [buttonClicked, setButtonClicked] = useState(false); @@ -28,6 +63,7 @@ function Field() } export default Field; +// export default withRouter(Field); // function Field() { diff --git a/containers/react/src/pages/canvas.js b/containers/react/src/pages/canvas.js index ec45cf8c..ae4785ff 100644 --- a/containers/react/src/pages/canvas.js +++ b/containers/react/src/pages/canvas.js @@ -2,19 +2,44 @@ import api from '../script/axiosApi'; -// import { useEffect } from 'react'; +// import { useEffect, useRef } from 'react'; import io from 'socket.io-client'; // const socket = io('http://192.168.1.14:4000'); // const socket = io('http://86.209.110.20:4000'); // const socket = io('http://172.29.113.91:4000'); -export function drawCanvas() { +function DrawCanvas(option) { + + + + console.log(`option= ${option}`); + const superpowerModifier = option & 1; // Retrieves the superpower modifier + const obstacleModifier = (option >> 1) & 1; // Retrieves the obstacle modifier + const speedModifier = (option >> 2) & 1; // Retrieves the speed modifier + + console.log(`superpowerModifier = ${superpowerModifier}`); + console.log(`obstacleModifier = ${obstacleModifier}`); + console.log(`speedModifier = ${speedModifier}`); + + + + // const socketRef = useRef(null); + // socketRef.current = io('http://localhost:4000'); const socket = io('http://localhost:4000'); - // const socket = io() + // const socket = socketRef.current console.log("start function"); const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); + // useEffect(() => { + // console.log("useeffect?????????????????") + // return () => { + // console.log("000000000000000000000000000000000") + // socketRef.current.disconnect(); + // }; + // }, []); + + //======================================================================================================== //======================================================================================================== // Var Declaration @@ -28,6 +53,7 @@ export function drawCanvas() { let opRank; //general canvas + let running = true; const scale = window.devicePixelRatio; canvas.width = canvas.offsetWidth; // canvas.height = canvas.width * 0.7 @@ -39,8 +65,9 @@ export function drawCanvas() { let paddleY = canvas.height / 2 - (paddleHeight / 2); let paddleX = canvas.width / 40; let paddleSpeed = canvas.height / 40; - + //opponent var + let opPaddleHeight = canvas.height * 0.25; let oPaddleY = paddleY; //mouse and touch @@ -81,6 +108,7 @@ export function drawCanvas() { console.log(`id ion matcj= ${myId}`) const info = { id: myId, + option: option, }; socket.emit('pong:matchmaking', info); } @@ -165,6 +193,27 @@ export function drawCanvas() { vY = data.vY; }); + + socket.on('pong:paddle', (data) => { + console.log("paddle info receive") + oPaddleY = (data.paddleY / data.height) * canvas.height + }); + + socket.on('pong:power', (data) => { + console.log("paddle info receive") + + oPaddleY = 0; + opPaddleHeight = canvas.height; + + setTimeout(() => { + // code à exécuter après 5 secondes + opPaddleHeight = canvas.height * 0.25; + oPaddleY = canvas.height / 2 - paddleHeight / 2; + console.log('Cinq secondes se sont écoulées.'); + }, 5000); + // oPaddleY = (data.paddleY / data.height) * canvas.height + }); + function send_info() { if (gameId === 0) @@ -183,11 +232,6 @@ export function drawCanvas() { socket.emit('pong:message', info); } - socket.on('pong:paddle', (data) => { - console.log("paddle info receive") - oPaddleY = (data.paddleY / data.height) * canvas.height - }); - function send_point() { if (gameId === 0) @@ -229,6 +273,17 @@ export function drawCanvas() { socket.emit('pong:paddle', info); } + function use_power() + { + const info = { + gameId: gameId, + width: canvas.width, + height: canvas.height, + id: myId, + } + socket.emit('pong:power', info); + } + function send_forced_info() { if (gameId === 0) @@ -275,7 +330,7 @@ export function drawCanvas() { function drawPaddle() { ctx.fillStyle = 'white'; ctx.fillRect(paddleX, paddleY, paddleWidth, paddleHeight); - ctx.fillRect(canvas.width - paddleX - paddleWidth, oPaddleY, paddleWidth, paddleHeight); + ctx.fillRect(canvas.width - paddleX - paddleWidth, oPaddleY, paddleWidth, opPaddleHeight); } function drawball() @@ -287,7 +342,7 @@ export function drawCanvas() { ctx.fill(); } - + //======================================================================================================== //======================================================================================================== // Loop @@ -298,9 +353,18 @@ matchmaking(); // while (!gameId) // ; + // Define a function to stop the drawing process + const stopDrawCanvas = () => { + running = false; + // Perform any necessary cleanup tasks + // ... + }; function draw(timestamp) { + console.log("send loose"); + if (!running) + return ; if (gameId === 0 ) { requestAnimationFrame(draw); @@ -333,7 +397,7 @@ function draw(timestamp) const deltaTime = timestamp - lastUpdateTime; lastUpdateTime = timestamp; ballX += vX * deltaTime * canvas.width; - ballY += vY * deltaTime * canvas.width; + ballY += vY * deltaTime * canvas.height; ctx.clearRect(0, 0, canvas.width, canvas.height); drawPaddle(); @@ -343,7 +407,8 @@ function draw(timestamp) is_out(); requestAnimationFrame(draw); } -requestAnimationFrame(draw); + + //======================================================================================================== //======================================================================================================== @@ -360,6 +425,13 @@ requestAnimationFrame(draw); vY = vX * Math.sin(-bounceAngle); if (vX < 0) vX = -vX; + if (speedModifier) + { + if (vX > 0) + vX += 0.0001; + else + vX -= 0.0001; + } } @@ -387,7 +459,12 @@ requestAnimationFrame(draw); } if (ballY - ballRadius - 2 <= 0 || ballY + ballRadius + 2 >= canvas.height) //touch up or down wall { + // if () vY = -vY; + if (ballY > (canvas.height / 2))//down wall + ballY = canvas.height - ballRadius - 2 + else + ballY = ballRadius + 2 // send_info(); } // else if (ballX + ballRadius + 2 >= canvas.width) //touch right wall @@ -416,6 +493,24 @@ requestAnimationFrame(draw); send_point(); // send_forced_info(); } + if (ballX > canvas.width) + { + console.log("win point") + // if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius) + // { + // console.log('true hehe'); + // ballX = paddleX + paddleWidth + ballRadius; + // updateVector(); + // return ; + // } + // ballX = canvas.width / 2; + // ballY = canvas.height / 2; + // vX = 0; + // vY = 0; + // hisScore += 1; + // send_point(); + // // send_forced_info(); + } } @@ -510,8 +605,11 @@ requestAnimationFrame(draw); } else if (event.code === "KeyR") { + if (!superpowerModifier) + return ; paddleY = 0; paddleHeight = canvas.height; + use_power(); setTimeout(() => { // code à exécuter après 5 secondes paddleHeight = canvas.height * 0.25; @@ -521,4 +619,9 @@ requestAnimationFrame(draw); } }); -} \ No newline at end of file + requestAnimationFrame(draw); + console.log("retuuuuuuuuuuurn") + return (stopDrawCanvas); +} + +export default DrawCanvas \ No newline at end of file diff --git a/containers/react/src/script/axiosApi.js b/containers/react/src/script/axiosApi.js index 07a89caa..bf7dbbb5 100644 --- a/containers/react/src/script/axiosApi.js +++ b/containers/react/src/script/axiosApi.js @@ -13,7 +13,7 @@ console.log(`getToken = ${getToken()}`) console.log(`Bearer ${localStorage.getItem("token")}`) let api = axios.create({ - baseURL: 'http://localhost/api', + baseURL: 'http://localhost/api', headers: { // Authorization: `Bearer ${getToken()}`, Authorization : `Bearer ${localStorage.getItem("token")}` diff --git a/docker-compose.yml b/docker-compose.yml index 0b3b1242..04b613cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,14 +8,25 @@ services: env_file: .env depends_on: - api + + # command: sh -c "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" ports: - 80:80 volumes: - - ./conf/nginx.conf:/etc/nginx/conf.d/default.conf + - ./conf/nginx.conf:/etc/nginx/conf.d/default.conf + # volumes: + # - "./conf:/etc/nginx/templates/" + # ports: + # - 80:80 + # volumes: + # - ./conf/nginx.conf:/etc/nginx/conf.d/default.conf + # command: sh -c "envsubst < /etc/nginx/conf.d/default.conf > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" # - ./containers/frontend:/var/www/html networks: - pongNetwork + + react_app: image: node:latest container_name: react_app @@ -48,6 +59,7 @@ services: entrypoint: ["sh", "-c" , "npm install && npm run start:dev"] postgresql: + env_file: .env image: postgres:14.1-alpine restart: unless-stopped container_name: postgresql