Browse Source

*users are set offline after 1min off

*friends and invitations are refresh on 5s interval with friends window open
master
nicolas-arnaud 2 years ago
parent
commit
fc8f15fbd8
  1. 82
      back/volume/package-lock.json
  2. 2
      back/volume/package.json
  3. 1
      back/volume/src/auth/42.strategy.ts
  4. 8
      back/volume/src/users/entity/user.entity.ts
  5. 3
      back/volume/src/users/users.module.ts
  6. 21
      back/volume/src/users/users.service.ts
  7. 10
      front/volume/src/App.svelte

82
back/volume/package-lock.json

@ -18,6 +18,7 @@
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0", "@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0",
"@nestjs/schematics": "^9.0.0", "@nestjs/schematics": "^9.0.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0", "@nestjs/testing": "^9.0.0",
@ -25,6 +26,7 @@
"@nestjs/websockets": "^9.2.0", "@nestjs/websockets": "^9.2.0",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/cron": "^2.0.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/express-session": "^1.17.6", "@types/express-session": "^1.17.6",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
@ -1786,6 +1788,20 @@
"rxjs": "^7.1.0" "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": { "node_modules/@nestjs/schematics": {
"version": "9.0.3", "version": "9.0.3",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz",
@ -2212,6 +2228,15 @@
"@types/node": "*" "@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": { "node_modules/@types/eslint": {
"version": "8.4.10", "version": "8.4.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
@ -2322,6 +2347,11 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true "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": { "node_modules/@types/mime": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -4143,6 +4173,14 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"devOptional": true "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": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -7706,6 +7744,14 @@
"node": ">=10" "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": { "node_modules/macos-release": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",
@ -12261,6 +12307,15 @@
"ws": "8.12.1" "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": { "@nestjs/schematics": {
"version": "9.0.3", "version": "9.0.3",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz",
@ -12592,6 +12647,15 @@
"@types/node": "*" "@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": { "@types/eslint": {
"version": "8.4.10", "version": "8.4.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
@ -12702,6 +12766,11 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true "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": { "@types/mime": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -14047,6 +14116,14 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"devOptional": true "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": { "cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -16680,6 +16757,11 @@
"yallist": "^4.0.0" "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": { "macos-release": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",

2
back/volume/package.json

@ -30,6 +30,7 @@
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0", "@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0",
"@nestjs/schematics": "^9.0.0", "@nestjs/schematics": "^9.0.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0", "@nestjs/testing": "^9.0.0",
@ -37,6 +38,7 @@
"@nestjs/websockets": "^9.2.0", "@nestjs/websockets": "^9.2.0",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/cron": "^2.0.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/express-session": "^1.17.6", "@types/express-session": "^1.17.6",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",

1
back/volume/src/auth/42.strategy.ts

@ -40,6 +40,7 @@ export class FtStrategy extends PassportStrategy(Strategy, '42') {
newUser.socketKey = randomUUID() newUser.socketKey = randomUUID()
newUser.username = profile.username as string newUser.username = profile.username as string
newUser.avatar = `${ftId}.jpg` newUser.avatar = `${ftId}.jpg`
newUser.lastAccess = Date.now()
void this.usersService.create(newUser) void this.usersService.create(newUser)
const file = createWriteStream(`avatars/${ftId}.jpg`) const file = createWriteStream(`avatars/${ftId}.jpg`)
get(profile._json.image.versions.small, function (response) { get(profile._json.image.versions.small, function (response) {

8
back/volume/src/users/entity/user.entity.ts

@ -4,7 +4,8 @@ import {
Column, Column,
OneToMany, OneToMany,
ManyToMany, ManyToMany,
JoinTable JoinTable,
UpdateDateColumn
} from 'typeorm' } from 'typeorm'
import Message from 'src/chat/entity/message.entity' import Message from 'src/chat/entity/message.entity'
@ -16,6 +17,9 @@ export class User {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number id: number
@Column({type: "bigint"})
lastAccess: number
@Column({ unique: true }) @Column({ unique: true })
ftId: number ftId: number
@ -68,8 +72,6 @@ export class User {
@JoinTable() @JoinTable()
friends: User[] friends: User[]
// @Column({ default: { wr: -1, place: -1 } })
// rank: { wr: number; place: number };
} }
export default User export default User

3
back/volume/src/users/users.module.ts

@ -4,9 +4,10 @@ import { User } from './entity/user.entity'
import { UsersController } from './users.controller' import { UsersController } from './users.controller'
import { UsersService } from './users.service' import { UsersService } from './users.service'
import { PongModule } from 'src/pong/pong.module' import { PongModule } from 'src/pong/pong.module'
import { ScheduleModule } from '@nestjs/schedule'
@Module({ @Module({
imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User])], imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User]), ScheduleModule.forRoot()],
controllers: [UsersController], controllers: [UsersController],
providers: [UsersService], providers: [UsersService],
exports: [UsersService] exports: [UsersService]

21
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 UserDto } from './dto/user.dto'
import { type Channel } from 'src/chat/entity/channel.entity' import { type Channel } from 'src/chat/entity/channel.entity'
import type Result from 'src/pong/entity/result.entity' import type Result from 'src/pong/entity/result.entity'
import { Cron } from '@nestjs/schedule'
@Injectable() @Injectable()
@Catch(QueryFailedError, EntityNotFoundError) @Catch(QueryFailedError, EntityNotFoundError)
@ -29,8 +30,24 @@ export class UsersService {
return user 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<User | null> { async findUser (ftId: number): Promise<User | null> {
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<User[]> { async findOnlineUsers (): Promise<User[]> {
@ -148,7 +165,7 @@ export class UsersService {
`Friend relation complete between ${user.username} and ${target.username}` `Friend relation complete between ${user.username} and ${target.username}`
) )
user.friends.push(target) 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) user.followers.slice(id, 1)
this.usersRepository.save(user) this.usersRepository.save(user)
} else { } else {

10
front/volume/src/App.svelte

@ -48,6 +48,7 @@
let friends: Friend[] = []; let friends: Friend[] = [];
let invits: Friend[] = []; let invits: Friend[] = [];
let friendsInterval: ReturnType<typeof setInterval>;
export async function getFriends(): Promise<Friend[]> { export async function getFriends(): Promise<Friend[]> {
let response = await fetch(API_URL + "/friends", { let response = await fetch(API_URL + "/friends", {
@ -69,6 +70,10 @@
isFriendOpen = true; isFriendOpen = true;
friends = await getFriends(); friends = await getFriends();
invits = await getInvits(); invits = await getInvits();
friendsInterval = setInterval(async () => {
friends = await getFriends();
invits = await getInvits();
}, 5000)
} }
// SPECTATE // SPECTATE
@ -147,7 +152,10 @@
{/if} {/if}
{#if isFriendOpen} {#if isFriendOpen}
<div <div
on:click={() => (isFriendOpen = false)} on:click={() => {
isFriendOpen = false;
clearInterval(friendsInterval)
}}
on:keydown={() => (isFriendOpen = false)} on:keydown={() => (isFriendOpen = false)}
> >
<Friends {friends} {invits} /> <Friends {friends} {invits} />

Loading…
Cancel
Save