Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page /sign-in/token #1290

Merged
merged 1 commit into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions spx-gui/src/pages/sign-in/callback.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<template>
<div class="container">
<h4>Logging in...</h4>
<h4>{{ $t(title) }}</h4>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { usePageTitle } from '@/utils/utils'
import { useI18n } from '@/utils/i18n'
import { useUserStore } from '@/stores/user'

const title = { en: 'Signing in...', zh: '登录中...' }

usePageTitle(title)

const userStore = useUserStore()
const i18n = useI18n()
Expand All @@ -27,7 +32,6 @@ try {
}
</script>
<style scoped lang="scss">
// Center the text
.container {
display: flex;
justify-content: center;
Expand Down
124 changes: 124 additions & 0 deletions spx-gui/src/pages/sign-in/token.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<template>
<div class="page">
<UIForm class="form" :form="form" @submit="handleSubmit.fn">
<h1 class="title">{{ $t(title) }}</h1>
<UIFormItem path="token">
<UITextInput
v-model:value="form.value.token"
class="input"
type="textarea"
:placeholder="$t({ en: 'Paste token here', zh: '在此粘贴 Token' })"
/>
</UIFormItem>
<footer class="footer">
<UIButton type="boring" @click="handleCancel">
{{ $t({ en: 'Cancel', zh: '取消' }) }}
</UIButton>
<UIButton type="primary" html-type="submit" :loading="handleSubmit.isLoading.value">
{{ buttonText }}
</UIButton>
</footer>
</UIForm>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from '@/utils/i18n'
import { usePageTitle } from '@/utils/utils'
import { useMessageHandle } from '@/utils/exception'
import { useUserStore, type UserInfo } from '@/stores/user'
import { UIForm, UIFormItem, UITextInput, UIButton, useForm } from '@/components/ui'

const title = {
en: 'Sign in with token',
zh: '使用 Token 登录'
}

usePageTitle(title)

const router = useRouter()
const userStore = useUserStore()
const i18n = useI18n()

const userInfo = ref<UserInfo | null>(null)
const buttonText = computed(() => {
if (userInfo.value == null) return i18n.t({ en: 'Sign in', zh: '登录' })
const username = userInfo.value.displayName || userInfo.value.name
return i18n.t({
en: `Sign in as ${username}`,
zh: `以 ${username} 登录`
})
})

const form = useForm({
token: ['', validateToken]
})

function validateToken(token: string) {
userInfo.value = null
token = token.trim()
if (token === '')
return i18n.t({
en: 'Token is required',
zh: '请提供 Token'
})
try {
userInfo.value = userStore.parseAccessToken(token)
} catch (e) {
return i18n.t({
en: 'Invalid token: ' + e,
zh: '无效的 Token:' + e
})
}
}

function handleCancel() {
router.push('/')
}

const handleSubmit = useMessageHandle(
async () => {
const token = form.value.token.trim()
userStore.signInWithAccessToken(token)
router.push('/')
},
{
en: 'Failed to signin',
zh: '登录失败'
}
)
</script>
<style scoped lang="scss">
.page {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}

.form {
width: 320px;
display: flex;
flex-direction: column;
}

.title {
margin-bottom: 1em;
font-size: 16px;
text-align: center;
}

.input {
justify-self: stretch;
height: 160px;
}

.footer {
margin-top: 1em;
display: flex;
justify-content: center;
gap: 1em;
}
</style>
4 changes: 4 additions & 0 deletions spx-gui/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ const routes: Array<RouteRecordRaw> = [
path: '/sign-in/callback',
component: () => import('@/pages/sign-in/callback.vue')
},
{
path: '/sign-in/token',
component: () => import('@/pages/sign-in/token.vue')
},
{
path: '/share/:owner/:name',
redirect: (to) => getProjectPageRoute(to.params.owner as string, to.params.name as string)
Expand Down
10 changes: 9 additions & 1 deletion spx-gui/src/stores/user/signed-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,18 @@ export const useUserStore = defineStore('spx-user', {
isSignedIn(): boolean {
return this.isAccessTokenValid() || this.refreshToken != null
},
parseAccessToken(accessToken: string) {
return jwtDecode<UserInfo>(accessToken)
},
// TODO: return type `User` instead of `UserInfo` to keep consistency with `getUser` in `src/apis/user.ts`
getSignedInUser(): UserInfo | null {
if (!this.isSignedIn()) return null
return jwtDecode<UserInfo>(this.accessToken!)
return this.parseAccessToken(this.accessToken!)
},
signInWithAccessToken(accessToken: string) {
this.accessToken = accessToken
this.accessTokenExpiresAt = null
this.refreshToken = null
}
},
persist: true
Expand Down
Loading