✨ (login) save login
This commit is contained in:
10
src/App.vue
10
src/App.vue
@@ -1,12 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-view class="app" />
|
<router-view v-if="isReady" class="app" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'App'
|
name: 'App',
|
||||||
|
setup() {
|
||||||
|
const { isReady, accessToken } = useGitHubLogin()
|
||||||
|
|
||||||
|
return { isReady, accessToken }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<router-link :to="{ name: 'RepoList' }" v-if="isLogged"
|
||||||
|
>go to repos</router-link
|
||||||
|
>
|
||||||
|
|
||||||
<form @submit.prevent>
|
<form @submit.prevent>
|
||||||
<div class="columns is-centered is-vcentered to-user-repo">
|
<div class="columns is-centered is-vcentered to-user-repo">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
@@ -85,11 +89,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { useForm } from '@/hooks/useForm.hook'
|
import { useForm } from '@/hooks/useForm.hook'
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'WelcomeWord',
|
name: 'WelcomeWord',
|
||||||
setup() {
|
setup() {
|
||||||
return { ...useForm() }
|
const { isLogged } = useGitHubLogin()
|
||||||
|
|
||||||
|
return { ...useForm(), isLogged }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
export enum DataType {}
|
export enum DataType {
|
||||||
|
GithubAccessToken = 'GithubAccessToken'
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import PouchDb from 'pouchdb-browser'
|
|
||||||
import { Model } from './models/Model'
|
|
||||||
import indexedDb from 'pouchdb-adapter-indexeddb'
|
|
||||||
import { DataType } from './DataType.enum'
|
import { DataType } from './DataType.enum'
|
||||||
|
import { Model } from './models/Model'
|
||||||
|
import PouchDb from 'pouchdb-browser'
|
||||||
|
import indexedDb from 'pouchdb-adapter-indexeddb'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
|
||||||
PouchDb.plugin(indexedDb)
|
PouchDb.plugin(indexedDb)
|
||||||
|
|
||||||
@@ -20,7 +21,9 @@ class Data {
|
|||||||
try {
|
try {
|
||||||
const result = await this.locale.put(model)
|
const result = await this.locale.put(model)
|
||||||
return result.ok
|
return result.ok
|
||||||
} catch {
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +68,10 @@ class Data {
|
|||||||
|
|
||||||
return response.rows.map((row) => row.doc) as T[]
|
return response.rows.map((row) => row.doc) as T[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public generateId(type: DataType, id?: string) {
|
||||||
|
return `${type}-${id || nanoid()}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const data = new Data()
|
export const data = new Data()
|
||||||
|
|||||||
7
src/data/models/GithubAccessToken.ts
Normal file
7
src/data/models/GithubAccessToken.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { DataType } from '@/data/DataType.enum'
|
||||||
|
import { Model } from '@/data/models/Model'
|
||||||
|
|
||||||
|
export interface GithubAccessToken extends Model<DataType.GithubAccessToken> {
|
||||||
|
username: string
|
||||||
|
personalAccessToken: string
|
||||||
|
}
|
||||||
52
src/hooks/useGitHubLogin.hook.ts
Normal file
52
src/hooks/useGitHubLogin.hook.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
import { DataType } from '@/data/DataType.enum'
|
||||||
|
import { GithubAccessToken } from '@/data/models/GithubAccessToken'
|
||||||
|
import { data } from '@/data/data'
|
||||||
|
|
||||||
|
const personalAccessTokenId = 'PAT'
|
||||||
|
const username = ref<string | null>(null)
|
||||||
|
const accessToken = ref<string | null>(null)
|
||||||
|
|
||||||
|
let init = true
|
||||||
|
|
||||||
|
export const useGitHubLogin = () => {
|
||||||
|
const getAccessToken = async () => {
|
||||||
|
const response = await data.get<
|
||||||
|
DataType.GithubAccessToken,
|
||||||
|
GithubAccessToken
|
||||||
|
>(data.generateId(DataType.GithubAccessToken, personalAccessTokenId))
|
||||||
|
username.value = response?.username || ''
|
||||||
|
accessToken.value = response?.personalAccessToken || ''
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init) {
|
||||||
|
init = false
|
||||||
|
getAccessToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveCredentials = async (username: string, token: string) => {
|
||||||
|
const actualPAT = await getAccessToken()
|
||||||
|
|
||||||
|
const personalAccessToken: GithubAccessToken = {
|
||||||
|
...actualPAT,
|
||||||
|
_id: data.generateId(DataType.GithubAccessToken, personalAccessTokenId),
|
||||||
|
$type: DataType.GithubAccessToken,
|
||||||
|
username,
|
||||||
|
personalAccessToken: token
|
||||||
|
}
|
||||||
|
|
||||||
|
await data.add(personalAccessToken)
|
||||||
|
getAccessToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLogged: !!username.value && !!accessToken.value,
|
||||||
|
isReady: computed(() => accessToken.value !== null),
|
||||||
|
username,
|
||||||
|
accessToken,
|
||||||
|
saveCredentials
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,13 +10,12 @@ export const useQueryStackedNotes = () => {
|
|||||||
const { query } = useRoute()
|
const { query } = useRoute()
|
||||||
if (initial) {
|
if (initial) {
|
||||||
initial = false
|
initial = false
|
||||||
stackedNotes.value = Array.isArray(query.stackedNotes)
|
stackedNotes.value = (Array.isArray(query.stackedNotes)
|
||||||
? (query.stackedNotes
|
? query.stackedNotes
|
||||||
.map((n) => n?.toString())
|
: [query.stackedNotes]
|
||||||
.filter((n) => !!n) as string[])
|
)
|
||||||
: ([query.stackedNotes]
|
.map((n) => n?.toString())
|
||||||
.map((n) => n?.toString())
|
.filter((n) => !!n) as string[]
|
||||||
.filter((n) => !!n) as string[])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Ref, onMounted, ref, watch } from '@vue/runtime-core'
|
import { Ref, onMounted, ref, watch } from '@vue/runtime-core'
|
||||||
|
|
||||||
import { request } from '@octokit/request'
|
import { Octokit } from '@octokit/rest'
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
import { useMarkdown } from '@/hooks/useMarkdown.hook'
|
||||||
|
|
||||||
interface Tree {
|
interface Tree {
|
||||||
@@ -13,6 +14,12 @@ interface Tree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useRepo = (owner: Ref<string>, repo: Ref<string>) => {
|
export const useRepo = (owner: Ref<string>, repo: Ref<string>) => {
|
||||||
|
const { accessToken } = useGitHubLogin()
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: accessToken.value
|
||||||
|
})
|
||||||
|
|
||||||
const { render } = useMarkdown()
|
const { render } = useMarkdown()
|
||||||
const readme = ref<string | null>(null)
|
const readme = ref<string | null>(null)
|
||||||
const notFound = ref(false)
|
const notFound = ref(false)
|
||||||
@@ -24,19 +31,22 @@ export const useRepo = (owner: Ref<string>, repo: Ref<string>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const README = await request('GET /repos/{owner}/{repo}/readme', {
|
const README = await octokit.repos.getReadme({
|
||||||
repo: repo.value,
|
owner: owner.value,
|
||||||
owner: owner.value
|
repo: repo.value
|
||||||
})
|
})
|
||||||
|
|
||||||
if (README) {
|
if (README) {
|
||||||
readme.value = render(README.data.content)
|
readme.value = render(README.data.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
const commits = await request('GET /repos/{owner}/{repo}/commits', {
|
const commits = await octokit.request(
|
||||||
repo: repo.value,
|
'GET /repos/{owner}/{repo}/commits',
|
||||||
owner: owner.value
|
{
|
||||||
})
|
repo: repo.value,
|
||||||
|
owner: owner.value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const lastCommit = commits.data.shift()
|
const lastCommit = commits.data.shift()
|
||||||
|
|
||||||
@@ -44,7 +54,7 @@ export const useRepo = (owner: Ref<string>, repo: Ref<string>) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const treeResponse = await request(
|
const treeResponse = await octokit.request(
|
||||||
'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
|
'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
|
||||||
{
|
{
|
||||||
repo: repo.value,
|
repo: repo.value,
|
||||||
|
|||||||
28
src/hooks/useRepos.hook.ts
Normal file
28
src/hooks/useRepos.hook.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Octokit } from '@octokit/rest'
|
||||||
|
import { useAsyncState } from '@vueuse/core'
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
|
|
||||||
|
export const useRepos = () => {
|
||||||
|
const { username, accessToken } = useGitHubLogin()
|
||||||
|
const repos = useAsyncState(async () => {
|
||||||
|
if (!accessToken.value || !username.value) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: accessToken.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const repoList = await octokit.request('GET /search/repositories', {
|
||||||
|
q: `user:${username.value}`,
|
||||||
|
per_page: 100
|
||||||
|
})
|
||||||
|
|
||||||
|
return repoList.data.items.map((item) => item.name)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
repos: repos.state,
|
||||||
|
isReady: repos.isReady
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,17 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import(/* webpackChunkName: "text-editor" */ '@/views/TextEditor.vue')
|
import(/* webpackChunkName: "text-editor" */ '@/views/TextEditor.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: () => import(/* webpackChunkName: "login" */ '@/views/Login.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/repo-list',
|
||||||
|
name: 'RepoList',
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "repo-list" */ '@/views/RepoList.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/:user?/:repo?',
|
path: '/:user?/:repo?',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
|
|||||||
@@ -3,11 +3,8 @@
|
|||||||
<welcome-world />
|
<welcome-world />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="notFound">
|
<div v-else-if="notFound">
|
||||||
<hr />
|
<div class="notification is-warning">
|
||||||
<div class="columns is-centered">
|
Not found.
|
||||||
<div class="column is-one-third notification is-warning" v-if="notFound">
|
|
||||||
Not found.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="home content note-container" v-else>
|
<div class="home content note-container" v-else>
|
||||||
|
|||||||
60
src/views/Login.vue
Normal file
60
src/views/Login.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<button class="button is-primary" @click="back">
|
||||||
|
<img src="@/assets/icons/left-arrow.svg" alt="back" />
|
||||||
|
</button>
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<form @submit.prevent>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Username</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" v-model="user" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Token</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Personal Access Token"
|
||||||
|
v-model="token"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="button is-primary"
|
||||||
|
type="submit"
|
||||||
|
@click="saveCredentials(user, token)"
|
||||||
|
>
|
||||||
|
register token
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from 'vue'
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Login',
|
||||||
|
setup() {
|
||||||
|
const { go } = useRouter()
|
||||||
|
const user = ref('')
|
||||||
|
const token = ref('')
|
||||||
|
|
||||||
|
return { ...useGitHubLogin(), user, token, back: () => go(-1) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.login {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
40
src/views/RepoList.vue
Normal file
40
src/views/RepoList.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="repo-list">
|
||||||
|
<h1>Repositories</h1>
|
||||||
|
<span v-if="!isReady">loading...</span>
|
||||||
|
<ul>
|
||||||
|
<li v-for="repo in repos" :key="repo">
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'Home', params: { user: username, repo: repo } }"
|
||||||
|
>
|
||||||
|
{{ repo }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { useRepos } from '@/hooks/useRepos.hook'
|
||||||
|
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'RepoList',
|
||||||
|
setup() {
|
||||||
|
const { username } = useGitHubLogin()
|
||||||
|
|
||||||
|
return {
|
||||||
|
...useRepos(),
|
||||||
|
username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.repo-list {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user