mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 03:56:16 +00:00
fix(custom-request): permission issues (#3306)
* fix(custom-request-plugin): cannot see custom request action in non-root role when acl doesn't set * fix: list all roles * feat: display all roles * feat: support * fix: remove unused code * fix: options is null * fix: translation * fix: migration error --------- Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
1adaa53c2b
commit
8ab69500c7
@ -191,6 +191,7 @@
|
|||||||
"Insert if not exists, or update": "Insert if not exists, or update",
|
"Insert if not exists, or update": "Insert if not exists, or update",
|
||||||
"Determine whether a record exists by the following fields": "Determine whether a record exists by the following fields",
|
"Determine whether a record exists by the following fields": "Determine whether a record exists by the following fields",
|
||||||
"Update": "Update",
|
"Update": "Update",
|
||||||
|
"Update record": "Update record",
|
||||||
"View": "View",
|
"View": "View",
|
||||||
"View record": "View record",
|
"View record": "View record",
|
||||||
"Refresh": "Refresh",
|
"Refresh": "Refresh",
|
||||||
|
@ -200,6 +200,7 @@
|
|||||||
"Action type": "操作类型",
|
"Action type": "操作类型",
|
||||||
"Actions": "操作",
|
"Actions": "操作",
|
||||||
"Update": "更新",
|
"Update": "更新",
|
||||||
|
"Update record": "更新数据",
|
||||||
"View": "查看",
|
"View": "查看",
|
||||||
"View record": "查看数据",
|
"View record": "查看数据",
|
||||||
"Refresh": "刷新",
|
"Refresh": "刷新",
|
||||||
|
@ -13,6 +13,7 @@ export const BlockInitializer = (props) => {
|
|||||||
const s = merge(schema || {}, item.schema || {});
|
const s = merge(schema || {}, item.schema || {});
|
||||||
item?.schemaInitialize?.(s);
|
item?.schemaInitialize?.(s);
|
||||||
insert(s);
|
insert(s);
|
||||||
|
props.onClick?.(s);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Action, useAPIClient, useRequest } from '@nocobase/client';
|
import { Action, useAPIClient, useRequest } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CustomRequestActionDesigner } from './CustomRequestActionDesigner';
|
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { listByCurrentRoleUrl } from '../constants';
|
import { listByCurrentRoleUrl } from '../constants';
|
||||||
import { useCustomizeRequestActionProps } from '../hooks';
|
import { useCustomizeRequestActionProps } from '../hooks';
|
||||||
|
import { CustomRequestActionDesigner } from './CustomRequestActionDesigner';
|
||||||
|
|
||||||
export const CustomRequestActionACLDecorator = (props) => {
|
export const CustomRequestActionACLDecorator = (props) => {
|
||||||
const apiClient = useAPIClient();
|
const apiClient = useAPIClient();
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
SchemaSettingsActionModalItem,
|
SchemaSettingsActionModalItem,
|
||||||
actionSettingsItems,
|
actionSettingsItems,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCurrentRoles,
|
|
||||||
useRequest,
|
useRequest,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -74,14 +73,11 @@ function CustomRequestACL() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentRoles = useCurrentRoles();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SchemaSettingsActionModalItem
|
<SchemaSettingsActionModalItem
|
||||||
title={t('Access Control')}
|
title={t('Access Control')}
|
||||||
schema={CustomRequestACLSchema}
|
schema={CustomRequestACLSchema}
|
||||||
scope={{ currentRoles }}
|
|
||||||
initialValues={{
|
initialValues={{
|
||||||
roles: data?.data?.roles,
|
roles: data?.data?.roles,
|
||||||
}}
|
}}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useCollection, useCollectionFilterOptions, useCompile } from '@nocobase/client';
|
import { useCollection, useCollectionFilterOptions, useCompile } from '@nocobase/client';
|
||||||
import { useTranslation } from '../locale';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { useTranslation } from '../locale';
|
||||||
|
|
||||||
export const useCustomRequestVariableOptions = () => {
|
export const useCustomRequestVariableOptions = () => {
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
@ -16,17 +16,17 @@ export const useCustomRequestVariableOptions = () => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'currentRecord',
|
name: 'currentRecord',
|
||||||
title: t('Current record'),
|
title: t('Current record', { ns: 'client' }),
|
||||||
children: [...fields],
|
children: [...fields],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'currentUser',
|
name: 'currentUser',
|
||||||
title: t('Current user'),
|
title: t('Current user', { ns: 'client' }),
|
||||||
children: userFields,
|
children: userFields,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'currentTime',
|
name: 'currentTime',
|
||||||
title: t('Current time'),
|
title: t('Current time', { ns: 'client' }),
|
||||||
children: null,
|
children: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -24,12 +24,10 @@ export const useCustomizeRequestActionProps = () => {
|
|||||||
const actionSchema = useFieldSchema();
|
const actionSchema = useFieldSchema();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const { fields, getField, getPrimaryKey } = useCollection();
|
const { getPrimaryKey } = useCollection();
|
||||||
const { field, resource, __parent, service } = useBlockRequestContext();
|
const { resource, __parent, service } = useBlockRequestContext();
|
||||||
const { getActiveFieldsName } = useFormActiveFields() || {};
|
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { data, runAsync } = useGetCustomRequest();
|
|
||||||
const actionField = useField();
|
const actionField = useField();
|
||||||
const { setVisible } = useActionContext();
|
const { setVisible } = useActionContext();
|
||||||
const { modal, message } = App.useApp();
|
const { modal, message } = App.useApp();
|
||||||
@ -38,29 +36,14 @@ export const useCustomizeRequestActionProps = () => {
|
|||||||
return {
|
return {
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {};
|
const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {};
|
||||||
const options = data ? data?.data?.options : (await runAsync())?.data?.options;
|
|
||||||
if (!options?.['url']) {
|
|
||||||
return message.error(t('Please configure the request settings first'));
|
|
||||||
}
|
|
||||||
const xAction = actionSchema?.['x-action'];
|
const xAction = actionSchema?.['x-action'];
|
||||||
if (skipValidator !== true && xAction === 'customize:form:request') {
|
if (skipValidator !== true && xAction === 'customize:form:request') {
|
||||||
await form.submit();
|
await form.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
let formValues = {};
|
let formValues = {};
|
||||||
const methods = ['POST', 'PUT', 'PATCH'];
|
if (xAction === 'customize:form:request') {
|
||||||
if (xAction === 'customize:form:request' && methods.includes(options['method'])) {
|
formValues = form.values;
|
||||||
const fieldNames = fields.map((field) => field.name);
|
|
||||||
const values = getFormValues({
|
|
||||||
filterByTk,
|
|
||||||
field,
|
|
||||||
form,
|
|
||||||
fieldNames,
|
|
||||||
getField,
|
|
||||||
resource,
|
|
||||||
actionFields: getActiveFieldsName?.('form') || [],
|
|
||||||
});
|
|
||||||
formValues = values;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
actionField.data ??= {};
|
actionField.data ??= {};
|
||||||
|
@ -27,14 +27,13 @@ export const CustomRequestInitializer: React.FC<any> = (props) => {
|
|||||||
<BlockInitializer
|
<BlockInitializer
|
||||||
{...itemConfig}
|
{...itemConfig}
|
||||||
item={itemConfig}
|
item={itemConfig}
|
||||||
insert={async (s) => {
|
onClick={async (s) => {
|
||||||
await customRequestsResource.updateOrCreate({
|
// create a custom request
|
||||||
|
await customRequestsResource.create({
|
||||||
values: {
|
values: {
|
||||||
key: s['x-uid'],
|
key: s['x-uid'],
|
||||||
},
|
},
|
||||||
filterKeys: ['key'],
|
|
||||||
});
|
});
|
||||||
await props?.insert(s);
|
|
||||||
}}
|
}}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
/>
|
/>
|
||||||
|
@ -10,15 +10,18 @@ export const CustomRequestACLSchema = {
|
|||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
tooltip: generateNTemplate('If not set, all roles can see this action'),
|
tooltip: generateNTemplate('If not set, all roles can see this action'),
|
||||||
},
|
},
|
||||||
'x-component': 'Select',
|
'x-component': 'RemoteSelect',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
|
objectValue: true,
|
||||||
|
service: {
|
||||||
|
resource: 'roles',
|
||||||
|
},
|
||||||
|
manual: false,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
label: 'title',
|
label: 'title',
|
||||||
value: 'name',
|
value: 'name',
|
||||||
},
|
},
|
||||||
objectValue: true,
|
|
||||||
options: '{{ currentRoles }}',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Context, Next } from '@nocobase/actions';
|
import { Context, Next } from '@nocobase/actions';
|
||||||
import { parse } from '@nocobase/utils';
|
import { parse } from '@nocobase/utils';
|
||||||
|
|
||||||
|
import { appendArrayColumn } from '@nocobase/evaluators';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import CustomRequestPlugin from '../plugin';
|
import CustomRequestPlugin from '../plugin';
|
||||||
import { appendArrayColumn } from '@nocobase/evaluators';
|
|
||||||
|
|
||||||
const getHeaders = (headers: Record<string, any>) => {
|
const getHeaders = (headers: Record<string, any>) => {
|
||||||
return Object.keys(headers).reduce((hds, key) => {
|
return Object.keys(headers).reduce((hds, key) => {
|
||||||
@ -84,7 +84,10 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next)
|
|||||||
|
|
||||||
ctx.withoutDataWrapping = true;
|
ctx.withoutDataWrapping = true;
|
||||||
|
|
||||||
const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options;
|
const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options || {};
|
||||||
|
if (!url) {
|
||||||
|
return ctx.throw(400, ctx.t('Please configure the request settings first', { ns: 'custom-request' }));
|
||||||
|
}
|
||||||
let currentRecordValues = {};
|
let currentRecordValues = {};
|
||||||
if (collectionName && typeof currentRecord.id !== 'undefined') {
|
if (collectionName && typeof currentRecord.id !== 'undefined') {
|
||||||
const recordRepo = ctx.db.getRepository(collectionName);
|
const recordRepo = ctx.db.getRepository(collectionName);
|
||||||
|
@ -36,7 +36,7 @@ export class CustomRequestPlugin extends Plugin {
|
|||||||
|
|
||||||
this.app.acl.registerSnippet({
|
this.app.acl.registerSnippet({
|
||||||
name: `ui.${this.name}`,
|
name: `ui.${this.name}`,
|
||||||
actions: ['customRequests:*'],
|
actions: ['customRequests:*', 'roles:list'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.app.acl.allow('customRequests', ['send', 'listByCurrentRole'], 'loggedIn');
|
this.app.acl.allow('customRequests', ['send', 'listByCurrentRole'], 'loggedIn');
|
||||||
|
@ -23,10 +23,14 @@ export default class AddUsersPhoneMigration extends Migration {
|
|||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
|
try {
|
||||||
type: 'unique',
|
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
|
||||||
fields: [field.columnName()],
|
type: 'unique',
|
||||||
});
|
fields: [field.columnName()],
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
this.db.removeCollection('users');
|
this.db.removeCollection('users');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,14 @@ export default class AddUserNameMigration extends Migration {
|
|||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
|
try {
|
||||||
type: 'unique',
|
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
|
||||||
fields: [field.columnName()],
|
type: 'unique',
|
||||||
});
|
fields: [field.columnName()],
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
this.db.removeCollection('users');
|
this.db.removeCollection('users');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user