Compare commits
10 Commits
2fed7fa4e7
...
7bbd607376
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bbd607376 | |||
| 4f5d5425fc | |||
| d48edbbf5f | |||
| 0d337849c7 | |||
| 717a0ed830 | |||
| 990e7fa226 | |||
| 5a8050145d | |||
| 1cc6e1e85d | |||
| 765c0749bb | |||
| 5080e5fdde |
@@ -18,9 +18,15 @@ class Registry:
|
||||
|
||||
self.current_firm = CurrentFirm.get_current(self.db)
|
||||
|
||||
async def set_user(self, user):
|
||||
def check_user(self, user):
|
||||
for firm in user.firms:
|
||||
if firm.instance == self.instance and firm.firm == self.firm:
|
||||
return True
|
||||
raise PermissionError
|
||||
|
||||
async def set_user(self, user):
|
||||
self.check_user(user)
|
||||
|
||||
partner = await Partner.get_by_user_id(self.db, user.id)
|
||||
partner_entity = await Entity.get(self.db, partner.entity_id)
|
||||
self.user = user
|
||||
@@ -33,26 +39,32 @@ class Registry:
|
||||
async def get_tenant_registry(instance: str, firm: str, db_client=Depends(get_db_client)) -> Registry:
|
||||
registry = Registry(db_client, instance, firm)
|
||||
if await registry.current_firm is None:
|
||||
raise HTTPException(status_code=405, detail=f"Firm needs to be initialized first")
|
||||
raise HTTPException(status_code=404, detail="This firm doesn't exist or you are not allowed to access it.")
|
||||
|
||||
return registry
|
||||
|
||||
async def get_authed_tenant_registry(registry=Depends(get_tenant_registry), user=Depends(get_current_user)) -> Registry:
|
||||
async def get_authed_tenant_registry(instance: str, firm: str, db_client=Depends(get_db_client), user=Depends(get_current_user)) -> Registry:
|
||||
registry = Registry(db_client, instance, firm)
|
||||
try:
|
||||
await registry.set_user(user)
|
||||
registry.check_user(user)
|
||||
except PermissionError:
|
||||
raise HTTPException(status_code=404, detail="This firm doesn't exist or you are not allowed to access it.")
|
||||
|
||||
if await registry.current_firm is None:
|
||||
raise HTTPException(status_code=405, detail=f"Firm needs to be initialized first")
|
||||
|
||||
await registry.set_user(user)
|
||||
return registry
|
||||
|
||||
async def get_uninitialized_registry(instance: str, firm: str, db_client=Depends(get_db_client), user=Depends(get_current_user)) -> Registry:
|
||||
registry = Registry(db_client, instance, firm)
|
||||
if await registry.current_firm is not None:
|
||||
raise HTTPException(status_code=409, detail="Firm configuration already exists")
|
||||
|
||||
try:
|
||||
await registry.set_user(user)
|
||||
registry.check_user(user)
|
||||
except PermissionError:
|
||||
raise HTTPException(status_code=404, detail="This firm doesn't exist or you are not allowed to access it.")
|
||||
|
||||
if await registry.current_firm is not None:
|
||||
raise HTTPException(status_code=409, detail="Firm configuration already exists")
|
||||
|
||||
await registry.set_user(user)
|
||||
return registry
|
||||
|
||||
@@ -67,7 +67,7 @@ class AuthenticationBackendMe(AuthenticationBackend):
|
||||
|
||||
class CookieTransportOauth(CookieTransport):
|
||||
async def get_login_response(self, token: str) -> Response:
|
||||
response = RedirectResponse("/auth/login?oauth=success", status_code=status.HTTP_301_MOVED_PERMANENTLY)
|
||||
response = RedirectResponse("/login?oauth=success", status_code=status.HTTP_301_MOVED_PERMANENTLY)
|
||||
return self._set_login_cookie(response, token)
|
||||
|
||||
@staticmethod
|
||||
|
||||
13
gui/rpk-gui/package-lock.json
generated
13
gui/rpk-gui/package-lock.json
generated
@@ -43,6 +43,7 @@
|
||||
"mui-tiptap": "^1.18.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-hook-form": "^7.30.0",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-router": "^7.0.2"
|
||||
@@ -9180,6 +9181,18 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz",
|
||||
"integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.56.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.1.tgz",
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"mui-tiptap": "^1.18.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-hook-form": "^7.30.0",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-router": "^7.0.2"
|
||||
|
||||
@@ -6,11 +6,7 @@ import { RefineSnackbarProvider, useNotificationProvider } from "@refinedev/mui"
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import GlobalStyles from "@mui/material/GlobalStyles";
|
||||
import HistoryEduIcon from '@mui/icons-material/HistoryEdu';
|
||||
import routerBindings, {
|
||||
CatchAllNavigate,
|
||||
DocumentTitleHandler,
|
||||
UnsavedChangesNotifier,
|
||||
} from "@refinedev/react-router";
|
||||
import routerBindings, { DocumentTitleHandler, UnsavedChangesNotifier } from "@refinedev/react-router";
|
||||
import { BrowserRouter, Outlet, Route, Routes } from "react-router";
|
||||
import authProvider from "./providers/auth-provider";
|
||||
import dataProvider from "./providers/data-provider";
|
||||
@@ -61,7 +57,8 @@ function App() {
|
||||
queries: {
|
||||
retry: (failureCount, error) => {
|
||||
// @ts-ignore
|
||||
if (error.statusCode >= 400 && error.statusCode <= 499) {
|
||||
const status = error.statusCode ? error.statusCode : error.status
|
||||
if (status >= 400 && status<= 499) {
|
||||
return false
|
||||
}
|
||||
return failureCount < 4
|
||||
@@ -76,7 +73,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route
|
||||
element={(
|
||||
<Authenticated key="authenticated-routes" redirectOnFail="/login" fallback={<CatchAllNavigate to="/login"/>}>
|
||||
<Authenticated key="authenticated-routes" fallback={<Login />}>
|
||||
<Outlet />
|
||||
</Authenticated>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { IFirm } from "../interfaces";
|
||||
import { useParams } from "react-router";
|
||||
import { useOne } from "@refinedev/core";
|
||||
import { CircularProgress } from "@mui/material";
|
||||
import { FirmInitForm } from "../pages/firm";
|
||||
import { Header } from "../components";
|
||||
|
||||
type FirmContextType = {
|
||||
currentFirm: IFirm,
|
||||
@@ -19,19 +21,27 @@ export const FirmContextProvider: React.FC<PropsWithChildren> = ({ children }: P
|
||||
const { data, isError, error, isLoading } = useOne({resource: 'firm', id: `${instance}/${firm}/`, errorNotification: false});
|
||||
|
||||
if (instance === undefined || firm === undefined) {
|
||||
return "Error"
|
||||
throw({statusCode: 400});
|
||||
}
|
||||
const currentFirm: IFirm = { instance, firm }
|
||||
|
||||
if (isLoading) {
|
||||
return <CircularProgress />
|
||||
}
|
||||
|
||||
let value: FirmContextType = {
|
||||
currentFirm: {instance, firm}
|
||||
if (isError && error) {
|
||||
if (error.statusCode == 405) {
|
||||
return <><Header /><FirmInitForm currentFirm={currentFirm} /></>
|
||||
}
|
||||
if (!isError || error?.statusCode != 405) {
|
||||
value.currentFirm.entity = data?.data.entity;
|
||||
value.partnerMap = new Map(data?.data.partner_list.map((item: any) => [item.id, item.label]));
|
||||
if (error.statusCode == 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
currentFirm.entity = data?.data.entity;
|
||||
let value: FirmContextType = {
|
||||
currentFirm: currentFirm,
|
||||
partnerMap: new Map(data?.data.partner_list.map((item: any) => [item.id, item.label])),
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,6 +2,7 @@ import { RJSFSchema } from '@rjsf/utils';
|
||||
import i18n from '../../../i18n'
|
||||
import { JSONSchema7Definition } from "json-schema";
|
||||
import { GridColDef } from "@mui/x-data-grid";
|
||||
import { GridColType } from "@mui/x-data-grid/models/colDef/gridColType";
|
||||
|
||||
const API_URL = "/api/v1";
|
||||
|
||||
@@ -120,20 +121,34 @@ function buildColumns (rawSchemas: RJSFSchema, resourceName: string, prefix: str
|
||||
const subresourceName = get_reference_name(prop.items);
|
||||
result = result.concat(buildColumns(rawSchemas, subresourceName, prefix ? `${prefix}.${prop_name}` : prop_name))
|
||||
} else {
|
||||
let column: GridColDef = {
|
||||
field: prefix ? `${prefix}.${prop_name}` : prop_name,
|
||||
headerName: i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(prop_name)}`, prop.title) as string,
|
||||
valueGetter: (value: any, row: any ) => {
|
||||
if (prefix === undefined) {
|
||||
return value;
|
||||
let valueGetter: undefined|((value: any, row: any) => any) = undefined;
|
||||
let type: GridColType = "string";
|
||||
if (is_array(prop)) {
|
||||
valueGetter = (value: any[], row: any ) => {
|
||||
return value.concat(".");
|
||||
}
|
||||
|
||||
} else if (prefix !== undefined) {
|
||||
valueGetter = (value: any, row: any ) => {
|
||||
let parent = row;
|
||||
for (const column of prefix.split(".")) {
|
||||
parent = parent[column];
|
||||
for (const col of prefix.split(".")) {
|
||||
parent = parent[col];
|
||||
}
|
||||
return parent ? parent[prop_name] : "";
|
||||
}
|
||||
} else {
|
||||
if (prop.type == "string" && prop.format == "date-time") {
|
||||
type = "dateTime"
|
||||
valueGetter = (value: string) => new Date(value)
|
||||
}
|
||||
}
|
||||
if (prop.type == "string" && prop.format == "date-time") {
|
||||
|
||||
}
|
||||
const column: GridColDef = {
|
||||
field: prefix ? `${prefix}.${prop_name}` : prop_name,
|
||||
headerName: i18n.t(`schemas.${shortResourceName}.${convertCamelToSnake(prop_name)}`, prop.title) as string,
|
||||
type: type,
|
||||
valueGetter: valueGetter
|
||||
}
|
||||
result.push(column);
|
||||
}
|
||||
|
||||
4
gui/rpk-gui/src/pages/ErrorPage.tsx
Normal file
4
gui/rpk-gui/src/pages/ErrorPage.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
export const Error404Page = () => {
|
||||
return <h2>EROR NO FUND</h2>
|
||||
};
|
||||
@@ -24,7 +24,7 @@ const ListEntity = () => {
|
||||
const columns = [
|
||||
{ field: "entity_data.type", column: { width: 110 }},
|
||||
{ field: "label", column: { flex: 1 }},
|
||||
{ field: "updated_at", column: { flex: 1 }},
|
||||
{ field: "updated_at", column: { width: 160 }},
|
||||
];
|
||||
return <List<Entity> resource={`entities`} schemaName={"Entity"} columns={columns} />
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ const Edit = <T,>(props: EditProps) => {
|
||||
return <Navigate to="../" />
|
||||
}
|
||||
|
||||
if (query.error?.status == 404) {
|
||||
throw query.error
|
||||
}
|
||||
|
||||
const record = query.data.data;
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -54,7 +54,7 @@ const List = <T extends GridValidRowModel>(props: ListProps) => {
|
||||
const { currentFirm } = useContext(FirmContext);
|
||||
const resourceBasePath = `firm/${currentFirm.instance}/${currentFirm.firm}`
|
||||
|
||||
const { dataGridProps } = useDataGrid<T>({
|
||||
const { dataGridProps, tableQueryResult } = useDataGrid<T>({
|
||||
resource: `${resourceBasePath}/${resource}`,
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
@@ -85,6 +85,10 @@ const List = <T extends GridValidRowModel>(props: ListProps) => {
|
||||
return <CircularProgress />
|
||||
}
|
||||
|
||||
if (tableQueryResult.error?.status == 404) {
|
||||
throw tableQueryResult.error
|
||||
}
|
||||
|
||||
return (
|
||||
<RefineList>
|
||||
<Link to={"create"} >
|
||||
@@ -94,7 +98,7 @@ const List = <T extends GridValidRowModel>(props: ListProps) => {
|
||||
{...dataGridProps}
|
||||
columns={columnSchema.columns}
|
||||
onRowClick={handleRowClick}
|
||||
pageSizeOptions={[10, 15, 20, 50, 100]}
|
||||
pageSizeOptions={[10, 15, 25, 50, 100]}
|
||||
initialState={{
|
||||
columns: {
|
||||
columnVisibilityModel: columnSchema.columnVisibilityModel
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Route, Routes, Link } from "react-router";
|
||||
import React, { useContext } from "react";
|
||||
import { useForm, useOne, useTranslation } from "@refinedev/core";
|
||||
import { FirmContext, FirmContextProvider } from "../../contexts/FirmContext";
|
||||
import React from "react";
|
||||
import { useForm, useTranslation } from "@refinedev/core";
|
||||
import { FirmContextProvider } from "../../contexts/FirmContext";
|
||||
import { Header } from "../../components";
|
||||
import { CrudForm } from "../../lib/crud/components/crud-form";
|
||||
import { IFirm } from "../../interfaces";
|
||||
@@ -10,11 +10,14 @@ import { ContractRoutes } from "./ContractRoutes";
|
||||
import { DraftRoutes } from "./DraftRoutes";
|
||||
import { TemplateRoutes } from "./TemplateRoutes";
|
||||
import { ProvisionRoutes } from "./ProvisionRoutes";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { Error404Page } from "../ErrorPage";
|
||||
|
||||
export const FirmRoutes = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/:instance/:firm/*" element={
|
||||
<ErrorBoundary fallback={<><Header /><Error404Page /></>} >
|
||||
<FirmContextProvider>
|
||||
<Header />
|
||||
<Routes>
|
||||
@@ -26,6 +29,7 @@ export const FirmRoutes = () => {
|
||||
<Route path="/contracts/*" element={ <ContractRoutes /> } />
|
||||
</Routes>
|
||||
</FirmContextProvider>
|
||||
</ErrorBoundary>
|
||||
} />
|
||||
</Routes>
|
||||
);
|
||||
@@ -51,7 +55,7 @@ type FirmInitFormPros = {
|
||||
currentFirm: IFirm
|
||||
}
|
||||
|
||||
const FirmInitForm = (props: FirmInitFormPros) => {
|
||||
export const FirmInitForm = (props: FirmInitFormPros) => {
|
||||
const { currentFirm } = props;
|
||||
const { translate: t } = useTranslation();
|
||||
const resourceBasePath = `firm`
|
||||
|
||||
@@ -12,7 +12,7 @@ const DEFAULT_LOGIN_REDIRECT = "/hub"
|
||||
const authProvider: AuthProvider = {
|
||||
login: async ({ providerName, email, password }) => {
|
||||
const to_param = findGetParameter("to");
|
||||
const redirect = to_param === null ? DEFAULT_LOGIN_REDIRECT : to_param
|
||||
const redirect = to_param === null ? getLoginRedirect() : to_param
|
||||
if (providerName) {
|
||||
let scope = {};
|
||||
if (providerName === "google") {
|
||||
@@ -61,18 +61,22 @@ const authProvider: AuthProvider = {
|
||||
const response = await fetch(`${API_URL}/hub/auth/logout`, { method: "POST" });
|
||||
if (response.status == 204 || response.status == 401) {
|
||||
forget_user();
|
||||
return {
|
||||
success: true,
|
||||
redirectTo: "/",
|
||||
};
|
||||
return { success: true };
|
||||
}
|
||||
return { success: false };
|
||||
},
|
||||
check: async () => {
|
||||
if (get_user() == null) {
|
||||
const user = get_user();
|
||||
if (user == null || isEmpty(user)) {
|
||||
const user_data = await get_me();
|
||||
|
||||
if (user_data) {
|
||||
store_user(user_data)
|
||||
return { authenticated: true }
|
||||
}
|
||||
|
||||
return {
|
||||
authenticated: false,
|
||||
redirectTo: "/login",
|
||||
logout: true
|
||||
}
|
||||
}
|
||||
@@ -84,11 +88,7 @@ const authProvider: AuthProvider = {
|
||||
return user;
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_URL}/hub/users/me`);
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
return null;
|
||||
}
|
||||
const user_data = await response.json();
|
||||
const user_data = get_me()
|
||||
store_user(user_data)
|
||||
|
||||
return user_data;
|
||||
@@ -154,9 +154,8 @@ const authProvider: AuthProvider = {
|
||||
if (error?.status === 401) {
|
||||
forget_user();
|
||||
return {
|
||||
redirectTo: "/login",
|
||||
logout: true,
|
||||
error: { message: "Authentication required" },
|
||||
logout: true,
|
||||
} as OnErrorResponse;
|
||||
}
|
||||
else if (error?.status === 403) {
|
||||
@@ -170,6 +169,15 @@ const authProvider: AuthProvider = {
|
||||
},
|
||||
};
|
||||
|
||||
async function get_me() {
|
||||
const response = await fetch(`${API_URL}/hub/users/me`);
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
return null;
|
||||
}
|
||||
const user_data = await response.json();
|
||||
return user_data
|
||||
}
|
||||
|
||||
function store_user(user: any) {
|
||||
localStorage.setItem(LOCAL_STORAGE_USER_KEY, JSON.stringify(user));
|
||||
}
|
||||
@@ -200,4 +208,11 @@ function findGetParameter(parameterName: string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLoginRedirect() {
|
||||
if (location.pathname == "/login") {
|
||||
return DEFAULT_LOGIN_REDIRECT
|
||||
}
|
||||
|
||||
return location.pathname + location.search;
|
||||
}
|
||||
export default authProvider;
|
||||
|
||||
@@ -2,6 +2,18 @@ import type { DataProvider, HttpError } from "@refinedev/core";
|
||||
|
||||
const API_URL = "/api/v1";
|
||||
|
||||
function handleErrors(response: { status: number, statusText: string }) {
|
||||
let message = response.statusText
|
||||
if (response.status == 405) {
|
||||
message = "Resource is not ready";
|
||||
}
|
||||
const error: HttpError = {
|
||||
message: message,
|
||||
statusCode: response.status,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const dataProvider: DataProvider = {
|
||||
getOne: async ({ resource, id, meta }) => {
|
||||
if (id === "") {
|
||||
@@ -9,14 +21,7 @@ const dataProvider: DataProvider = {
|
||||
}
|
||||
const response = await fetch(`${API_URL}/${resource}/${id}`);
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
if (response.status == 405) {
|
||||
const error: HttpError = {
|
||||
message: "Resource is not ready",
|
||||
statusCode: 405,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
}
|
||||
throw response;
|
||||
return handleErrors(response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
@@ -35,17 +40,10 @@ const dataProvider: DataProvider = {
|
||||
});
|
||||
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
if (response.status == 405) {
|
||||
const error: HttpError = {
|
||||
message: "Resource is not ready",
|
||||
statusCode: 405,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
return handleErrors(response);
|
||||
}
|
||||
throw response;
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
const data = await response.json();
|
||||
return { data };
|
||||
},
|
||||
getList: async ({ resource, pagination, filters, sorters, meta }) => {
|
||||
@@ -72,14 +70,7 @@ const dataProvider: DataProvider = {
|
||||
const response = await fetch(`${API_URL}/${resource}/?${params.toString()}`);
|
||||
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
if (response.status == 405) {
|
||||
const error: HttpError = {
|
||||
message: "Resource is not ready",
|
||||
statusCode: 405,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
}
|
||||
throw response;
|
||||
return handleErrors(response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
@@ -105,18 +96,10 @@ const dataProvider: DataProvider = {
|
||||
});
|
||||
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
if (response.status == 405) {
|
||||
const error: HttpError = {
|
||||
message: "Resource is not ready",
|
||||
statusCode: 405,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
}
|
||||
throw response;
|
||||
return handleErrors(response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return { data };
|
||||
},
|
||||
deleteOne: async ({ resource, id, variables, meta }) => {
|
||||
@@ -125,21 +108,11 @@ const dataProvider: DataProvider = {
|
||||
});
|
||||
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
if (response.status == 405) {
|
||||
const error: HttpError = {
|
||||
message: "Resource is not ready",
|
||||
statusCode: 405,
|
||||
};
|
||||
return Promise.reject(error);
|
||||
}
|
||||
throw response;
|
||||
return handleErrors(response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
data
|
||||
};
|
||||
return { data };
|
||||
},
|
||||
getApiUrl: () => API_URL,
|
||||
// Optional methods:
|
||||
|
||||
Reference in New Issue
Block a user