diff --git a/back/volume/package-lock.json b/back/volume/package-lock.json index 472814f..0ad498a 100644 --- a/back/volume/package-lock.json +++ b/back/volume/package-lock.json @@ -18,6 +18,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-ws": "^9.2.0", + "@nestjs/schedule": "^2.2.0", "@nestjs/schematics": "^9.0.0", "@nestjs/swagger": "^6.2.1", "@nestjs/testing": "^9.0.0", @@ -25,6 +26,7 @@ "@nestjs/websockets": "^9.2.0", "@types/bcrypt": "^5.0.0", "@types/cookie-parser": "^1.4.3", + "@types/cron": "^2.0.0", "@types/express": "^4.17.13", "@types/express-session": "^1.17.6", "@types/multer": "^1.4.7", @@ -1786,6 +1788,20 @@ "rxjs": "^7.1.0" } }, + "node_modules/@nestjs/schedule": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz", + "integrity": "sha512-wrDnUONTxBkD6lTWh9ecYk/kvJTbA3PylotjBoRsECmcS1SNvgInFXuL38UnHiFnXM3CHSFnzRLB259Bc1mOdQ==", + "dependencies": { + "cron": "2.2.0", + "uuid": "9.0.0" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.12" + } + }, "node_modules/@nestjs/schematics": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", @@ -2212,6 +2228,15 @@ "@types/node": "*" } }, + "node_modules/@types/cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-xZM08fqvwIXgghtPVkSPKNgC+JoMQ2OHazEvyTKnNf7aWu1aB6/4lBbQFrb03Td2cUGG7ITzMv3mFYnMu6xRaQ==", + "dependencies": { + "@types/luxon": "*", + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -2322,6 +2347,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/luxon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.2.0.tgz", + "integrity": "sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA==" + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -4143,6 +4173,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "node_modules/cron": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz", + "integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==", + "dependencies": { + "luxon": "^3.2.1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7706,6 +7744,14 @@ "node": ">=10" } }, + "node_modules/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "engines": { + "node": ">=12" + } + }, "node_modules/macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", @@ -12261,6 +12307,15 @@ "ws": "8.12.1" } }, + "@nestjs/schedule": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz", + "integrity": "sha512-wrDnUONTxBkD6lTWh9ecYk/kvJTbA3PylotjBoRsECmcS1SNvgInFXuL38UnHiFnXM3CHSFnzRLB259Bc1mOdQ==", + "requires": { + "cron": "2.2.0", + "uuid": "9.0.0" + } + }, "@nestjs/schematics": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", @@ -12592,6 +12647,15 @@ "@types/node": "*" } }, + "@types/cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-xZM08fqvwIXgghtPVkSPKNgC+JoMQ2OHazEvyTKnNf7aWu1aB6/4lBbQFrb03Td2cUGG7ITzMv3mFYnMu6xRaQ==", + "requires": { + "@types/luxon": "*", + "@types/node": "*" + } + }, "@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -12702,6 +12766,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/luxon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.2.0.tgz", + "integrity": "sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA==" + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -14047,6 +14116,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "cron": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz", + "integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==", + "requires": { + "luxon": "^3.2.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -16680,6 +16757,11 @@ "yallist": "^4.0.0" } }, + "luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" + }, "macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", diff --git a/back/volume/package.json b/back/volume/package.json index de8efb2..ddd628f 100644 --- a/back/volume/package.json +++ b/back/volume/package.json @@ -30,6 +30,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-ws": "^9.2.0", + "@nestjs/schedule": "^2.2.0", "@nestjs/schematics": "^9.0.0", "@nestjs/swagger": "^6.2.1", "@nestjs/testing": "^9.0.0", @@ -37,6 +38,7 @@ "@nestjs/websockets": "^9.2.0", "@types/bcrypt": "^5.0.0", "@types/cookie-parser": "^1.4.3", + "@types/cron": "^2.0.0", "@types/express": "^4.17.13", "@types/express-session": "^1.17.6", "@types/multer": "^1.4.7", diff --git a/back/volume/src/auth/42.strategy.ts b/back/volume/src/auth/42.strategy.ts index 5b86d8b..4ab5974 100644 --- a/back/volume/src/auth/42.strategy.ts +++ b/back/volume/src/auth/42.strategy.ts @@ -40,6 +40,7 @@ export class FtStrategy extends PassportStrategy(Strategy, '42') { newUser.socketKey = randomUUID() newUser.username = profile.username as string newUser.avatar = `${ftId}.jpg` + newUser.lastAccess = Date.now() void this.usersService.create(newUser) const file = createWriteStream(`avatars/${ftId}.jpg`) get(profile._json.image.versions.small, function (response) { diff --git a/back/volume/src/users/entity/user.entity.ts b/back/volume/src/users/entity/user.entity.ts index 2316af9..319d851 100644 --- a/back/volume/src/users/entity/user.entity.ts +++ b/back/volume/src/users/entity/user.entity.ts @@ -4,7 +4,8 @@ import { Column, OneToMany, ManyToMany, - JoinTable + JoinTable, + UpdateDateColumn } from 'typeorm' import Message from 'src/chat/entity/message.entity' @@ -16,6 +17,9 @@ export class User { @PrimaryGeneratedColumn() id: number + @Column({type: "bigint"}) + lastAccess: number + @Column({ unique: true }) ftId: number @@ -68,8 +72,6 @@ export class User { @JoinTable() friends: User[] - // @Column({ default: { wr: -1, place: -1 } }) - // rank: { wr: number; place: number }; } export default User diff --git a/back/volume/src/users/users.module.ts b/back/volume/src/users/users.module.ts index f4f5190..16aed34 100644 --- a/back/volume/src/users/users.module.ts +++ b/back/volume/src/users/users.module.ts @@ -4,9 +4,10 @@ import { User } from './entity/user.entity' import { UsersController } from './users.controller' import { UsersService } from './users.service' import { PongModule } from 'src/pong/pong.module' +import { ScheduleModule } from '@nestjs/schedule' @Module({ - imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User])], + imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User]), ScheduleModule.forRoot()], controllers: [UsersController], providers: [UsersService], exports: [UsersService] diff --git a/back/volume/src/users/users.service.ts b/back/volume/src/users/users.service.ts index cc318b1..dca34f4 100644 --- a/back/volume/src/users/users.service.ts +++ b/back/volume/src/users/users.service.ts @@ -5,6 +5,7 @@ import { User } from './entity/user.entity' import { type UserDto } from './dto/user.dto' import { type Channel } from 'src/chat/entity/channel.entity' import type Result from 'src/pong/entity/result.entity' +import { Cron } from '@nestjs/schedule' @Injectable() @Catch(QueryFailedError, EntityNotFoundError) @@ -29,8 +30,24 @@ export class UsersService { return user } + @Cron('0 */60 * * *') + async updateStatus() { + let users = await this.usersRepository.find({}) + users.forEach((usr) => { + if (Date.now() - usr.lastAccess > 60) { + usr.status= "offline" + this.usersRepository.save(usr) + } + }) + } + async findUser (ftId: number): Promise { - return await this.usersRepository.findOneBy({ ftId }) + let user = await this.usersRepository.findOneBy({ ftId }) + if (!user) return null + user.lastAccess = Date.now() + user.status = "online" + this.usersRepository.save(user) + return user } async findOnlineUsers (): Promise { @@ -148,7 +165,7 @@ export class UsersService { `Friend relation complete between ${user.username} and ${target.username}` ) user.friends.push(target) - if (user != target) target.friends.push(user) + if (user.ftId != target.ftId) target.friends.push(user) user.followers.slice(id, 1) this.usersRepository.save(user) } else { diff --git a/front/volume/src/App.svelte b/front/volume/src/App.svelte index 592b0cd..c341147 100644 --- a/front/volume/src/App.svelte +++ b/front/volume/src/App.svelte @@ -48,6 +48,7 @@ let friends: Friend[] = []; let invits: Friend[] = []; + let friendsInterval: ReturnType; export async function getFriends(): Promise { let response = await fetch(API_URL + "/friends", { @@ -69,6 +70,10 @@ isFriendOpen = true; friends = await getFriends(); invits = await getInvits(); + friendsInterval = setInterval(async () => { + friends = await getFriends(); + invits = await getInvits(); + }, 5000) } // SPECTATE @@ -147,7 +152,10 @@ {/if} {#if isFriendOpen}
(isFriendOpen = false)} + on:click={() => { + isFriendOpen = false; + clearInterval(friendsInterval) + }} on:keydown={() => (isFriendOpen = false)} >