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;