Browse Source

add auth protection, login and logout and fixed username update

master
nicolas-arnaud 2 years ago
parent
commit
6d7d526ec2
  1. 2
      back/volume/src/auth/auth.controller.ts
  2. 4
      back/volume/src/main.ts
  3. 20
      back/volume/src/users/users.controller.ts
  4. 11
      back/volume/src/users/users.service.ts
  5. 152
      front/volume/package-lock.json
  6. 1
      front/volume/package.json
  7. 142
      front/volume/src/App.svelte
  8. 37
      front/volume/src/Auth.ts
  9. 34
      front/volume/src/components/Friends.svelte
  10. 10
      front/volume/src/components/Profile.svelte

2
back/volume/src/auth/auth.controller.ts

@ -29,7 +29,7 @@ export class AuthController {
}
@Get('out')
@Redirect('/')
@Redirect('http://' + process.env.HOST + ':' + process.env.FRONT_PORT + '/')
logOut (@Req() req: Request) {
req.logOut(function (err) {
if (err) return err

4
back/volume/src/main.ts

@ -33,7 +33,7 @@ async function bootstrap () {
app.use(passport.session())
app.enableCors(cors)
app.useWebSocketAdapter(new WsAdapter(app))
await app.listen(port)
logger.log(`Application listening on port ${port}`)
await app.listen(port)
logger.log(`Application listening on port ${port}`)
}
bootstrap()

20
back/volume/src/users/users.controller.ts

@ -54,7 +54,7 @@ export class UsersController {
async getInvits (@FtUser() profile: Profile) {
return await this.usersService.getInvits(profile.id)
}
@Post('avatar')
@UseGuards(AuthenticatedGuard)
@UseInterceptors(
@ -92,6 +92,22 @@ export class UsersController {
return await this.getAvatarById(profile.id, response);
}
@Get('user/:name')
async getUserByName(
@Param('name') username: string
): Promise<User | null> {
return await this.usersService.findUserByName(username);
}
@Post('invit/:id')
@UseGuards(AuthenticatedGuard)
async invitUser (
@FtUser() profile: Profile,
@Param('id', ParseIntPipe) id: number
) {
return await this.usersService.invit(profile.id, id)
}
@Get('avatar/:id')
async getAvatarById (
@Param('id', ParseIntPipe) ftId: number,
@ -124,7 +140,7 @@ export class UsersController {
async create (@Body() payload: UserDto, @FtUser() profile: Profile) {
const user = await this.usersService.findUser(profile.id)
if (user) {
return await this.usersService.update(user.id, payload)
return await this.usersService.update(user, payload)
} else {
return await this.usersService.create(payload)
}

11
back/volume/src/users/users.service.ts

@ -44,11 +44,9 @@ export class UsersService {
.getMany()
}
async update (ftId: number, changes: UserDto):Promise < User | null> {
const updatedUser = await this.findUser(ftId)
if (!updatedUser) return null
this.usersRepository.merge(updatedUser, changes)
return await this.usersRepository.save(updatedUser)
async update (user: User, changes: UserDto):Promise < User | null> {
this.usersRepository.merge(user, changes)
return await this.usersRepository.save(user)
}
async addAvatar (ftId: number, filename: string) {
@ -87,8 +85,7 @@ export class UsersService {
return new NotFoundException(
`Error: user id ${targetFtId} isn't in our db.`
)
}
const id = user.followers.findIndex(
} const id = user.followers.findIndex(
(follower) => follower.ftId === targetFtId
)
if (id != -1) {

152
front/volume/package-lock.json

@ -10,6 +10,7 @@
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0",
"axios": "^1.3.4",
"svelte": "^3.55.1",
"vite": "^4.1.0"
},
@ -469,6 +470,21 @@
"node": ">= 8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -551,6 +567,17 @@
"fsevents": "~2.3.2"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -581,6 +608,14 @@
"node": ">=0.10.0"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -669,6 +704,38 @@
"node": ">=8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -868,6 +935,25 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@ -1044,6 +1130,11 @@
"svelte": "^3.2.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -1682,6 +1773,21 @@
"picomatch": "^2.0.4"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -1741,6 +1847,14 @@
"readdirp": "~3.6.0"
}
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -1760,6 +1874,11 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
"integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -1832,6 +1951,21 @@
"to-regex-range": "^5.0.1"
}
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -1979,6 +2113,19 @@
"picomatch": "^2.3.1"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@ -2094,6 +2241,11 @@
"dev": true,
"requires": {}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",

1
front/volume/package.json

@ -20,6 +20,7 @@
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0",
"axios": "^1.3.4",
"svelte": "^3.55.1",
"vite": "^4.1.0"
}

142
front/volume/src/App.svelte

@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte'
import Navbar from "./components/NavBar.svelte";
import Profile from "./components/Profile.svelte";
import MatchHistory from "./components/MatchHistory.svelte";
@ -14,8 +15,15 @@
import Channels from "./components/Channels.svelte";
import type { ChannelsType } from "./components/Channels.svelte";
import { store, getUser, login, logout } from "./Auth";
// PROFILE
onMount(() => {
getUser()
})
let isProfileOpen = false;
function clickProfile() {
isProfileOpen = true;
@ -82,69 +90,79 @@
</script>
<main>
<Navbar
{clickProfile}
{clickHistory}
{clickFriends}
{clickSpectate}
{clickChannels}
/>
{#if isChannelsOpen}
{#if selectedChannel}
<div on:click={() => (selectedChannel = undefined)} on:keydown={() => (selectedChannel = undefined)}>
<Chat2 chatMessages={selectedChannel.messages} />
</div>
{/if}
{#if !selectedChannel}
<div
on:click={() => (isChannelsOpen = false)}
on:keydown={() => (isChannelsOpen = false)}
>
<Channels channels={channels} onSelectChannel={handleSelectChannel} />
</div>
<div>
{#if $store === null}
<h1><button type="button" on:click={login}>Log In</button></h1>
{:else}
<h1><button type="button" on:click={logout}>Log Out</button></h1>
<Navbar
{clickProfile}
{clickHistory}
{clickFriends}
{clickSpectate}
{clickChannels}
/>
{#if isChannelsOpen}
{#if selectedChannel}
<div
on:click={() => (selectedChannel = undefined)}
on:keydown={() => (selectedChannel = undefined)}
>
<Chat2 chatMessages={selectedChannel.messages} />
</div>
{/if}
{#if !selectedChannel}
<div
on:click={() => (isChannelsOpen = false)}
on:keydown={() => (isChannelsOpen = false)}
>
<Channels {channels} onSelectChannel={handleSelectChannel} />
</div>
{/if}
{/if}
{#if isSpectateOpen}
<div
on:click={() => (isSpectateOpen = false)}
on:keydown={() => (isSpectateOpen = false)}
>
<Spectate {spectate} />
</div>
{/if}
{#if isFriendOpen}
<div
on:click={() => (isFriendOpen = false)}
on:keydown={() => (isFriendOpen = false)}
>
<Friends {friends} />
</div>
{/if}
{#if isHistoryOpen}
<div
on:click={() => (isHistoryOpen = false)}
on:keydown={() => (isHistoryOpen = false)}
>
<MatchHistory {matches} />
</div>
{/if}
{#if isProfileOpen}
<div
on:click={() => (isProfileOpen = false)}
on:keydown={() => (isProfileOpen = false)}
>
<Profile
username="Alice"
wins={10}
losses={5}
elo={256}
rank={23}
is2faEnabled={false}
/>
</div>
{/if}
<Play />
<Pong />
{/if}
{/if}
{#if isSpectateOpen}
<div
on:click={() => (isSpectateOpen = false)}
on:keydown={() => (isSpectateOpen = false)}
>
<Spectate {spectate} />
</div>
{/if}
{#if isFriendOpen}
<div
on:click={() => (isFriendOpen = false)}
on:keydown={() => (isFriendOpen = false)}
>
<Friends {friends} />
</div>
{/if}
{#if isHistoryOpen}
<div
on:click={() => (isHistoryOpen = false)}
on:keydown={() => (isHistoryOpen = false)}
>
<MatchHistory {matches} />
</div>
{/if}
{#if isProfileOpen}
<div
on:click={() => (isProfileOpen = false)}
on:keydown={() => (isProfileOpen = false)}
>
<Profile
username="Alice"
wins={10}
losses={5}
elo={256}
rank={23}
is2faEnabled={false}
/>
</div>
{/if}
<Play />
<Pong />
</div>
</main>
<style>

37
front/volume/src/Auth.ts

@ -0,0 +1,37 @@
import { writable } from 'svelte/store';
let _user = localStorage.getItem('user');
export const store = writable(_user ? JSON.parse(_user) : null);
store.subscribe((value) => {
if (value) localStorage.setItem('user', JSON.stringify(value));
else localStorage.removeItem('user');
});
export const API_URL =
"http://" + import.meta.env.VITE_HOST +
":" + import.meta.env.VITE_BACK_PORT;
export async function getUser() {
const res = await fetch(API_URL, {
method: "get",
mode: 'cors',
cache: "no-cache",
credentials: 'include',
redirect: "follow",
referrerPolicy: "no-referrer",
})
let user = await res.json()
if (user.username) {
console.log(user)
store.set(user)
}
}
export function login() {
window.location.replace(API_URL + "/log/in")
}
export function logout() {
window.location.replace(API_URL + "/log/out")
store.set(null)
}

34
front/volume/src/components/Friends.svelte

@ -6,7 +6,9 @@
</script>
<script lang="ts">
export let friends: Array<Friend> = [];
let api = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT
export let friends: Array<Friend>
fetch(api + "/friends").then((response) => response.json()).then((ret) => {friends = ret});
async function addFriend(event: any) {
console.log(typeof event);
@ -15,19 +17,23 @@
console.log(usernameInput);
const username = usernameInput.value;
// const response = await fetch('', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({ username })
// });
// if (response.ok) {
// console.log('Friend added successfully');
// } else {
// console.log('Failed to add friend');
// }
// usernameInput.value = '';
let ftId: number
fetch(api + "/user/" + username).then((response) => response.json()).then((ret) => {ftId = ret.ftID});
fetch(api + "/friends").then((response) => response.json()).then((ret) => {friends = JSON.parse(ret)});
const response = await fetch(api + "/invit/" + ftId, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username })
});
if (response.ok) {
console.log('Invitation send.');
} else {
console.log('Unknown user.');
}
usernameInput.value = '';
alert("Trying to add friend" + username);
}
</script>

10
front/volume/src/components/Profile.svelte

@ -5,7 +5,11 @@
</script>
<script lang="ts">
let api = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT
export const API_URL = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT
export const AUTH_SERVER_URL = API_URL + "/log/in"
export let avatar = API_URL + "/avatar"
export let username = "";
export let realname = "";
export let wins = 0;
@ -13,10 +17,10 @@
export let elo = 0;
export let rank = -1;
export let is2faEnabled = false;
export let avatar = api + "/avatar";
const handleSubmit = () => {
const user: User = { username : username};
fetch("http://localhost:3001/", {
headers: {"content-type": "application/json"},
method: "POST",
body: JSON.stringify(user),
credentials: 'include'
@ -40,7 +44,7 @@
<div class="profile-header">
<img class="profile-img" src={avatar} alt="avatar" />
<h3>{realname}</h3>
<form action="http://localhost:3001/avatar"
<form action={avatar}
method="post"
enctype="multipart/form-data">
<label for="mavatar-input">Select a file:</label>

Loading…
Cancel
Save