Browse Source

feat(nc-gui): implement unstorage

feat/ssr-storage
braks 2 years ago
parent
commit
e9a01e0061
  1. 1
      packages/nc-gui/composables/useApi/index.ts
  2. 11
      packages/nc-gui/composables/useGlobal/index.ts
  3. 3
      packages/nc-gui/composables/useGlobal/state.ts
  4. 8
      packages/nc-gui/composables/useUnstorage/index.ts
  5. 34
      packages/nc-gui/middleware/auth.global.ts
  6. 10
      packages/nc-gui/nuxt.config.ts
  7. 380
      packages/nc-gui/package-lock.json
  8. 4
      packages/nc-gui/package.json
  9. 16
      packages/nc-gui/pages/signin.vue
  10. 21
      packages/nc-gui/plugins/a.state.ts
  11. 14
      packages/nc-gui/plugins/feedbackForm.ts
  12. 10
      packages/nc-gui/plugins/tele.ts

1
packages/nc-gui/composables/useApi/index.ts

@ -74,6 +74,7 @@ export function useApi<Data = any, RequestConfig = any>({
function onRequestFinish() { function onRequestFinish() {
/** local count */ /** local count */
dec() dec()
/** global count */ /** global count */
nuxtApp.$state.runningRequests.dec() nuxtApp.$state.runningRequests.dec()

11
packages/nc-gui/composables/useGlobal/index.ts

@ -2,7 +2,7 @@ import { useGlobalActions } from './actions'
import { useGlobalGetters } from './getters' import { useGlobalGetters } from './getters'
import { useGlobalState } from './state' import { useGlobalState } from './state'
import type { UseGlobalReturn } from './types' import type { UseGlobalReturn } from './types'
import { createGlobalState, useNuxtApp, watch } from '#imports' import { createGlobalState, watch } from '#imports'
export * from './types' export * from './types'
@ -37,8 +37,6 @@ export * from './types'
* ``` * ```
*/ */
export const useGlobal = createGlobalState((): UseGlobalReturn => { export const useGlobal = createGlobalState((): UseGlobalReturn => {
const { provide } = useNuxtApp()
const state = useGlobalState() const state = useGlobalState()
const getters = useGlobalGetters(state) const getters = useGlobalGetters(state)
@ -77,10 +75,5 @@ export const useGlobal = createGlobalState((): UseGlobalReturn => {
{ immediate: true }, { immediate: true },
) )
const globalState = { ...state, ...getters, ...actions } as UseGlobalReturn return { ...state, ...getters, ...actions } as UseGlobalReturn
/** provide a fresh state instance into nuxt app */
provide('state', globalState)
return globalState
}) })

3
packages/nc-gui/composables/useGlobal/state.ts

@ -1,4 +1,3 @@
import { useStorage } from '@vueuse/core'
import type { JwtPayload } from 'jwt-decode' import type { JwtPayload } from 'jwt-decode'
import type { AppInfo, State, StoredState } from './types' import type { AppInfo, State, StoredState } from './types'
import { BASE_URL, computed, ref, toRefs, useCounter, useJwt, useNuxtApp, usePreferredLanguages, useTimestamp } from '#imports' import { BASE_URL, computed, ref, toRefs, useCounter, useJwt, useNuxtApp, usePreferredLanguages, useTimestamp } from '#imports'
@ -63,7 +62,7 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
} }
/** saves a reactive state, any change to these values will write/delete to localStorage */ /** saves a reactive state, any change to these values will write/delete to localStorage */
const storage = useStorage<StoredState>(storageKey, initialState, localStorage, { mergeDefaults: true }) const storage = ref(initialState)
/** force turn off of dark mode, regardless of previously stored settings */ /** force turn off of dark mode, regardless of previously stored settings */
storage.value.darkMode = false storage.value.darkMode = false

8
packages/nc-gui/composables/useUnstorage/index.ts

@ -0,0 +1,8 @@
import { createStorage } from 'unstorage'
import memoryDriver from 'unstorage/drivers/memory'
const unstorage = createStorage({
driver: memoryDriver(),
})
export const useUnstorage = unstorage

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

@ -37,7 +37,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
const { allRoles } = useRoles() const { allRoles } = useRoles()
/** if user isn't signed in and google auth is enabled, try to check if sign-in data is present */ /** if user isn't signed in and google auth is enabled, try to check if sign-in data is present */
if (!state.signedIn && state.appInfo.value.googleAuthEnabled) await tryGoogleAuth(api, state.signIn) if (!state.signedIn.value && state.appInfo.value.googleAuthEnabled) await tryGoogleAuth(api, state.signIn)
/** if public allow all visitors */ /** if public allow all visitors */
if (to.meta.public) return if (to.meta.public) return
@ -89,22 +89,26 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
* If present, try using google auth data to sign user in before navigating to the next page * If present, try using google auth data to sign user in before navigating to the next page
*/ */
async function tryGoogleAuth(api: Api<any>, signIn: Actions['signIn']) { async function tryGoogleAuth(api: Api<any>, signIn: Actions['signIn']) {
if (window.location.search && /\bscope=|\bstate=/.test(window.location.search) && /\bcode=/.test(window.location.search)) { if (process.client) {
try { if (window.location.search && /\bscope=|\bstate=/.test(window.location.search) && /\bcode=/.test(window.location.search)) {
const { try {
data: { token }, const {
} = await api.instance.post( data: { token },
`/auth/${window.location.search.includes('state=github') ? 'github' : 'google'}/genTokenByCode${window.location.search}`, } = await api.instance.post(
) `/auth/${window.location.search.includes('state=github') ? 'github' : 'google'}/genTokenByCode${
window.location.search
}`,
)
signIn(token) signIn(token)
} catch (e: any) { } catch (e: any) {
if (e.response && e.response.data && e.response.data.msg) { if (e.response && e.response.data && e.response.data.msg) {
message.error({ content: e.response.data.msg }) message.error({ content: e.response.data.msg })
}
} }
}
const newURL = window.location.href.split('?')[0] const newURL = window.location.href.split('?')[0]
window.history.pushState('object', document.title, newURL) window.history.pushState('object', document.title, newURL)
}
} }
} }

10
packages/nc-gui/nuxt.config.ts

@ -11,7 +11,7 @@ import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfil
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: ['@vueuse/nuxt', 'nuxt-windicss', '@nuxt/image-edge'], modules: ['@vueuse/nuxt', 'nuxt-windicss', '@nuxt/image-edge'],
ssr: false, ssr: true,
app: { app: {
pageTransition: { pageTransition: {
@ -27,13 +27,7 @@ export default defineNuxtConfig({
cdnURL: process.env.NODE_ENV === 'production' ? '.' : undefined, cdnURL: process.env.NODE_ENV === 'production' ? '.' : undefined,
}, },
css: [ css: ['virtual:windi.css', '~/assets/style/fonts.css', '~/assets/css/global.css', '~/assets/style.scss'],
'virtual:windi.css',
'virtual:windi-devtools',
'~/assets/style/fonts.css',
'~/assets/css/global.css',
'~/assets/style.scss',
],
meta: { meta: {
title: 'NocoDB', title: 'NocoDB',

380
packages/nc-gui/package-lock.json generated

File diff suppressed because it is too large Load Diff

4
packages/nc-gui/package.json

@ -26,8 +26,7 @@
"test:ui": "vitest -c test/vite.config.ts --ui", "test:ui": "vitest -c test/vite.config.ts --ui",
"coverage": "vitest -c test/vite.config.ts run --coverage", "coverage": "vitest -c test/vite.config.ts run --coverage",
"build:copy": "npm run generate; rm -rf ../nc-lib-gui/lib/dist/; rsync -rvzh ./dist/ ../nc-lib-gui/lib/dist/", "build:copy": "npm run generate; rm -rf ../nc-lib-gui/lib/dist/; rsync -rvzh ./dist/ ../nc-lib-gui/lib/dist/",
"build:copy:publish": "npm run generate; rm -rf ../nc-lib-gui/lib/dist/; rsync -rvzh ./dist/ ../nc-lib-gui/lib/dist/; npm publish ../nc-lib-gui", "build:copy:publish": "npm run generate; rm -rf ../nc-lib-gui/lib/dist/; rsync -rvzh ./dist/ ../nc-lib-gui/lib/dist/; npm publish ../nc-lib-gui"
"postinstall": "node scripts/updateNuxtRouting.js"
}, },
"dependencies": { "dependencies": {
"@ckpack/vue-color": "^1.2.0", "@ckpack/vue-color": "^1.2.0",
@ -54,6 +53,7 @@
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tinycolor2": "^1.4.2", "tinycolor2": "^1.4.2",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"unstorage": "^0.5.6",
"vue-dompurify-html": "^3.0.0", "vue-dompurify-html": "^3.0.0",
"vue-github-button": "^3.0.3", "vue-github-button": "^3.0.3",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",

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

@ -44,7 +44,14 @@ const formRules: Record<string, RuleObject[]> = {
], ],
} }
async function signIn() { async function signIn(e: MouseEvent) {
console.log('clicked')
e.stopImmediatePropagation()
e.preventDefault()
console.log('clicked')
if (!formValidator.value.validate()) return if (!formValidator.value.validate()) return
resetError() resetError()
@ -114,12 +121,7 @@ function resetError() {
</div> </div>
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center"> <div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center">
<button <button data-cy="nc-form-signin__submit" class="scaling-btn bg-opacity-100">
data-cy="nc-form-signin__submit"
data-nc="nc-form-signin__submit"
class="scaling-btn bg-opacity-100"
type="submit"
>
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
<MdiLogin /> <MdiLogin />
{{ $t('general.signIn') }} {{ $t('general.signIn') }}

21
packages/nc-gui/plugins/state.ts → packages/nc-gui/plugins/a.state.ts

@ -1,5 +1,7 @@
import { defineNuxtPlugin, useApi, useGlobal } from '#imports' import { loadLocaleMessages, setI18nLanguage } from './a.i18n'
import { loadLocaleMessages, setI18nLanguage } from '~/plugins/a.i18n' import { defineNuxtPlugin, useApi, useGlobal, watch } from '#imports'
import { useUnstorage } from '~/composables/useUnstorage'
import type { StoredState } from '~/composables/useGlobal'
import { Language, LanguageAlias } from '~/lib' import { Language, LanguageAlias } from '~/lib'
/** /**
@ -14,9 +16,11 @@ import { Language, LanguageAlias } from '~/lib'
* console.log($state.lang.value) // 'en' * console.log($state.lang.value) // 'en'
* ``` * ```
*/ */
export default defineNuxtPlugin(async () => { export default defineNuxtPlugin(async (nuxt) => {
const state = useGlobal() const state = useGlobal()
nuxt.provide('state', state)
const { api } = useApi({ useGlobalInstance: true }) const { api } = useApi({ useGlobalInstance: true })
let currentLang = state.lang.value let currentLang = state.lang.value
@ -30,6 +34,17 @@ export default defineNuxtPlugin(async () => {
/** set i18n locale to stored language */ /** set i18n locale to stored language */
await setI18nLanguage(currentLang) await setI18nLanguage(currentLang)
const storedState = await useUnstorage.getItem('global:state')
state.storage.value = { ...state.storage.value, ...(storedState as StoredState) }
watch(
state.storage,
(nextState) => {
useUnstorage.setItem('global:state', nextState)
},
{ deep: true, flush: 'post' },
)
try { try {
state.appInfo.value = await api.utils.appInfo() state.appInfo.value = await api.utils.appInfo()
} catch (e) { } catch (e) {

14
packages/nc-gui/plugins/feedbackForm.ts

@ -2,17 +2,17 @@ import dayjs from 'dayjs'
import { defineNuxtPlugin, useGlobal, useNuxtApp } from '#imports' import { defineNuxtPlugin, useGlobal, useNuxtApp } from '#imports'
const handleFeedbackForm = async () => { const handleFeedbackForm = async () => {
let { feedbackForm: currentFeedbackForm } = $(useGlobal()) const { feedbackForm: currentFeedbackForm, signedIn } = useGlobal()
if (!currentFeedbackForm) return if (!currentFeedbackForm.value || !signedIn.value) return
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const isFirstTimePolling = !currentFeedbackForm.lastFormPollDate const isFirstTimePolling = !currentFeedbackForm.value.lastFormPollDate
const now = dayjs() const now = dayjs()
const lastFormPolledDate = dayjs(currentFeedbackForm.lastFormPollDate) const lastFormPolledDate = dayjs(currentFeedbackForm.value.lastFormPollDate)
if (isFirstTimePolling || dayjs.duration(now.diff(lastFormPolledDate)).days() > 0) { if (isFirstTimePolling || dayjs.duration(now.diff(lastFormPolledDate)).days() > 0) {
$api.instance $api.instance
@ -21,13 +21,13 @@ const handleFeedbackForm = async () => {
try { try {
const { data: feedbackForm } = response const { data: feedbackForm } = response
if (!feedbackForm.error) { if (!feedbackForm.error) {
const isFetchedFormDuplicate = currentFeedbackForm.url === feedbackForm.url const isFetchedFormDuplicate = currentFeedbackForm.value.url === feedbackForm.url
currentFeedbackForm = { currentFeedbackForm.value = {
url: feedbackForm.url, url: feedbackForm.url,
lastFormPollDate: now.toISOString(), lastFormPollDate: now.toISOString(),
createdAt: feedbackForm.created_at, createdAt: feedbackForm.created_at,
isHidden: isFetchedFormDuplicate ? currentFeedbackForm.isHidden : false, isHidden: isFetchedFormDuplicate ? currentFeedbackForm.value.isHidden : false,
} }
} }
} catch (e) {} } catch (e) {}

10
packages/nc-gui/plugins/tele.ts

@ -8,7 +8,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
const route = useRoute() const route = useRoute()
const { appInfo } = $(useGlobal()) const state = useGlobal()
let socket: Socket let socket: Socket
@ -16,7 +16,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
try { try {
if (socket) socket.disconnect() if (socket) socket.disconnect()
const url = new URL(appInfo.ncSiteUrl, window.location.href.split(/[?#]/)[0]).href const url = new URL(state.appInfo.value.ncSiteUrl, window.location.href.split(/[?#]/)[0]).href
socket = io(url, { socket = io(url, {
extraHeaders: { 'xc-auth': token }, extraHeaders: { 'xc-auth': token },
@ -28,8 +28,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
} catch {} } catch {}
} }
if (nuxtApp.$state.signedIn.value) { if (state.signedIn.value && state.token.value) {
await init(nuxtApp.$state.token.value) await init(state.token.value)
} }
router.afterEach((to, from) => { router.afterEach((to, from) => {
@ -78,7 +78,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
} }
} }
watch((nuxtApp.$state as ReturnType<typeof useGlobal>).token, (newToken, oldToken) => { watch(state.token, (newToken, oldToken) => {
if (newToken && newToken !== oldToken) init(newToken) if (newToken && newToken !== oldToken) init(newToken)
else if (!newToken) socket.disconnect() else if (!newToken) socket.disconnect()
}) })

Loading…
Cancel
Save