diff --git a/package.json b/package.json index 576f3a2..fadff3f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@vueuse/core": "^4.4.0", "bulma": "^0.9.2", "core-js": "^3.9.0", + "date-fns": "^2.21.1", "markdown-it": "^12.0.4", "markdown-it-block-embed": "^0.0.3", "nanoid": "^3.1.22", diff --git a/src/components/Authorize.vue b/src/components/Authorize.vue new file mode 100644 index 0000000..585ee3d --- /dev/null +++ b/src/components/Authorize.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/components/SignInGithub.vue b/src/components/SignInGithub.vue new file mode 100644 index 0000000..994163e --- /dev/null +++ b/src/components/SignInGithub.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/WelcomeWorld.vue b/src/components/WelcomeWorld.vue index d87c1e4..c3a7f63 100644 --- a/src/components/WelcomeWorld.vue +++ b/src/components/WelcomeWorld.vue @@ -3,14 +3,20 @@

Lite Note

- Get started +
+ Get started + about +
+

@@ -90,8 +96,6 @@ rel="noopener noreferrer" >Julien - | - about

@@ -102,8 +106,10 @@ import { defineComponent } from 'vue' import { useForm } from '@/hooks/useForm.hook' import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook' import { useFavoriteRepos } from '@/modules/repo/hooks/useFavoriteRepos.hook' +import SignInGithub from '@/components/SignInGithub.vue' export default defineComponent({ + components: { SignInGithub }, name: 'WelcomeWord', setup() { const { isLogged, username } = useGitHubLogin() @@ -119,7 +125,9 @@ export default defineComponent({ padding: 1rem; margin: auto; display: flex; + flex: 1; flex-direction: column; + justify-content: space-between; .get-started { margin: center; @@ -130,6 +138,10 @@ export default defineComponent({ h4 { text-align: center; } + + .github-login { + margin-top: 1rem; + } } footer { diff --git a/src/data/models/GithubAccessToken.ts b/src/data/models/GithubAccessToken.ts index d58931b..4e0ebe8 100644 --- a/src/data/models/GithubAccessToken.ts +++ b/src/data/models/GithubAccessToken.ts @@ -3,5 +3,10 @@ import { Model } from '@/data/models/Model' export interface GithubAccessToken extends Model { username: string - personalAccessToken: string + token: string + expirationDate: string + expiresIn: number + refreshToken: string + refreshTokenExpiresIn: number + refreshTokenExpirationDate: string } diff --git a/src/hooks/useGitHubLogin.hook.ts b/src/hooks/useGitHubLogin.hook.ts index b74a4ac..b02fa2c 100644 --- a/src/hooks/useGitHubLogin.hook.ts +++ b/src/hooks/useGitHubLogin.hook.ts @@ -1,11 +1,14 @@ import { computed, ref } from 'vue' import { DataType } from '@/data/DataType.enum' -import { GithubAccessToken } from '@/data/models/GithubAccessToken' import { data } from '@/data/data' import { confirmMessage } from '@/utils/notif' +import { GithubAccessToken } from '@/data/models/GithubAccessToken' +import { Octokit } from '@octokit/rest' +import { GithubToken } from '@/modules/user/interfaces/GithubToken' +import { addMilliseconds } from 'date-fns' -const personalAccessTokenId = 'PAT' +const personalTokenId = 'token' const username = ref(null) const accessToken = ref(null) @@ -16,9 +19,9 @@ export const useGitHubLogin = () => { const response = await data.get< DataType.GithubAccessToken, GithubAccessToken - >(data.generateId(DataType.GithubAccessToken, personalAccessTokenId)) + >(data.generateId(DataType.GithubAccessToken, personalTokenId)) username.value = response?.username || '' - accessToken.value = response?.personalAccessToken || '' + accessToken.value = response?.token || '' return response } @@ -28,24 +31,45 @@ export const useGitHubLogin = () => { getAccessToken() } - const saveCredentials = async (username: string, token: string) => { + const saveCredentials = async (githubToken: GithubToken) => { const actualPAT = await getAccessToken() - const personalAccessToken: GithubAccessToken = { + const accessToken: GithubAccessToken = { ...actualPAT, - _id: data.generateId(DataType.GithubAccessToken, personalAccessTokenId), + _id: data.generateId(DataType.GithubAccessToken, personalTokenId), $type: DataType.GithubAccessToken, - username, - personalAccessToken: token + token: githubToken.access_token, + expiresIn: githubToken.expires_in, + expirationDate: addMilliseconds( + new Date(), + githubToken.expires_in + ).toISOString(), + refreshToken: githubToken.refresh_token, + refreshTokenExpiresIn: githubToken.refresh_token_expires_in, + refreshTokenExpirationDate: addMilliseconds( + new Date(), + githubToken.refresh_token_expires_in + ).toISOString(), + username: '' } - await data.add(personalAccessToken) + console.log(accessToken) + + const octokit = new Octokit({ + auth: accessToken.token + }) + + const user = await octokit.request('GET /user') + accessToken.username = user.data.login + username.value = accessToken.username + + await data.add(accessToken) getAccessToken() - confirmMessage('token saved!') + confirmMessage(`${accessToken.username} is logged in!`) } return { - isLogged: !!username.value && !!accessToken.value, + isLogged: !!accessToken.value, isReady: computed(() => accessToken.value !== null), username, accessToken, diff --git a/src/modules/repo/services/repo.ts b/src/modules/repo/services/repo.ts index bb8605c..f02e0d6 100644 --- a/src/modules/repo/services/repo.ts +++ b/src/modules/repo/services/repo.ts @@ -1,9 +1,65 @@ +import { data } from '@/data/data' +import { DataType } from '@/data/DataType.enum' +import { GithubAccessToken } from '@/data/models/GithubAccessToken' import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook' import { useMarkdown } from '@/hooks/useMarkdown.hook' import { useNoteCache } from '@/modules/note/hooks/useNoteCache' import { RepoFile } from '@/modules/repo/interfaces/RepoFile' import { UserSettings } from '@/modules/repo/interfaces/UserSettings' +import { GithubToken } from '@/modules/user/interfaces/GithubToken' +import { GithubTokenError } from '@/modules/user/interfaces/GithubTokenError' import { Octokit } from '@octokit/rest' +import { addMilliseconds } from 'date-fns' + +const personalTokenId = 'token' + +const GITHUB_URL = 'https://github.com/login/oauth/access_token' + +const refreshToken = async () => { + const accessToken = await data.get< + DataType.GithubAccessToken, + GithubAccessToken + >(data.generateId(DataType.GithubAccessToken, personalTokenId)) + if (!accessToken) { + return + } + + if (new Date(accessToken.expirationDate) >= new Date()) { + const response = await fetch(GITHUB_URL, { + body: JSON.stringify({ + refresh_token: accessToken.refreshToken, + grant_type: 'refresh_token' + }) + }) + const githubToken = (await response.json()) as + | GithubToken + | GithubTokenError + + if ('error' in githubToken) { + return + } + + const updatedAccessToken: GithubAccessToken = { + ...accessToken, + token: githubToken.access_token, + expiresIn: githubToken.expires_in, + expirationDate: addMilliseconds( + new Date(), + githubToken.expires_in + ).toISOString(), + refreshToken: githubToken.refresh_token, + refreshTokenExpiresIn: githubToken.refresh_token_expires_in, + refreshTokenExpirationDate: addMilliseconds( + new Date(), + githubToken.refresh_token_expires_in + ).toISOString() + } + + await data.add({ + ...updatedAccessToken + }) + } +} export const getFiles = async ( owner: string, @@ -12,6 +68,7 @@ export const getFiles = async ( if (!owner || !repo) { return [] } + await refreshToken() const { accessToken } = useGitHubLogin() @@ -47,6 +104,8 @@ export const getMainReadme = async (owner: string, repo: string) => { if (!owner || !repo) { return null } + await refreshToken() + const { render } = useMarkdown() const { getCachedNote, saveCacheNote } = useNoteCache('README') @@ -111,6 +170,8 @@ export const getFileContent = async ( null } + await refreshToken() + const file = await octokit.request( 'GET /repos/{owner}/{repo}/git/blobs/{file_sha}', { diff --git a/src/modules/user/interfaces/GithubToken.ts b/src/modules/user/interfaces/GithubToken.ts new file mode 100644 index 0000000..83919bc --- /dev/null +++ b/src/modules/user/interfaces/GithubToken.ts @@ -0,0 +1,8 @@ +export interface GithubToken { + access_token: string + expires_in: number + refresh_token: string + refresh_token_expires_in: number + scope: string + token_type: 'bearer' +} diff --git a/src/modules/user/interfaces/GithubTokenError.ts b/src/modules/user/interfaces/GithubTokenError.ts new file mode 100644 index 0000000..5b43646 --- /dev/null +++ b/src/modules/user/interfaces/GithubTokenError.ts @@ -0,0 +1,5 @@ +export interface GithubTokenError { + error: string + error_description: string + error_uri: string +} diff --git a/src/views/About.vue b/src/views/About.vue index 305a9d4..c9d9f25 100644 --- a/src/views/About.vue +++ b/src/views/About.vue @@ -1,24 +1,22 @@