diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 3c5e1f9..afe7048 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -1,6 +1,6 @@ - SBa Season {{ seasonNumber }} + SBa Season {{ seasonNumber() }} @@ -13,7 +13,7 @@ Free Agents + :to="{ name: 'team', params: { seasonNumber: seasonNumber(), teamAbbreviation: 'FA' } }">Free Agents @@ -23,11 +23,16 @@ News - Login with Discord + Login with Discord - - - + + + Clear Cookie + + + + + @@ -68,17 +73,23 @@ import { RouterLink } from 'vue-router' import type { MenuOption } from 'naive-ui' import { fetchPlayers, type Player } from '@/services/playersService' import { CURRENT_SEASON } from '@/services/utilities' +import type { Team } from '@/services/apiResponseTypes' +import { fetchActiveTeamByOwnerId } from '@/services/teamsService' +import { authenticate, clearCookie, completeAuthentication, getOwnerId, isDiscordAuthenticated, parseCookie } from '@/services/authenticationService' export default { name: 'NavBar', data() { return { + userTeam: undefined as Team | undefined, players: [] as Player[], searchPlayerName: undefined } }, created() { this.populatePlayerNames() + this.completeAuthentication() + this.fetchUserTeam() }, computed: { menuOptions(): MenuOption[] { @@ -267,23 +278,43 @@ export default { { label: 'News', key: 'News' } ] }, - seasonNumber(): number { - // TODO pull this from DB? - return CURRENT_SEASON - }, sortedPlayerNames(): string[] { return this.players.sort((p1, p2) => p2.wara - p1.wara).map(p => p.name) + }, + isAuthenticated(): boolean { + return isDiscordAuthenticated() + }, + ownerId(): string | undefined { + return getOwnerId() } }, methods: { async getPlayers(): Promise { - return await fetchPlayers(this.seasonNumber) + return await fetchPlayers(this.seasonNumber()) }, async populatePlayerNames(): Promise { this.players = await this.getPlayers() }, searchPlayers(): void { - this.$router.push({ path: `/players/${this.seasonNumber}/${this.searchPlayerName}` }) + this.$router.push({ path: `/players/${this.seasonNumber()}/${this.searchPlayerName}` }) + }, + authenticate(): void { + authenticate() + }, + async completeAuthentication(): Promise { + if (this.ownerId) return + + completeAuthentication() + }, + async fetchUserTeam(): Promise { + if (!this.ownerId) return + this.userTeam = await fetchActiveTeamByOwnerId(this.ownerId) + }, + seasonNumber(): number { + return CURRENT_SEASON + }, + clearCookie(): void { + clearCookie() } } } diff --git a/src/services/authenticationService.ts b/src/services/authenticationService.ts new file mode 100644 index 0000000..bbca375 --- /dev/null +++ b/src/services/authenticationService.ts @@ -0,0 +1,67 @@ +import { SITE_URL } from './utilities' + +export function authenticate(): void { + // MAJOR DOMO - 712002920950005870 + const clientID = '712002920950005870' + // TODO: dynamically redirect to current view by getting current query params and adding them back on after? + const redirectURI = encodeURIComponent(SITE_URL) + const scope = 'identify' // Adjust the scope as needed + window.location.href = `https://discord.com/oauth2/authorize?client_id=${clientID}&redirect_uri=${redirectURI}&response_type=token&scope=${scope}` +} + +export function isDiscordAuthenticated(): boolean { + return !!getOwnerId() +} + +export function getOwnerId(): string | undefined { + return parseCookie(document.cookie)?.discord +} + +export async function completeAuthentication(): Promise { + if (!window.location.hash) { + console.warn('No token hash found in return URL') + return + } + + // Extract the access token from the URL params + const urlParams = new URLSearchParams(window.location.hash.slice(1)) + const accessToken = urlParams.get('access_token') + const tokenType = urlParams.get('token_type') //should be 'Bearer' + const userResponse = await fetch('https://discord.com/api/users/@me', { + headers: { + authorization: `${tokenType} ${accessToken}` + } + }) + if (!userResponse.ok) { + console.warn('userResponse was not OK', userResponse) + return + } + // right now we only care about the id property but we could make a full discord account object if desired + const user: { id: string } = await userResponse.json() + if (!user?.id) { + console.warn('No user or id found while authenticating') + return + } + + window.location.href = SITE_URL + saveOwnerIdCookie(user.id) +} + +// Based on https://www.30secondsofcode.org/js/s/parse-cookie/ +export function parseCookie(cookie: string | undefined): { [key: string]: string } { + if (!cookie) return {} + return cookie.split(';') + .map(v => v.split('=')) + .reduce((acc: { [key: string]: string }, v) => { + acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim()) + return acc + }, {}) +} + +export function saveOwnerIdCookie(ownerId: string): void { + document.cookie = `discord=${ownerId}; expires=Fri, 1 Jan 2100 12:00:00 UTC; path=/` +} + +export function clearCookie(): void { + document.cookie = "discord=;-1;path=/" +} diff --git a/src/services/teamsService.ts b/src/services/teamsService.ts index 94a5a7f..391dfcb 100644 --- a/src/services/teamsService.ts +++ b/src/services/teamsService.ts @@ -1,5 +1,5 @@ import type { Team } from './apiResponseTypes' -import { SITE_URL } from './utilities' +import { CURRENT_SEASON, SITE_URL } from './utilities' export async function fetchTeam(seasonNumber: number, teamAbbreviation: string): Promise { const response = await fetch(`${SITE_URL}/api/v3/teams?season=${seasonNumber}&team_abbrev=${teamAbbreviation}`) @@ -15,3 +15,19 @@ export async function fetchTeam(seasonNumber: number, teamAbbreviation: string): return teamResponse.teams[0] } + +export async function fetchActiveTeamByOwnerId(ownerId: string): Promise { + const response = await fetch(`${SITE_URL}/api/v3/teams?season=${CURRENT_SEASON}&active_only=True&owner_id=${ownerId}`) + + const teamResponse: { + count: number + teams: Team[] + } = await response.json() + + if (teamResponse.count === 0) { + console.warn('teamServices.fetchActiveTeamByOwnerId - Received 0 active teams for owner id, are they active?') + return undefined + } + + return teamResponse.teams[0] +} diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 30b661d..0171d71 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1,3 +1,3 @@ export const SITE_URL = 'https://sba.manticorum.com' -export const CURRENT_SEASON = 8 \ No newline at end of file +export const CURRENT_SEASON = 8 diff --git a/src/views/PlayerView.vue b/src/views/PlayerView.vue index b3909ef..2289b85 100644 --- a/src/views/PlayerView.vue +++ b/src/views/PlayerView.vue @@ -59,6 +59,7 @@