Browse Source

fix: add custom url update setup

Ramesh Mane 4 days ago
parent
commit
e63118a17d
  1. 70
      packages/nc-gui/components/dlg/share-and-collaborate/SharePage.vue
  2. 1
      packages/nocodb/src/models/Base.ts
  3. 45
      packages/nocodb/src/models/CustomUrl.ts
  4. 11
      packages/nocodb/src/models/View.ts
  5. 7
      packages/nocodb/src/schema/swagger.json
  6. 33
      packages/nocodb/src/services/views.service.ts

70
packages/nc-gui/components/dlg/share-and-collaborate/SharePage.vue

@ -20,6 +20,7 @@ const isUpdating = ref({
public: false,
password: false,
download: false,
customUrl: false,
})
const activeView = computed<(ViewType & { meta: object & Record<string, any> }) | undefined>({
@ -100,6 +101,40 @@ const togglePasswordProtected = async () => {
}
}
const isOpenCustomUrlLocal = ref(false)
const isOpenCustomUrl = computed(() => {
return !!activeView.value?.password || isOpenCustomUrlLocal.value
})
const customUrl = computed({
get: () => (isOpenCustomUrl.value ? activeView.value?.custom_path ?? '' : ''),
set: async (value) => {
if (!activeView.value) return
activeView.value = { ...(activeView.value as any), custom_path: isOpenCustomUrl.value ? value : null }
},
})
const toggleCustomUrl = async () => {
isOpenCustomUrlLocal.value = !isOpenCustomUrl.value
if (!activeView.value) return
if (isUpdating.value.customUrl) return
isUpdating.value.password = true
try {
if (passwordProtected.value) {
activeView.value = { ...(activeView.value as any), custom_path: null }
} else {
activeView.value = { ...(activeView.value as any), custom_path: '' }
}
await updateSharedView()
} finally {
isUpdating.value.password = false
}
}
const withRTL = computed({
get: () => {
if (!activeView.value?.meta) return false
@ -282,6 +317,7 @@ async function updateSharedView() {
await $api.dbViewShare.update(activeView.value.id!, {
meta,
password: activeView.value.password,
...(activeView.value?.custom_path ? { custom_path: activeView.value?.custom_path } : {}),
})
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
@ -290,6 +326,14 @@ async function updateSharedView() {
return true
}
const updateSharedViewWithDebounce = useDebounceFn(
async () => {
await updateSharedView()
},
250,
{ maxWait: 2000 },
)
async function savePreFilledMode() {
await updateSharedView()
}
@ -316,6 +360,32 @@ watchEffect(() => {})
<div class="mt-0.5 border-t-1 border-gray-100 pt-3">
<GeneralCopyUrl v-model:url="url" />
</div>
<div class="flex flex-col justify-between mt-1 py-2 px-3 bg-gray-50 rounded-md">
<div class="flex flex-row items-center justify-between">
<div class="flex text-black">Custom url</div>
<a-switch
v-e="['c:share:view:custom-url:toggle']"
:checked="isOpenCustomUrl"
:loading="isUpdating.customUrl"
class="share-custom-url-toggle !mt-0.25"
data-testid="share-custom-url-toggle"
size="small"
@click="toggleCustomUrl"
/>
</div>
<Transition mode="out-in" name="layout">
<div v-if="isOpenCustomUrl" class="flex gap-2 mt-2 w-2/3">
<a-input
v-model:value="customUrl"
placeholder="Enter custom url"
class="!rounded-lg !py-1 !bg-white"
data-testid="nc-modal-share-view__custom-url"
size="small"
@update:value="updateSharedViewWithDebounce"
/>
</div>
</Transition>
</div>
<div class="flex flex-col justify-between mt-1 py-2 px-3 bg-gray-50 rounded-md">
<div class="flex flex-row items-center justify-between">
<div class="flex text-black">{{ $t('activity.restrictAccessWithPassword') }}</div>

1
packages/nocodb/src/models/Base.ts

@ -38,6 +38,7 @@ export default class Base implements BaseType {
uuid?: string;
password?: string;
roles?: string;
fk_custom_url_id?: string;
constructor(base: Partial<Base>) {
Object.assign(this, base);

45
packages/nocodb/src/models/CustomUrl.ts

@ -7,6 +7,7 @@ import {
RootScopes,
} from '~/utils/globals';
import NocoCache from '~/cache/NocoCache';
import { NcContext } from 'src/interface/config';
export default class CustomUrl {
id?: string;
@ -22,24 +23,24 @@ export default class CustomUrl {
}
public static async get(
params: {
id?: string;
view_id?: string;
custom_path?: string;
},
_context: NcContext,
params: Pick<CustomUrl, 'id' | 'view_id' | 'custom_path'>,
ncMeta = Noco.ncMeta,
) {
const condition = extractProps(params, ['id', 'view_id', 'custom_path']);
return await ncMeta.metaGet2(
const customUrl = await ncMeta.metaGet2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.CUSTOM_URLS,
condition,
);
return customUrl && new CustomUrl(customUrl);
}
public static async getByCustomPath(
_context: NcContext,
customPath: string,
ncMeta = Noco.ncMeta,
) {
@ -67,6 +68,7 @@ export default class CustomUrl {
}
public static async insert(
_context: NcContext,
customUrl: Partial<CustomUrl>,
ncMeta = Noco.ncMeta,
) {
@ -79,20 +81,19 @@ export default class CustomUrl {
'custom_path',
]);
return await ncMeta.metaInsert2(
const insertedCustomUrl = await ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.CUSTOM_URLS,
insertData,
);
return insertedCustomUrl && new CustomUrl(insertedCustomUrl);
}
public static async list(
params: {
fk_workspace_id?: string;
base_id?: string;
fk_model_id?: string;
},
_context: NcContext,
params: Pick<CustomUrl, 'fk_workspace_id' | 'base_id' | 'fk_model_id'>,
ncMeta = Noco.ncMeta,
) {
const condition = extractProps(params, [
@ -110,10 +111,11 @@ export default class CustomUrl {
},
);
return customUrlList;
return customUrlList.map((customUrl) => new CustomUrl(customUrl));
}
public static async update(
_context: NcContext,
id: string,
customUrl: Partial<CustomUrl>,
ncMeta = Noco.ncMeta,
@ -131,4 +133,21 @@ export default class CustomUrl {
id,
);
}
static async delete(
_context: NcContext,
customUrl: Pick<CustomUrl, 'id' | 'view_id'>,
ncMeta = Noco.ncMeta,
) {
const condition = extractProps(customUrl, ['id', 'view_id']);
const res = await ncMeta.metaDelete(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.CUSTOM_URLS,
condition,
);
return res;
}
}

11
packages/nocodb/src/models/View.ts

@ -41,6 +41,7 @@ import {
} from '~/utils/modelUtils';
import { LinkToAnotherRecordColumn } from '~/models';
import { cleanCommandPaletteCache } from '~/helpers/commandPaletteHelpers';
import CustomUrl from './CustomUrl';
const { v4: uuidv4 } = require('uuid');
@ -101,6 +102,7 @@ export default class View implements ViewType {
source_id?: string;
show_system_fields?: boolean;
meta?: any;
fk_custom_url_id?: string;
constructor(data: View) {
Object.assign(this, data);
@ -1247,6 +1249,8 @@ export default class View implements ViewType {
viewId,
);
await CustomUrl.delete(context, { view_id: viewId });
await NocoCache.update(`${CacheScope.VIEW}:${viewId}`, {
uuid: null,
});
@ -1265,6 +1269,7 @@ export default class View implements ViewType {
meta?: any;
owned_by?: string;
created_by?: string;
fk_custom_url_id?: string;
},
includeCreatedByAndUpdateBy = false,
ncMeta = Noco.ncMeta,
@ -1278,6 +1283,7 @@ export default class View implements ViewType {
'password',
'meta',
'uuid',
'fk_custom_url_id',
...(includeCreatedByAndUpdateBy ? ['owned_by', 'created_by'] : []),
]);
@ -1406,6 +1412,11 @@ export default class View implements ViewType {
});
}
}
if (view.fk_custom_url_id) {
await CustomUrl.delete(context, { id: view.fk_custom_url_id });
}
// on update, delete any optimised single query cache
await View.clearSingleQueryCache(context, view.fk_model_id, [view], ncMeta);

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

@ -24957,7 +24957,8 @@
"examples": [
{
"meta": {},
"password": "123456789"
"password": "123456789",
"custom_path": "feedback-form"
}
],
"title": "Shared View Request Model",
@ -24970,6 +24971,10 @@
"password": {
"$ref": "#/components/schemas/StringOrNull",
"description": "Password to restrict access"
},
"custom_path": {
"$ref": "#/components/schemas/StringOrNull",
"description": "Custom url"
}
},
"x-stoplight": {

33
packages/nocodb/src/services/views.service.ts

@ -10,6 +10,7 @@ import { AppHooksService } from '~/services/app-hooks/app-hooks.service';
import { validatePayload } from '~/helpers';
import { NcError } from '~/helpers/catchError';
import { BaseUser, Model, ModelRoleVisibility, View } from '~/models';
import CustomUrl from 'src/models/CustomUrl';
// todo: move
async function xcVisibilityMetaGet(
@ -272,7 +273,36 @@ export class ViewsService {
NcError.viewNotFound(param.viewId);
}
const result = await View.update(context, param.viewId, param.sharedView);
let customUrl: CustomUrl | undefined = await CustomUrl.get(context, {
view_id: view.id,
id: view.fk_custom_url_id,
});
if (customUrl?.id) {
if (param.sharedView.custom_path) {
await CustomUrl.update(context, view.fk_custom_url_id, {
custom_path: param.sharedView.custom_path,
});
} else {
await CustomUrl.delete(context, { id: view.fk_custom_url_id });
customUrl = undefined;
}
} else {
customUrl = await CustomUrl.insert(context, {
fk_workspace_id: view.fk_workspace_id,
base_id: view.base_id,
fk_model_id: view.fk_model_id,
view_id: view.id,
// Todo: add original path
original_path: '',
custom_path: param.sharedView.custom_path,
});
}
const result = await View.update(context, param.viewId, {
...param.sharedView,
fk_custom_url_id: customUrl?.id ?? null,
});
this.appHooksService.emit(AppEvents.SHARED_VIEW_UPDATE, {
user: param.user,
@ -296,6 +326,7 @@ export class ViewsService {
if (!view) {
NcError.viewNotFound(param.viewId);
}
await View.sharedViewDelete(context, param.viewId);
this.appHooksService.emit(AppEvents.SHARED_VIEW_DELETE, {

Loading…
Cancel
Save