Browse Source

Merge branch 'develop' into qr-scanner-integration-3-2022-12-20

pull/4898/head
Daniel Spaude 2 years ago
parent
commit
33ab171d4a
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  1. 2
      packages/nc-gui/components.d.ts
  2. 4
      packages/nc-gui/components/account/License.vue
  3. 13
      packages/nc-gui/components/cell/Decimal.vue
  4. 13
      packages/nc-gui/components/cell/Float.vue
  5. 13
      packages/nc-gui/components/cell/Integer.vue
  6. 52
      packages/nc-gui/components/dashboard/TreeView.vue
  7. 67
      packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue
  8. 6
      packages/nc-gui/components/dlg/AirtableImport.vue
  9. 6
      packages/nc-gui/components/general/ViewIcon.vue
  10. 12
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  11. 33
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  12. 2
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  13. 27
      packages/nc-gui/components/virtual-cell/Lookup.vue
  14. 17
      packages/nc-gui/components/virtual-cell/Rollup.vue
  15. 1
      packages/nc-gui/composables/useGlobal/types.ts
  16. 13
      packages/nc-gui/composables/useSharedView.ts
  17. 4
      packages/nc-gui/composables/useViewData.ts
  18. 6
      packages/nc-gui/lang/ar.json
  19. 6
      packages/nc-gui/lang/bn_IN.json
  20. 6
      packages/nc-gui/lang/da.json
  21. 6
      packages/nc-gui/lang/de.json
  22. 6
      packages/nc-gui/lang/es.json
  23. 6
      packages/nc-gui/lang/fa.json
  24. 6
      packages/nc-gui/lang/fi.json
  25. 6
      packages/nc-gui/lang/fr.json
  26. 6
      packages/nc-gui/lang/he.json
  27. 6
      packages/nc-gui/lang/hi.json
  28. 6
      packages/nc-gui/lang/hr.json
  29. 6
      packages/nc-gui/lang/id.json
  30. 6
      packages/nc-gui/lang/it.json
  31. 6
      packages/nc-gui/lang/ja.json
  32. 6
      packages/nc-gui/lang/ko.json
  33. 6
      packages/nc-gui/lang/lv.json
  34. 6
      packages/nc-gui/lang/nl.json
  35. 6
      packages/nc-gui/lang/no.json
  36. 6
      packages/nc-gui/lang/pl.json
  37. 6
      packages/nc-gui/lang/pt.json
  38. 6
      packages/nc-gui/lang/pt_BR.json
  39. 6
      packages/nc-gui/lang/ru.json
  40. 6
      packages/nc-gui/lang/sl.json
  41. 6
      packages/nc-gui/lang/sv.json
  42. 6
      packages/nc-gui/lang/th.json
  43. 6
      packages/nc-gui/lang/tr.json
  44. 6
      packages/nc-gui/lang/uk.json
  45. 6
      packages/nc-gui/lang/vi.json
  46. 6
      packages/nc-gui/lang/zh-Hans.json
  47. 6
      packages/nc-gui/lang/zh-Hant.json
  48. 2
      packages/nc-gui/layouts/shared-view.vue
  49. 8
      packages/nc-gui/middleware/auth.global.ts
  50. 476
      packages/nc-gui/package-lock.json
  51. 2
      packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
  52. 12
      packages/nc-gui/pages/account/index.vue
  53. 1
      packages/nc-gui/pages/account/index/[page].vue
  54. 72
      packages/nc-gui/pages/index/index/create-external.vue
  55. 3
      packages/nc-gui/pages/index/index/index.vue
  56. 2
      packages/nc-lib-gui/package.json
  57. 6
      packages/noco-docs/content/en/engineering/builds-and-releases.md
  58. 2
      packages/noco-docs/content/en/getting-started/installation.md
  59. 32
      packages/noco-docs/content/en/setup-and-usages/dashboard.md
  60. 93
      packages/noco-docs/content/en/setup-and-usages/meta-management.md
  61. 21
      packages/noco-docs/content/en/setup-and-usages/project-settings.md
  62. 2
      packages/noco-docs/content/en/setup-and-usages/sync-schema.md
  63. 4
      packages/nocodb-sdk/package-lock.json
  64. 2
      packages/nocodb-sdk/package.json
  65. 22
      packages/nocodb/package-lock.json
  66. 4
      packages/nocodb/package.json
  67. 15
      packages/nocodb/src/lib/Noco.ts
  68. 19
      packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts
  69. 19
      packages/nocodb/src/lib/db/sql-client/lib/ee/SqlClientFactoryEE.ts
  70. 156
      packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts
  71. 2
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts
  72. 17
      packages/nocodb/src/lib/meta/api/baseApis.ts
  73. 11
      packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts
  74. 8
      packages/nocodb/src/lib/meta/api/dataApis/dataApis.ts
  75. 2
      packages/nocodb/src/lib/meta/api/filterApis.ts
  76. 3
      packages/nocodb/src/lib/meta/api/orgLicenseApis.ts
  77. 37
      packages/nocodb/src/lib/meta/api/sync/helpers/fetchAT.ts
  78. 20
      packages/nocodb/src/lib/meta/api/sync/helpers/job.ts
  79. 3
      packages/nocodb/src/lib/meta/api/sync/importApis.ts
  80. 6
      packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts
  81. 26
      packages/nocodb/src/lib/meta/api/userApi/initStrategies.ts
  82. 81
      packages/nocodb/src/lib/meta/api/userApi/userApis.ts
  83. 1
      packages/nocodb/src/lib/meta/api/utilApis.ts
  84. 5
      packages/nocodb/src/lib/meta/helpers/extractProps.ts
  85. 6
      packages/nocodb/src/lib/meta/helpers/getHandler.ts
  86. 5
      packages/nocodb/src/lib/meta/helpers/syncMigration.ts
  87. 1
      packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts
  88. 4
      packages/nocodb/src/lib/models/SyncSource.ts

2
packages/nc-gui/components.d.ts vendored

@ -144,6 +144,7 @@ declare module '@vue/runtime-core' {
MdiCloseCircleOutline: typeof import('~icons/mdi/close-circle-outline')['default'] MdiCloseCircleOutline: typeof import('~icons/mdi/close-circle-outline')['default']
MdiCloseThick: typeof import('~icons/mdi/close-thick')['default'] MdiCloseThick: typeof import('~icons/mdi/close-thick')['default']
MdiCodeJson: typeof import('~icons/mdi/code-json')['default'] MdiCodeJson: typeof import('~icons/mdi/code-json')['default']
MdiCodeTags: typeof import('~icons/mdi/code-tags')['default']
MdiCog: typeof import('~icons/mdi/cog')['default'] MdiCog: typeof import('~icons/mdi/cog')['default']
MdiCommentTextOutline: typeof import('~icons/mdi/comment-text-outline')['default'] MdiCommentTextOutline: typeof import('~icons/mdi/comment-text-outline')['default']
MdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] MdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
@ -189,6 +190,7 @@ declare module '@vue/runtime-core' {
MdiHook: typeof import('~icons/mdi/hook')['default'] MdiHook: typeof import('~icons/mdi/hook')['default']
MdiInformation: typeof import('~icons/mdi/information')['default'] MdiInformation: typeof import('~icons/mdi/information')['default']
MdiJson: typeof import('~icons/mdi/json')['default'] MdiJson: typeof import('~icons/mdi/json')['default']
MdiKey: typeof import('~icons/mdi/key')['default']
MdiKeyboard: typeof import('~icons/mdi/keyboard')['default'] MdiKeyboard: typeof import('~icons/mdi/keyboard')['default']
MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
MdiKeyChange: typeof import('~icons/mdi/key-change')['default'] MdiKeyChange: typeof import('~icons/mdi/key-change')['default']

4
packages/nc-gui/components/account/License.vue

@ -35,10 +35,12 @@ loadLicense()
<template> <template>
<div class="h-full overflow-y-scroll scrollbar-thin-dull"> <div class="h-full overflow-y-scroll scrollbar-thin-dull">
<div class="text-xl mt-4 mb-8 text-center font-weight-bold">License</div> <div class="text-xl mt-4 mb-8 text-center font-weight-bold">License</div>
<div class="mx-auto w-150">
<div> <div>
<a-textarea v-model:value="key" placeholder="License key" class="!mt-2 !max-w-[600px]"></a-textarea> <a-textarea v-model:value="key" placeholder="License key" class="!mt-2 !max-w-[600px]"></a-textarea>
</div> </div>
<a-button class="mt-4" @click="setLicense" type="primary">Save license key</a-button> <div class="text-center"> <a-button class="mt-4" @click="setLicense" type="primary">Save license key</a-button></div>
</div>
</div> </div>
</template> </template>

13
packages/nc-gui/components/cell/Decimal.vue

@ -16,7 +16,18 @@ const emits = defineEmits<Emits>()
const editEnabled = inject(EditModeInj) const editEnabled = inject(EditModeInj)
const vModel = useVModel(props, 'modelValue', emits) const _vModel = useVModel(props, 'modelValue', emits)
const vModel = computed({
get: () => _vModel.value,
set: (value: string) => {
if (value === '') {
_vModel.value = null
} else {
_vModel.value = value
}
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus() const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
</script> </script>

13
packages/nc-gui/components/cell/Float.vue

@ -16,7 +16,18 @@ const emits = defineEmits<Emits>()
const editEnabled = inject(EditModeInj) const editEnabled = inject(EditModeInj)
const vModel = useVModel(props, 'modelValue', emits) const _vModel = useVModel(props, 'modelValue', emits)
const vModel = computed({
get: () => _vModel.value,
set: (value: string) => {
if (value === '') {
_vModel.value = null
} else {
_vModel.value = value
}
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus() const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
</script> </script>

13
packages/nc-gui/components/cell/Integer.vue

@ -16,7 +16,18 @@ const emits = defineEmits<Emits>()
const editEnabled = inject(EditModeInj) const editEnabled = inject(EditModeInj)
const vModel = useVModel(props, 'modelValue', emits) const _vModel = useVModel(props, 'modelValue', emits)
const vModel = computed({
get: () => _vModel.value,
set: (value: string) => {
if (value === '') {
_vModel.value = null
} else {
_vModel.value = value
}
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus() const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()

52
packages/nc-gui/components/dashboard/TreeView.vue

@ -18,6 +18,7 @@ import {
ref, ref,
resolveComponent, resolveComponent,
useDialog, useDialog,
useGlobal,
useNuxtApp, useNuxtApp,
useProject, useProject,
useRoute, useRoute,
@ -48,6 +49,8 @@ const route = useRoute()
const [searchActive, toggleSearchActive] = useToggle() const [searchActive, toggleSearchActive] = useToggle()
const { appInfo } = useGlobal()
const toggleDialog = inject(ToggleDialogInj, () => {}) const toggleDialog = inject(ToggleDialogInj, () => {})
const keys = $ref<Record<string, number>>({}) const keys = $ref<Record<string, number>>({})
@ -416,7 +419,11 @@ const setIcon = async (icon: string, table: TableType) => {
MSSQL MSSQL
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"> <a-menu-item
v-if="appInfo.ee"
key="connect-new-source"
@click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"
>
<div class="color-transition nc-project-menu-item group"> <div class="color-transition nc-project-menu-item group">
<LogosSnowflakeIcon class="group-hover:text-accent" /> <LogosSnowflakeIcon class="group-hover:text-accent" />
Snowflake Snowflake
@ -532,7 +539,11 @@ const setIcon = async (icon: string, table: TableType) => {
MSSQL MSSQL
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"> <a-menu-item
v-if="appInfo.ee"
key="connect-new-source"
@click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"
>
<div class="color-transition nc-project-menu-item group"> <div class="color-transition nc-project-menu-item group">
<LogosSnowflakeIcon class="group-hover:text-accent" /> <LogosSnowflakeIcon class="group-hover:text-accent" />
Snowflake Snowflake
@ -887,18 +898,37 @@ const setIcon = async (icon: string, table: TableType) => {
<template #title>{{ table.table_name }}</template> <template #title>{{ table.table_name }}</template>
<div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)"> <div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)">
<div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`"> <div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`">
<MdiDragVertical <component
v-if="isUIAllowed('treeview-drag-n-drop')" :is="isUIAllowed('tableIconCustomisation') ? Dropdown : 'div'"
:class="`nc-child-draggable-icon-${table.title}`" trigger="click"
class="nc-drag-icon text-xs hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 cursor-move" destroy-popup-on-hide
@click.stop.prevent class="flex items-center"
/> @click.stop
>
<div class="flex items-center" @click.stop>
<component :is="isUIAllowed('tableIconCustomisation') ? Tooltip : 'div'">
<span v-if="table.meta?.icon" :key="table.meta?.icon" class="nc-table-icon flex items-center">
<Icon
:key="table.meta?.icon"
:data-testid="`nc-icon-${table.meta?.icon}`"
class="text-xl"
:icon="table.meta?.icon"
></Icon>
</span>
<component <component
:is="icon(table)" :is="icon(table)"
class="nc-view-icon text-xs" v-else
:class="{ 'group-hover:hidden group-hover:text-gray-500': isUIAllowed('treeview-drag-n-drop') }" class="nc-table-icon nc-view-icon w-5"
:class="{ 'group-hover:text-gray-500': isUIAllowed('treeview-drag-n-drop') }"
/> />
<template v-if="isUIAllowed('tableIconCustomisation')" #title>Change icon</template>
</component>
</div>
<template v-if="isUIAllowed('tableIconCustomisation')" #overlay>
<GeneralEmojiIcons class="shadow bg-white p-2" @select-icon="setIcon($event, table)" />
</template>
</component>
</div> </div>
<div class="nc-tbl-title flex-1"> <div class="nc-tbl-title flex-1">

67
packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue

@ -8,7 +8,7 @@ import {
DefaultConnection, DefaultConnection,
SQLiteConnection, SQLiteConnection,
SSLUsage, SSLUsage,
clientTypes, clientTypes as _clientTypes,
computed, computed,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
fieldRequiredValidator, fieldRequiredValidator,
@ -21,6 +21,7 @@ import {
readFile, readFile,
ref, ref,
useApi, useApi,
useGlobal,
useI18n, useI18n,
useNuxtApp, useNuxtApp,
watch, watch,
@ -30,6 +31,8 @@ const { connectionType } = defineProps<{ connectionType: ClientType }>()
const emit = defineEmits(['baseCreated']) const emit = defineEmits(['baseCreated'])
const { appInfo } = useGlobal()
const { project, loadProject } = useProject() const { project, loadProject } = useProject()
const useForm = Form.useForm const useForm = Form.useForm
@ -68,23 +71,29 @@ const customFormState = ref<ProjectCreateForm>({
extraParameters: [], extraParameters: [],
}) })
const clientTypes = computed(() => {
return _clientTypes.filter((type) => {
return appInfo.value?.ee || type.value !== ClientType.SNOWFLAKE
})
})
const validators = computed(() => { const validators = computed(() => {
return { let clientValidations: Record<string, any[]> = {
'title': [ 'dataSource.connection.host': [fieldRequiredValidator()],
{ 'dataSource.connection.port': [fieldRequiredValidator()],
required: true, 'dataSource.connection.user': [fieldRequiredValidator()],
message: 'Base name is required', 'dataSource.connection.password': [fieldRequiredValidator()],
}, 'dataSource.connection.database': [fieldRequiredValidator()],
projectTitleValidator, }
],
'extraParameters': [extraParameterValidator], switch (formState.dataSource.client) {
'dataSource.client': [fieldRequiredValidator()], case ClientType.SQLITE:
...(formState.dataSource.client === ClientType.SQLITE clientValidations = {
? {
'dataSource.connection.connection.filename': [fieldRequiredValidator()], 'dataSource.connection.connection.filename': [fieldRequiredValidator()],
} }
: formState.dataSource.client === ClientType.SNOWFLAKE break
? { case ClientType.SNOWFLAKE:
clientValidations = {
'dataSource.connection.account': [fieldRequiredValidator()], 'dataSource.connection.account': [fieldRequiredValidator()],
'dataSource.connection.username': [fieldRequiredValidator()], 'dataSource.connection.username': [fieldRequiredValidator()],
'dataSource.connection.password': [fieldRequiredValidator()], 'dataSource.connection.password': [fieldRequiredValidator()],
@ -92,18 +101,24 @@ const validators = computed(() => {
'dataSource.connection.database': [fieldRequiredValidator()], 'dataSource.connection.database': [fieldRequiredValidator()],
'dataSource.connection.schema': [fieldRequiredValidator()], 'dataSource.connection.schema': [fieldRequiredValidator()],
} }
: { break
'dataSource.connection.host': [fieldRequiredValidator()], case ClientType.PG:
'dataSource.connection.port': [fieldRequiredValidator()], case ClientType.MSSQL:
'dataSource.connection.user': [fieldRequiredValidator()], clientValidations['dataSource.searchPath.0'] = [fieldRequiredValidator()]
'dataSource.connection.password': [fieldRequiredValidator()], break
'dataSource.connection.database': [fieldRequiredValidator()],
...([ClientType.PG, ClientType.MSSQL].includes(formState.dataSource.client)
? {
'dataSource.searchPath.0': [fieldRequiredValidator()],
} }
: {}),
}), return {
'title': [
{
required: true,
message: 'Base name is required',
},
projectTitleValidator,
],
'extraParameters': [extraParameterValidator],
'dataSource.client': [fieldRequiredValidator()],
...clientValidations,
} }
}) })

6
packages/nc-gui/components/dlg/AirtableImport.vue

@ -45,8 +45,6 @@ const enableAbort = ref(false)
let socket: Socket | null let socket: Socket | null
let socketInterval: NodeJS.Timer
const syncSource = ref({ const syncSource = ref({
id: '', id: '',
type: 'Airtable', type: 'Airtable',
@ -275,10 +273,10 @@ onMounted(async () => {
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (socket) { if (socket) {
socket.removeAllListeners() socket.off('disconnect')
socket.disconnect() socket.disconnect()
socket.removeAllListeners()
} }
clearInterval(socketInterval)
}) })
</script> </script>

6
packages/nc-gui/components/general/ViewIcon.vue

@ -1,11 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon as IcIcon } from '@iconify/vue' import { Icon as IcIcon } from '@iconify/vue'
import type { TableType } from 'nocodb-sdk' import type { TableType } from 'nocodb-sdk'
import { viewIcons } from '#imports' import { toRef, viewIcons } from '#imports'
const { meta: viewMeta } = defineProps<{ const props = defineProps<{
meta: TableType meta: TableType
}>() }>()
const viewMeta = toRef(props, 'meta')
</script> </script>
<template> <template>

12
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -168,9 +168,9 @@ function onStopEdit() {
<div v-e="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2" data-testid="view-item"> <div v-e="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2" data-testid="view-item">
<div class="flex w-auto min-w-5" :data-testid="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`"> <div class="flex w-auto min-w-5" :data-testid="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`">
<a-dropdown :trigger="['click']" @click.stop> <a-dropdown :trigger="['click']" @click.stop>
<component :is="isUIAllowed('tableIconCustomisation') ? Tooltip : 'div'"> <component :is="isUIAllowed('viewIconCustomisation') ? Tooltip : 'div'">
<GeneralViewIcon :meta="props.view" class="nc-view-icon"></GeneralViewIcon> <GeneralViewIcon :meta="props.view" class="nc-view-icon"></GeneralViewIcon>
<template v-if="isUIAllowed('tableIconCustomisation')" #title>Change icon</template> <template v-if="isUIAllowed('viewIconCustomisation')" #title>Change icon</template>
</component> </component>
<template v-if="isUIAllowed('viewIconCustomisation')" #overlay> <template v-if="isUIAllowed('viewIconCustomisation')" #overlay>
@ -179,7 +179,13 @@ function onStopEdit() {
</a-dropdown> </a-dropdown>
</div> </div>
<a-input v-if="isEditing" :ref="focusInput" v-model:value="vModel.title" @blur="onCancel" @keydown="onKeyDown($event)" /> <a-input
v-if="isEditing"
:ref="focusInput"
v-model:value="vModel.title"
@blur="onCancel"
@keydown.stop="onKeyDown($event)"
/>
<div v-else> <div v-else>
<LazyGeneralTruncateText>{{ vModel.alias || vModel.title }}</LazyGeneralTruncateText> <LazyGeneralTruncateText>{{ vModel.alias || vModel.title }}</LazyGeneralTruncateText>

33
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -196,6 +196,26 @@ watch(passwordProtected, (value) => {
const { locale } = useI18n() const { locale } = useI18n()
const isRtl = computed(() => isRtlLang(locale.value as any)) const isRtl = computed(() => isRtlLang(locale.value as any))
const iframeCode = computed(() => {
if (!sharedViewUrl.value) return
return `<iframe class="nc-embed"
src="${sharedViewUrl.value}?embed"
frameborder="0"
width="100%"
height="700"
style="background: transparent; border: 1px solid #ddd"/>`
})
const copyIframeCode = async () => {
if (iframeCode.value) {
await copy(iframeCode.value)
// Copied to clipboard
message.success(t('msg.info.copiedToClipboard'))
}
}
</script> </script>
<template> <template>
@ -228,15 +248,22 @@ const isRtl = computed(() => isRtlLang(locale.value as any))
data-testid="nc-modal-share-view__link" data-testid="nc-modal-share-view__link"
class="share-link-box !bg-primary !bg-opacity-5 ring-1 ring-accent ring-opacity-100" class="share-link-box !bg-primary !bg-opacity-5 ring-1 ring-accent ring-opacity-100"
> >
<div class="flex-1 h-min text-xs">{{ sharedViewUrl }}</div> <div class="flex-1 h-min text-xs text-gray-500">{{ sharedViewUrl }}</div>
<a v-e="['c:view:share:open-url']" :href="sharedViewUrl" target="_blank"> <a v-e="['c:view:share:open-url']" :href="sharedViewUrl" target="_blank">
<MdiOpenInNew class="text-sm text-gray-500 mt-2" /> <MdiOpenInNew class="text-sm text-gray-500" />
</a> </a>
<MdiContentCopy v-e="['c:view:share:copy-url']" class="text-gray-500 text-sm cursor-pointer" @click="copyLink" /> <MdiContentCopy v-e="['c:view:share:copy-url']" class="text-gray-500 text-sm cursor-pointer" @click="copyLink" />
</div> </div>
<div
class="flex gap-1 items-center pb-1 text-gray-500 cursor-pointer font-weight-medium mb-2 mt-4 pl-1"
@click="copyIframeCode"
>
<MdiCodeTags class="text-gray-500" /> Embed this view in your site
</div>
<div class="px-1 mt-2 flex flex-col gap-3"> <div class="px-1 mt-2 flex flex-col gap-3">
<!-- todo: i18n --> <!-- todo: i18n -->
<div class="text-gray-500 border-b-1">Options</div> <div class="text-gray-500 border-b-1">Options</div>
@ -352,7 +379,7 @@ const isRtl = computed(() => isRtlLang(locale.value as any))
<style scoped> <style scoped>
.share-link-box { .share-link-box {
@apply flex p-2 w-full items-center items-center gap-1 bg-gray-100 rounded; @apply flex p-2 w-full items-center items-center gap-2 bg-gray-100 rounded;
} }
:deep(.ant-collapse-header) { :deep(.ant-collapse-header) {

2
packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue

@ -95,7 +95,7 @@ useMenuCloseOnEsc(open)
<GeneralViewIcon :meta="selectedView"></GeneralViewIcon> <GeneralViewIcon :meta="selectedView"></GeneralViewIcon>
<span class="!text-sm font-weight-normal"> <span class="!text-sm font-weight-normal">
<GeneralTruncateText>{{ selectedView?.title }}</GeneralTruncateText> <GeneralTruncateText :key="selectedView?.title">{{ selectedView?.title }}</GeneralTruncateText>
</span> </span>
<component :is="Icon" class="text-gray-500" :class="`nc-icon-${selectedView?.lock_type}`" /> <component :is="Icon" class="text-gray-500" :class="`nc-icon-${selectedView?.lock_type}`" />

27
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -12,8 +12,8 @@ import {
isAttachment, isAttachment,
provide, provide,
ref, ref,
refAutoReset,
useMetas, useMetas,
useShowNotEditableWarning,
watch, watch,
} from '#imports' } from '#imports'
@ -75,26 +75,13 @@ provide(MetaInj, lookupTableMeta)
provide(CellUrlDisableOverlayInj, ref(true)) provide(CellUrlDisableOverlayInj, ref(true))
const timeout = 3000 // in ms const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activateShowEditNonEditableFieldWarning } =
useShowNotEditableWarning()
const showEditWarning = refAutoReset(false, timeout)
const showClearWarning = refAutoReset(false, timeout)
useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEvent) => {
switch (e.key) {
case 'Enter':
showEditWarning.value = true
break
default:
showClearWarning.value = true
}
})
</script> </script>
<template> <template>
<div class="h-full"> <div class="h-full" @dblclick="activateShowEditNonEditableFieldWarning">
<div class="h-full flex gap-1 overflow-x-auto p-1" @dblclick="showEditWarning = true"> <div class="h-full flex gap-1 overflow-x-auto p-1">
<template v-if="lookupColumn"> <template v-if="lookupColumn">
<!-- Render virtual cell --> <!-- Render virtual cell -->
<div v-if="isVirtualCol(lookupColumn)"> <div v-if="isVirtualCol(lookupColumn)">
@ -133,10 +120,10 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
</template> </template>
</div> </div>
<div> <div>
<div v-if="showEditWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> <div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.info.computedFieldEditWarning') }} {{ $t('msg.info.computedFieldEditWarning') }}
</div> </div>
<div v-if="showClearWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> <div v-if="showClearNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.info.computedFieldDeleteWarning') }} {{ $t('msg.info.computedFieldDeleteWarning') }}
</div> </div>
</div> </div>

17
packages/nc-gui/components/virtual-cell/Rollup.vue

@ -1,28 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { CellValueInj, inject, refAutoReset } from '#imports' import { CellValueInj, inject, useShowNotEditableWarning } from '#imports'
const value = inject(CellValueInj) const value = inject(CellValueInj)
const timeout = 3000 // in ms const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activateShowEditNonEditableFieldWarning } =
useShowNotEditableWarning()
const showEditWarning = refAutoReset(false, timeout)
const showClearWarning = refAutoReset(false, timeout)
useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), () => (showClearWarning.value = true))
</script> </script>
<template> <template>
<div> <div @dblclick="activateShowEditNonEditableFieldWarning">
<span class="text-center pl-3"> <span class="text-center pl-3">
{{ value }} {{ value }}
</span> </span>
<div> <div>
<div v-if="showEditWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> <div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.info.computedFieldEditWarning') }} {{ $t('msg.info.computedFieldEditWarning') }}
</div> </div>
<div v-if="showClearWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> <div v-if="showClearNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.info.computedFieldDeleteWarning') }} {{ $t('msg.info.computedFieldDeleteWarning') }}
</div> </div>
</div> </div>

1
packages/nc-gui/composables/useGlobal/types.ts

@ -25,6 +25,7 @@ export interface AppInfo {
teleEnabled: boolean teleEnabled: boolean
type: string type: string
version: string version: string
ee?: boolean
} }
export interface StoredState { export interface StoredState {

13
packages/nc-gui/composables/useSharedView.ts

@ -17,7 +17,7 @@ export function useSharedView() {
const { appInfo } = $(useGlobal()) const { appInfo } = $(useGlobal())
const { loadProject } = useProject() const { project } = useProject()
const appInfoDefaultLimit = appInfo.defaultLimit || 25 const appInfoDefaultLimit = appInfo.defaultLimit || 25
@ -77,7 +77,16 @@ export function useSharedView() {
await setMeta(viewMeta.model) await setMeta(viewMeta.model)
await loadProject(true, viewMeta.project_id) // if project is not defined then set it with an object containing base
if (!project.value?.bases)
project.value = {
bases: [
{
id: viewMeta.base_id,
type: viewMeta.client,
},
],
}
const relatedMetas = { ...viewMeta.relatedMetas } const relatedMetas = { ...viewMeta.relatedMetas }
Object.keys(relatedMetas).forEach((key) => setMeta(relatedMetas[key])) Object.keys(relatedMetas).forEach((key) => setMeta(relatedMetas[key]))

4
packages/nc-gui/composables/useViewData.ts

@ -264,7 +264,7 @@ export function useViewData(
project?.value.id as string, project?.value.id as string,
metaValue?.id as string, metaValue?.id as string,
viewMetaValue?.id as string, viewMetaValue?.id as string,
id, encodeURIComponent(id),
{ {
// if value is undefined treat it as null // if value is undefined treat it as null
[property]: toUpdate.row[property] ?? null, [property]: toUpdate.row[property] ?? null,
@ -275,7 +275,7 @@ export function useViewData(
// } // }
) )
// audit // audit
$api.utils.auditRowUpdate(id, { $api.utils.auditRowUpdate(encodeURIComponent(id), {
fk_model_id: metaValue?.id as string, fk_model_id: metaValue?.id as string,
column_name: property, column_name: property,
row_id: id, row_id: id,

6
packages/nc-gui/lang/ar.json

@ -248,7 +248,10 @@
"sqlOutput": "إخراج SQL", "sqlOutput": "إخراج SQL",
"addOption": "إضافة خيار", "addOption": "إضافة خيار",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "وظيفة التجميع", "aggregateFunction": "وظيفة التجميع",
"dbCreateIfNotExists": "قاعدة البيانات: إنشاء إذا لم يكن موجودا", "dbCreateIfNotExists": "قاعدة البيانات: إنشاء إذا لم يكن موجودا",
"clientKey": "مفتاح العميل", "clientKey": "مفتاح العميل",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/bn_IN.json

@ -248,7 +248,10 @@
"sqlOutput": "এসকিউএল আউটপট", "sqlOutput": "এসকিউএল আউটপট",
"addOption": "বিকলপ যগ করন", "addOption": "বিকলপ যগ করন",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "সমগিক ফশন", "aggregateFunction": "সমগিক ফশন",
"dbCreateIfNotExists": "ডস: উপসিত নকলি করন", "dbCreateIfNotExists": "ডস: উপসিত নকলি করন",
"clientKey": "কট ক", "clientKey": "কট ক",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/da.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL Output.", "sqlOutput": "SQL Output.",
"addOption": "Tilføj option", "addOption": "Tilføj option",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Aggregate Function.", "aggregateFunction": "Aggregate Function.",
"dbCreateIfNotExists": "DATABASE: Opret, hvis ikke eksisterer", "dbCreateIfNotExists": "DATABASE: Opret, hvis ikke eksisterer",
"clientKey": "Klientnøgle", "clientKey": "Klientnøgle",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/de.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL-Ausgabe", "sqlOutput": "SQL-Ausgabe",
"addOption": "Option hinzufügen", "addOption": "Option hinzufügen",
"qrCodeValueColumn": "Spalte mit QR-Code", "qrCodeValueColumn": "Spalte mit QR-Code",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Zu viele Zeichen für einen QR-Code", "qrCodeValueTooLong": "Zu viele Zeichen für einen QR-Code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Globale Funktion", "aggregateFunction": "Globale Funktion",
"dbCreateIfNotExists": "Datenbank: Erstellen, falls nicht vorhanden", "dbCreateIfNotExists": "Datenbank: Erstellen, falls nicht vorhanden",
"clientKey": "Client-Schlüssel", "clientKey": "Client-Schlüssel",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed.", "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed.",

6
packages/nc-gui/lang/es.json

@ -248,7 +248,10 @@
"sqlOutput": "Salida SQL", "sqlOutput": "Salida SQL",
"addOption": "Añadir opción", "addOption": "Añadir opción",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Función agregada", "aggregateFunction": "Función agregada",
"dbCreateIfNotExists": "Base de datos : Crear si no existe", "dbCreateIfNotExists": "Base de datos : Crear si no existe",
"clientKey": "Clave de Cliente", "clientKey": "Clave de Cliente",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/fa.json

@ -248,7 +248,10 @@
"sqlOutput": "خروجی SQL", "sqlOutput": "خروجی SQL",
"addOption": "افزودن گزینه", "addOption": "افزودن گزینه",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "تابع جمع", "aggregateFunction": "تابع جمع",
"dbCreateIfNotExists": "پایگاه داده: ایجاد در صورت عدم وجود", "dbCreateIfNotExists": "پایگاه داده: ایجاد در صورت عدم وجود",
"clientKey": "کلید Client", "clientKey": "کلید Client",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/fi.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL-lähtö", "sqlOutput": "SQL-lähtö",
"addOption": "Lisää vaihtoehto", "addOption": "Lisää vaihtoehto",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Kokonaistoiminto", "aggregateFunction": "Kokonaistoiminto",
"dbCreateIfNotExists": "Tietokanta: Luo jos ei ole olemassa", "dbCreateIfNotExists": "Tietokanta: Luo jos ei ole olemassa",
"clientKey": "Asiakasnäppäin", "clientKey": "Asiakasnäppäin",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/fr.json

@ -248,7 +248,10 @@
"sqlOutput": "Sortie SQL", "sqlOutput": "Sortie SQL",
"addOption": "Ajouter une option", "addOption": "Ajouter une option",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Trop de caractères pour un code QR", "qrCodeValueTooLong": "Trop de caractères pour un code QR",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Fonction agrégée", "aggregateFunction": "Fonction agrégée",
"dbCreateIfNotExists": "Base de données : la créer si elle n'existe pas", "dbCreateIfNotExists": "Base de données : la créer si elle n'existe pas",
"clientKey": "Clé client", "clientKey": "Clé client",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/he.json

@ -248,7 +248,10 @@
"sqlOutput": "פלט SQL", "sqlOutput": "פלט SQL",
"addOption": "הוסף אפשרות", "addOption": "הוסף אפשרות",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "פונקציה מצטברת", "aggregateFunction": "פונקציה מצטברת",
"dbCreateIfNotExists": "מסד נתונים: צור אם לא קיים", "dbCreateIfNotExists": "מסד נתונים: צור אם לא קיים",
"clientKey": "מפתח הלקוח", "clientKey": "מפתח הלקוח",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/hi.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL आउटपट", "sqlOutput": "SQL आउटपट",
"addOption": "विकलप ज", "addOption": "विकलप ज",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "कल समह", "aggregateFunction": "कल समह",
"dbCreateIfNotExists": "डस: बन यदिद नह", "dbCreateIfNotExists": "डस: बन यदिद नह",
"clientKey": "गहक क", "clientKey": "गहक क",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/hr.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL izlaz", "sqlOutput": "SQL izlaz",
"addOption": "Dodajte opciju", "addOption": "Dodajte opciju",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Agregatna funkcija", "aggregateFunction": "Agregatna funkcija",
"dbCreateIfNotExists": "Baza podataka: stvoriti ako ne postoji", "dbCreateIfNotExists": "Baza podataka: stvoriti ako ne postoji",
"clientKey": "Ključ klijenta", "clientKey": "Ključ klijenta",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/id.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL Output", "sqlOutput": "SQL Output",
"addOption": "Tambahkan opsi", "addOption": "Tambahkan opsi",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Fungsi agregat.", "aggregateFunction": "Fungsi agregat.",
"dbCreateIfNotExists": "Basis Data: Buat jika tidak ada", "dbCreateIfNotExists": "Basis Data: Buat jika tidak ada",
"clientKey": "Kunci klien", "clientKey": "Kunci klien",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/it.json

@ -248,7 +248,10 @@
"sqlOutput": "Output SQL", "sqlOutput": "Output SQL",
"addOption": "Aggiungi opzione", "addOption": "Aggiungi opzione",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Aggrega funzione", "aggregateFunction": "Aggrega funzione",
"dbCreateIfNotExists": "Database: crea se non esiste", "dbCreateIfNotExists": "Database: crea se non esiste",
"clientKey": "Chiave client", "clientKey": "Chiave client",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/ja.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL出力", "sqlOutput": "SQL出力",
"addOption": "オプションを追加", "addOption": "オプションを追加",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "集約関数", "aggregateFunction": "集約関数",
"dbCreateIfNotExists": "データベース:存在しない場合は作成", "dbCreateIfNotExists": "データベース:存在しない場合は作成",
"clientKey": "クライアントキー", "clientKey": "クライアントキー",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "警告: 計算フィールド - テキストをクリアできません", "computedFieldUnableToClear": "警告: 計算フィールド - テキストをクリアできません",
"qrFieldsCannotBeDirectlyChanged": "警告:QRコードフィールドは直接変更できません。" "qrFieldsCannotBeDirectlyChanged": "警告:QRコードフィールドは直接変更できません。"

6
packages/nc-gui/lang/ko.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL 출력", "sqlOutput": "SQL 출력",
"addOption": "옵션 추가", "addOption": "옵션 추가",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "집합 함수", "aggregateFunction": "집합 함수",
"dbCreateIfNotExists": "데이터베이스 : 존재하지 않는 경우 생성", "dbCreateIfNotExists": "데이터베이스 : 존재하지 않는 경우 생성",
"clientKey": "클라이언트 키", "clientKey": "클라이언트 키",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/lv.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL izvade", "sqlOutput": "SQL izvade",
"addOption": "Pievienot iespēju", "addOption": "Pievienot iespēju",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Agregācijas funkcija", "aggregateFunction": "Agregācijas funkcija",
"dbCreateIfNotExists": "Datubāze : izveidotm ja neeksistē", "dbCreateIfNotExists": "Datubāze : izveidotm ja neeksistē",
"clientKey": "Klienta atslēga", "clientKey": "Klienta atslēga",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/nl.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL Uitvoer", "sqlOutput": "SQL Uitvoer",
"addOption": "Optie toevoegen", "addOption": "Optie toevoegen",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Geaggregeerde functie", "aggregateFunction": "Geaggregeerde functie",
"dbCreateIfNotExists": "Database: creëer als het niet bestaat", "dbCreateIfNotExists": "Database: creëer als het niet bestaat",
"clientKey": "Client Key", "clientKey": "Client Key",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/no.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL-utgang", "sqlOutput": "SQL-utgang",
"addOption": "Legg til alternativ", "addOption": "Legg til alternativ",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Samlet funksjon", "aggregateFunction": "Samlet funksjon",
"dbCreateIfNotExists": "Database: Opprett hvis ikke eksisterer", "dbCreateIfNotExists": "Database: Opprett hvis ikke eksisterer",
"clientKey": "Klientnøkkel", "clientKey": "Klientnøkkel",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/pl.json

@ -248,7 +248,10 @@
"sqlOutput": "Wyjście SQL.", "sqlOutput": "Wyjście SQL.",
"addOption": "Dodaj opcję.", "addOption": "Dodaj opcję.",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Funkcja kruszywa", "aggregateFunction": "Funkcja kruszywa",
"dbCreateIfNotExists": "Baza danych: Utwórz, jeśli nie istnieje", "dbCreateIfNotExists": "Baza danych: Utwórz, jeśli nie istnieje",
"clientKey": "Klucz klienta", "clientKey": "Klucz klienta",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/pt.json

@ -248,7 +248,10 @@
"sqlOutput": "Saída SQL.", "sqlOutput": "Saída SQL.",
"addOption": "Adicionar opção", "addOption": "Adicionar opção",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Função agregada", "aggregateFunction": "Função agregada",
"dbCreateIfNotExists": "Base de Dados : criar se não existir", "dbCreateIfNotExists": "Base de Dados : criar se não existir",
"clientKey": "Chave do Cliente", "clientKey": "Chave do Cliente",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/pt_BR.json

@ -248,7 +248,10 @@
"sqlOutput": "Saída SQL.", "sqlOutput": "Saída SQL.",
"addOption": "Adicionar opção", "addOption": "Adicionar opção",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Função agregada", "aggregateFunction": "Função agregada",
"dbCreateIfNotExists": "Base de Dados : criar se não existir", "dbCreateIfNotExists": "Base de Dados : criar se não existir",
"clientKey": "Chave do Cliente", "clientKey": "Chave do Cliente",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/ru.json

@ -248,7 +248,10 @@
"sqlOutput": "Вывод SQL", "sqlOutput": "Вывод SQL",
"addOption": "Добавить настройку", "addOption": "Добавить настройку",
"qrCodeValueColumn": "Столбец с QR-кодом", "qrCodeValueColumn": "Столбец с QR-кодом",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Слишком много символов для QR-кода", "qrCodeValueTooLong": "Слишком много символов для QR-кода",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Агрегатная функция", "aggregateFunction": "Агрегатная функция",
"dbCreateIfNotExists": "База данных: создать, если не существует", "dbCreateIfNotExists": "База данных: создать, если не существует",
"clientKey": "Ключ клиента", "clientKey": "Ключ клиента",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Предупреждение: Вычисляемое поле - невозможно очистить текст", "computedFieldUnableToClear": "Предупреждение: Вычисляемое поле - невозможно очистить текст",
"qrFieldsCannotBeDirectlyChanged": "Внимание: QR-поля не могут быть изменены напрямую." "qrFieldsCannotBeDirectlyChanged": "Внимание: QR-поля не могут быть изменены напрямую."

6
packages/nc-gui/lang/sl.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL izhod", "sqlOutput": "SQL izhod",
"addOption": "Dodaj možnost", "addOption": "Dodaj možnost",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Agregatna funkcija", "aggregateFunction": "Agregatna funkcija",
"dbCreateIfNotExists": "Baza podatkov: Ustvari, če ne obstaja", "dbCreateIfNotExists": "Baza podatkov: Ustvari, če ne obstaja",
"clientKey": "Odjemalski ključ", "clientKey": "Odjemalski ključ",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/sv.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL-utgång", "sqlOutput": "SQL-utgång",
"addOption": "Lägg till alternativ", "addOption": "Lägg till alternativ",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Aggregatfunktion", "aggregateFunction": "Aggregatfunktion",
"dbCreateIfNotExists": "Databas: Skapa om det inte finns", "dbCreateIfNotExists": "Databas: Skapa om det inte finns",
"clientKey": "Klientnyckel", "clientKey": "Klientnyckel",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/th.json

@ -248,7 +248,10 @@
"sqlOutput": "เอาตต SQL", "sqlOutput": "เอาตต SQL",
"addOption": "เพมตวเลอก", "addOption": "เพมตวเลอก",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "ฟงกนรวม", "aggregateFunction": "ฟงกนรวม",
"dbCreateIfNotExists": "ฐานขอมล: สรางถาไมอย", "dbCreateIfNotExists": "ฐานขอมล: สรางถาไมอย",
"clientKey": "รหสลกคา", "clientKey": "รหสลกคา",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/tr.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL Çıktısı", "sqlOutput": "SQL Çıktısı",
"addOption": "Seçenek ekle", "addOption": "Seçenek ekle",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Birleştirme fonksiyonu", "aggregateFunction": "Birleştirme fonksiyonu",
"dbCreateIfNotExists": "Veritabanı : yoksa oluştur", "dbCreateIfNotExists": "Veritabanı : yoksa oluştur",
"clientKey": "İstemci Anahtarı", "clientKey": "İstemci Anahtarı",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/uk.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL Output", "sqlOutput": "SQL Output",
"addOption": "Додайте опцію", "addOption": "Додайте опцію",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Сукупна функція", "aggregateFunction": "Сукупна функція",
"dbCreateIfNotExists": "База даних: створити, якщо не існує", "dbCreateIfNotExists": "База даних: створити, якщо не існує",
"clientKey": "Клієнтський ключ", "clientKey": "Клієнтський ключ",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/vi.json

@ -248,7 +248,10 @@
"sqlOutput": "Đầu ra SQL.", "sqlOutput": "Đầu ra SQL.",
"addOption": "Thêm tùy chọn", "addOption": "Thêm tùy chọn",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "Chức năng tổng hợp", "aggregateFunction": "Chức năng tổng hợp",
"dbCreateIfNotExists": "Cơ sở dữ liệu: Tạo nếu không tồn tại", "dbCreateIfNotExists": "Cơ sở dữ liệu: Tạo nếu không tồn tại",
"clientKey": "Khóa khách", "clientKey": "Khóa khách",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/zh-Hans.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL输出", "sqlOutput": "SQL输出",
"addOption": "添加选项", "addOption": "添加选项",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "汇总功能", "aggregateFunction": "汇总功能",
"dbCreateIfNotExists": "数据库 : 如果不存在则创建", "dbCreateIfNotExists": "数据库 : 如果不存在则创建",
"clientKey": "客户端 Key", "clientKey": "客户端 Key",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

6
packages/nc-gui/lang/zh-Hant.json

@ -248,7 +248,10 @@
"sqlOutput": "SQL 輸出", "sqlOutput": "SQL 輸出",
"addOption": "新增選項", "addOption": "新增選項",
"qrCodeValueColumn": "Column with QR code value", "qrCodeValueColumn": "Column with QR code value",
"barcodeValueColumn": "Column with Barcode value",
"barcodeFormat": "Barcode format",
"qrCodeValueTooLong": "Too many characters for a QR code", "qrCodeValueTooLong": "Too many characters for a QR code",
"barcodeValueTooLong": "Too many characters for a barcode",
"aggregateFunction": "匯總功能", "aggregateFunction": "匯總功能",
"dbCreateIfNotExists": "資料庫:不存在則建立", "dbCreateIfNotExists": "資料庫:不存在則建立",
"clientKey": "用戶端金鑰", "clientKey": "用戶端金鑰",
@ -496,6 +499,9 @@
}, },
"msg": { "msg": {
"warning": { "warning": {
"barcode": {
"renderError": "Barcode error - please check compatibility between input and barcode type"
},
"nonEditableFields": { "nonEditableFields": {
"computedFieldUnableToClear": "Warning: Computed field - unable to clear text", "computedFieldUnableToClear": "Warning: Computed field - unable to clear text",
"qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed." "qrFieldsCannotBeDirectlyChanged": "Warning: QR fields cannot be directly changed."

2
packages/nc-gui/layouts/shared-view.vue

@ -59,7 +59,7 @@ export default {
</template> </template>
<div v-else class="text-xl font-semibold truncate text-white nc-shared-view-title flex gap-2 items-center"> <div v-else class="text-xl font-semibold truncate text-white nc-shared-view-title flex gap-2 items-center">
<GeneralViewIcon class="!text-xl" :meta="sharedView" /> <GeneralViewIcon v-if="sharedView" class="!text-xl" :meta="sharedView" />
{{ sharedView?.title }} {{ sharedView?.title }}
</div> </div>
</div> </div>

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

@ -1,6 +1,6 @@
import type { Api } from 'nocodb-sdk' import type { Api } from 'nocodb-sdk'
import type { Actions } from '~/composables/useGlobal/types' import type { Actions } from '~/composables/useGlobal/types'
import { defineNuxtRouteMiddleware, message, navigateTo, useApi, useGlobal, useRoles } from '#imports' import { defineNuxtRouteMiddleware, extractSdkResponseErrorMsg, message, navigateTo, useApi, useGlobal, useRoles } from '#imports'
/** /**
* Global auth middleware * Global auth middleware
@ -98,10 +98,8 @@ async function tryGoogleAuth(api: Api<any>, signIn: Actions['signIn']) {
) )
signIn(token) signIn(token)
} catch (e: any) { } catch (e) {
if (e.response && e.response.data && e.response.data.msg) { message.error({ content: await extractSdkResponseErrorMsg(e) })
message.error({ content: e.response.data.msg })
}
} }
const newURL = window.location.href.split('?')[0] const newURL = window.location.href.split('?')[0]

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

@ -3651,12 +3651,13 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==", "integrity": "sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
@ -3665,30 +3666,33 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==", "integrity": "sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw==",
"dev": true,
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.41"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==", "integrity": "sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/compiler-dom": "3.2.45", "@vue/compiler-dom": "3.2.41",
"@vue/compiler-ssr": "3.2.45", "@vue/compiler-ssr": "3.2.41",
"@vue/reactivity-transform": "3.2.45", "@vue/reactivity-transform": "3.2.41",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"postcss": "^8.1.10", "postcss": "^8.1.10",
@ -3699,6 +3703,7 @@
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"dependencies": { "dependencies": {
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
@ -3707,17 +3712,19 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==", "integrity": "sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ==",
"dev": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.2.45", "@vue/compiler-dom": "3.2.41",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.41"
} }
}, },
"node_modules/@vue/devtools-api": { "node_modules/@vue/devtools-api": {
@ -3734,13 +3741,14 @@
} }
}, },
"node_modules/@vue/reactivity-transform": { "node_modules/@vue/reactivity-transform": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==", "integrity": "sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7" "magic-string": "^0.25.7"
} }
@ -3749,10 +3757,16 @@
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"dependencies": { "dependencies": {
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/@vue/reactivity/node_modules/@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
@ -3762,6 +3776,11 @@
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
} }
}, },
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
@ -3772,6 +3791,11 @@
"csstype": "^2.6.8" "csstype": "^2.6.8"
} }
}, },
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
@ -3784,11 +3808,54 @@
"vue": "3.2.45" "vue": "3.2.45"
} }
}, },
"node_modules/@vue/shared": { "node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"dependencies": {
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"dependencies": {
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==" "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
}, },
"node_modules/@vue/server-renderer/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@vue/shared": {
"version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.41.tgz",
"integrity": "sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw==",
"dev": true
},
"node_modules/@vue/test-utils": { "node_modules/@vue/test-utils": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.2.tgz",
@ -4429,9 +4496,9 @@
} }
}, },
"node_modules/@zxing/library": { "node_modules/@zxing/library": {
"version": "0.18.6", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.18.6.tgz", "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.19.1.tgz",
"integrity": "sha512-bulZ9JHoLFd9W36pi+7e7DnEYNJhljYjZ1UTsKPOoLMU3qtC+REHITeCRNx40zTRJZx18W5TBRXt5pq2Uopjsw==", "integrity": "sha512-rKwvl3Uuqs8yf364iU9l3HDDaIx8yPv+CH6DbtQaQr67VdKLG22G1ukEp9fOdDefE6tpLtRAdMnTrgtpiaKAZw==",
"dependencies": { "dependencies": {
"ts-custom-error": "^3.0.0" "ts-custom-error": "^3.0.0"
}, },
@ -4907,6 +4974,20 @@
"jsqr": "^1.3.1" "jsqr": "^1.3.1"
} }
}, },
"node_modules/barcode-detector/node_modules/@zxing/library": {
"version": "0.18.6",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.18.6.tgz",
"integrity": "sha512-bulZ9JHoLFd9W36pi+7e7DnEYNJhljYjZ1UTsKPOoLMU3qtC+REHITeCRNx40zTRJZx18W5TBRXt5pq2Uopjsw==",
"dependencies": {
"ts-custom-error": "^3.0.0"
},
"engines": {
"node": ">= 10.4.0"
},
"optionalDependencies": {
"@zxing/text-encoding": "~0.9.0"
}
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -12054,7 +12135,7 @@
} }
}, },
"node_modules/nocodb-sdk": { "node_modules/nocodb-sdk": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"resolved": "file:../nocodb-sdk", "resolved": "file:../nocodb-sdk",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
@ -17171,20 +17252,6 @@
"@zxing/library": "^0.19.1" "@zxing/library": "^0.19.1"
} }
}, },
"node_modules/vue-barcode-reader/node_modules/@zxing/library": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.19.1.tgz",
"integrity": "sha512-rKwvl3Uuqs8yf364iU9l3HDDaIx8yPv+CH6DbtQaQr67VdKLG22G1ukEp9fOdDefE6tpLtRAdMnTrgtpiaKAZw==",
"dependencies": {
"ts-custom-error": "^3.0.0"
},
"engines": {
"node": ">= 10.4.0"
},
"optionalDependencies": {
"@zxing/text-encoding": "~0.9.0"
}
},
"node_modules/vue-bundle-renderer": { "node_modules/vue-bundle-renderer": {
"version": "0.4.4", "version": "0.4.4",
"resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-0.4.4.tgz", "resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-0.4.4.tgz",
@ -17324,6 +17391,85 @@
"vue": "^3.0.0" "vue": "^3.0.0"
} }
}, },
"node_modules/vue/node_modules/@vue/compiler-core": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/vue/node_modules/@vue/compiler-dom": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"dependencies": {
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/vue/node_modules/@vue/compiler-sfc": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45",
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-ssr": "3.2.45",
"@vue/reactivity-transform": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
}
},
"node_modules/vue/node_modules/@vue/compiler-ssr": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"dependencies": {
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/vue/node_modules/@vue/reactivity-transform": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"node_modules/vue/node_modules/@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/vue/node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dependencies": {
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/vue/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vuedraggable": { "node_modules/vuedraggable": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
@ -20601,12 +20747,13 @@
} }
}, },
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.41.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==", "integrity": "sha512-oA4mH6SA78DT+96/nsi4p9DX97PHcNROxs51lYk7gb9Z4BPKQ3Mh+BLn6CQZBw857Iuhu28BfMSRHAlPvD4vlw==",
"dev": true,
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
}, },
@ -20614,30 +20761,33 @@
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
} }
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.41.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==", "integrity": "sha512-xe5TbbIsonjENxJsYRbDJvthzqxLNk+tb3d/c47zgREDa/PCp6/Y4gC/skM4H6PIuX5DAxm7fFJdbjjUH2QTMw==",
"dev": true,
"requires": { "requires": {
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.41"
} }
}, },
"@vue/compiler-sfc": { "@vue/compiler-sfc": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.41.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==", "integrity": "sha512-+1P2m5kxOeaxVmJNXnBskAn3BenbTmbxBxWOtBq3mQTCokIreuMULFantBUclP0+KnzNCMOvcnKinqQZmiOF8w==",
"dev": true,
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/compiler-dom": "3.2.45", "@vue/compiler-dom": "3.2.41",
"@vue/compiler-ssr": "3.2.45", "@vue/compiler-ssr": "3.2.41",
"@vue/reactivity-transform": "3.2.45", "@vue/reactivity-transform": "3.2.41",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"postcss": "^8.1.10", "postcss": "^8.1.10",
@ -20648,6 +20798,7 @@
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"requires": { "requires": {
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
@ -20655,17 +20806,19 @@
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
} }
} }
}, },
"@vue/compiler-ssr": { "@vue/compiler-ssr": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.41.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==", "integrity": "sha512-Y5wPiNIiaMz/sps8+DmhaKfDm1xgj6GrH99z4gq2LQenfVQcYXmHIOBcs5qPwl7jaW3SUQWjkAPKMfQemEQZwQ==",
"dev": true,
"requires": { "requires": {
"@vue/compiler-dom": "3.2.45", "@vue/compiler-dom": "3.2.41",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.41"
} }
}, },
"@vue/devtools-api": { "@vue/devtools-api": {
@ -20679,16 +20832,24 @@
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==", "integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
"requires": { "requires": {
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
}
} }
}, },
"@vue/reactivity-transform": { "@vue/reactivity-transform": {
"version": "3.2.45", "version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.41.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==", "integrity": "sha512-mK5+BNMsL4hHi+IR3Ft/ho6Za+L3FA5j8WvreJ7XzHrqkPq8jtF/SMo7tuc9gHjLDwKZX1nP1JQOKo9IEAn54A==",
"dev": true,
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45", "@vue/compiler-core": "3.2.41",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.41",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7" "magic-string": "^0.25.7"
}, },
@ -20697,6 +20858,7 @@
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"requires": { "requires": {
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
@ -20710,6 +20872,13 @@
"requires": { "requires": {
"@vue/reactivity": "3.2.45", "@vue/reactivity": "3.2.45",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
}
} }
}, },
"@vue/runtime-dom": { "@vue/runtime-dom": {
@ -20720,6 +20889,13 @@
"@vue/runtime-core": "3.2.45", "@vue/runtime-core": "3.2.45",
"@vue/shared": "3.2.45", "@vue/shared": "3.2.45",
"csstype": "^2.6.8" "csstype": "^2.6.8"
},
"dependencies": {
"@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
}
} }
}, },
"@vue/server-renderer": { "@vue/server-renderer": {
@ -20729,6 +20905,35 @@
"requires": { "requires": {
"@vue/compiler-ssr": "3.2.45", "@vue/compiler-ssr": "3.2.45",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"requires": {
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/compiler-ssr": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"requires": {
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
} }
}, },
"@vue/shared": { "@vue/shared": {
@ -20736,6 +20941,19 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==" "integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
}, },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"@vue/shared": {
"version": "3.2.41",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.41.tgz",
"integrity": "sha512-W9mfWLHmJhkfAmV+7gDjcHeAWALQtgGT3JErxULl0oz6R6+3ug91I7IErs93eCFhPCZPHBs4QJS7YWEV7A3sxw==",
"dev": true
},
"@vue/test-utils": { "@vue/test-utils": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.2.tgz",
@ -21159,9 +21377,9 @@
} }
}, },
"@zxing/library": { "@zxing/library": {
"version": "0.18.6", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.18.6.tgz", "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.19.1.tgz",
"integrity": "sha512-bulZ9JHoLFd9W36pi+7e7DnEYNJhljYjZ1UTsKPOoLMU3qtC+REHITeCRNx40zTRJZx18W5TBRXt5pq2Uopjsw==", "integrity": "sha512-rKwvl3Uuqs8yf364iU9l3HDDaIx8yPv+CH6DbtQaQr67VdKLG22G1ukEp9fOdDefE6tpLtRAdMnTrgtpiaKAZw==",
"requires": { "requires": {
"@zxing/text-encoding": "~0.9.0", "@zxing/text-encoding": "~0.9.0",
"ts-custom-error": "^3.0.0" "ts-custom-error": "^3.0.0"
@ -21515,6 +21733,17 @@
"requires": { "requires": {
"@zxing/library": "^0.18.4", "@zxing/library": "^0.18.4",
"jsqr": "^1.3.1" "jsqr": "^1.3.1"
},
"dependencies": {
"@zxing/library": {
"version": "0.18.6",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.18.6.tgz",
"integrity": "sha512-bulZ9JHoLFd9W36pi+7e7DnEYNJhljYjZ1UTsKPOoLMU3qtC+REHITeCRNx40zTRJZx18W5TBRXt5pq2Uopjsw==",
"requires": {
"@zxing/text-encoding": "~0.9.0",
"ts-custom-error": "^3.0.0"
}
}
} }
}, },
"base64-js": { "base64-js": {
@ -26682,7 +26911,7 @@
} }
}, },
"nocodb-sdk": { "nocodb-sdk": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"requires": { "requires": {
"axios": "^0.21.1", "axios": "^0.21.1",
"jsep": "^1.3.6" "jsep": "^1.3.6"
@ -30330,6 +30559,84 @@
"@vue/runtime-dom": "3.2.45", "@vue/runtime-dom": "3.2.45",
"@vue/server-renderer": "3.2.45", "@vue/server-renderer": "3.2.45",
"@vue/shared": "3.2.45" "@vue/shared": "3.2.45"
},
"dependencies": {
"@vue/compiler-core": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"requires": {
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/compiler-sfc": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45",
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-ssr": "3.2.45",
"@vue/reactivity-transform": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
}
},
"@vue/compiler-ssr": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"requires": {
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/reactivity-transform": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"@vue/shared": {
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"requires": {
"sourcemap-codec": "^1.4.8"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
} }
}, },
"vue-barcode-reader": { "vue-barcode-reader": {
@ -30338,17 +30645,6 @@
"integrity": "sha512-z4mv7+ai/8vECppBTb00tHnyFMMx6W1rAaQe+v214ihoaWK9iGrn8ZZsmgSxf3lwnrtGaibLdkonTtMrGsO+dA==", "integrity": "sha512-z4mv7+ai/8vECppBTb00tHnyFMMx6W1rAaQe+v214ihoaWK9iGrn8ZZsmgSxf3lwnrtGaibLdkonTtMrGsO+dA==",
"requires": { "requires": {
"@zxing/library": "^0.19.1" "@zxing/library": "^0.19.1"
},
"dependencies": {
"@zxing/library": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.19.1.tgz",
"integrity": "sha512-rKwvl3Uuqs8yf364iU9l3HDDaIx8yPv+CH6DbtQaQr67VdKLG22G1ukEp9fOdDefE6tpLtRAdMnTrgtpiaKAZw==",
"requires": {
"@zxing/text-encoding": "~0.9.0",
"ts-custom-error": "^3.0.0"
}
}
} }
}, },
"vue-bundle-renderer": { "vue-bundle-renderer": {

2
packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue

@ -75,7 +75,7 @@ function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
<span class="flex-1" /> <span class="flex-1" />
<div class="flex justify-center self-center mr-2 min-w-[115px]"> <div class="flex justify-center self-center mr-2 min-w-[115px]">
<div v-show="isLoading" class="flex items-center gap-2 ml-3 text-gray-200" data-testid="nc-loading"> <div v-if="isLoading" class="flex items-center gap-2 ml-3 text-gray-200" data-testid="nc-loading">
{{ $t('general.loading') }} {{ $t('general.loading') }}
<MdiLoading class="animate-infinite animate-spin" /> <MdiLoading class="animate-infinite animate-spin" />

12
packages/nc-gui/pages/account/index.vue

@ -79,6 +79,18 @@ const openKeys = ref([/^\/account\/users/.test($route.fullPath) && 'users'])
<div class="select-none">App Store</div> <div class="select-none">App Store</div>
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item
v-if="isUIAllowed('license')"
key="apps"
class="group active:(!ring-0) hover:(!bg-primary !bg-opacity-25)"
@click="navigateTo('/account/license')"
>
<div class="flex items-center space-x-2">
<MdiKey />
<div class="select-none">License</div>
</div>
</a-menu-item>
</a-menu> </a-menu>
</div> </div>
</a-layout-sider> </a-layout-sider>

1
packages/nc-gui/pages/account/index/[page].vue

@ -2,5 +2,6 @@
<AccountUserManagement v-if="$route.params.page === 'users'" /> <AccountUserManagement v-if="$route.params.page === 'users'" />
<AccountToken v-else-if="$route.params.page === 'tokens'" /> <AccountToken v-else-if="$route.params.page === 'tokens'" />
<AccountAppStore v-else-if="$route.params.page === 'apps'" /> <AccountAppStore v-else-if="$route.params.page === 'apps'" />
<AccountLicense v-else-if="$route.params.page === 'license'" />
<span v-else></span> <span v-else></span>
</template> </template>

72
packages/nc-gui/pages/index/index/create-external.vue

@ -7,7 +7,7 @@ import {
Form, Form,
Modal, Modal,
SSLUsage, SSLUsage,
clientTypes, clientTypes as _clientTypes,
computed, computed,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
fieldRequiredValidator, fieldRequiredValidator,
@ -28,6 +28,8 @@ import {
watch, watch,
} from '#imports' } from '#imports'
const { appInfo } = useGlobal()
const useForm = Form.useForm const useForm = Form.useForm
const testSuccess = ref(false) const testSuccess = ref(false)
@ -64,23 +66,29 @@ const customFormState = ref<ProjectCreateForm>({
extraParameters: [], extraParameters: [],
}) })
const clientTypes = computed(() => {
return _clientTypes.filter((type) => {
return appInfo.value?.ee || type.value !== ClientType.SNOWFLAKE
})
})
const validators = computed(() => { const validators = computed(() => {
return { let clientValidations: Record<string, any[]> = {
'title': [ 'dataSource.connection.host': [fieldRequiredValidator()],
{ 'dataSource.connection.port': [fieldRequiredValidator()],
required: true, 'dataSource.connection.user': [fieldRequiredValidator()],
message: 'Project name is required', 'dataSource.connection.password': [fieldRequiredValidator()],
}, 'dataSource.connection.database': [fieldRequiredValidator()],
projectTitleValidator, }
],
'extraParameters': [extraParameterValidator], switch (formState.dataSource.client) {
'dataSource.client': [fieldRequiredValidator()], case ClientType.SQLITE:
...(formState.dataSource.client === ClientType.SQLITE clientValidations = {
? {
'dataSource.connection.connection.filename': [fieldRequiredValidator()], 'dataSource.connection.connection.filename': [fieldRequiredValidator()],
} }
: formState.dataSource.client === ClientType.SNOWFLAKE break
? { case ClientType.SNOWFLAKE:
clientValidations = {
'dataSource.connection.account': [fieldRequiredValidator()], 'dataSource.connection.account': [fieldRequiredValidator()],
'dataSource.connection.username': [fieldRequiredValidator()], 'dataSource.connection.username': [fieldRequiredValidator()],
'dataSource.connection.password': [fieldRequiredValidator()], 'dataSource.connection.password': [fieldRequiredValidator()],
@ -88,18 +96,24 @@ const validators = computed(() => {
'dataSource.connection.database': [fieldRequiredValidator()], 'dataSource.connection.database': [fieldRequiredValidator()],
'dataSource.connection.schema': [fieldRequiredValidator()], 'dataSource.connection.schema': [fieldRequiredValidator()],
} }
: { break
'dataSource.connection.host': [fieldRequiredValidator()], case ClientType.PG:
'dataSource.connection.port': [fieldRequiredValidator()], case ClientType.MSSQL:
'dataSource.connection.user': [fieldRequiredValidator()], clientValidations['dataSource.searchPath.0'] = [fieldRequiredValidator()]
'dataSource.connection.password': [fieldRequiredValidator()], break
'dataSource.connection.database': [fieldRequiredValidator()],
...([ClientType.PG, ClientType.MSSQL].includes(formState.dataSource.client)
? {
'dataSource.searchPath.0': [fieldRequiredValidator()],
} }
: {}),
}), return {
'title': [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
],
'extraParameters': [extraParameterValidator],
'dataSource.client': [fieldRequiredValidator()],
...clientValidations,
} }
}) })
@ -484,7 +498,7 @@ onMounted(async () => {
</template> </template>
<a-form-item label="SSL mode"> <a-form-item label="SSL mode">
<a-select v-model:value="formState.sslUse" dropdown-class-name="nc-dropdown-ssl-mode" @select="onSSLModeChange"> <a-select v-model:value="formState.sslUse" dropdown-class-name="nc-dropdown-ssl-mode" @select="onSSLModeChange">
<a-select-option v-for="opt in Object.values(SSLUsage)" :key="opt" :value="opt">{{ opt }}</a-select-option> <a-select-option v-for="opt in Object.values(SSLUsage)" :key="opt" :value="opt">{{ opt }} </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -547,7 +561,9 @@ onMounted(async () => {
</div> </div>
</div> </div>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewParam"> <a-button type="dashed" class="w-full caption mt-2" @click="addNewParam">
<div class="flex items-center justify-center"><MdiPlus /></div> <div class="flex items-center justify-center">
<MdiPlus />
</div>
</a-button> </a-button>
</a-card> </a-card>
</a-form-item> </a-form-item>

3
packages/nc-gui/pages/index/index/index.vue

@ -17,7 +17,6 @@ import {
useApi, useApi,
useBreakpoints, useBreakpoints,
useCopy, useCopy,
useGlobal,
useNuxtApp, useNuxtApp,
useUIPermission, useUIPermission,
} from '#imports' } from '#imports'
@ -38,8 +37,6 @@ const filterQuery = ref('')
const projects = ref<ProjectType[]>() const projects = ref<ProjectType[]>()
const { appInfo } = useGlobal()
const loadProjects = async () => { const loadProjects = async () => {
const response = await api.project.list({}) const response = await api.project.list({})
projects.value = response.list projects.value = response.list

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{ {
"name": "nc-lib-gui", "name": "nc-lib-gui",
"version": "0.100.2", "version": "0.101.0-beta.0",
"description": "NocoDB GUI", "description": "NocoDB GUI",
"author": { "author": {
"name": "NocoDB", "name": "NocoDB",

6
packages/noco-docs/content/en/engineering/builds-and-releases.md

@ -34,9 +34,9 @@ Below is an overview of how to make these builds and what happens behind the sce
The naming convention would be following given the actual release tag is `0.100.0` The naming convention would be following given the actual release tag is `0.100.0`
- `0.100.0-beta.1` (first version of pre-release) - `0.100.0-beta.0` (first version of pre-release)
- `0.100.0-beta.2` (include bug fix changes on top of the previous version) - `0.100.0-beta.1` (include bug fix changes on top of the previous version)
- `0.100.0-beta.3`(include bug fix changes on top of the previous version) - `0.100.0-beta.2`(include bug fix changes on top of the previous version)
- and so on ... - and so on ...
- `0.100.0` (actual release) - `0.100.0` (actual release)
- `0.100.1` (minor bug fix release) - `0.100.1` (minor bug fix release)

2
packages/noco-docs/content/en/getting-started/installation.md

@ -465,7 +465,7 @@ It is mandatory to configure `NC_DB` environment variables for production usecas
| NC_PUBLIC_URL | Yes | Used for sending Email invitations | Best guess from http request params | | | NC_PUBLIC_URL | Yes | Used for sending Email invitations | Best guess from http request params | |
| NC_JWT_EXPIRES_IN | No | JWT token expiry time | `10h` | | | NC_JWT_EXPIRES_IN | No | JWT token expiry time | `10h` | |
| NC_CONNECT_TO_EXTERNAL_DB_DISABLED | No | Disable Project creation with external database | | | | NC_CONNECT_TO_EXTERNAL_DB_DISABLED | No | Disable Project creation with external database | | |
| NC_INVITE_ONLY_SIGNUP | No | Allow users to signup only via invite url, value should be any non-empty string. | | | | NC_INVITE_ONLY_SIGNUP | No | <strong>Removed</strong> since version <kbd>0.99.0</kbd> and now it's recommended to use [super admin settings menu](/setup-and-usages/account-settings#enable--disable-signup). <br><br>Allow users to signup only via invite url, value should be any non-empty string. | | |
| NUXT_PUBLIC_NC_BACKEND_URL | No | Custom Backend URL | ``http://localhost:8080`` will be used | | | NUXT_PUBLIC_NC_BACKEND_URL | No | Custom Backend URL | ``http://localhost:8080`` will be used | |
| NC_REQUEST_BODY_SIZE | No | Request body size [limit](https://expressjs.com/en/resources/middleware/body-parser.html#limit) | `1048576` | | | NC_REQUEST_BODY_SIZE | No | Request body size [limit](https://expressjs.com/en/resources/middleware/body-parser.html#limit) | `1048576` | |
| NC_EXPORT_MAX_TIMEOUT | No | After NC_EXPORT_MAX_TIMEOUT csv gets downloaded in batches | Default value 5000(in millisecond) will be used | | | NC_EXPORT_MAX_TIMEOUT | No | After NC_EXPORT_MAX_TIMEOUT csv gets downloaded in batches | Default value 5000(in millisecond) will be used | |

32
packages/noco-docs/content/en/setup-and-usages/dashboard.md

@ -15,38 +15,48 @@ Enter your work email and your password.
<img width="1492" alt="image" src="https://user-images.githubusercontent.com/35857179/194793294-fa027496-c3c3-44eb-a613-2ba3e3bd26c1.png"> <img width="1492" alt="image" src="https://user-images.githubusercontent.com/35857179/194793294-fa027496-c3c3-44eb-a613-2ba3e3bd26c1.png">
<alert id="password-conditions"> <alert id="password-conditions">
Your password has at least 8 letters with one uppercase, one number and one special letter Your password has at least 8 letters. No other constraints on case, numbers or special characters.
</alert> </alert>
## Initialize Your First Project ## Initialize Your First Project
Once you have logged into NocoDB, you should see `My Projects`. Once you have logged into NocoDB, you should see `My Projects`.
<img width="1494" alt="image" src="https://user-images.githubusercontent.com/35857179/194793424-c4451bf5-1486-46cf-b62f-86fc6d788d77.png"> ![Screenshot 2022-12-29 at 2 54 43 PM](https://user-images.githubusercontent.com/86527202/209932699-743ffea2-986f-443f-8198-f56b597de706.png)
<!-- <img width="1494" alt="image" src="https://user-images.githubusercontent.com/35857179/194793424-c4451bf5-1486-46cf-b62f-86fc6d788d77.png"> -->
To create a project, you can click `New Project`. You can choose create an empty project or a project connecting to an external database. To create a new project, you can click `New Project`.
<!-- <img width="1492" alt="image" src="https://user-images.githubusercontent.com/35857179/194793457-e18e1112-2b44-4efc-8d98-5261a83a150c.png"> -->
<img width="1492" alt="image" src="https://user-images.githubusercontent.com/35857179/194793457-e18e1112-2b44-4efc-8d98-5261a83a150c.png"> You need to specify the project name. The data will be stored in `NC_DB`. If it is not specified, a local SQLite will be created and used.
### Creating Empty Project
Click `Create Project`, you need to specify the project name. The data will be stored in `NC_DB`. If it is not specified, a local SQLite will be created and used.
<alert> <alert>
NC_DB is an environment variable used to store the meta data in the given database. NC_DB is an environment variable used to store the meta data in the given database.
</alert> </alert>
<img width="1496" alt="image" src="https://user-images.githubusercontent.com/35857179/194793478-a4c20517-1c38-474d-8905-d1d3da560136.png"> ![Screenshot 2022-12-29 at 2 54 57 PM](https://user-images.githubusercontent.com/86527202/209932936-8fe7334c-1a94-4073-ba19-478efb620808.png)
<!-- <img width="1496" alt="image" src="https://user-images.githubusercontent.com/35857179/194793478-a4c20517-1c38-474d-8905-d1d3da560136.png"> -->
### Connecting to External Database ### Connecting to External Database
Click `Create By Connecting To An External Database`, you need to specify the project name, API type, and other database parameters. Click on three-dot menu adjacent to `BASES`. Pick required database option from the menu `Connect to new datasource`.
<alert type="success"> <alert type="success">
Tip: If you are running NocoDB on Docker and your local DB is running on your host machine, your Host Address would be host.docker.internal instead of localhost. Tip: If you are running NocoDB on Docker and your local DB is running on your host machine, your Host Address would be host.docker.internal instead of localhost.
</alert> </alert>
<img width="1500" alt="image" src="https://user-images.githubusercontent.com/35857179/194793497-3b740bf2-ffc7-48bf-836e-e4cd26631568.png">
<!-- <img width="1500" alt="image" src="https://user-images.githubusercontent.com/35857179/194793497-3b740bf2-ffc7-48bf-836e-e4cd26631568.png"> -->
![Screenshot 2022-12-29 at 2 55 39 PM](https://user-images.githubusercontent.com/86527202/209933294-9327ff16-21db-4aca-bf16-8cea8a1eb415.png)
Above menu is also accessible from `Team & Settings` > `Data Sources`. Click on `New` button to add existing database.
![Screenshot 2022-12-29 at 3 16 36 PM](https://user-images.githubusercontent.com/86527202/209936942-f82a293f-2e91-48da-b9cd-79ec143d7d6f.png)
You need to specify the project name, API type, and other database parameters.
![Screenshot 2022-12-29 at 2 56 25 PM](https://user-images.githubusercontent.com/86527202/209933326-e76eee13-4942-4ba8-b184-a28099c089ab.png)
Currently it supports MySQL, Postgres, MSSQL and SQLite. Currently it supports MySQL, Postgres, MSSQL and SQLite.

93
packages/noco-docs/content/en/setup-and-usages/meta-management.md

@ -1,81 +1,92 @@
--- ---
title: 'Team & Settings > Project Metadata' title: 'Team & Settings > Data Sources'
description: 'NocoDB Project Metadata' description: 'NocoDB Data-Source sync, access control & re-config'
position: 600 position: 600
category: 'Product' category: 'Product'
menuTitle: 'Team & Settings > Project Metadata' menuTitle: 'Team & Settings > Data Sources'
--- ---
Project Metadata includes Database Metadata, UI Access Control and Miscellaneous. `Data Sources` sub-menu includes
- Database Metadata
- UI Access Control
- ERD
- Add/Remove new data source
- Edit existing data source configuration
- Edit data source visibility options
To access it, click the down arrow button next to Project Name on the top left side, then select `Team & Settings` and clicking `Project Metadata`.
<img width="322" alt="image" src="https://user-images.githubusercontent.com/35857179/194856648-67936db0-ee4d-4060-be3d-af9f86ef8fc6.png"> | <img width="471" alt="image" src="https://user-images.githubusercontent.com/35857179/194850848-869c69a4-e9b6-4a84-8cc0-7fd4b01eb1ad.png"> Note that, currently only one external data source can be added per project.
|--|--|
<!-- ## Project Metadata To access it, click the down arrow button next to Project Name on the top left side, then select `Team & Settings` and clicking `Data Sources`.
The metadata is stored in meta directory in project level, database level, and API level. <!-- ![Screenshot 2022-12-29 at 4 26 27 PM](https://user-images.githubusercontent.com/86527202/209941709-1bfdbb01-ebd0-4c85-a966-2a8b4fc6ade7.png) -->
![Screenshot 2022-12-29 at 4 29 24 PM](https://user-images.githubusercontent.com/86527202/209941906-a9c8d48d-d604-4a2f-8ffb-7a9a494bac6b.png)
![Screenshot 2022-12-29 at 4 27 14 PM](https://user-images.githubusercontent.com/86527202/209941716-70f2aaa7-b035-42b2-835e-eb2ca348be42.png)
Under ``Project Metadata``, you can perform the following operations.
- Export all metadata from the meta tables to meta directory <!-- ![Screenshot 2022-12-29 at 3 54 55 PM](https://user-images.githubusercontent.com/86527202/209938195-7384b4d8-0289-447f-bd39-1ec600cd1723.png) -->
<!-- <img width="322" alt="image" src="https://user-images.githubusercontent.com/86527202/209941709-1bfdbb01-ebd0-4c85-a966-2a8b4fc6ade7.png"> | <img alt="image" src="https://user-images.githubusercontent.com/86527202/209941716-70f2aaa7-b035-42b2-835e-eb2ca348be42.png"> -->
<!-- |--|--| -->
- Import all metadata from the meta directory to meta tables ![Screenshot 2022-12-29 at 4 15 00 PM](https://user-images.githubusercontent.com/86527202/209940452-5b867b71-b9f1-4e64-af69-14715ab73be7.png)
- Export project meta to zip file and download
- Import project meta zip file and restart
- Clear all metadata from meta tables ## Sync Metadata
<alert> Go to `Data Sources`, click ``Sync Metadata``, you can see your metadata sync status. If it is out of sync, you can sync the schema. See <a href="./sync-schema">Sync Schema</a> for more.0
Import won't work with zip files exported from the older version of apps (< 0.11.6). <br>
Import / Export will only transfer metadata and files related to the project and not any table data in the project.
</alert>
## Migration Example ![Screenshot 2022-12-29 at 4 19 35 PM](https://user-images.githubusercontent.com/86527202/209940903-396650b4-e219-494a-863f-c3f1beb51c5e.png)
### Export Metadata
From the source project, go to `Project Metadata`. Under ``Export / Import Metadata`` tab, select ``Export zip``, click ``Submit``. This step extracts project metadata and stores it in compressed (zip) format. <!-- <img width="1333" alt="image" src="https://user-images.githubusercontent.com/35857179/194850034-5330458e-85a9-4a3c-87a3-dd2f3edc5b46.png"> -->
![image](https://user-images.githubusercontent.com/35857179/161904400-b926494a-4533-41e4-85c3-5c6ca9ea0803.png) ## UI Access Control
### Import Metadata Go to `Data Sources`, click ``UI ACL``, you can control the access to each table by roles.
From the destination project, go to `Project Metadata`. Under ``Export / Import Metadata`` tab, select ``Import zip``, select ``meta.zip`` file stored in previous step. This step imports project metadata from compressed file (zip) selected and restarts the project. <!-- <img width="1336" alt="image" src="https://user-images.githubusercontent.com/35857179/194850281-9030f4c5-06bc-4780-b8fd-5d0c209867e0.png"> -->
![Screenshot 2022-12-29 at 4 20 57 PM](https://user-images.githubusercontent.com/86527202/209941141-deed80a9-7682-48e1-8de9-9c965c990d2d.png)
![image](https://user-images.githubusercontent.com/35857179/161904452-da0ac683-1715-438a-9c9c-91b34f8f45ba.png) --> ## ERD
## Database Metadata Go to `Data Sources`, click ``ERD``, you can see the ERD of your database.
Go to `Project Metadata`, under ``Metadata``, you can see your metadata sync status. If it is out of sync, you can sync the schema. See <a href="./sync-schema">Sync Schema</a> for more. ![Screenshot 2022-12-29 at 4 21 55 PM](https://user-images.githubusercontent.com/86527202/209941168-b53d2898-8448-47fa-a8b3-6f3572f6b3a2.png)
<!-- <img width="1338" alt="image" src="https://user-images.githubusercontent.com/35857179/194850416-54bc49cf-c32f-45e8-aea1-62b07645c26e.png"> -->
<img width="1333" alt="image" src="https://user-images.githubusercontent.com/35857179/194850034-5330458e-85a9-4a3c-87a3-dd2f3edc5b46.png"> ### Junction table names within ERD
## UI Access Control - Enable `Show M2M Tables` within `Project Settings` menu
- Double click on `Show Columns` to see additional checkboxes get enabled.
- Enabling which you should be able to see junction tables and their table names.
Go to `Project Metadata`, under ``UI Access Control``, you can control the access to each table by roles. <img width="1681" alt="Show Junction table names for many to many table" src="https://user-images.githubusercontent.com/5435402/192140913-9da37700-28fe-404d-88e8-35ba0c8e2f53.png">
<img width="1336" alt="image" src="https://user-images.githubusercontent.com/35857179/194850281-9030f4c5-06bc-4780-b8fd-5d0c209867e0.png"> ## Edit external database configuration parameters
## ERD Go to `Data Sources`, click ``Edit``, you can re-configure database credentials.
Please make sure database configuration parameters are valid. Any incorrect parameters could lead to schema loss!
Go to `Project Metadata`, under ``ERD View``, you can see the ERD of your database.
<img width="1338" alt="image" src="https://user-images.githubusercontent.com/35857179/194850416-54bc49cf-c32f-45e8-aea1-62b07645c26e.png"> ![Screenshot 2022-12-29 at 4 22 08 PM](https://user-images.githubusercontent.com/86527202/209941211-de9670c9-a73c-4719-9957-eeaf05f3a7ee.png)
### Junction table names within ERD
- Enable `Show M2M Tables` within Miscellaneous tab ## Unlink data source
- Double click on `Show Columns` to see additional checkboxes get enabled.
- Enabling which you should be able to see junction tables and their table names.
<img width="1681" alt="Show Junction table names for many to many table" src="https://user-images.githubusercontent.com/5435402/192140913-9da37700-28fe-404d-88e8-35ba0c8e2f53.png"> Go to `Data Sources`, click ``Delete`` against the data source that you wish to un-link.
![Screenshot 2022-12-29 at 4 31 16 PM](https://user-images.githubusercontent.com/86527202/209942178-5ae40f14-0e87-41f7-9630-e2bf6f59a906.png)
## Data source visibility
Go to `Data Sources`, toggle ``Radio-button`` against the data source that you wish to hide/un-hide.
![Screenshot 2022-12-29 at 4 31 16 PM 2](https://user-images.githubusercontent.com/86527202/209942198-627f7f14-761b-4709-b9ca-fde5111fa207.png)
## Miscellaneous <!-- ## Miscellaneous
- Enabling, `Show M2M Tables` will show junction tables between many to many tables. - Enabling, `Show M2M Tables` will show junction tables between many to many tables.
<img width="1340" alt="image" src="https://user-images.githubusercontent.com/35857179/194850461-3e88752a-ba4f-4ead-9426-9a9e57020061.png"> <img width="1340" alt="image" src="https://user-images.githubusercontent.com/35857179/194850461-3e88752a-ba4f-4ead-9426-9a9e57020061.png">
-->

21
packages/noco-docs/content/en/setup-and-usages/project-settings.md

@ -0,0 +1,21 @@
---
title: 'Team & Settings > Project settings'
description: 'General project configuration options'
position: 612
category: 'Product'
menuTitle: 'Team & Settings > Project Settings'
---
## Overview
Generic project configuration options are retained under `Project Settings` menu. To access it, click the down arrow button next to Project Name on the top left side, then select `Team & Settings`.
<img width="322" alt="image" src="https://user-images.githubusercontent.com/35857179/194856648-67936db0-ee4d-4060-be3d-af9f86ef8fc6.png">
Then, under SETTINGS, click `Project Settings`.
## Miscellaneous
- Enabling, `Show M2M Tables` will show junction tables between many to many tables.
![Screenshot 2022-12-29 at 7 05 04 PM](https://user-images.githubusercontent.com/86527202/209961654-ffe8ddc6-c7e2-4c0d-9762-2b57fb883cfa.png)

2
packages/noco-docs/content/en/setup-and-usages/sync-schema.md

@ -1,7 +1,7 @@
--- ---
title: 'Sync Schema' title: 'Sync Schema'
description: 'Schema changes made to database from outside NocoDB GUI can be synced' description: 'Schema changes made to database from outside NocoDB GUI can be synced'
position: 610 position: 613
category: 'Product' category: 'Product'
menuTitle: 'Sync Schema' menuTitle: 'Sync Schema'
--- ---

4
packages/nocodb-sdk/package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "nocodb-sdk", "name": "nocodb-sdk",
"version": "0.100.2", "version": "0.101.0-beta.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "nocodb-sdk", "name": "nocodb-sdk",
"version": "0.100.2", "version": "0.101.0-beta.0",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"axios": "^0.21.1", "axios": "^0.21.1",

2
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb-sdk", "name": "nocodb-sdk",
"version": "0.100.2", "version": "0.101.0-beta.0",
"description": "NocoDB SDK", "description": "NocoDB SDK",
"main": "build/main/index.js", "main": "build/main/index.js",
"typings": "build/main/index.d.ts", "typings": "build/main/index.d.ts",

22
packages/nocodb/package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "nocodb", "name": "nocodb",
"version": "0.100.2", "version": "0.101.0-beta.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "nocodb", "name": "nocodb",
"version": "0.100.2", "version": "0.101.0-beta.0",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@google-cloud/storage": "^5.7.2", "@google-cloud/storage": "^5.7.2",
@ -65,7 +65,7 @@
"mysql2": "^2.2.5", "mysql2": "^2.2.5",
"nanoid": "^3.1.20", "nanoid": "^3.1.20",
"nc-help": "0.2.85", "nc-help": "0.2.85",
"nc-lib-gui": "0.100.2", "nc-lib-gui": "0.101.0-beta.0",
"nc-plugin": "0.1.2", "nc-plugin": "0.1.2",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk", "nocodb-sdk": "file:../nocodb-sdk",
@ -11091,9 +11091,9 @@
} }
}, },
"node_modules/nc-lib-gui": { "node_modules/nc-lib-gui": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.100.2.tgz", "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.101.0-beta.0.tgz",
"integrity": "sha512-28jYhI15Y1Hsca9CWjHTaxb901wPo6a8V5OMmcHYS5jb8PLzsqq4Gi/Ho3jbelUICWP0viOFYOU7cLk1bpJq4A==", "integrity": "sha512-7NuNn16fYm397ZcdqDY21f2smydPzk6gog9qZm8UdDn5i3Y49OnlGwpUWkqFX+ZM4T18uJ3rd+1Fe2T2sKWrWg==",
"dependencies": { "dependencies": {
"express": "^4.17.1" "express": "^4.17.1"
} }
@ -11152,7 +11152,7 @@
"dev": true "dev": true
}, },
"node_modules/nocodb-sdk": { "node_modules/nocodb-sdk": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"resolved": "file:../nocodb-sdk", "resolved": "file:../nocodb-sdk",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
@ -27438,9 +27438,9 @@
} }
}, },
"nc-lib-gui": { "nc-lib-gui": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.100.2.tgz", "resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.101.0-beta.0.tgz",
"integrity": "sha512-28jYhI15Y1Hsca9CWjHTaxb901wPo6a8V5OMmcHYS5jb8PLzsqq4Gi/Ho3jbelUICWP0viOFYOU7cLk1bpJq4A==", "integrity": "sha512-7NuNn16fYm397ZcdqDY21f2smydPzk6gog9qZm8UdDn5i3Y49OnlGwpUWkqFX+ZM4T18uJ3rd+1Fe2T2sKWrWg==",
"requires": { "requires": {
"express": "^4.17.1" "express": "^4.17.1"
} }
@ -27487,7 +27487,7 @@
"dev": true "dev": true
}, },
"nocodb-sdk": { "nocodb-sdk": {
"version": "0.100.2", "version": "0.101.0-beta.0",
"requires": { "requires": {
"axios": "^0.21.1", "axios": "^0.21.1",
"jsep": "^1.3.6" "jsep": "^1.3.6"

4
packages/nocodb/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb", "name": "nocodb",
"version": "0.100.2", "version": "0.101.0-beta.0",
"description": "NocoDB Backend", "description": "NocoDB Backend",
"main": "dist/bundle.js", "main": "dist/bundle.js",
"author": { "author": {
@ -105,7 +105,7 @@
"mysql2": "^2.2.5", "mysql2": "^2.2.5",
"nanoid": "^3.1.20", "nanoid": "^3.1.20",
"nc-help": "0.2.85", "nc-help": "0.2.85",
"nc-lib-gui": "0.100.2", "nc-lib-gui": "0.101.0-beta.0",
"nc-plugin": "0.1.2", "nc-plugin": "0.1.2",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk", "nocodb-sdk": "file:../nocodb-sdk",

15
packages/nocodb/src/lib/Noco.ts

@ -16,7 +16,9 @@ import requestIp from 'request-ip';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { NcConfig } from '../interface/config'; import { NcConfig } from '../interface/config';
import { NC_LICENSE_KEY } from './constants';
import Migrator from './db/sql-migrator/lib/KnexMigrator'; import Migrator from './db/sql-migrator/lib/KnexMigrator';
import Store from './models/Store';
import NcConfigFactory from './utils/NcConfigFactory'; import NcConfigFactory from './utils/NcConfigFactory';
import { Tele } from 'nc-help'; import { Tele } from 'nc-help';
@ -53,6 +55,7 @@ const NcProjectBuilder = process.env.EE
export default class Noco { export default class Noco {
private static _this: Noco; private static _this: Noco;
private static ee: boolean;
public static get dashboardUrl(): string { public static get dashboardUrl(): string {
let siteUrl = `http://localhost:${process.env.PORT || 8080}`; let siteUrl = `http://localhost:${process.env.PORT || 8080}`;
@ -192,6 +195,7 @@ export default class Noco {
} }
await Noco._ncMeta.metaInit(); await Noco._ncMeta.metaInit();
await Noco.loadEEState();
await this.initJwt(); await this.initJwt();
await initAdminFromEnv(); await initAdminFromEnv();
@ -542,4 +546,15 @@ export default class Noco {
public static getConfig(): NcConfig { public static getConfig(): NcConfig {
return Noco.config; return Noco.config;
} }
public static isEE(): boolean {
return Noco.ee;
}
public static async loadEEState(): Promise<boolean> {
try {
return (Noco.ee = !!(await Store.get(NC_LICENSE_KEY))?.value);
} catch {}
return (Noco.ee = false);
}
} }

19
packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts

@ -1,3 +1,5 @@
import Noco from '../../../Noco';
import SqlClientFactoryEE from './ee/SqlClientFactoryEE';
import MySqlClient from './mysql/MysqlClient'; import MySqlClient from './mysql/MysqlClient';
import MssqlClient from './mssql/MssqlClient'; import MssqlClient from './mssql/MssqlClient';
import OracleClient from './oracle/OracleClient'; import OracleClient from './oracle/OracleClient';
@ -6,10 +8,8 @@ import PgClient from './pg/PgClient';
import YugabyteClient from './pg/YugabyteClient'; import YugabyteClient from './pg/YugabyteClient';
import TidbClient from './mysql/TidbClient'; import TidbClient from './mysql/TidbClient';
import VitessClient from './mysql/VitessClient'; import VitessClient from './mysql/VitessClient';
import SfClient from './snowflake/SnowflakeClient';
import { SnowflakeClient } from 'nc-help';
class SqlClientFactory { export class SqlClientFactory {
static create(connectionConfig) { static create(connectionConfig) {
connectionConfig.meta = connectionConfig.meta || {}; connectionConfig.meta = connectionConfig.meta || {};
connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 }; connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 };
@ -33,13 +33,18 @@ class SqlClientFactory {
if (connectionConfig.meta.dbtype === 'yugabyte') if (connectionConfig.meta.dbtype === 'yugabyte')
return new YugabyteClient(connectionConfig); return new YugabyteClient(connectionConfig);
return new PgClient(connectionConfig); return new PgClient(connectionConfig);
} else if (connectionConfig.client === 'snowflake') {
connectionConfig.client = SnowflakeClient;
return new SfClient(connectionConfig);
} }
throw new Error('Database not supported'); throw new Error('Database not supported');
} }
} }
export default SqlClientFactory; export default class {
static create(connectionConfig) {
if (Noco.isEE()) {
return SqlClientFactoryEE.create(connectionConfig);
}
return SqlClientFactory.create(connectionConfig);
}
}

19
packages/nocodb/src/lib/db/sql-client/lib/ee/SqlClientFactoryEE.ts

@ -0,0 +1,19 @@
import { SqlClientFactory } from '../SqlClientFactory';
import SfClient from '../snowflake/SnowflakeClient';
import { SnowflakeClient } from 'nc-help';
class SqlClientFactoryEE {
static create(connectionConfig) {
connectionConfig.meta = connectionConfig.meta || {};
connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 };
connectionConfig.meta.dbtype = connectionConfig.meta.dbtype || '';
if (connectionConfig.client === 'snowflake') {
connectionConfig.client = SnowflakeClient;
return new SfClient(connectionConfig);
}
return SqlClientFactory.create(connectionConfig);
}
}
export default SqlClientFactoryEE;

156
packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts

@ -9,14 +9,14 @@ import lodash from 'lodash';
const log = new Debug('SnowflakeClient'); const log = new Debug('SnowflakeClient');
const rowsToLower = (arr) => { const rowsToLower = (arr) => {
for(const a of arr) { for (const a of arr) {
for(const [k, v] of Object.entries(a)) { for (const [k, v] of Object.entries(a)) {
delete a[k]; delete a[k];
a[k.toLowerCase()] = v; a[k.toLowerCase()] = v;
} }
} }
return arr; return arr;
} };
class SnowflakeClient extends KnexClient { class SnowflakeClient extends KnexClient {
private queries: any; private queries: any;
@ -309,7 +309,9 @@ class SnowflakeClient extends KnexClient {
try { try {
result.data.object = {}; result.data.object = {};
const data = await this.sqlClient.raw('SELECT CURRENT_VERSION() as "server_version"'); const data = await this.sqlClient.raw(
'SELECT CURRENT_VERSION() as "server_version"'
);
log.debug(data.rows[0]); log.debug(data.rows[0]);
result.data.object.version = data.rows[0].server_version; result.data.object.version = data.rows[0].server_version;
const versions = data.rows[0].server_version.split('.'); const versions = data.rows[0].server_version.split('.');
@ -363,8 +365,10 @@ class SnowflakeClient extends KnexClient {
await this.sqlClient.raw(`CREATE DATABASE ??`, [args.database]); await this.sqlClient.raw(`CREATE DATABASE ??`, [args.database]);
} }
await this.sqlClient.raw(`CREATE SCHEMA IF NOT EXISTS ??.??`,[args.database, this.schema]); await this.sqlClient.raw(`CREATE SCHEMA IF NOT EXISTS ??.??`, [
args.database,
this.schema,
]);
} catch (e) { } catch (e) {
log.ppe(e, _func); log.ppe(e, _func);
throw e; throw e;
@ -549,8 +553,9 @@ class SnowflakeClient extends KnexClient {
log.api(`${_func}:args:`, args); log.api(`${_func}:args:`, args);
try { try {
const { rows } = await this const { rows } = await this.raw(
.raw(`SELECT SCHEMA_NAME as "schema_name" FROM "${this.database}".information_schema.SCHEMATA order by schema_name;`); `SELECT SCHEMA_NAME as "schema_name" FROM "${this.database}".information_schema.SCHEMATA order by schema_name;`
);
result.data.list = rows; result.data.list = rows;
} catch (e) { } catch (e) {
@ -595,22 +600,30 @@ class SnowflakeClient extends KnexClient {
log.api(`${_func}:args:`, args); log.api(`${_func}:args:`, args);
try { try {
args.databaseName = this.database; args.databaseName = this.database;
await this.sqlClient.raw(`SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`); await this.sqlClient.raw(
await this.sqlClient.raw(`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`); `SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
await this.sqlClient.raw(
`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
const lastQueries = await this.sqlClient.raw(` const lastQueries = await this.sqlClient.raw(`
select * from table("${this.database}".information_schema.query_history()) select * from table("${this.database}".information_schema.query_history())
WHERE query_text like 'SHOW%' WHERE query_text like 'SHOW%'
ORDER BY start_time DESC ORDER BY start_time DESC
LIMIT 30;` LIMIT 30;`);
);
let pk_query_id, uq_query_id; let pk_query_id, uq_query_id;
for (const r of lastQueries.rows) { for (const r of lastQueries.rows) {
if (r.QUERY_TEXT === `SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { if (
r.QUERY_TEXT ===
`SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
pk_query_id = r.QUERY_ID; pk_query_id = r.QUERY_ID;
} else if (r.QUERY_TEXT === `SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { } else if (
r.QUERY_TEXT ===
`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
uq_query_id = r.QUERY_ID; uq_query_id = r.QUERY_ID;
} }
if (pk_query_id && uq_query_id) { if (pk_query_id && uq_query_id) {
@ -766,21 +779,30 @@ class SnowflakeClient extends KnexClient {
const result = new Result(); const result = new Result();
log.api(`${_func}:args:`, args); log.api(`${_func}:args:`, args);
try { try {
await this.sqlClient.raw(`SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`); await this.sqlClient.raw(
await this.sqlClient.raw(`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`); `SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
await this.sqlClient.raw(
`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
const lastQueries = await this.sqlClient.raw(` const lastQueries = await this.sqlClient.raw(`
select * from table("${this.database}".INFORMATION_SCHEMA.query_history()) select * from table("${this.database}".INFORMATION_SCHEMA.query_history())
WHERE query_text like 'SHOW%' WHERE query_text like 'SHOW%'
ORDER BY start_time DESC ORDER BY start_time DESC
LIMIT 30;` LIMIT 30;`);
);
let pk_query_id, uq_query_id; let pk_query_id, uq_query_id;
for (const r of lastQueries.rows) { for (const r of lastQueries.rows) {
if (r.QUERY_TEXT === `SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { if (
r.QUERY_TEXT ===
`SHOW PRIMARY KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
pk_query_id = r.QUERY_ID; pk_query_id = r.QUERY_ID;
} else if (r.QUERY_TEXT === `SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { } else if (
r.QUERY_TEXT ===
`SHOW UNIQUE KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
uq_query_id = r.QUERY_ID; uq_query_id = r.QUERY_ID;
} }
if (pk_query_id && uq_query_id) { if (pk_query_id && uq_query_id) {
@ -797,7 +819,8 @@ class SnowflakeClient extends KnexClient {
LEFT JOIN (select * from table(result_scan('${pk_query_id}')) UNION select * from table(result_scan('${uq_query_id}'))) pk LEFT JOIN (select * from table(result_scan('${pk_query_id}')) UNION select * from table(result_scan('${uq_query_id}'))) pk
ON "PK"."constraint_name" = tc.constraint_name ON "PK"."constraint_name" = tc.constraint_name
WHERE tc.table_catalog = ? and tc.table_schema = ? and tc.table_name = ?;`, WHERE tc.table_catalog = ? and tc.table_schema = ? and tc.table_name = ?;`,
[this.database, this.schema, args.tn]); [this.database, this.schema, args.tn]
);
result.data.list = rows; result.data.list = rows;
} catch (e) { } catch (e) {
@ -831,18 +854,22 @@ class SnowflakeClient extends KnexClient {
const result = new Result(); const result = new Result();
log.api(`${_func}:args:`, args); log.api(`${_func}:args:`, args);
try { try {
await this.sqlClient.raw(`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`); await this.sqlClient.raw(
`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
const lastQueries = await this.sqlClient.raw(` const lastQueries = await this.sqlClient.raw(`
select * from table("${this.database}".information_schema.query_history()) select * from table("${this.database}".information_schema.query_history())
WHERE query_text like 'SHOW%' WHERE query_text like 'SHOW%'
ORDER BY start_time DESC ORDER BY start_time DESC
LIMIT 30;` LIMIT 30;`);
);
let ik_query_id; let ik_query_id;
for (const r of lastQueries.rows) { for (const r of lastQueries.rows) {
if (r.QUERY_TEXT === `SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { if (
r.QUERY_TEXT ===
`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
ik_query_id = r.QUERY_ID; ik_query_id = r.QUERY_ID;
break; break;
} }
@ -897,18 +924,22 @@ class SnowflakeClient extends KnexClient {
const result = new Result(); const result = new Result();
log.api(`${_func}:args:`, args); log.api(`${_func}:args:`, args);
try { try {
await this.sqlClient.raw(`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`); await this.sqlClient.raw(
`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`
);
const lastQueries = await this.sqlClient.raw(` const lastQueries = await this.sqlClient.raw(`
select * from table("${this.database}".information_schema.query_history()) select * from table("${this.database}".information_schema.query_history())
WHERE query_text like 'SHOW%' WHERE query_text like 'SHOW%'
ORDER BY start_time DESC ORDER BY start_time DESC
LIMIT 30;` LIMIT 30;`);
);
let ik_query_id; let ik_query_id;
for (const r of lastQueries.rows) { for (const r of lastQueries.rows) {
if (r.QUERY_TEXT === `SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`) { if (
r.QUERY_TEXT ===
`SHOW IMPORTED KEYS IN SCHEMA "${this.database}"."${this.schema}";`
) {
ik_query_id = r.QUERY_ID; ik_query_id = r.QUERY_ID;
break; break;
} }
@ -1434,7 +1465,9 @@ class SnowflakeClient extends KnexClient {
throw new Error('Function not supported for Snowflake yet'); throw new Error('Function not supported for Snowflake yet');
const upQuery = const upQuery =
this.querySeparator() + this.querySeparator() +
`CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`; `CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${
args.event
}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`;
await this.sqlClient.raw(upQuery); await this.sqlClient.raw(upQuery);
const downQuery = const downQuery =
this.querySeparator() + this.querySeparator() +
@ -1472,7 +1505,9 @@ class SnowflakeClient extends KnexClient {
this.querySeparator() + `DROP TRIGGER ${args.procedure_name}`; this.querySeparator() + `DROP TRIGGER ${args.procedure_name}`;
const upQuery = const upQuery =
this.querySeparator() + this.querySeparator() +
`CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`; `CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${
args.event
}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`;
await this.sqlClient.raw(query); await this.sqlClient.raw(query);
await this.sqlClient.raw(upQuery); await this.sqlClient.raw(upQuery);
@ -1507,7 +1542,9 @@ class SnowflakeClient extends KnexClient {
throw new Error('Function not supported for Snowflake yet'); throw new Error('Function not supported for Snowflake yet');
const upQuery = const upQuery =
this.querySeparator() + this.querySeparator() +
`CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`; `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${
args.event
}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`;
await this.sqlClient.raw(upQuery); await this.sqlClient.raw(upQuery);
result.data.object = { result.data.object = {
upStatement: [{ sql: upQuery }], upStatement: [{ sql: upQuery }],
@ -1544,7 +1581,9 @@ class SnowflakeClient extends KnexClient {
`DROP TRIGGER ${args.trigger_name} ON ${args.tn}` `DROP TRIGGER ${args.trigger_name} ON ${args.tn}`
); );
await this.sqlClient.raw( await this.sqlClient.raw(
`CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}` `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${
args.event
}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`
); );
result.data.object = { result.data.object = {
@ -1554,10 +1593,14 @@ class SnowflakeClient extends KnexClient {
args.tn args.tn
};${this.querySeparator()}CREATE TRIGGER ${args.trigger_name} \n${ };${this.querySeparator()}CREATE TRIGGER ${args.trigger_name} \n${
args.timing args.timing
} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.statement}`, } ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${
args.statement
}`,
downStatement: downStatement:
this.querySeparator() + this.querySeparator() +
`CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.oldStatement}`, `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${
args.event
}\nON ${this.getTnPath(args.tn)} FOR EACH ROW\n${args.oldStatement}`,
}; };
} catch (e) { } catch (e) {
log.ppe(e, func); log.ppe(e, func);
@ -1712,7 +1755,9 @@ class SnowflakeClient extends KnexClient {
const downStatement = const downStatement =
this.querySeparator() + this.querySeparator() +
this.sqlClient.raw(`DROP TABLE ??`, [this.getTnPath(args.table)]).toString(); this.sqlClient
.raw(`DROP TABLE ??`, [this.getTnPath(args.table)])
.toString();
this.emit(`Success : ${upQuery}`); this.emit(`Success : ${upQuery}`);
@ -1857,7 +1902,7 @@ class SnowflakeClient extends KnexClient {
//downQuery = `ALTER TABLE "${args.columns[0].tn}" ${downQuery};`; //downQuery = `ALTER TABLE "${args.columns[0].tn}" ${downQuery};`;
} }
for(const q of upQuery.split(';')) { for (const q of upQuery.split(';')) {
if (q && q.trim() !== '') { if (q && q.trim() !== '') {
await this.sqlClient.raw(q); await this.sqlClient.raw(q);
} }
@ -1902,7 +1947,9 @@ class SnowflakeClient extends KnexClient {
/** ************** create up & down statements *************** */ /** ************** create up & down statements *************** */
const upStatement = const upStatement =
this.querySeparator() + this.querySeparator() +
this.sqlClient.raw(`DROP TABLE ??`, [this.getTnPath(args.tn)]).toString(); this.sqlClient
.raw(`DROP TABLE ??`, [this.getTnPath(args.tn)])
.toString();
let downQuery = this.createTable(args.tn, args); let downQuery = this.createTable(args.tn, args);
/** /**
@ -2371,7 +2418,10 @@ class SnowflakeClient extends KnexClient {
: ''; : '';
if (numOfPksInNew.length) { if (numOfPksInNew.length) {
if (createTable) { if (createTable) {
query += this.genQuery(`, constraint ?? PRIMARY KEY (??)`, [`${t}_pkey`, numOfPksInNew]); query += this.genQuery(`, constraint ?? PRIMARY KEY (??)`, [
`${t}_pkey`,
numOfPksInNew,
]);
} else { } else {
query += this.genQuery( query += this.genQuery(
`alter TABLE ?? add constraint ?? PRIMARY KEY(??);`, `alter TABLE ?? add constraint ?? PRIMARY KEY(??);`,
@ -2416,7 +2466,9 @@ class SnowflakeClient extends KnexClient {
query += this.alterTablePK(table, args.columns, [], query, true); query += this.alterTablePK(table, args.columns, [], query, true);
query = this.genQuery(`CREATE TABLE ?? (${query});`, [this.getTnPath(args.tn)]); query = this.genQuery(`CREATE TABLE ?? (${query});`, [
this.getTnPath(args.tn),
]);
return query; return query;
} }
@ -2430,7 +2482,11 @@ class SnowflakeClient extends KnexClient {
if (change === 0) { if (change === 0) {
query = existingQuery ? ',' : ''; query = existingQuery ? ',' : '';
if (n.ai) { if (n.ai) {
query += this.genQuery(` ?? NUMBER(38,0) NOT NULL autoincrement UNIQUE`, [n.cn], shouldSanitize); query += this.genQuery(
` ?? NUMBER(38,0) NOT NULL autoincrement UNIQUE`,
[n.cn],
shouldSanitize
);
} else { } else {
query += this.genQuery(` ?? ${n.dt}`, [n.cn], shouldSanitize); query += this.genQuery(` ?? ${n.dt}`, [n.cn], shouldSanitize);
query += n.dtxp && n.dt !== 'text' ? `(${n.dtxp})` : ''; query += n.dtxp && n.dt !== 'text' ? `(${n.dtxp})` : '';
@ -2442,7 +2498,11 @@ class SnowflakeClient extends KnexClient {
query += n.dtxp && n.dt !== 'text' ? `(${n.dtxp})` : ''; query += n.dtxp && n.dt !== 'text' ? `(${n.dtxp})` : '';
query += n.rqd ? ' NOT NULL' : ' NULL'; query += n.rqd ? ' NOT NULL' : ' NULL';
query += defaultValue ? ` DEFAULT ${defaultValue}` : ''; query += defaultValue ? ` DEFAULT ${defaultValue}` : '';
query = this.genQuery(`ALTER TABLE ?? ${query};`, [this.getTnPath(t)], shouldSanitize); query = this.genQuery(
`ALTER TABLE ?? ${query};`,
[this.getTnPath(t)],
shouldSanitize
);
} else { } else {
if (n.cn !== o.cn) { if (n.cn !== o.cn) {
query += this.genQuery( query += this.genQuery(
@ -2483,17 +2543,11 @@ class SnowflakeClient extends KnexClient {
} }
get schema() { get schema() {
return ( return this.connectionConfig && this.connectionConfig.connection.schema;
(this.connectionConfig &&
this.connectionConfig.connection.schema)
);
} }
get database() { get database() {
return ( return this.connectionConfig && this.connectionConfig.connection.database;
(this.connectionConfig &&
this.connectionConfig.connection.database)
);
} }
getTnPath(t) { getTnPath(t) {

2
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts

@ -1022,7 +1022,7 @@ function CustomKnex(arg: string | Knex.Config<any> | any): CustomKnex {
value: () => { value: () => {
return typeof arg === 'string' return typeof arg === 'string'
? arg.match(/^(\w+):/) ?? [1] ? arg.match(/^(\w+):/) ?? [1]
: (typeof arg.client === 'string') : typeof arg.client === 'string'
? arg.client ? arg.client
: arg.client?.prototype?.dialect || arg.client?.prototype?.driverName; : arg.client?.prototype?.dialect || arg.client?.prototype?.driverName;
}, },

17
packages/nocodb/src/lib/meta/api/baseApis.ts

@ -35,19 +35,17 @@ export async function baseUpdate(
) { ) {
const baseBody = req.body; const baseBody = req.body;
const project = await Project.getWithInfo(req.params.projectId); const project = await Project.getWithInfo(req.params.projectId);
const base = await Base.updateBase(req.params.baseId, const base = await Base.updateBase(req.params.baseId, {
{
...baseBody, ...baseBody,
type: baseBody.config?.client, type: baseBody.config?.client,
projectId: project.id, projectId: project.id,
id: req.params.baseId, id: req.params.baseId,
} });
);
delete base.config; delete base.config;
Tele.emit('evt', { Tele.emit('evt', {
evt_type: 'base:updated' evt_type: 'base:updated',
}); });
res.json(base); res.json(base);
@ -66,9 +64,8 @@ export async function baseList(
bases: new PagedResponseImpl(bases, { bases: new PagedResponseImpl(bases, {
count: bases.length, count: bases.length,
limit: bases.length, limit: bases.length,
}) }),
} });
);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
next(e); next(e);
@ -104,7 +101,7 @@ async function baseCreate(req: Request<any, any>, res) {
delete base.config; delete base.config;
Tele.emit('evt', { Tele.emit('evt', {
evt_type: 'base:created' evt_type: 'base:created',
}); });
res.json(base); res.json(base);
@ -142,7 +139,6 @@ async function populateMeta(base: Base, project: Project): Promise<any> {
return t; return t;
}); });
/* filter based on prefix */ /* filter based on prefix */
if (base.is_meta && project?.prefix) { if (base.is_meta && project?.prefix) {
tables = tables.filter((t) => { tables = tables.filter((t) => {
@ -150,7 +146,6 @@ async function populateMeta(base: Base, project: Project): Promise<any> {
}); });
} }
info.tablesCount = tables.length; info.tablesCount = tables.length;
tables.forEach((t) => { tables.forEach((t) => {

11
packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts

@ -124,8 +124,10 @@ async function getDataList(model, view: View, req) {
); );
count = await baseModel.count(listArgs); count = await baseModel.count(listArgs);
} catch (e) { } catch (e) {
// show empty result instead of throwing error here console.log(e);
// e.g. search some text in a numeric field NcError.internalServerError(
'Internal Server Error, check server log for more details'
);
} }
return new PagedResponseImpl(data, { return new PagedResponseImpl(data, {
@ -281,8 +283,9 @@ async function getGroupedDataList(model, view: View, req) {
}); });
} catch (e) { } catch (e) {
console.log(e); console.log(e);
// show empty result instead of throwing error here NcError.internalServerError(
// e.g. search some text in a numeric field 'Internal Server Error, check server log for more details'
);
} }
return data; return data;
} }

8
packages/nocodb/src/lib/meta/api/dataApis/dataApis.ts

@ -328,7 +328,9 @@ async function dataRead(req: Request, res: Response, next) {
); );
} catch (e) { } catch (e) {
console.log(e); console.log(e);
res.status(500).json({ msg: e.message }); NcError.internalServerError(
'Internal Server Error, check server log for more details'
);
} }
} }
@ -465,6 +467,10 @@ async function getDataList(model, view: View, req) {
} catch (e) { } catch (e) {
// show empty result instead of throwing error here // show empty result instead of throwing error here
// e.g. search some text in a numeric field // e.g. search some text in a numeric field
console.log(e);
NcError.internalServerError(
'Internal Server Error, check server log for more details'
);
} }
return new PagedResponseImpl(data, { return new PagedResponseImpl(data, {

2
packages/nocodb/src/lib/meta/api/filterApis.ts

@ -17,7 +17,7 @@ import { metaApiMetrics } from '../helpers/apiMetrics';
// @ts-ignore // @ts-ignore
export async function filterGet(req: Request, res: Response, next) { export async function filterGet(req: Request, res: Response, next) {
try { try {
const filter = await Filter.getFilterObject({ viewId: req.params.viewId }); const filter = await Filter.get(req.params.filterId);
res.json(filter); res.json(filter);
} catch (e) { } catch (e) {

3
packages/nocodb/src/lib/meta/api/orgLicenseApis.ts

@ -2,6 +2,7 @@ import { Router } from 'express';
import { OrgUserRoles } from 'nocodb-sdk'; import { OrgUserRoles } from 'nocodb-sdk';
import { NC_LICENSE_KEY } from '../../constants'; import { NC_LICENSE_KEY } from '../../constants';
import Store from '../../models/Store'; import Store from '../../models/Store';
import Noco from '../../Noco';
import { metaApiMetrics } from '../helpers/apiMetrics'; import { metaApiMetrics } from '../helpers/apiMetrics';
import ncMetaAclMw from '../helpers/ncMetaAclMw'; import ncMetaAclMw from '../helpers/ncMetaAclMw';
@ -13,7 +14,7 @@ async function licenseGet(_req, res) {
async function licenseSet(req, res) { async function licenseSet(req, res) {
await Store.saveOrUpdate({ value: req.body.key, key: NC_LICENSE_KEY }); await Store.saveOrUpdate({ value: req.body.key, key: NC_LICENSE_KEY });
await Noco.loadEEState();
res.json({ msg: 'License key saved' }); res.json({ msg: 'License key saved' });
} }

37
packages/nocodb/src/lib/meta/api/sync/helpers/fetchAT.ts

@ -36,6 +36,12 @@ async function initialize(shareId) {
info.cookie += ck.split(';')[0] + '; '; info.cookie += ck.split(';')[0] + '; ';
} }
return response.data; return response.data;
})
.catch(() => {
throw {
message:
'Invalid Shared Base ID :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}); });
info.headers = JSON.parse( info.headers = JSON.parse(
@ -64,6 +70,14 @@ async function initialize(shareId) {
} catch (e) { } catch (e) {
console.log(e); console.log(e);
info.initialized = false; info.initialized = false;
if (e.message) {
throw e;
} else {
throw {
message:
'Error processing Shared Base :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}
} }
} }
@ -94,7 +108,10 @@ async function read() {
return response.data; return response.data;
}) })
.catch(() => { .catch(() => {
throw 'Error while fetching'; throw {
message:
'Error Reading :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}); });
return { return {
@ -103,7 +120,9 @@ async function read() {
baseInfo: info.baseInfo, baseInfo: info.baseInfo,
}; };
} else { } else {
throw 'Please initialize first!'; throw {
message: 'Error Initializing :: please try again !!',
};
} }
} }
@ -149,11 +168,16 @@ async function readView(viewId) {
return response.data; return response.data;
}) })
.catch(() => { .catch(() => {
throw 'Error while fetching'; throw {
message:
'Error Reading View :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}); });
return { view: resreq.data }; return { view: resreq.data };
} else { } else {
throw 'Please initialize first!'; throw {
message: 'Error Initializing :: please try again !!',
};
} }
} }
@ -192,7 +216,10 @@ async function readTemplate(templateId) {
return response.data; return response.data;
}) })
.catch(() => { .catch(() => {
throw 'Error while fetching'; throw {
message:
'Error Fetching :: Ensure www.airtable.com/templates/featured/<TemplateID> is accessible.',
};
}); });
return { template: resreq }; return { template: resreq };
} }

20
packages/nocodb/src/lib/meta/api/sync/helpers/job.ts

@ -191,6 +191,13 @@ export default async (
rtc.fetchAt.count++; rtc.fetchAt.count++;
rtc.fetchAt.time += duration; rtc.fetchAt.time += duration;
if (!ft.baseId) {
throw {
message:
'Invalid Shared Base ID :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}
const file = ft.schema; const file = ft.schema;
baseId = ft.baseId; baseId = ft.baseId;
base = new Airtable({ apiKey: sDB.apiKey }).base(baseId); base = new Airtable({ apiKey: sDB.apiKey }).base(baseId);
@ -214,7 +221,8 @@ export default async (
} }
function getRootDbType() { function getRootDbType() {
return ncCreatedProjectSchema?.bases.find((el) => el.id === syncDB.baseId)?.type; return ncCreatedProjectSchema?.bases.find((el) => el.id === syncDB.baseId)
?.type;
} }
// base mapping table // base mapping table
@ -312,7 +320,10 @@ export default async (
// @ts-ignore // @ts-ignore
async function nc_DumpTableSchema() { async function nc_DumpTableSchema() {
console.log('['); console.log('[');
const ncTblList = await api.base.tableList(ncCreatedProjectSchema.id, syncDB.baseId); const ncTblList = await api.base.tableList(
ncCreatedProjectSchema.id,
syncDB.baseId
);
for (let i = 0; i < ncTblList.list.length; i++) { for (let i = 0; i < ncTblList.list.length; i++) {
const ncTbl = await api.dbTable.read(ncTblList.list[i].id); const ncTbl = await api.dbTable.read(ncTblList.list[i].id);
console.log(JSON.stringify(ncTbl, null, 2)); console.log(JSON.stringify(ncTbl, null, 2));
@ -2230,7 +2241,10 @@ export default async (
try { try {
// await nc_DumpTableSchema(); // await nc_DumpTableSchema();
const _perfStart = recordPerfStart(); const _perfStart = recordPerfStart();
const ncTblList = await api.base.tableList(ncCreatedProjectSchema.id, syncDB.baseId); const ncTblList = await api.base.tableList(
ncCreatedProjectSchema.id,
syncDB.baseId
);
recordPerfStats(_perfStart, 'base.tableList'); recordPerfStats(_perfStart, 'base.tableList');
logBasic('Reading Records...'); logBasic('Reading Records...');

3
packages/nocodb/src/lib/meta/api/sync/importApis.ts

@ -107,6 +107,7 @@ export default (
baseURL = `http://localhost:${process.env.PORT || 8080}`; baseURL = `http://localhost:${process.env.PORT || 8080}`;
} }
setTimeout(() => {
NocoJobs.jobsMgr.add<AirtableSyncConfig>(AIRTABLE_IMPORT_JOB, { NocoJobs.jobsMgr.add<AirtableSyncConfig>(AIRTABLE_IMPORT_JOB, {
id: req.params.syncId, id: req.params.syncId,
...(syncSource?.details || {}), ...(syncSource?.details || {}),
@ -115,6 +116,8 @@ export default (
authToken: token, authToken: token,
baseURL, baseURL,
}); });
}, 1000);
jobs[req.params.syncId] = { jobs[req.params.syncId] = {
last_message: { last_message: {
msg: 'Sync started', msg: 'Sync started',

6
packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts

@ -8,7 +8,11 @@ import Project from '../../../models/Project';
export async function syncSourceList(req: Request, res: Response) { export async function syncSourceList(req: Request, res: Response) {
// todo: pagination // todo: pagination
res.json(new PagedResponseImpl(await SyncSource.list(req.params.projectId, req.params.baseId))); res.json(
new PagedResponseImpl(
await SyncSource.list(req.params.projectId, req.params.baseId)
)
);
} }
export async function syncCreate(req: Request, res: Response) { export async function syncCreate(req: Request, res: Response) {

26
packages/nocodb/src/lib/meta/api/userApi/initStrategies.ts

@ -7,7 +7,6 @@ import passport from 'passport';
import passportJWT from 'passport-jwt'; import passportJWT from 'passport-jwt';
import { Strategy as AuthTokenStrategy } from 'passport-auth-token'; import { Strategy as AuthTokenStrategy } from 'passport-auth-token';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
import { randomTokenString } from '../../helpers/stringHelpers';
const PassportLocalStrategy = require('passport-local').Strategy; const PassportLocalStrategy = require('passport-local').Strategy;
const ExtractJwt = passportJWT.ExtractJwt; const ExtractJwt = passportJWT.ExtractJwt;
@ -24,6 +23,7 @@ import { CacheGetType, CacheScope } from '../../../utils/globals';
import ApiToken from '../../../models/ApiToken'; import ApiToken from '../../../models/ApiToken';
import Noco from '../../../Noco'; import Noco from '../../../Noco';
import Plugin from '../../../models/Plugin'; import Plugin from '../../../models/Plugin';
import { registerNewUserIfAllowed } from './userApis';
export function initStrategies(router): void { export function initStrategies(router): void {
passport.use( passport.use(
@ -284,6 +284,8 @@ export function initStrategies(router): void {
User.getByEmail(email) User.getByEmail(email)
.then(async (user) => { .then(async (user) => {
if (user) {
// if project id defined extract project level roles
if (req.ncProjectId) { if (req.ncProjectId) {
ProjectUser.get(req.ncProjectId, user.id) ProjectUser.get(req.ncProjectId, user.id)
.then(async (projectUser) => { .then(async (projectUser) => {
@ -296,30 +298,22 @@ export function initStrategies(router): void {
}) })
.catch((e) => done(e)); .catch((e) => done(e));
} else { } else {
// const roles = projectUser?.roles ? JSON.parse(projectUser.roles) : {guest: true};
if (user) {
return done(null, user); return done(null, user);
} else {
let roles = 'editor';
if (!(await User.isFirst())) {
roles = 'owner';
}
if (roles === 'editor') {
return done(new Error('User not found'));
} }
// if user not found create new user if allowed
// or return error
} else {
const salt = await promisify(bcrypt.genSalt)(10); const salt = await promisify(bcrypt.genSalt)(10);
user = await await User.insert({ const user = await registerNewUserIfAllowed({
firstname: null,
lastname: null,
email_verification_token: null,
email: profile.emails[0].value, email: profile.emails[0].value,
password: '', password: '',
salt, salt,
roles,
email_verified: true,
token_version: randomTokenString(),
}); });
return done(null, user); return done(null, user);
} }
}
}) })
.catch((err) => { .catch((err) => {
return done(err); return done(err);

81
packages/nocodb/src/lib/meta/api/userApi/userApis.ts

@ -25,6 +25,58 @@ import Noco from '../../../Noco';
import { genJwt } from './helpers'; import { genJwt } from './helpers';
import { randomTokenString } from '../../helpers/stringHelpers'; import { randomTokenString } from '../../helpers/stringHelpers';
export async function registerNewUserIfAllowed({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
}: {
firstname;
lastname;
email: string;
salt: any;
password;
email_verification_token;
}) {
let roles: string = OrgUserRoles.CREATOR;
if (await User.isFirst()) {
roles = `${OrgUserRoles.CREATOR},${OrgUserRoles.SUPER_ADMIN}`;
// todo: update in nc_store
// roles = 'owner,creator,editor'
Tele.emit('evt', {
evt_type: 'project:invite',
count: 1,
});
} else {
let settings: { invite_only_signup?: boolean } = {};
try {
settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value);
} catch {}
if (settings?.invite_only_signup) {
NcError.badRequest('Not allowed to signup, contact super admin.');
} else {
roles = OrgUserRoles.VIEWER;
}
}
const token_version = randomTokenString();
return await User.insert({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
roles,
token_version,
});
}
export async function signup(req: Request, res: Response<TableType>) { export async function signup(req: Request, res: Response<TableType>) {
const { const {
email: _email, email: _email,
@ -88,40 +140,13 @@ export async function signup(req: Request, res: Response<TableType>) {
NcError.badRequest('User already exist'); NcError.badRequest('User already exist');
} }
} else { } else {
let roles: string = OrgUserRoles.CREATOR; await registerNewUserIfAllowed({
if (await User.isFirst()) {
roles = `${OrgUserRoles.CREATOR},${OrgUserRoles.SUPER_ADMIN}`;
// todo: update in nc_store
// roles = 'owner,creator,editor'
Tele.emit('evt', {
evt_type: 'project:invite',
count: 1,
});
} else {
let settings: { invite_only_signup?: boolean } = {};
try {
settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value);
} catch {}
if (settings?.invite_only_signup) {
NcError.badRequest('Not allowed to signup, contact super admin.');
} else {
roles = OrgUserRoles.VIEWER;
}
}
const token_version = randomTokenString();
await User.insert({
firstname, firstname,
lastname, lastname,
email, email,
salt, salt,
password, password,
email_verification_token, email_verification_token,
roles,
token_version,
}); });
} }
user = await User.getByEmail(email); user = await User.getByEmail(email);

1
packages/nocodb/src/lib/meta/api/utilApis.ts

@ -55,6 +55,7 @@ export async function appInfo(req: Request, res: Response) {
ncMin: !!process.env.NC_MIN, ncMin: !!process.env.NC_MIN,
teleEnabled: !process.env.NC_DISABLE_TELE, teleEnabled: !process.env.NC_DISABLE_TELE,
ncSiteUrl: (req as any).ncSiteUrl, ncSiteUrl: (req as any).ncSiteUrl,
ee: Noco.isEE(),
}; };
res.json(result); res.json(result);

5
packages/nocodb/src/lib/meta/helpers/extractProps.ts

@ -1,6 +1,9 @@
import DOMPurify from 'isomorphic-dompurify'; import DOMPurify from 'isomorphic-dompurify';
export function extractProps<T extends object>(body: T, props: string[]): Partial<T> { export function extractProps<T extends object>(
body: T,
props: string[]
): Partial<T> {
// todo: throw error if no props found // todo: throw error if no props found
return props.reduce((o, key) => { return props.reduce((o, key) => {
if (key in body) o[key] = body[key]; if (key in body) o[key] = body[key];

6
packages/nocodb/src/lib/meta/helpers/getHandler.ts

@ -1,14 +1,12 @@
import express from 'express'; import express from 'express';
import { NC_LICENSE_KEY } from '../../constants'; import Noco from '../../Noco';
import Store from '../../models/Store';
export default function getHandler( export default function getHandler(
defaultHandler: express.Handler, defaultHandler: express.Handler,
eeHandler: express.Handler eeHandler: express.Handler
): express.Handler { ): express.Handler {
return async (...args) => { return async (...args) => {
const key = await Store.get(NC_LICENSE_KEY); if (Noco.isEE()) {
if (!key?.value) {
return defaultHandler(...args); return defaultHandler(...args);
} }
return eeHandler(...args); return eeHandler(...args);

5
packages/nocodb/src/lib/meta/helpers/syncMigration.ts

@ -21,7 +21,10 @@ export default async function syncMigration(project: Project): Promise<void> {
} }
} }
export async function syncBaseMigration(project: Project, base: Base): Promise<void> { export async function syncBaseMigration(
project: Project,
base: Base
): Promise<void> {
try { try {
/* create sql-migrator */ /* create sql-migrator */
const migrator = new Migrator(project); const migrator = new Migrator(project);

1
packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts

@ -38,6 +38,7 @@ const up = async (knex: Knex) => {
}; };
const down = async (knex) => { const down = async (knex) => {
await knex.schema.dropTable(MetaTable.SYNC_LOGS);
await knex.schema.dropTable(MetaTable.SYNC_SOURCE); await knex.schema.dropTable(MetaTable.SYNC_SOURCE);
}; };

4
packages/nocodb/src/lib/models/SyncSource.ts

@ -39,7 +39,9 @@ export default class SyncSource {
} }
static async list(projectId: string, baseId?: string, ncMeta = Noco.ncMeta) { static async list(projectId: string, baseId?: string, ncMeta = Noco.ncMeta) {
const condition = baseId ? { project_id: projectId, base_id: baseId } : { project_id: projectId }; const condition = baseId
? { project_id: projectId, base_id: baseId }
: { project_id: projectId };
const syncSources = await ncMeta.metaList( const syncSources = await ncMeta.metaList(
null, null,
null, null,

Loading…
Cancel
Save