Skip to content

Commit 3d9d5cd

Browse files
committed
page /sign-in/token
1 parent c0ed9c5 commit 3d9d5cd

File tree

4 files changed

+134
-4
lines changed

4 files changed

+134
-4
lines changed

spx-gui/src/pages/sign-in/callback.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
<template>
22
<div class="container">
3-
<h4>Logging in...</h4>
3+
<h4>{{ $t(title) }}</h4>
44
</div>
55
</template>
66
<script setup lang="ts">
7-
import { useUserStore } from '@/stores/user'
7+
import { usePageTitle } from '@/utils/utils'
88
import { useI18n } from '@/utils/i18n'
9+
import { useUserStore } from '@/stores/user'
10+
11+
const title = { en: 'Signing in...', zh: '登录中...' }
12+
13+
usePageTitle(title)
914
1015
const userStore = useUserStore()
1116
const i18n = useI18n()
@@ -27,7 +32,6 @@ try {
2732
}
2833
</script>
2934
<style scoped lang="scss">
30-
// Center the text
3135
.container {
3236
display: flex;
3337
justify-content: center;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<template>
2+
<div class="page">
3+
<UIForm class="form" :form="form" @submit="handleSubmit.fn">
4+
<h1 class="title">{{ $t(title) }}</h1>
5+
<UIFormItem path="token">
6+
<UITextInput
7+
v-model:value="form.value.token"
8+
class="input"
9+
type="textarea"
10+
:placeholder="$t({ en: 'Paste token here', zh: '在此粘贴 Token' })"
11+
/>
12+
</UIFormItem>
13+
<footer class="footer">
14+
<UIButton type="primary" html-type="submit" :loading="handleSubmit.isLoading.value">
15+
{{ buttonText }}
16+
</UIButton>
17+
</footer>
18+
</UIForm>
19+
</div>
20+
</template>
21+
<script setup lang="ts">
22+
import { computed, ref } from 'vue'
23+
import { useI18n } from '@/utils/i18n'
24+
import { usePageTitle } from '@/utils/utils'
25+
import { useMessageHandle } from '@/utils/exception'
26+
import { useUserStore, type UserInfo } from '@/stores/user'
27+
import { UIForm, UIFormItem, UITextInput, UIButton, useForm } from '@/components/ui'
28+
29+
const title = {
30+
en: 'Sign in with token',
31+
zh: '使用 Token 登录'
32+
}
33+
34+
usePageTitle(title)
35+
36+
const userStore = useUserStore()
37+
const i18n = useI18n()
38+
39+
const userInfo = ref<UserInfo | null>(null)
40+
const buttonText = computed(() => {
41+
if (userInfo.value == null) return i18n.t({ en: 'Sign in', zh: '登录' })
42+
const username = userInfo.value.displayName || userInfo.value.name
43+
return i18n.t({
44+
en: `Sign in as ${username}`,
45+
zh: `以 ${username} 登录`
46+
})
47+
})
48+
49+
const form = useForm({
50+
token: ['', validateToken]
51+
})
52+
53+
function validateToken(token: string) {
54+
userInfo.value = null
55+
token = token.trim()
56+
if (token === '')
57+
return i18n.t({
58+
en: 'Token is required',
59+
zh: '请提供 Token'
60+
})
61+
try {
62+
userInfo.value = userStore.parseAccessToken(token)
63+
} catch (e) {
64+
return i18n.t({
65+
en: 'Invalid token: ' + e,
66+
zh: '无效的 Token:' + e
67+
})
68+
}
69+
}
70+
71+
const handleSubmit = useMessageHandle(
72+
async () => {
73+
const token = form.value.token.trim()
74+
userStore.signInWithAccessToken(token)
75+
window.location.replace('/')
76+
},
77+
{
78+
en: 'Failed to signin',
79+
zh: '登录失败'
80+
}
81+
)
82+
</script>
83+
<style scoped lang="scss">
84+
.page {
85+
width: 100%;
86+
height: 100%;
87+
display: flex;
88+
justify-content: center;
89+
align-items: center;
90+
}
91+
92+
.form {
93+
width: 320px;
94+
display: flex;
95+
flex-direction: column;
96+
}
97+
98+
.title {
99+
margin-bottom: 1em;
100+
font-size: 16px;
101+
text-align: center;
102+
}
103+
104+
.input {
105+
justify-self: stretch;
106+
height: 160px;
107+
}
108+
109+
.footer {
110+
margin-top: 1em;
111+
display: flex;
112+
justify-content: center;
113+
}
114+
</style>

spx-gui/src/router.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ const routes: Array<RouteRecordRaw> = [
112112
path: '/sign-in/callback',
113113
component: () => import('@/pages/sign-in/callback.vue')
114114
},
115+
{
116+
path: '/sign-in/token',
117+
component: () => import('@/pages/sign-in/token.vue')
118+
},
115119
{
116120
path: '/share/:owner/:name',
117121
redirect: (to) => getProjectPageRoute(to.params.owner as string, to.params.name as string)

spx-gui/src/stores/user/signed-in.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,18 @@ export const useUserStore = defineStore('spx-user', {
9595
isSignedIn(): boolean {
9696
return this.isAccessTokenValid() || this.refreshToken != null
9797
},
98+
parseAccessToken(accessToken: string) {
99+
return jwtDecode<UserInfo>(accessToken)
100+
},
98101
// TODO: return type `User` instead of `UserInfo` to keep consistency with `getUser` in `src/apis/user.ts`
99102
getSignedInUser(): UserInfo | null {
100103
if (!this.isSignedIn()) return null
101-
return jwtDecode<UserInfo>(this.accessToken!)
104+
return this.parseAccessToken(this.accessToken!)
105+
},
106+
signInWithAccessToken(accessToken: string) {
107+
this.accessToken = accessToken
108+
this.accessTokenExpiresAt = null
109+
this.refreshToken = null
102110
}
103111
},
104112
persist: true

0 commit comments

Comments
 (0)