Browse Source

Merge pull request #6559 from nocodb/feat/continue-after

feat: continue after signin
pull/6560/head
mertmit 11 months ago committed by GitHub
parent
commit
a44136844c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      packages/nc-gui/components/general/ShareProject.vue
  2. 2
      packages/nc-gui/composables/useApi/interceptors.ts
  3. 25
      packages/nc-gui/middleware/auth.global.ts
  4. 13
      packages/nc-gui/pages/forgot-password.vue
  5. 27
      packages/nc-gui/pages/signin.vue
  6. 14
      packages/nc-gui/pages/signup/[[token]].vue
  7. 42
      packages/nocodb/src/modules/jobs/jobs.module.ts
  8. 4
      packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts

30
packages/nc-gui/components/general/ShareProject.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { isDrawerOrModalExist, isMac, useNuxtApp, useRoles } from '#imports'
import { isDrawerOrModalExist, isEeUI, isMac, useNuxtApp, useRoles } from '#imports'
interface Props {
disabled?: boolean
@ -13,12 +13,14 @@ const { isMobileMode } = useGlobal()
const { visibility, showShareModal } = storeToRefs(useShare())
const { activeTable } = storeToRefs(useTablesStore())
const { base } = storeToRefs(useBase())
const { base, isSharedBase } = storeToRefs(useBase())
const { $e } = useNuxtApp()
const { isUIAllowed } = useRoles()
const route = useRoute()
useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey
if (e.altKey && !e.shiftKey && !cmdOrCtrl) {
@ -34,11 +36,15 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
}
}
})
const copySharedBase = async () => {
navigateTo(`/copy-shared-base?base=${route.params.baseId}`)
}
</script>
<template>
<div
v-if="isUIAllowed('baseShare') && visibility !== 'hidden' && (activeTable || base)"
v-if="!isSharedBase && isUIAllowed('baseShare') && visibility !== 'hidden' && (activeTable || base)"
class="flex flex-col justify-center h-full"
data-testid="share-base-button"
:data-sharetype="visibility"
@ -64,6 +70,24 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
</NcButton>
</div>
<template v-else-if="isSharedBase && isEeUI">
<div class="flex-1"></div>
<div class="flex flex-col justify-center h-full">
<div class="flex flex-row items-center w-full">
<NcButton
class="z-10 !rounded-lg !px-2 !bg-[#ff133e]"
size="small"
type="primary"
:disabled="disabled"
@click="copySharedBase"
>
<GeneralIcon class="mr-1" icon="duplicate" />
Copy Base
</NcButton>
</div>
</div>
</template>
<LazyDlgShareAndCollaborateView :is-view-toolbar="isViewToolbar" />
</template>

2
packages/nc-gui/composables/useApi/interceptors.ts

@ -23,8 +23,10 @@ export function addAxiosInterceptors(api: Api<any>) {
if (!config.url?.endsWith('/user/me') && !config.url?.endsWith('/admin/roles')) {
if (route.value && route.value.params && route.value.params.typeOrId === 'base') {
config.headers['xc-shared-base-id'] = route.value.params.baseId
delete config.headers['xc-auth']
} else if (route.value && route.value.params && route.value.params.typeOrId === 'ERD') {
config.headers['xc-shared-erd-id'] = route.value.params.erdUuid
delete config.headers['xc-auth']
}
}

25
packages/nc-gui/middleware/auth.global.ts

@ -58,14 +58,22 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
if ((to.meta.requiresAuth || typeof to.meta.requiresAuth === 'undefined') && !state.signedIn.value) {
/** If this is the first usern navigate to signup page directly */
if (state.appInfo.value.firstUser) {
return navigateTo('/signup')
return navigateTo({
path: '/signup',
query: to.fullPath !== '/' && to.fullPath.match(/^\/(?!\?)/) ? { continueAfterSignIn: to.fullPath } : {},
})
}
/** try generating access token using refresh token */
await state.refreshToken()
/** if user is still not signed in, redirect to signin page */
if (!state.signedIn.value) return navigateTo('/signin')
if (!state.signedIn.value) {
return navigateTo({
path: '/signin',
query: to.fullPath !== '/' && to.fullPath.match(/^\/(?!\?)/) ? { continueAfterSignIn: to.fullPath } : {},
})
}
} else if (to.meta.requiresAuth === false && state.signedIn.value) {
if (to.query?.logout) {
await state.signOut(true)
@ -108,6 +116,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
*/
async function tryGoogleAuth(api: Api<any>, signIn: Actions['signIn']) {
if (window.location.search && /\bscope=|\bstate=/.test(window.location.search) && /\bcode=/.test(window.location.search)) {
let extraProps: any = {}
try {
let authProvider = 'google'
if (window.location.search.includes('state=github')) {
@ -115,16 +124,24 @@ async function tryGoogleAuth(api: Api<any>, signIn: Actions['signIn']) {
} else if (window.location.search.includes('state=oidc')) {
authProvider = 'oidc'
}
const {
data: { token },
data: { token, extra },
} = await api.instance.post(`/auth/${authProvider}/genTokenByCode${window.location.search}`)
extraProps = extra
signIn(token)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
const newURL = window.location.href.split('?')[0]
window.history.pushState('object', document.title, newURL)
window.history.pushState(
'object',
document.title,
`${extraProps.continueAfterSignIn ? `${newURL}#/?continueAfterSignIn=${extraProps.continueAfterSignIn}` : newURL}`,
)
window.location.reload()
}
}

13
packages/nc-gui/pages/forgot-password.vue

@ -6,6 +6,8 @@ definePageMeta({
requiresAuth: false,
})
const route = useRoute()
const { api, isLoading, error } = useApi({ useGlobalInstance: true })
const { t } = useI18n()
@ -48,6 +50,13 @@ async function resetPassword() {
function resetError() {
if (error.value) error.value = null
}
function navigateSignIn() {
navigateTo({
path: '/signin',
query: route.query,
})
}
</script>
<template>
@ -71,7 +80,7 @@ function resetError() {
{{ $t('msg.info.passwordRecovery.success') }} <ClaritySuccessLine />
</div>
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
<nuxt-link @click="navigateSignIn">{{ $t('general.signIn') }}</nuxt-link>
</template>
</div>
@ -99,7 +108,7 @@ function resetError() {
<div class="text-end prose-sm">
{{ $t('msg.info.signUp.alreadyHaveAccount') }}
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
<nuxt-link @click="navigateSignIn">{{ $t('general.signIn') }}</nuxt-link>
</div>
</div>
</a-form>

27
packages/nc-gui/pages/signin.vue

@ -18,6 +18,8 @@ definePageMeta({
title: 'title.headLogin',
})
const route = useRoute()
const { signIn: _signIn, appInfo } = useGlobal()
const { api, isLoading, error } = useApi({ useGlobalInstance: true })
@ -63,13 +65,30 @@ async function signIn() {
api.auth.signin(form).then(async ({ token }) => {
_signIn(token!)
await navigateTo('/')
await navigateTo({
path: '/',
query: route.query,
})
})
}
function resetError() {
if (error.value) error.value = null
}
function navigateSignUp() {
navigateTo({
path: '/signup',
query: route.query,
})
}
function navigateForgotPassword() {
navigateTo({
path: '/forgot-password',
query: route.query,
})
}
</script>
<template>
@ -118,7 +137,7 @@ function resetError() {
</a-form-item>
<div class="hidden md:block text-right">
<nuxt-link class="prose-sm" to="/forgot-password">
<nuxt-link class="prose-sm" @click="navigateForgotPassword">
{{ $t('msg.info.signUp.forgotPassword') }}
</nuxt-link>
</div>
@ -167,11 +186,11 @@ function resetError() {
<div class="text-end prose-sm">
{{ $t('msg.info.signUp.dontHaveAccount') }}
<nuxt-link to="/signup">{{ $t('general.signUp') }}</nuxt-link>
<nuxt-link @click="navigateSignUp">{{ $t('general.signUp') }}</nuxt-link>
</div>
<template v-if="!appInfo.disableEmailAuth">
<div class="md:hidden">
<nuxt-link class="prose-sm" to="/forgot-password">
<nuxt-link class="prose-sm" @click="navigateForgotPassword">
{{ $t('msg.info.signUp.forgotPassword') }}
</nuxt-link>
</div>

14
packages/nc-gui/pages/signup/[[token]].vue

@ -102,7 +102,10 @@ async function signUp() {
console.error(e)
}
await navigateTo('/')
await navigateTo({
path: '/',
query: route.query,
})
})
}
@ -110,6 +113,13 @@ function resetError() {
if (error.value) error.value = null
}
function navigateSignIn() {
navigateTo({
path: '/signin',
query: route.query,
})
}
onMounted(async () => {
await clearWorkspaces()
})
@ -220,7 +230,7 @@ onMounted(async () => {
<div class="text-end prose-sm">
{{ $t('msg.info.signUp.alreadyHaveAccount') }}
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
<nuxt-link @click="navigateSignIn">{{ $t('general.signIn') }}</nuxt-link>
</div>
</div>
</a-form>

42
packages/nocodb/src/modules/jobs/jobs.module.ts

@ -2,31 +2,31 @@ import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
// Jobs
import { ExportService } from './jobs/export-import/export.service';
import { ImportService } from './jobs/export-import/import.service';
import { AtImportController } from './jobs/at-import/at-import.controller';
import { AtImportProcessor } from './jobs/at-import/at-import.processor';
import { DuplicateController } from './jobs/export-import/duplicate.controller';
import { DuplicateProcessor } from './jobs/export-import/duplicate.processor';
import { MetaSyncController } from './jobs/meta-sync/meta-sync.controller';
import { MetaSyncProcessor } from './jobs/meta-sync/meta-sync.processor';
import { SourceCreateController } from './jobs/source-create/source-create.controller';
import { SourceCreateProcessor } from './jobs/source-create/source-create.processor';
import { SourceDeleteController } from './jobs/source-delete/source-delete.controller';
import { SourceDeleteProcessor } from './jobs/source-delete/source-delete.processor';
import { ExportService } from '~/modules/jobs/jobs/export-import/export.service';
import { ImportService } from '~/modules/jobs/jobs/export-import/import.service';
import { AtImportController } from '~/modules/jobs/jobs/at-import/at-import.controller';
import { AtImportProcessor } from '~/modules/jobs/jobs/at-import/at-import.processor';
import { DuplicateController } from '~/modules/jobs/jobs/export-import/duplicate.controller';
import { DuplicateProcessor } from '~/modules/jobs/jobs/export-import/duplicate.processor';
import { MetaSyncController } from '~/modules/jobs/jobs/meta-sync/meta-sync.controller';
import { MetaSyncProcessor } from '~/modules/jobs/jobs/meta-sync/meta-sync.processor';
import { SourceCreateController } from '~/modules/jobs/jobs/source-create/source-create.controller';
import { SourceCreateProcessor } from '~/modules/jobs/jobs/source-create/source-create.processor';
import { SourceDeleteController } from '~/modules/jobs/jobs/source-delete/source-delete.controller';
import { SourceDeleteProcessor } from '~/modules/jobs/jobs/source-delete/source-delete.processor';
// Jobs Module Related
import { JobsLogService } from './jobs/jobs-log.service';
// import { JobsGateway } from './jobs.gateway';
import { JobsController } from './jobs.controller';
import { JobsService } from './redis/jobs.service';
import { JobsRedisService } from './redis/jobs-redis.service';
import { JobsEventService } from './redis/jobs-event.service';
import { JobsLogService } from '~/modules/jobs/jobs/jobs-log.service';
// import { JobsGateway } from '~/modules/jobs/jobs.gateway';
import { JobsController } from '~/modules/jobs/jobs.controller';
import { JobsService } from '~/modules/jobs/redis/jobs.service';
import { JobsRedisService } from '~/modules/jobs/redis/jobs-redis.service';
import { JobsEventService } from '~/modules/jobs/redis/jobs-event.service';
// Fallback
import { JobsService as FallbackJobsService } from './fallback/jobs.service';
import { QueueService as FallbackQueueService } from './fallback/fallback-queue.service';
import { JobsEventService as FallbackJobsEventService } from './fallback/jobs-event.service';
import { JobsService as FallbackJobsService } from '~/modules/jobs/fallback/jobs.service';
import { QueueService as FallbackQueueService } from '~/modules/jobs/fallback/fallback-queue.service';
import { JobsEventService as FallbackJobsEventService } from '~/modules/jobs/fallback/jobs-event.service';
import { JOBS_QUEUE } from '~/interface/Jobs';
import { MetasModule } from '~/modules/metas/metas.module';
import { DatasModule } from '~/modules/datas/datas.module';

4
packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts

@ -20,8 +20,8 @@ import { JobTypes } from '~/interface/Jobs';
@UseGuards(GlobalGuard)
export class DuplicateController {
constructor(
@Inject('JobsService') private readonly jobsService,
private readonly basesService: BasesService,
@Inject('JobsService') protected readonly jobsService,
protected readonly basesService: BasesService,
) {}
@Post([

Loading…
Cancel
Save