From a09bd53a240273cabf93f10f346645c25981031d Mon Sep 17 00:00:00 2001 From: tejaskh3 Date: Fri, 17 Jan 2025 03:57:14 +0530 Subject: [PATCH 1/3] feat: add discord route --- app/constants/urls.js | 3 +- app/controllers/discord.js | 65 +++++++++++++++++++++++++++++++ app/router.js | 1 + app/routes/discord.js | 59 ++++++++++++++++++++++++++++ app/templates/disocrd.hbs | 4 ++ app/utils/redirect-auth.js | 9 +++++ tests/unit/routes/discord-test.js | 11 ++++++ 7 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 app/controllers/discord.js create mode 100644 app/routes/discord.js create mode 100644 app/templates/disocrd.hbs create mode 100644 app/utils/redirect-auth.js create mode 100644 tests/unit/routes/discord-test.js diff --git a/app/constants/urls.js b/app/constants/urls.js index 019846c3..9635567f 100644 --- a/app/constants/urls.js +++ b/app/constants/urls.js @@ -98,6 +98,7 @@ export const SOCIALS = { WHITE_ICON: 'assets/icons/instagram-white.png', }, }; - +export const AUTH_URL = + 'https://github.com/login/oauth/authorize?client_id=23c78f66ab7964e5ef97'; export const ANKUSH_TWITTER = 'https://twitter.com/ankushdharkar'; export const RDS_TWITTER = 'https://x.com/realdevsquad'; diff --git a/app/controllers/discord.js b/app/controllers/discord.js new file mode 100644 index 00000000..157b111c --- /dev/null +++ b/app/controllers/discord.js @@ -0,0 +1,65 @@ +import Controller from '@ember/controller'; +import { TOAST_OPTIONS } from '../constants/toast-options'; +import { APPS } from '../constants/urls'; +import { inject as service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; + +export default class DiscordController extends Controller { + @service router; + @service toast; + @tracked discordId = + this.model.externalAccountData.attributes.discordId || ''; + @tracked linkStatus = 'not-linked'; + @tracked isLinking = false; + @tracked consent = false; + + @tracked token = ''; + + queryParams = { + token: { refreshModel: true }, + }; + + async model() { + this.token = this.paramsFor('discord').token; + } + @action setConsent() { + this.consent = !this.consent; + } + + @action async linkDiscordAccount() { + try { + this.isLinking = true; + + if (this.consent) { + const response = await fetch( + `${APPS.API_BACKEND}/external-accounts/link/${this.token}`, + { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + }, + ); + + if (response.status === 204) { + this.linkStatus = 'linked'; + } else { + this.linkStatus = 'failure'; + } + } else { + this.toast.error( + 'Please provide the consent by clicking the checkbox!', + 'ERROR', + TOAST_OPTIONS, + ); + } + } catch (error) { + this.linkStatus = 'failure'; + console.error(error.message); + } finally { + this.isLinking = false; + } + } +} diff --git a/app/router.js b/app/router.js index ae71582e..c04e7df3 100644 --- a/app/router.js +++ b/app/router.js @@ -18,4 +18,5 @@ Router.map(function () { this.route('subscribe'); this.route('login'); this.route('identity'); + this.route('discord'); }); diff --git a/app/routes/discord.js b/app/routes/discord.js new file mode 100644 index 00000000..9c325105 --- /dev/null +++ b/app/routes/discord.js @@ -0,0 +1,59 @@ +import Route from '@ember/routing/route'; +import { APPS } from '../constants/urls'; +import { inject as service } from '@ember/service'; +import redirectAuth from '../utils/redirect-auth'; +import { TOAST_OPTIONS } from '../constants/toast-options'; + +export default class DiscordRoute extends Route { + @service router; + @service toast; + + queryParams = { + token: { refreshModel: true }, + }; + + beforeModel(transition) { + if (!transition.to.queryParams.dev) { + this.router.transitionTo('page-not-found'); + } + } + + async model() { + try { + const token = this.paramsFor('discord').token; + const externalAccountResponse = await fetch( + `${APPS.API_BACKEND}/external-accounts/${token}`, + { + credentials: 'include', + }, + ); + + const userResponse = await fetch( + `${APPS.API_BACKEND}/users?profile=true`, + { + credentials: 'include', + }, + ); + + const externalAccountData = await externalAccountResponse.json(); + const userData = await userResponse.json(); + + if ( + userResponse.status === 200 && + externalAccountResponse.status === 200 + ) { + return { externalAccountData, userData, isTokenEpired: false }; + } else if (userResponse.status === 401) { + this.toast.error(userData.message, '', TOAST_OPTIONS); + setTimeout(redirectAuth, 2000); + } else if (externalAccountResponse.status === 401) { + this.toast.error(externalAccountData.message, '', TOAST_OPTIONS); + + return { isTokenExpired: true }; + } + } catch (error) { + this.toast.error(error.message, '', TOAST_OPTIONS); + console.error(error.message); + } + } +} diff --git a/app/templates/disocrd.hbs b/app/templates/disocrd.hbs new file mode 100644 index 00000000..74e435ef --- /dev/null +++ b/app/templates/disocrd.hbs @@ -0,0 +1,4 @@ +
+ {{!-- TODO: add discord hbs file in next PRs--}} +

Discord Page

+
diff --git a/app/utils/redirect-auth.js b/app/utils/redirect-auth.js new file mode 100644 index 00000000..11de3fcb --- /dev/null +++ b/app/utils/redirect-auth.js @@ -0,0 +1,9 @@ +import { AUTH_URL } from '../constants/urls'; + +export default function () { + let authUrl = AUTH_URL; + if (typeof window !== 'undefined') { + authUrl = `${authUrl}&state=${window.location.href}`; + } + window.open(authUrl, '_self'); +} diff --git a/tests/unit/routes/discord-test.js b/tests/unit/routes/discord-test.js new file mode 100644 index 00000000..c755f687 --- /dev/null +++ b/tests/unit/routes/discord-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'website-www/tests/helpers'; + +module('Unit | Route | discord', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:discord'); + assert.ok(route); + }); +}); From 89b1b100c63eddf5f4ed703373de808e53e8fb63 Mon Sep 17 00:00:00 2001 From: tejaskh3 Date: Sat, 18 Jan 2025 00:37:52 +0530 Subject: [PATCH 2/3] refactor: route and remove client_id --- app/constants/urls.js | 3 +-- app/controllers/discord.js | 6 +----- app/routes/discord.js | 30 ++++++++++++++++++------------ app/utils/redirect-auth.js | 7 ++++--- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/app/constants/urls.js b/app/constants/urls.js index 9635567f..019846c3 100644 --- a/app/constants/urls.js +++ b/app/constants/urls.js @@ -98,7 +98,6 @@ export const SOCIALS = { WHITE_ICON: 'assets/icons/instagram-white.png', }, }; -export const AUTH_URL = - 'https://github.com/login/oauth/authorize?client_id=23c78f66ab7964e5ef97'; + export const ANKUSH_TWITTER = 'https://twitter.com/ankushdharkar'; export const RDS_TWITTER = 'https://x.com/realdevsquad'; diff --git a/app/controllers/discord.js b/app/controllers/discord.js index 157b111c..981c80ca 100644 --- a/app/controllers/discord.js +++ b/app/controllers/discord.js @@ -6,6 +6,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class DiscordController extends Controller { + queryParams = ['token']; @service router; @service toast; @tracked discordId = @@ -13,13 +14,8 @@ export default class DiscordController extends Controller { @tracked linkStatus = 'not-linked'; @tracked isLinking = false; @tracked consent = false; - @tracked token = ''; - queryParams = { - token: { refreshModel: true }, - }; - async model() { this.token = this.paramsFor('discord').token; } diff --git a/app/routes/discord.js b/app/routes/discord.js index 9c325105..fa76af45 100644 --- a/app/routes/discord.js +++ b/app/routes/discord.js @@ -7,7 +7,6 @@ import { TOAST_OPTIONS } from '../constants/toast-options'; export default class DiscordRoute extends Route { @service router; @service toast; - queryParams = { token: { refreshModel: true }, }; @@ -27,7 +26,6 @@ export default class DiscordRoute extends Route { credentials: 'include', }, ); - const userResponse = await fetch( `${APPS.API_BACKEND}/users?profile=true`, { @@ -35,25 +33,33 @@ export default class DiscordRoute extends Route { }, ); - const externalAccountData = await externalAccountResponse.json(); - const userData = await userResponse.json(); + if (userResponse.status === 401) { + const userData = await userResponse.json(); + this.toast.error(userData.message, '', TOAST_OPTIONS); + setTimeout(redirectAuth, 2000); + return { isTokenExpired: true }; + } + + if (externalAccountResponse.status === 401) { + const externalAccountData = await externalAccountResponse.json(); + this.toast.error(externalAccountData.message, '', TOAST_OPTIONS); + return { isTokenExpired: true }; + } if ( userResponse.status === 200 && externalAccountResponse.status === 200 ) { - return { externalAccountData, userData, isTokenEpired: false }; - } else if (userResponse.status === 401) { - this.toast.error(userData.message, '', TOAST_OPTIONS); - setTimeout(redirectAuth, 2000); - } else if (externalAccountResponse.status === 401) { - this.toast.error(externalAccountData.message, '', TOAST_OPTIONS); - - return { isTokenExpired: true }; + const externalAccountData = await externalAccountResponse.json(); + const userData = await userResponse.json(); + return { externalAccountData, userData, isTokenExpired: false }; } + + throw new Error('Unexpected response status'); } catch (error) { this.toast.error(error.message, '', TOAST_OPTIONS); console.error(error.message); + return { isTokenExpired: true, error: error.message }; } } } diff --git a/app/utils/redirect-auth.js b/app/utils/redirect-auth.js index 11de3fcb..0b8a5afe 100644 --- a/app/utils/redirect-auth.js +++ b/app/utils/redirect-auth.js @@ -1,9 +1,10 @@ -import { AUTH_URL } from '../constants/urls'; +import { AUTH } from '../constants/urls'; export default function () { - let authUrl = AUTH_URL; + let authUrl = AUTH.GITHUB_SIGN_IN; if (typeof window !== 'undefined') { - authUrl = `${authUrl}&state=${window.location.href}`; + const separator = authUrl.includes('?') ? '&' : '?'; + authUrl = `${authUrl}${separator}redirectURL=${encodeURIComponent(window.location.href)}`; } window.open(authUrl, '_self'); } From 0a8430c74c08a9e499667a990955f0ed9df24ad4 Mon Sep 17 00:00:00 2001 From: tejaskh3 Date: Sun, 19 Jan 2025 02:13:29 +0530 Subject: [PATCH 3/3] refactor: controller --- app/controllers/discord.js | 49 ++++++++++++++++++-------------------- app/utils/redirect-auth.js | 10 ++++---- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/app/controllers/discord.js b/app/controllers/discord.js index 981c80ca..174d47de 100644 --- a/app/controllers/discord.js +++ b/app/controllers/discord.js @@ -12,7 +12,7 @@ export default class DiscordController extends Controller { @tracked discordId = this.model.externalAccountData.attributes.discordId || ''; @tracked linkStatus = 'not-linked'; - @tracked isLinking = false; + @tracked isLinkingInProgress = false; @tracked consent = false; @tracked token = ''; @@ -24,38 +24,35 @@ export default class DiscordController extends Controller { } @action async linkDiscordAccount() { + if (!this.consent) { + this.toast.error( + 'Please provide consent by checking the checkbox', + 'ERROR', + TOAST_OPTIONS, + ); + return; + } + try { - this.isLinking = true; + this.isLinkingInProgress = true; - if (this.consent) { - const response = await fetch( - `${APPS.API_BACKEND}/external-accounts/link/${this.token}`, - { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', + const response = await fetch( + `${APPS.API_BACKEND}/external-accounts/link/${this.token}`, + { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', }, - ); + credentials: 'include', + }, + ); - if (response.status === 204) { - this.linkStatus = 'linked'; - } else { - this.linkStatus = 'failure'; - } - } else { - this.toast.error( - 'Please provide the consent by clicking the checkbox!', - 'ERROR', - TOAST_OPTIONS, - ); - } + this.linkStatus = response.status === 204 ? 'linked' : 'failure'; } catch (error) { this.linkStatus = 'failure'; - console.error(error.message); + console.error('Failed to link Discord account:', error.message); } finally { - this.isLinking = false; + this.isLinkingInProgress = false; } } } diff --git a/app/utils/redirect-auth.js b/app/utils/redirect-auth.js index 0b8a5afe..0e14b94d 100644 --- a/app/utils/redirect-auth.js +++ b/app/utils/redirect-auth.js @@ -1,10 +1,12 @@ import { AUTH } from '../constants/urls'; -export default function () { +export default function redirectAuth() { let authUrl = AUTH.GITHUB_SIGN_IN; if (typeof window !== 'undefined') { - const separator = authUrl.includes('?') ? '&' : '?'; - authUrl = `${authUrl}${separator}redirectURL=${encodeURIComponent(window.location.href)}`; + const url = new URL(authUrl); + const searchParams = new URLSearchParams(url.search); + searchParams.set('redirectURL', window.location.href); + url.search = searchParams.toString(); + window.location.href = url.toString(); } - window.open(authUrl, '_self'); }