diff --git a/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue index a7fb2485e7..ed449b573f 100644 --- a/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue @@ -10,6 +10,8 @@ const props = defineProps<{ const emit = defineEmits(['update:value']) +const { appInfo } = $(useGlobal()) + const vModel = useVModel(props, 'value', emit) const meta = $(inject(MetaInj, ref())) @@ -31,10 +33,10 @@ if (!vModel.value.childTable) vModel.value.childTable = meta?.table_name if (!vModel.value.parentTable) vModel.value.parentTable = vModel.value.rtn || '' if (!vModel.value.parentColumn) vModel.value.parentColumn = vModel.value.rcn || '' -if (!vModel.value.type) vModel.value.type = 'hm' +if (!vModel.value.type) vModel.value.type = 'mm' if (!vModel.value.onUpdate) vModel.value.onUpdate = onUpdateDeleteOptions[0] if (!vModel.value.onDelete) vModel.value.onDelete = onUpdateDeleteOptions[0] -if (!vModel.value.virtual) vModel.value.virtual = sqlUi === SqliteUi +if (!vModel.value.virtual) vModel.value.virtual = appInfo.isCloud || sqlUi === SqliteUi if (!vModel.value.alias) vModel.value.alias = vModel.value.column_name const advancedOptions = $(ref(false)) @@ -55,7 +57,7 @@ const filterOption = (value: string, option: { key: string }) => option.key.toLo
- Has Many + Has Many Many To Many @@ -127,7 +129,9 @@ const filterOption = (value: string, option: { key: string }) => option.key.toLo
- Virtual Relation + Virtual Relation
diff --git a/packages/nc-gui/components/webhook/Editor.vue b/packages/nc-gui/components/webhook/Editor.vue index 37cf556cc4..4e5b0b3d0d 100644 --- a/packages/nc-gui/components/webhook/Editor.vue +++ b/packages/nc-gui/components/webhook/Editor.vue @@ -13,6 +13,7 @@ import { reactive, ref, useApi, + useGlobal, useI18n, useNuxtApp, watch, @@ -32,6 +33,8 @@ const { $e } = useNuxtApp() const { api, isLoading: loading } = useApi() +const { appInfo } = $(useGlobal()) + const meta = inject(MetaInj, ref()) const useForm = Form.useForm @@ -170,16 +173,20 @@ const eventList = [ { text: ['After', 'Delete'], value: ['after', 'delete'] }, ] -const notificationList = [ - { type: 'URL' }, - { type: 'Email' }, - { type: 'Slack' }, - { type: 'Microsoft Teams' }, - { type: 'Discord' }, - { type: 'Mattermost' }, - { type: 'Twilio' }, - { type: 'Whatsapp Twilio' }, -] +const notificationList = computed(() => { + return appInfo.isCloud + ? [{ type: 'URL' }] + : [ + { type: 'URL' }, + { type: 'Email' }, + { type: 'Slack' }, + { type: 'Microsoft Teams' }, + { type: 'Discord' }, + { type: 'Mattermost' }, + { type: 'Twilio' }, + { type: 'Whatsapp Twilio' }, + ] +}) const methodList = [ { title: 'GET' }, @@ -306,6 +313,7 @@ async function onEventChange() { } async function loadPluginList() { + if (appInfo.isCloud) return try { const plugins = (await api.plugin.list()).list! diff --git a/packages/nc-gui/composables/useGlobal/state.ts b/packages/nc-gui/composables/useGlobal/state.ts index df6e886e85..90d01d4c25 100644 --- a/packages/nc-gui/composables/useGlobal/state.ts +++ b/packages/nc-gui/composables/useGlobal/state.ts @@ -99,6 +99,7 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State { version: '0.0.0', ncAttachmentFieldSize: 20, ncMaxAttachmentsAllowed: 10, + isCloud: false, }) /** reactive token payload */ diff --git a/packages/nc-gui/composables/useGlobal/types.ts b/packages/nc-gui/composables/useGlobal/types.ts index 584b2ca9ec..82971cfe66 100644 --- a/packages/nc-gui/composables/useGlobal/types.ts +++ b/packages/nc-gui/composables/useGlobal/types.ts @@ -22,6 +22,7 @@ export interface AppInfo { ee?: boolean ncAttachmentFieldSize: number ncMaxAttachmentsAllowed: number + isCloud: boolean } export interface StoredState { diff --git a/packages/nc-gui/pages/account/index.vue b/packages/nc-gui/pages/account/index.vue index 39ec108112..4958f682cf 100644 --- a/packages/nc-gui/pages/account/index.vue +++ b/packages/nc-gui/pages/account/index.vue @@ -2,8 +2,11 @@ import { navigateTo, useUIPermission } from '#imports' const { isUIAllowed } = useUIPermission() + const $route = useRoute() +const { appInfo } = useGlobal() + const selectedKeys = computed(() => [ /^\/account\/users\/?$/.test($route.fullPath) ? isUIAllowed('superAdminUserManagement') @@ -68,7 +71,7 @@ const openKeys = ref([/^\/account\/users/.test($route.fullPath) && 'users']) +const { appInfo } = useGlobal() + + diff --git a/packages/nocodb/src/lib/controllers/plugin.ctl.ts b/packages/nocodb/src/lib/controllers/plugin.ctl.ts index fb3ed46341..084c0565c0 100644 --- a/packages/nocodb/src/lib/controllers/plugin.ctl.ts +++ b/packages/nocodb/src/lib/controllers/plugin.ctl.ts @@ -17,6 +17,7 @@ export async function pluginTest(req: Request, res: Response) { export async function pluginRead(req: Request, res: Response) { res.json(await pluginService.pluginRead({ pluginId: req.params.pluginId })); } + export async function pluginUpdate( req: Request, res: Response @@ -27,36 +28,48 @@ export async function pluginUpdate( }); res.json(plugin); } + export async function isPluginActive(req: Request, res: Response) { res.json( await pluginService.isPluginActive({ pluginTitle: req.params.pluginTitle }) ); } +const blockInCloudMw = (_req, res, next) => { + if (process.env.NC_CLOUD === 'true') { + res.status(403).send('Not allowed'); + } else next(); +}; + const router = Router({ mergeParams: true }); router.get( '/api/v1/db/meta/plugins', + blockInCloudMw, metaApiMetrics, ncMetaAclMw(pluginList, 'pluginList') ); router.post( '/api/v1/db/meta/plugins/test', metaApiMetrics, + blockInCloudMw, ncMetaAclMw(pluginTest, 'pluginTest') ); router.get( '/api/v1/db/meta/plugins/:pluginId', metaApiMetrics, + blockInCloudMw, ncMetaAclMw(pluginRead, 'pluginRead') ); router.patch( '/api/v1/db/meta/plugins/:pluginId', metaApiMetrics, + blockInCloudMw, ncMetaAclMw(pluginUpdate, 'pluginUpdate') ); router.get( '/api/v1/db/meta/plugins/:pluginTitle/status', metaApiMetrics, + blockInCloudMw, ncMetaAclMw(isPluginActive, 'isPluginActive') ); export default router; diff --git a/packages/nocodb/src/lib/services/column.svc.ts b/packages/nocodb/src/lib/services/column.svc.ts index 3da6cba5ed..397ca76dd3 100644 --- a/packages/nocodb/src/lib/services/column.svc.ts +++ b/packages/nocodb/src/lib/services/column.svc.ts @@ -1549,7 +1549,10 @@ async function createLTARColumn(param: { // todo: create index for virtual relations as well // create index for foreign key in pg - if (param.base.type === 'pg') { + if ( + param.base.type === 'pg' || + (param.column as LinkToAnotherColumnReqType).virtual + ) { await createColumnIndex({ column: new Column({ ...newColumn, diff --git a/packages/nocodb/src/lib/services/hook.svc.ts b/packages/nocodb/src/lib/services/hook.svc.ts index 89e94ef38d..0267ff0f4b 100644 --- a/packages/nocodb/src/lib/services/hook.svc.ts +++ b/packages/nocodb/src/lib/services/hook.svc.ts @@ -1,11 +1,26 @@ import { T } from 'nc-help'; import { validatePayload } from '../meta/api/helpers'; +import { NcError } from '../meta/helpers/catchError'; import { Hook, Model } from '../models'; import { invokeWebhook } from '../meta/helpers/webhookHelpers'; import populateSamplePayload from '../meta/helpers/populateSamplePayload'; import type { HookReqType, HookTestReqType } from 'nocodb-sdk'; +function validateHookPayload(notificationJsonOrObject: string | object) { + let notification: { type?: string } = {}; + try { + notification = + typeof notificationJsonOrObject === 'string' + ? JSON.parse(notificationJsonOrObject) + : notificationJsonOrObject; + } catch {} + + if (notification.type !== 'URL' && process.env.NC_CLOUD === 'true') { + NcError.badRequest('Only URL notification is supported'); + } +} + export async function hookList(param: { tableId: string }) { // todo: pagination return await Hook.list({ fk_model_id: param.tableId }); @@ -17,6 +32,8 @@ export async function hookCreate(param: { }) { validatePayload('swagger.json#/components/schemas/HookReq', param.hook); + validateHookPayload(param.hook.notification); + T.emit('evt', { evt_type: 'webhooks:created' }); // todo: type correction const hook = await Hook.insert({ @@ -37,6 +54,8 @@ export async function hookUpdate(param: { hookId: string; hook: HookReqType }) { T.emit('evt', { evt_type: 'webhooks:updated' }); + validateHookPayload(param.hook.notification); + // todo: correction in swagger return await Hook.update(param.hookId, param.hook as any); } @@ -50,6 +69,8 @@ export async function hookTest(param: { param.hookTest ); + validateHookPayload(param.hookTest.hook?.notification); + const model = await Model.getByIdOrName({ id: param.tableId }); const { @@ -69,6 +90,7 @@ export async function hookTest(param: { return true; } + export async function tableSampleData(param: { tableId: string; operation: 'insert' | 'update'; diff --git a/packages/nocodb/src/lib/services/util.svc.ts b/packages/nocodb/src/lib/services/util.svc.ts index 2887fcff8a..27326e3847 100644 --- a/packages/nocodb/src/lib/services/util.svc.ts +++ b/packages/nocodb/src/lib/services/util.svc.ts @@ -56,6 +56,7 @@ export async function appInfo(param: { req: { ncSiteUrl: string } }) { ee: Noco.isEE(), ncAttachmentFieldSize: NC_ATTACHMENT_FIELD_SIZE, ncMaxAttachmentsAllowed: +(process.env.NC_MAX_ATTACHMENTS_ALLOWED || 10), + isCloud: process.env.NC_CLOUD === 'true', }; return result;