Browse Source

fix: Miscellaneous bugs (#9149)

* feat: support pretty print in webhook json

* fix: on renaming table update default view name as well

* fix: show default view name alias as 'Default view'

* fix:  chunk loading error handling

* fix: global error boundary handling improvements

* fix: typo correction

* chore: sentry integration

* refactor: destroy the toast message after reload

* chore: add missing dependencies

Signed-off-by: Pranav C <pranavxc@gmail.com>

* chore: sentry error reporting

Signed-off-by: Pranav C <pranavxc@gmail.com>

* refactor: improved error toast message

Signed-off-by: Pranav C <pranavxc@gmail.com>

* refactor: timeout correction

Signed-off-by: Pranav C <pranavxc@gmail.com>

---------

Signed-off-by: Pranav C <pranavxc@gmail.com>
Co-authored-by: Raju Udava <86527202+dstala@users.noreply.github.com>
pull/9170/head
Pranav C 4 months ago committed by GitHub
parent
commit
d4f76d348a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      packages/nc-gui/components/dlg/ViewDelete.vue
  2. 3
      packages/nc-gui/components/dlg/share-and-collaborate/View.vue
  3. 201
      packages/nc-gui/components/nc/ErrorBoundary.vue
  4. 3
      packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
  5. 1
      packages/nc-gui/composables/useGlobal/state.ts
  6. 1
      packages/nc-gui/composables/useGlobal/types.ts
  7. 1
      packages/nc-gui/lang/en.json
  8. 2
      packages/nc-gui/package.json
  9. 21
      packages/nc-gui/plugins/error-handler.ts
  10. 69
      packages/nc-gui/plugins/sentry.ts
  11. 3
      packages/noco-docs/docs/020.getting-started/050.self-hosted/020.environment-variables.md
  12. 3
      packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/020.environment-variables.md
  13. 184
      packages/nocodb-sdk/src/lib/Api.ts
  14. 2
      packages/nocodb/package.json
  15. 6
      packages/nocodb/src/helpers/webhookHelpers.ts
  16. 10
      packages/nocodb/src/models/Model.ts
  17. 5
      packages/nocodb/src/schema/swagger-v2.json
  18. 5
      packages/nocodb/src/schema/swagger.json
  19. 1
      packages/nocodb/src/services/utils.service.ts
  20. 1110
      pnpm-lock.yaml

3
packages/nc-gui/components/dlg/ViewDelete.vue

@ -52,7 +52,10 @@ async function onDelete() {
class="capitalize text-ellipsis overflow-hidden select-none w-full pl-3" class="capitalize text-ellipsis overflow-hidden select-none w-full pl-3"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }" :style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
> >
<span v-if="view.is_default">{{ $t('labels.defaultView') }}</span>
<span v-else>
{{ view.title }} {{ view.title }}
</span>
</div> </div>
</div> </div>
</template> </template>

3
packages/nc-gui/components/dlg/share-and-collaborate/View.vue

@ -147,7 +147,10 @@ watch(showShareModal, (val) => {
class="max-w-79/100 ml-2 px-2 py-0.5 rounded-md bg-gray-100 capitalize text-ellipsis overflow-hidden" class="max-w-79/100 ml-2 px-2 py-0.5 rounded-md bg-gray-100 capitalize text-ellipsis overflow-hidden"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap' }" :style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap' }"
> >
<span v-if="activeView.is_default">{{ $t('labels.defaultView') }}</span>
<span v-else>
{{ activeView.title }} {{ activeView.title }}
</span>
</div> </div>
</div> </div>
<DlgShareAndCollaborateSharePage /> <DlgShareAndCollaborateSharePage />

201
packages/nc-gui/components/nc/ErrorBoundary.vue

@ -1,6 +1,9 @@
<script lang="ts"> <script lang="ts">
// modified version of default NuxtErrorBoundary component - https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-error-boundary.ts // modified version of default NuxtErrorBoundary component - https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-error-boundary.ts
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import * as Sentry from '@sentry/vue'
const MESSAGE_KEY = 'ErrorMessageKey'
export default { export default {
emits: { emits: {
@ -14,14 +17,114 @@ export default {
const prevError = ref() const prevError = ref()
const errModal = computed(() => !!error.value) const errModal = computed(() => !!error.value)
const key = ref(0) const key = ref(0)
const repeated: Record<string, number> = {}
const isErrorExpanded = ref(false) const isErrorExpanded = ref(false)
const { copy } = useCopy() const { copy } = useCopy()
const reload = () => {
error.value = null
key.value++
// destroy the toast message
message.destroy(MESSAGE_KEY)
}
const navigateToHome = () => {
error.value = null
location.hash = '/'
location.reload()
}
const close = () => {
error.value = null
// destroy the toast message
message.destroy(MESSAGE_KEY)
}
onErrorCaptured((err) => { onErrorCaptured((err) => {
if (import.meta.client && (!nuxtApp.isHydrating || !nuxtApp.payload.serverRendered)) { if (import.meta.client && (!nuxtApp.isHydrating || !nuxtApp.payload.serverRendered)) {
console.error('UI Error :', err) console.error('UI Error :', err)
emit('error', err) emit('error', err)
error.value = err error.value = err
repeated[err.message] = (repeated[err.message] || 0) + 1
// reset repeated count after 30 seconds
setTimeout(() => {
repeated[err.message] = 0
}, 30000)
try {
Sentry.captureException(err)
} catch {
// ignore
}
// destroy any previous toast message to avoid duplicate messages
message.destroy(MESSAGE_KEY)
message.open({
key: MESSAGE_KEY,
content: h('div', [
h(
'div',
{
class: 'flex gap-3 py-1.5',
},
[
h(resolveComponent('GeneralIcon'), { icon: 'error', class: 'text-2xl text-red-500 -mt-1' }),
h('div', { class: 'text-left flex flex-col gap-1' }, [
h('div', { class: 'font-weight-bold' }, 'Page Loading Error'),
h('div', [h('span', { class: 'text-sm text-gray-500' }, 'Something went wrong while loading page!')]),
]),
h(
'div',
{
class: 'flex gap-1 justify-end',
},
[
repeated[err.message] > 2
? h(
resolveComponent('NcButton'),
{
onClick: navigateToHome,
type: 'text',
size: 'xsmall',
class: '!text-sm !px-2 !text-primary',
},
'Home',
)
: h(
resolveComponent('NcButton'),
{
onClick: reload,
type: 'text',
size: 'xsmall',
class: '!text-sm !px-2 !text-primary',
},
'Reload',
),
h(
resolveComponent('NcButton'),
{
onClick: close,
type: 'text',
size: 'xsmall',
class: 'flex items-center gap-1',
},
[h(resolveComponent('GeneralIcon'), { icon: 'close', class: '' })],
),
],
),
],
),
]),
duration: 5,
style: {
position: 'fixed',
bottom: '20px',
right: '20px',
},
})
return false return false
} }
}) })
@ -35,19 +138,6 @@ export default {
} }
} }
const reload = () => {
prevError.value = error.value
error.value = null
key.value++
}
const navigateToHome = () => {
prevError.value = error.value
error.value = null
location.hash = '/'
location.reload()
}
return { return {
errModal, errModal,
error, error,
@ -64,89 +154,4 @@ export default {
<template> <template>
<slot :key="key"></slot> <slot :key="key"></slot>
<slot name="error">
<NcModal
v-if="error"
v-model:visible="errModal"
:class="{ active: errModal }"
:centered="true"
:closable="false"
:footer="null"
>
<div class="w-full flex flex-col gap-1">
<h2 class="text-xl font-semibold">Oops! Something unexpected happened :/</h2>
<p class="mb-0">
<span
>Please report this error in our
<a href="https://discord.gg/5RgZmkW" target="_blank" rel="noopener noreferrer">Discord channel</a>. You can copy the
error message by clicking the "Copy" button below.</span
>
</p>
<span class="cursor-pointer" @click="isErrorExpanded = !isErrorExpanded"
>{{ isErrorExpanded ? 'Hide' : 'Show' }} details
<GeneralIcon
icon="arrowDown"
class="transition-transform transform duration-300"
:class="{
'rotate-180': isErrorExpanded,
}"
/></span>
<div
class="nc-error"
:class="{
active: isErrorExpanded,
}"
>
<div class="nc-left-vertical-bar"></div>
<div class="nc-error-content">
<span class="font-weight-bold">Message: {{ error.message }}</span>
<br />
<div class="text-gray-500 mt-2">{{ error.stack }}</div>
</div>
</div>
<div class="flex justify-end gap-2">
<NcButton size="small" type="secondary" @click="copyError">
<div class="flex items-center gap-1">
<GeneralIcon icon="copy" />
Copy Error
</div>
</NcButton>
<NcButton v-if="!prevError || error.message !== prevError.message" size="small" @click="reload">
<div class="flex items-center gap-1">
<GeneralIcon icon="reload" />
Reload
</div>
</NcButton>
<NcButton v-else size="small" @click="navigateToHome">
<div class="flex items-center gap-1">
<GeneralIcon icon="link" />
Home
</div>
</NcButton>
</div>
</div>
</NcModal>
</slot>
</template> </template>
<style scoped lang="scss">
.nc-error {
@apply flex gap-2 mb-2 max-h-0;
white-space: pre;
transition: max-height 300ms linear;
&.active {
max-height: 250px;
}
.nc-left-vertical-bar {
@apply w-6px min-w-6px rounded min-h-full bg-gray-300;
}
.nc-error-content {
@apply min-w-0 overflow-auto pl-2 flex-shrink;
}
}
</style>

3
packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue

@ -346,7 +346,8 @@ const onFilterLabelClick = () => {
<div class="min-w-5 flex items-center justify-center"> <div class="min-w-5 flex items-center justify-center">
<GeneralViewIcon :meta="view" class="text-gray-500" /> <GeneralViewIcon :meta="view" class="text-gray-500" />
</div> </div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only> <span v-if="view.is_default">{{ $t('labels.defaultView') }}</span>
<NcTooltip v-else class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ view.title }}</template> <template #title>{{ view.title }}</template>
<span>{{ view.title }}</span> <span>{{ view.title }}</span>
</NcTooltip> </NcTooltip>

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

@ -98,6 +98,7 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
oneClick: false, oneClick: false,
baseHasAdmin: false, baseHasAdmin: false,
teleEnabled: true, teleEnabled: true,
errorReportingEnabled: false,
auditEnabled: true, auditEnabled: true,
type: 'nocodb', type: 'nocodb',
version: '0.0.0', version: '0.0.0',

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

@ -22,6 +22,7 @@ export interface AppInfo {
oneClick: boolean oneClick: boolean
baseHasAdmin: boolean baseHasAdmin: boolean
teleEnabled: boolean teleEnabled: boolean
errorReportingEnabled: boolean
auditEnabled: boolean auditEnabled: boolean
type: string type: string
version: string version: string

1
packages/nc-gui/lang/en.json

@ -561,6 +561,7 @@
"directlyInRealTime": "Directly in real time" "directlyInRealTime": "Directly in real time"
}, },
"labels": { "labels": {
"defaultView": "Default view",
"recordInsert": "Record Insert", "recordInsert": "Record Insert",
"recordUpdate": "Record Update", "recordUpdate": "Record Update",
"recordDelete": "Record Delete", "recordDelete": "Record Delete",

2
packages/nc-gui/package.json

@ -40,6 +40,8 @@
"@iconify/vue": "^4.1.2", "@iconify/vue": "^4.1.2",
"@nuxt/image": "^1.3.0", "@nuxt/image": "^1.3.0",
"@pinia/nuxt": "^0.5.1", "@pinia/nuxt": "^0.5.1",
"@sentry/tracing": "^7.72.0",
"@sentry/vue": "^7.72.0",
"@tiptap/extension-link": "^2.4.0", "@tiptap/extension-link": "^2.4.0",
"@tiptap/extension-placeholder": "^2.4.0", "@tiptap/extension-placeholder": "^2.4.0",
"@tiptap/extension-task-list": "2.4.0", "@tiptap/extension-task-list": "2.4.0",

21
packages/nc-gui/plugins/error-handler.ts

@ -3,8 +3,7 @@ export default defineNuxtPlugin((nuxtApp) => {
const MAX_RETRIES = 2 const MAX_RETRIES = 2
const QUERY_PARAM_NAME = 'reload_attempt' const QUERY_PARAM_NAME = 'reload_attempt'
// Handle "Failed to fetch dynamically imported module ..." or similar issues const reload = () => {
nuxtApp.hook('app:chunkError', () => {
const searchParams = new URLSearchParams(window.location.search) const searchParams = new URLSearchParams(window.location.search)
const currentRetry = Number(searchParams.get(QUERY_PARAM_NAME)) || 0 const currentRetry = Number(searchParams.get(QUERY_PARAM_NAME)) || 0
if (currentRetry < MAX_RETRIES) { if (currentRetry < MAX_RETRIES) {
@ -13,5 +12,23 @@ export default defineNuxtPlugin((nuxtApp) => {
// Changing the search also causes a refresh // Changing the search also causes a refresh
window.location.search = searchParams.toString() window.location.search = searchParams.toString()
} }
}
// Handle "Failed to fetch dynamically imported module ..." or similar issues
nuxtApp.hook('app:chunkError', () => {
reload()
})
nuxtApp.hook('app:error', (error) => {
const reload_error_list = [
'error loading dynamically imported module',
'Importing a module script failed',
'Failed to fetch dynamically imported module',
]
for (const message of reload_error_list) {
if (error.message.includes(message)) {
reload()
}
}
}) })
}) })

69
packages/nc-gui/plugins/sentry.ts

@ -0,0 +1,69 @@
// ref: https://localazy.com/blog/nuxt-3-tailwind-i18n-eslint-starter#add-sentry
// https://docs.sentry.io/platforms/javascript/guides/vue/
import * as Sentry from '@sentry/vue'
import { defineNuxtPlugin } from 'nuxt/app'
export default defineNuxtPlugin((nuxtApp) => {
if (isEeUI) return
const { vueApp } = nuxtApp
const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'
if (process.env.CI || process.env.PLAYWRIGHT) {
return
}
if (env !== 'production' && !process.env.NC_ENABLE_DEV_SENTRY) {
return
}
let initialized = false
const init = () => {
// prevent multiple init
if (initialized) return
initialized = true
Sentry.init({
app: [vueApp],
dsn: 'https://64cb4904bcbec03a1b9a0be02a2d10a9@o4505953073889280.ingest.us.sentry.io/4507725383663616',
environment: env,
integrations: [
new Sentry.BrowserTracing({
tracingOrigins: ['*'],
routingInstrumentation: Sentry.vueRouterInstrumentation(nuxtApp.$router),
}),
],
beforeSend(event) {
if (process.env.NODE_ENV === 'production') {
event.extra = event.extra || {}
try {
// set additional context
const appInfo = (nuxtApp.$state as ReturnType<typeof useGlobal>).appInfo.value
event.extra.version = appInfo?.version
} catch {
// ignore
}
return event
}
return null
},
autoSessionTracking: false,
tracesSampleRate: 0.5,
})
}
// load sentry only if enabled
watch(
() => (nuxtApp.$state as ReturnType<typeof useGlobal>).appInfo?.value?.errorReportingEnabled,
(enabled) => {
try {
if (enabled) init()
} catch (e) {
// ignore
}
},
{ immediate: true },
)
})

3
packages/noco-docs/docs/020.getting-started/050.self-hosted/020.environment-variables.md

@ -93,8 +93,9 @@ For production use cases, it is crucial to set all environment variables marked
## Logging & Monitoring ## Logging & Monitoring
| Variable | Mandatory | Description | If Not Set | | Variable | Mandatory | Description | If Not Set |
| -------- | --------- | ----------- | ---------- | | -------- | --------- |---------------------------------------------------------------------------------------|------------|
| `NC_SENTRY_DSN` | No | Data Source Name (DSN) for integrating with Sentry for monitoring and error tracking. | | | `NC_SENTRY_DSN` | No | Data Source Name (DSN) for integrating with Sentry for monitoring and error tracking. | |
| `NC_DISABLE_ERR_REPORTS` | No | Disable default Sentry error reporting. | TRUE |
## Debugging Only ## Debugging Only
| Variable | Mandatory | Description | If Not Set | | Variable | Mandatory | Description | If Not Set |

3
packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/020.environment-variables.md vendored

@ -11,7 +11,7 @@ For production usecases, it is **recommended** to configure
- `NC_REDIS_URL` - `NC_REDIS_URL`
| Variable | Comments | If absent | | Variable | Comments | If absent |
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|
| NC_DB | See our database URLs | A local SQLite will be created in root folder if `NC_DB` is not provided | | NC_DB | See our database URLs | A local SQLite will be created in root folder if `NC_DB` is not provided |
| NC_DB_JSON | Can be used instead of `NC_DB` and value should be valid knex connection JSON | | | NC_DB_JSON | Can be used instead of `NC_DB` and value should be valid knex connection JSON | |
| NC_DB_JSON_FILE | Can be used instead of `NC_DB` and value should be a valid path to knex connection JSON | | | NC_DB_JSON_FILE | Can be used instead of `NC_DB` and value should be a valid path to knex connection JSON | |
@ -31,6 +31,7 @@ For production usecases, it is **recommended** to configure
| NC_REQUEST_BODY_SIZE | Request body size [limit](https://expressjs.com/en/resources/middleware/body-parser.html#limit) | `1048576` | | NC_REQUEST_BODY_SIZE | Request body size [limit](https://expressjs.com/en/resources/middleware/body-parser.html#limit) | `1048576` |
| NC_EXPORT_MAX_TIMEOUT | After NC_EXPORT_MAX_TIMEOUT csv gets downloaded in batches | Default value 5000(in millisecond) will be used | | NC_EXPORT_MAX_TIMEOUT | After NC_EXPORT_MAX_TIMEOUT csv gets downloaded in batches | Default value 5000(in millisecond) will be used |
| NC_DISABLE_TELE | Disable telemetry | | | NC_DISABLE_TELE | Disable telemetry | |
| NC_DISABLE_ERR_REPORTS | Disable default sentry error reporting | |
| NC_DASHBOARD_URL | Custom dashboard url path | `/dashboard` | | NC_DASHBOARD_URL | Custom dashboard url path | `/dashboard` |
| NC_GOOGLE_CLIENT_ID | Google client id to enable google authentication | | | NC_GOOGLE_CLIENT_ID | Google client id to enable google authentication | |
| NC_GOOGLE_CLIENT_SECRET | Google client secret to enable google authentication | | | NC_GOOGLE_CLIENT_SECRET | Google client secret to enable google authentication | |

184
packages/nocodb-sdk/src/lib/Api.ts

@ -256,8 +256,12 @@ export interface AuditRowUpdateReqType {
* Model for Source * Model for Source
*/ */
export interface SourceType { export interface SourceType {
/** Source Name - Default BASE will be null by default */ /** Source Name */
alias?: StringOrNullType; alias?: StringOrNullType;
/** Integration Name */
integration_title?: StringOrNullType;
/** Integration Id */
fk_integration_id?: StringOrNullType;
/** Source Configuration */ /** Source Configuration */
config?: any; config?: any;
/** Is this source enabled */ /** Is this source enabled */
@ -304,6 +308,43 @@ export interface SourceType {
| 'databricks'; | 'databricks';
} }
/**
* Model for Integration
*/
export interface IntegrationType {
/** Source Name - Default BASE will be null by default */
title?: StringOrNullType;
/** Source Configuration */
config?: any;
/** Is this Intgration enabled */
enabled?: BoolType;
/** Unique Integration ID */
id?: string;
/** Unique Workspace ID */
fk_workspace_id?: string;
/**
* The order of the list of sources
* @example 1
*/
order?: number;
/** The base ID that this source belongs to */
base_id?: string;
/** Model for Bool */
is_private?: BoolType;
/** Integration Type */
type?: IntegrationsType;
/**
* DB Type
* @example mysql2
*/
sub_type?: string;
/**
* DB Type
* @example mysql2
*/
created_by?: string;
}
/** /**
* Model for Source List * Model for Source List
*/ */
@ -353,6 +394,35 @@ export interface BaseReqType {
| 'snowflake' | 'snowflake'
| 'sqlite3' | 'sqlite3'
| 'databricks'; | 'databricks';
fk_integration_id?: string;
}
/**
* Integration Type
*/
export enum IntegrationsType {
Database = 'database',
}
/**
* Model for Integration Request
*/
export interface IntegrationReqType {
/**
* Integration Name - Default BASE will be null by default
* @example Integration
*/
title: string;
/** Source Configuration */
config: any;
/** Integration metas */
meta?: any;
/** Integration Type */
type: IntegrationsType;
/** Sub Type */
sub_type?: string;
/** ID of integration to be copied from. Used in Copy Integration. */
copy_from_id?: StringOrNullType;
} }
/** /**
@ -12279,4 +12349,116 @@ export class Api<
...params, ...params,
}), }),
}; };
integration = {
/**
* @description List integrations
*
* @tags Integration
* @name List
* @summary List integrations
* @request GET:/api/v2/meta/integrations
* @response `200` `any` OK
*/
list: (
query?: {
/** Integration Type */
type?: IntegrationsType;
includeDatabaseInfo?: boolean;
limit?: number;
offset?: number;
baseId?: string;
query?: string;
},
params: RequestParams = {}
) =>
this.request<any, any>({
path: `/api/v2/meta/integrations`,
method: 'GET',
query: query,
format: 'json',
...params,
}),
/**
* @description Create integration
*
* @tags Integration
* @name Create
* @summary Create integration
* @request POST:/api/v2/meta/integrations
* @response `200` `IntegrationType` OK
*/
create: (data: IntegrationReqType, params: RequestParams = {}) =>
this.request<IntegrationType, any>({
path: `/api/v2/meta/integrations`,
method: 'POST',
body: data,
type: ContentType.Json,
format: 'json',
...params,
}),
/**
* @description Read integration
*
* @tags Integration
* @name Read
* @summary Read integration
* @request GET:/api/v2/meta/integrations/{integrationId}
* @response `200` `IntegrationType` OK
*/
read: (
integrationId: string,
query?: {
includeConfig?: boolean;
includeSources?: boolean;
},
params: RequestParams = {}
) =>
this.request<IntegrationType, any>({
path: `/api/v2/meta/integrations/${integrationId}`,
method: 'GET',
query: query,
format: 'json',
...params,
}),
/**
* @description Update integration
*
* @tags Integration
* @name Update
* @summary Update integration
* @request PATCH:/api/v2/meta/integrations/{integrationId}
* @response `200` `void` OK
*/
update: (
integrationId: string,
data: IntegrationReqType,
params: RequestParams = {}
) =>
this.request<void, any>({
path: `/api/v2/meta/integrations/${integrationId}`,
method: 'PATCH',
body: data,
type: ContentType.Json,
...params,
}),
/**
* @description Delete integration
*
* @tags Integration
* @name Delete
* @summary Delete integration
* @request DELETE:/api/v2/meta/integrations/{integrationId}
* @response `200` `void` OK
*/
delete: (integrationId: string, params: RequestParams = {}) =>
this.request<void, any>({
path: `/api/v2/meta/integrations/${integrationId}`,
method: 'DELETE',
...params,
}),
};
} }

2
packages/nocodb/package.json

@ -122,7 +122,7 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"mysql2": "^3.9.7", "mysql2": "^3.9.7",
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"nc-help": "0.3.1", "nc-help": "0.3.4",
"nc-lib-gui": "0.251.3", "nc-lib-gui": "0.251.3",
"nc-plugin": "^0.1.6", "nc-plugin": "^0.1.6",
"nestjs-throttler-storage-redis": "^0.4.4", "nestjs-throttler-storage-redis": "^0.4.4",

6
packages/nocodb/src/helpers/webhookHelpers.ts

@ -18,7 +18,11 @@ dayjs.extend(isBetween);
dayjs.extend(isSameOrBefore); dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrAfter);
Handlebars.registerHelper('json', function (context) { Handlebars.registerHelper('json', function (context, pretty = false) {
if (pretty === true || pretty === 'true') {
// Pretty print with 2-space indentation
return JSON.stringify(context, null, 2);
}
return JSON.stringify(context); return JSON.stringify(context);
}); });

10
packages/nocodb/src/models/Model.ts

@ -749,6 +749,16 @@ export default class Model implements TableType {
tableId, tableId,
); );
// get default view and update alias
{
const defaultView = await View.getDefaultView(context, tableId, ncMeta);
if (defaultView) {
await View.update(context, defaultView.id, {
title,
});
}
}
await NocoCache.update(`${CacheScope.MODEL}:${tableId}`, { await NocoCache.update(`${CacheScope.MODEL}:${tableId}`, {
title, title,
table_name, table_name,

5
packages/nocodb/src/schema/swagger-v2.json

@ -10286,6 +10286,7 @@
"defaultLimit": 25, "defaultLimit": 25,
"ncMin": false, "ncMin": false,
"teleEnabled": false, "teleEnabled": false,
"errorReportingEnabled": true,
"auditEnabled": true, "auditEnabled": true,
"ncSiteUrl": "http://localhost:8080", "ncSiteUrl": "http://localhost:8080",
"ee": false, "ee": false,
@ -10330,6 +10331,9 @@
"teleEnabled": { "teleEnabled": {
"type": "boolean" "type": "boolean"
}, },
"errorReportingEnabled": {
"type": "boolean"
},
"auditEnabled": { "auditEnabled": {
"type": "boolean" "type": "boolean"
}, },
@ -10380,6 +10384,7 @@
"defaultLimit": 25, "defaultLimit": 25,
"ncMin": false, "ncMin": false,
"teleEnabled": false, "teleEnabled": false,
"errorReportingEnabled": true,
"auditEnabled": true, "auditEnabled": true,
"ncSiteUrl": "http://localhost:8080", "ncSiteUrl": "http://localhost:8080",
"ee": false, "ee": false,

5
packages/nocodb/src/schema/swagger.json

@ -15787,6 +15787,7 @@
"defaultLimit": 25, "defaultLimit": 25,
"ncMin": false, "ncMin": false,
"teleEnabled": false, "teleEnabled": false,
"errorReportingEnabled": true,
"auditEnabled": true, "auditEnabled": true,
"ncSiteUrl": "http://localhost:8080", "ncSiteUrl": "http://localhost:8080",
"ee": false, "ee": false,
@ -15831,6 +15832,9 @@
"teleEnabled": { "teleEnabled": {
"type": "boolean" "type": "boolean"
}, },
"errorReportingEnabled": {
"type": "boolean"
},
"auditEnabled": { "auditEnabled": {
"type": "boolean" "type": "boolean"
}, },
@ -15881,6 +15885,7 @@
"defaultLimit": 25, "defaultLimit": 25,
"ncMin": false, "ncMin": false,
"teleEnabled": false, "teleEnabled": false,
"errorReportingEnabled": true,
"auditEnabled": true, "auditEnabled": true,
"ncSiteUrl": "http://localhost:8080", "ncSiteUrl": "http://localhost:8080",
"ee": false, "ee": false,

1
packages/nocodb/src/services/utils.service.ts

@ -437,6 +437,7 @@ export class UtilsService {
timezone: defaultConnectionConfig.timezone, timezone: defaultConnectionConfig.timezone,
ncMin: !!process.env.NC_MIN, ncMin: !!process.env.NC_MIN,
teleEnabled: process.env.NC_DISABLE_TELE !== 'true', teleEnabled: process.env.NC_DISABLE_TELE !== 'true',
errorReportingEnabled: process.env.NC_DISABLE_ERR_REPORTS !== 'true',
auditEnabled: process.env.NC_DISABLE_AUDIT !== 'true', auditEnabled: process.env.NC_DISABLE_AUDIT !== 'true',
ncSiteUrl: (param.req as any).ncSiteUrl, ncSiteUrl: (param.req as any).ncSiteUrl,
ee: Noco.isEE(), ee: Noco.isEE(),

1110
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save