authhero 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/release.yml +34 -0
  4. package/.prettierignore +3 -0
  5. package/.prettierrc.json +1 -0
  6. package/README.md +9 -4
  7. package/apps/react-admin/.eslintrc.js +21 -0
  8. package/apps/react-admin/README.md +50 -0
  9. package/apps/react-admin/index.html +125 -0
  10. package/apps/react-admin/package.json +45 -0
  11. package/apps/react-admin/prettier.config.js +1 -0
  12. package/apps/react-admin/public/favicon.ico +0 -0
  13. package/apps/react-admin/public/manifest.json +15 -0
  14. package/apps/react-admin/src/App.spec.tsx +39 -0
  15. package/apps/react-admin/src/App.tsx +75 -0
  16. package/apps/react-admin/src/Layout.tsx +12 -0
  17. package/apps/react-admin/src/TenantsApp.tsx +21 -0
  18. package/apps/react-admin/src/auth0DataProvider.ts +220 -0
  19. package/apps/react-admin/src/authProvider.ts +42 -0
  20. package/apps/react-admin/src/components/TenantAppBar.tsx +46 -0
  21. package/apps/react-admin/src/components/TenantLayout.tsx +17 -0
  22. package/apps/react-admin/src/components/applications/create.tsx +29 -0
  23. package/apps/react-admin/src/components/applications/edit.tsx +40 -0
  24. package/apps/react-admin/src/components/applications/index.ts +3 -0
  25. package/apps/react-admin/src/components/applications/list.tsx +37 -0
  26. package/apps/react-admin/src/components/common/DateAgo.tsx +6 -0
  27. package/apps/react-admin/src/components/common/JsonOutput.tsx +3 -0
  28. package/apps/react-admin/src/components/common/index.ts +1 -0
  29. package/apps/react-admin/src/components/connections/create.tsx +11 -0
  30. package/apps/react-admin/src/components/connections/edit.tsx +66 -0
  31. package/apps/react-admin/src/components/connections/index.ts +3 -0
  32. package/apps/react-admin/src/components/connections/list.tsx +15 -0
  33. package/apps/react-admin/src/components/domains/create.tsx +11 -0
  34. package/apps/react-admin/src/components/domains/edit.tsx +49 -0
  35. package/apps/react-admin/src/components/domains/index.ts +3 -0
  36. package/apps/react-admin/src/components/domains/list.tsx +15 -0
  37. package/apps/react-admin/src/components/listActions/PostListActions.tsx +10 -0
  38. package/apps/react-admin/src/components/tenants/create.tsx +15 -0
  39. package/apps/react-admin/src/components/tenants/edit.tsx +51 -0
  40. package/apps/react-admin/src/components/tenants/index.ts +2 -0
  41. package/apps/react-admin/src/components/tenants/list.tsx +48 -0
  42. package/apps/react-admin/src/components/users/create.tsx +15 -0
  43. package/apps/react-admin/src/components/users/edit.tsx +108 -0
  44. package/apps/react-admin/src/components/users/index.ts +3 -0
  45. package/apps/react-admin/src/components/users/list.tsx +30 -0
  46. package/apps/react-admin/src/data.json +121 -0
  47. package/apps/react-admin/src/dataProvider.ts +48 -0
  48. package/apps/react-admin/src/index.tsx +30 -0
  49. package/apps/react-admin/src/lib/logs.ts +19 -0
  50. package/apps/react-admin/tsconfig.json +9 -0
  51. package/apps/react-admin/tsconfig.node.json +4 -0
  52. package/apps/react-admin/vercel.json +20 -0
  53. package/apps/react-admin/vite.config.ts +19 -0
  54. package/eslint.config.mjs +25 -0
  55. package/package.json +22 -16
  56. package/packages/authhero/CHANGELOG.md +7 -0
  57. package/packages/authhero/README.md +9 -0
  58. package/packages/authhero/package.json +25 -0
  59. package/packages/authhero/src/bun.ts +16 -0
  60. package/packages/authhero/src/index.ts +34 -0
  61. package/packages/authhero/src/routes/oauth2/index.ts +1 -0
  62. package/packages/authhero/src/routes/oauth2/well-known.ts +179 -0
  63. package/{src → packages/authhero/src}/types/Bindings.ts +3 -0
  64. package/packages/authhero/src/types/JWKS.ts +37 -0
  65. package/packages/authhero/src/types/Variables.ts +1 -0
  66. package/packages/authhero/src/types/index.ts +3 -0
  67. package/packages/authhero/src/vite-env.d.ts +1 -0
  68. package/packages/authhero/tsconfig.json +11 -0
  69. package/packages/authhero/tsconfig.node.json +4 -0
  70. package/packages/create-authhero/package.json +22 -0
  71. package/packages/create-authhero/src/index.ts +72 -0
  72. package/packages/create-authhero/src/vite-env.d.ts +1 -0
  73. package/packages/create-authhero/templates/sqlite/package.json +17 -0
  74. package/packages/create-authhero/templates/sqlite/src/index.ts +19 -0
  75. package/packages/create-authhero/templates/sqlite/yarn.lock +48 -0
  76. package/packages/create-authhero/tsconfig.json +9 -0
  77. package/packages/create-authhero/tsconfig.node.json +4 -0
  78. package/packages/create-authhero/vite.config.ts +14 -0
  79. package/pnpm-workspace.yaml +3 -0
  80. package/tsconfig.json +28 -8
  81. package/tsconfig.node.json +7 -2
  82. package/dist/authhero.js +0 -4912
  83. package/dist/index.d.ts +0 -4
  84. package/dist/index.d.ts.map +0 -1
  85. package/dist/types/Bindings.d.ts +0 -5
  86. package/dist/types/Bindings.d.ts.map +0 -1
  87. package/src/index.ts +0 -13
  88. /package/{src → apps/react-admin/src}/vite-env.d.ts +0 -0
  89. /package/{vite.config.ts → packages/authhero/vite.config.ts} +0 -0
@@ -0,0 +1,220 @@
1
+ import querystring from "query-string";
2
+ import { fetchUtils, DataProvider } from "ra-core";
3
+ import { UpdateParams } from "react-admin";
4
+
5
+ function removeExtraFields(params: UpdateParams) {
6
+ // delete params.data?.id; // this is required for patch... but not for put?
7
+ delete params.data?.tenant_id;
8
+ delete params.data?.updated_at;
9
+ delete params.data?.created_at;
10
+ delete params.data?.identities;
11
+
12
+ // hmmmmm, this is an issue we have here with mismatching structure?
13
+ // seems like we need to modify our endpoints to accept connections.
14
+ // TBD with Markus
15
+ delete params.data?.connections;
16
+
17
+ // actually Auth0 does not require this for patching. seems dangerous not to rely on an auto-id
18
+ // as may get rejected for having the same id
19
+ delete params.data?.id;
20
+ // for user we don't want to include this
21
+ delete params.data?.user_id;
22
+
23
+ // extra user fields
24
+ delete params.data?.last_login;
25
+ delete params.data?.provider;
26
+
27
+ // Remove empty properties
28
+ Object.keys(params.data).forEach((key) => {
29
+ if (params.data[key] === undefined) {
30
+ delete params.data[key];
31
+ }
32
+ });
33
+
34
+ return params;
35
+ }
36
+
37
+ function getIdKeyFromResource(resource: string) {
38
+ switch (resource) {
39
+ case "connections":
40
+ return "connnection_id";
41
+ case "domains":
42
+ return "domain_id";
43
+ case "users":
44
+ return "user_id";
45
+ case "logs":
46
+ return "log_id";
47
+ case "tenants":
48
+ return "tenant_id";
49
+ case "applications":
50
+ return "application_id";
51
+ default:
52
+ throw new Error(`unknown resource ${resource}`);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Maps react-admin queries to the auth0 mamagement api
58
+ */
59
+ export default (
60
+ apiUrl: string,
61
+ httpClient = fetchUtils.fetchJson,
62
+ tenantId?: string,
63
+ ): DataProvider => ({
64
+ getList: async (resource, params) => {
65
+ const { page, perPage } = params.pagination || { page: 1, perPage: 10 };
66
+ const { field, order } = params.sort || { field: "id", order: "ASC" };
67
+
68
+ const query = {
69
+ include_totals: true,
70
+ page: page - 1,
71
+ per_page: perPage,
72
+ sort: `${field}:${order === "DESC" ? "-1" : "1"}`,
73
+ q: params.filter.q,
74
+ };
75
+ const url = `${apiUrl}/api/v2/${resource}?${querystring.stringify(query)}`;
76
+
77
+ const headers = new Headers();
78
+
79
+ if (tenantId) {
80
+ headers.set("tenant-id", tenantId);
81
+ }
82
+
83
+ const res = await httpClient(url, { headers });
84
+
85
+ return {
86
+ data: res.json[resource].map((item: any) => ({
87
+ id: item[getIdKeyFromResource(resource)],
88
+ ...item,
89
+ })),
90
+ total: res.json.length,
91
+ };
92
+ },
93
+
94
+ getOne: (resource, params) => {
95
+ const headers = new Headers();
96
+
97
+ if (tenantId) {
98
+ headers.set("tenant-id", tenantId);
99
+ }
100
+
101
+ return httpClient(`${apiUrl}/api/v2/${resource}/${params.id}`, {
102
+ headers,
103
+ }).then(({ json }) => ({
104
+ data: {
105
+ id: json[getIdKeyFromResource(resource)],
106
+ ...json,
107
+ },
108
+ }));
109
+ },
110
+
111
+ getMany: (resource, params) => {
112
+ const query = `id:(${params.ids.join(" ")})})`;
113
+
114
+ const url = `${apiUrl}/api/v2/${resource}?q=${query}`;
115
+ return httpClient(url).then(({ json }) => ({
116
+ data: {
117
+ id: json[getIdKeyFromResource(resource)],
118
+ ...json,
119
+ },
120
+ }));
121
+ },
122
+
123
+ getManyReference: async (resource, params) => {
124
+ const { page, perPage } = params.pagination;
125
+ const { field, order } = params.sort;
126
+
127
+ if (resource !== "logs") {
128
+ return Promise.reject(
129
+ "not supporting getManyReference for anything but resource logs",
130
+ );
131
+ }
132
+
133
+ const query = {
134
+ include_totals: true,
135
+ page: page - 1,
136
+ per_page: perPage,
137
+ sort: `${field}:${order === "DESC" ? "-1" : "1"}`,
138
+ q: `user_id:${params.id}`,
139
+ };
140
+
141
+ const headers = new Headers();
142
+
143
+ if (tenantId) {
144
+ headers.set("tenant-id", tenantId);
145
+ }
146
+
147
+ const url = `${apiUrl}/api/v2/${resource}?${querystring.stringify(query)}`;
148
+
149
+ const res = await httpClient(url, { headers });
150
+
151
+ return {
152
+ data: res.json.logs.map((item: any) => ({
153
+ id: item[getIdKeyFromResource(resource)],
154
+ ...item,
155
+ })),
156
+ total: res.json.length,
157
+ };
158
+ },
159
+
160
+ update: (resource, params) => {
161
+ const headers = new Headers();
162
+
163
+ if (tenantId) {
164
+ headers.set("tenant-id", tenantId);
165
+ }
166
+
167
+ const cleanParams = removeExtraFields(params);
168
+
169
+ return httpClient(`${apiUrl}/api/v2/${resource}/${params.id}`, {
170
+ headers,
171
+ method: "PATCH",
172
+ body: JSON.stringify(cleanParams.data),
173
+ }).then(({ json }) => ({ data: json }));
174
+ },
175
+
176
+ updateMany: () => Promise.reject("not supporting updateMany"),
177
+
178
+ create: async (resource, params) => {
179
+ const headers = new Headers();
180
+
181
+ if (tenantId) {
182
+ headers.set("tenant-id", tenantId);
183
+ }
184
+
185
+ const res = await httpClient(`${apiUrl}/api/v2/${resource}`, {
186
+ method: "POST",
187
+ body: JSON.stringify(params.data),
188
+ headers,
189
+ });
190
+
191
+ const data = {
192
+ ...res.json,
193
+ id: res.json.id,
194
+ };
195
+
196
+ return {
197
+ data,
198
+ };
199
+ },
200
+
201
+ delete: async (resource, params) => {
202
+ const headers = new Headers({
203
+ "Content-Type": "text/plain",
204
+ });
205
+
206
+ if (tenantId) {
207
+ headers.set("tenant-id", tenantId);
208
+ }
209
+
210
+ const res = await httpClient(`${apiUrl}/api/v2/${resource}/${params.id}`, {
211
+ method: "DELETE",
212
+ headers,
213
+ });
214
+
215
+ return {
216
+ data: res.json,
217
+ };
218
+ },
219
+ deleteMany: () => Promise.reject("not supporting updateMany"),
220
+ });
@@ -0,0 +1,42 @@
1
+ import { Auth0AuthProvider, httpClient } from "ra-auth-auth0";
2
+ import { Auth0Client } from "@auth0/auth0-spa-js";
3
+
4
+ const getUrlByEnvironment = () => {
5
+ // eslint-disable-next-line no-undef
6
+ const VITE_VERCEL_URL = process.env.VITE_VERCEL_URL;
7
+
8
+ if (VITE_VERCEL_URL) {
9
+ return `https://${VITE_VERCEL_URL}`;
10
+ }
11
+
12
+ // rename this? and nuke VITE_LOGIN_REDIRECT_URL env var
13
+ return import.meta.env.VITE_LOGOUT_REDIRECT_URL;
14
+ };
15
+
16
+ export const baseRedirectUri = getUrlByEnvironment();
17
+
18
+ const auth0 = new Auth0Client({
19
+ domain: import.meta.env.VITE_AUTH0_DOMAIN,
20
+ clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
21
+ cacheLocation: "localstorage",
22
+ authorizationParams: {
23
+ audience: import.meta.env.VITE_AUTH0_AUDIENCE,
24
+ },
25
+ });
26
+
27
+ const authProvider = Auth0AuthProvider(auth0, {
28
+ loginRedirectUri: `${getUrlByEnvironment()}/auth-callback`,
29
+ logoutRedirectUri: getUrlByEnvironment(),
30
+ });
31
+
32
+ const authorizedHttpClient = httpClient(auth0);
33
+
34
+ // Temp debug code
35
+ // function logAccessToken() {
36
+ // auth0.getTokenSilently().then((token) => {
37
+ // console.log("Access Token: ", token);
38
+ // });
39
+ // }
40
+ // logAccessToken();
41
+
42
+ export { authProvider, authorizedHttpClient };
@@ -0,0 +1,46 @@
1
+ import { AppBar, TitlePortal } from "react-admin";
2
+ import { useEffect, useState } from "react";
3
+ import { Link } from "@mui/material";
4
+ import { authorizedHttpClient } from "../authProvider";
5
+
6
+ type TenantResponse = {
7
+ audience: string;
8
+ created_at: string;
9
+ id: string;
10
+ language: string;
11
+ logo: string;
12
+ updated_at: string;
13
+ name: string;
14
+ primary_color: string;
15
+ secondary_color: string;
16
+ sender_email: string;
17
+ sender_name: string;
18
+ };
19
+
20
+ export function TenantAppBar() {
21
+ const pathSegments = location.pathname.split("/").filter(Boolean);
22
+ const tenantId = pathSegments[0];
23
+ const [tenant, setTenant] = useState<TenantResponse>();
24
+
25
+ useEffect(() => {
26
+ authorizedHttpClient(
27
+ `${import.meta.env.VITE_SIMPLE_REST_URL}/api/v2/tenants/${tenantId}`,
28
+ {},
29
+ ).then((response) => {
30
+ const res: TenantResponse = JSON.parse(response.body);
31
+ setTenant(res);
32
+ });
33
+ }, [tenantId]);
34
+
35
+ const isDefaultSettings = tenantId === "DEFAULT_SETTINGS";
36
+
37
+ return (
38
+ <AppBar sx={{ ...(isDefaultSettings && { backgroundColor: "red" }) }}>
39
+ <TitlePortal />
40
+ <p>{tenant?.name}&nbsp;-&nbsp;</p>
41
+ <Link color="inherit" href="/tenants" underline="none">
42
+ Tenants
43
+ </Link>
44
+ </AppBar>
45
+ );
46
+ }
@@ -0,0 +1,17 @@
1
+ import { Layout } from "react-admin";
2
+
3
+ import { TenantAppBar } from "./TenantAppBar";
4
+
5
+ export function tenantLayout(props: any) {
6
+ const tenantId = location.pathname.split("/").filter(Boolean)[0];
7
+
8
+ const isDefaultSettings = tenantId === "DEFAULT_SETTINGS";
9
+
10
+ return (
11
+ <Layout
12
+ {...props}
13
+ appBar={TenantAppBar}
14
+ sx={{ ...(isDefaultSettings && { backgroundColor: "red" }) }}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ Create,
3
+ SimpleForm,
4
+ TextInput,
5
+ required,
6
+ SelectInput,
7
+ } from "react-admin";
8
+
9
+ export function ApplicationCreate() {
10
+ return (
11
+ <Create>
12
+ <SimpleForm>
13
+ <TextInput source="name" validate={[required()]} />
14
+ <TextInput source="allowed_callback_urls" fullWidth multiline={true} />
15
+ <TextInput source="allowed_logout_urls" fullWidth multiline={true} />
16
+ <TextInput source="allowed_web_origins" fullWidth multiline={true} />
17
+ <TextInput source="id" validate={[required()]} />
18
+ <SelectInput
19
+ source="email_validation"
20
+ choices={[
21
+ { id: "disabled", name: "Disabled" },
22
+ { id: "enabled", name: "Enabled" },
23
+ { id: "enforced", name: "Enforced" },
24
+ ]}
25
+ />
26
+ </SimpleForm>
27
+ </Create>
28
+ );
29
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ DateField,
3
+ Edit,
4
+ FieldTitle,
5
+ Labeled,
6
+ SelectInput,
7
+ SimpleForm,
8
+ TextInput,
9
+ BooleanInput,
10
+ } from "react-admin";
11
+
12
+ export function ApplicationEdit() {
13
+ return (
14
+ <Edit>
15
+ <SimpleForm>
16
+ <TextInput source="id" />
17
+ <TextInput source="name" />
18
+ <TextInput source="client_secret" />
19
+ <SelectInput
20
+ source="email_validation"
21
+ choices={[
22
+ { id: "disabled", name: "Disabled" },
23
+ { id: "enabled", name: "Enabled" },
24
+ { id: "enforced", name: "Enforced" },
25
+ ]}
26
+ />
27
+ <BooleanInput source="disable_sign_ups" />
28
+ <TextInput source="allowed_callback_urls" fullWidth multiline={true} />
29
+ <TextInput source="allowed_logout_urls" fullWidth multiline={true} />
30
+ <TextInput source="allowed_web_origins" fullWidth multiline={true} />
31
+ <Labeled label={<FieldTitle source="created_at" />}>
32
+ <DateField source="created_at" showTime={true} />
33
+ </Labeled>
34
+ <Labeled label={<FieldTitle source="updated_at" />}>
35
+ <DateField source="updated_at" showTime={true} />
36
+ </Labeled>
37
+ </SimpleForm>
38
+ </Edit>
39
+ );
40
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./create";
2
+ export * from "./list";
3
+ export * from "./edit";
@@ -0,0 +1,37 @@
1
+ import {
2
+ List,
3
+ Datagrid,
4
+ TextField,
5
+ DateField,
6
+ FunctionField,
7
+ } from "react-admin";
8
+ import { PostListActions } from "../listActions/PostListActions";
9
+
10
+ export function ApplicationsList() {
11
+ return (
12
+ <List actions={<PostListActions />}>
13
+ <Datagrid rowClick="edit" bulkActionButtons={false}>
14
+ <TextField source="id" />
15
+ <TextField source="name" />
16
+ <FunctionField
17
+ label="Login"
18
+ render={(record: any) => (
19
+ <a
20
+ href={`${
21
+ import.meta.env.VITE_SIMPLE_REST_URL
22
+ }/authorize?client_id=${record.id}&redirect_uri=${
23
+ import.meta.env.VITE_SIMPLE_REST_URL
24
+ }/u/info&scope=profile%20email%20openid&state=1234&response_type=code`}
25
+ target="_blank"
26
+ rel="noopener noreferrer"
27
+ >
28
+ Login
29
+ </a>
30
+ )}
31
+ />
32
+ <DateField source="created_at" showTime={true} />
33
+ <DateField source="updated_at" showTime={true} />
34
+ </Datagrid>
35
+ </List>
36
+ );
37
+ }
@@ -0,0 +1,6 @@
1
+ import { formatDistance } from "date-fns";
2
+
3
+ export function DateAgo({ date }: { date?: string }) {
4
+ if (!date) return "N/A";
5
+ return `${formatDistance(new Date(date), new Date())} ago`;
6
+ }
@@ -0,0 +1,3 @@
1
+ export function JsonOutput({ data }: { data: any }) {
2
+ return <pre>{JSON.stringify(data, null, 2)}</pre>;
3
+ }
@@ -0,0 +1 @@
1
+ export * from "./DateAgo";
@@ -0,0 +1,11 @@
1
+ import { Create, SimpleForm, TextInput, required } from "react-admin";
2
+
3
+ export function ConnectionCreate() {
4
+ return (
5
+ <Create>
6
+ <SimpleForm>
7
+ <TextInput source="name" validate={[required()]} />
8
+ </SimpleForm>
9
+ </Create>
10
+ );
11
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ DateField,
3
+ Edit,
4
+ FieldTitle,
5
+ Labeled,
6
+ SelectInput,
7
+ SimpleForm,
8
+ TextInput,
9
+ } from "react-admin";
10
+
11
+ export function ConnectionEdit() {
12
+ return (
13
+ <Edit>
14
+ <SimpleForm>
15
+ <TextInput source="id" />
16
+ <TextInput source="name" />
17
+ <TextInput
18
+ source="client_id"
19
+ label="Client ID"
20
+ style={{ width: "800px" }}
21
+ />
22
+ <TextInput
23
+ source="client_secret"
24
+ label="Client Secret"
25
+ style={{ width: "800px" }}
26
+ />
27
+ <SelectInput
28
+ source="response_type"
29
+ label="Response Type"
30
+ choices={[
31
+ { id: "code", name: "Code" },
32
+ { id: "code id_token", name: "Code ID-token" },
33
+ ]}
34
+ />
35
+ <SelectInput
36
+ source="response_mode"
37
+ label="Response Mode"
38
+ choices={[
39
+ { id: "query", name: "Query" },
40
+ { id: "fragment", name: "Fragment" },
41
+ { id: "web_message", name: "Web Message" },
42
+ { id: "form_post", name: "Form Post" },
43
+ ]}
44
+ />
45
+ <TextInput source="scope" fullWidth />
46
+ <TextInput
47
+ source="private_key"
48
+ label="Private Key"
49
+ style={{ width: "800px" }}
50
+ multiline={true}
51
+ />
52
+ <TextInput source="kid" label="Key ID" />
53
+ <TextInput source="team_id" label="Team ID" />
54
+ <TextInput source="token_endpoint" fullWidth />
55
+ <TextInput source="authorization_endpoint" fullWidth />
56
+ <TextInput source="userinfo_endpoint" fullWidth />
57
+ <Labeled label={<FieldTitle source="created_at" />}>
58
+ <DateField source="created_at" showTime={true} />
59
+ </Labeled>
60
+ <Labeled label={<FieldTitle source="updated_at" />}>
61
+ <DateField source="updated_at" showTime={true} />
62
+ </Labeled>
63
+ </SimpleForm>
64
+ </Edit>
65
+ );
66
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./create";
2
+ export * from "./list";
3
+ export * from "./edit";
@@ -0,0 +1,15 @@
1
+ import { List, Datagrid, TextField, DateField } from "react-admin";
2
+ import { PostListActions } from "../listActions/PostListActions";
3
+
4
+ export function ConnectionsList() {
5
+ return (
6
+ <List actions={<PostListActions />}>
7
+ <Datagrid rowClick="edit" bulkActionButtons={false}>
8
+ <TextField source="id" />
9
+ <TextField source="name" />
10
+ <DateField source="created_at" showTime={true} />
11
+ <DateField source="updated_at" showTime={true} />
12
+ </Datagrid>
13
+ </List>
14
+ );
15
+ }
@@ -0,0 +1,11 @@
1
+ import { Create, SimpleForm, TextInput, required } from "react-admin";
2
+
3
+ export function DomainCreate() {
4
+ return (
5
+ <Create>
6
+ <SimpleForm>
7
+ <TextInput source="domain" validate={[required()]} />
8
+ </SimpleForm>
9
+ </Create>
10
+ );
11
+ }
@@ -0,0 +1,49 @@
1
+ import {
2
+ DateField,
3
+ Edit,
4
+ FieldTitle,
5
+ Labeled,
6
+ SelectInput,
7
+ SimpleForm,
8
+ TextInput,
9
+ } from "react-admin";
10
+
11
+ export function DomainEdit() {
12
+ return (
13
+ <Edit>
14
+ <SimpleForm>
15
+ <TextInput source="domain" />
16
+ <SelectInput
17
+ source="email_service"
18
+ choices={[
19
+ { id: "mailchannels", name: "Mailchannels" },
20
+ { id: "mailgun", name: "Mailgun" },
21
+ ]}
22
+ />
23
+ <TextInput
24
+ label="PEM Private Key"
25
+ source="dkim_private_key"
26
+ style={{ width: "800px" }}
27
+ multiline={true}
28
+ />
29
+ <TextInput
30
+ label="PEM Public Key"
31
+ source="dkim_public_key"
32
+ style={{ width: "800px" }}
33
+ multiline={true}
34
+ />
35
+ <TextInput
36
+ label="Api Key"
37
+ source="email_api_key"
38
+ style={{ width: "800px" }}
39
+ />
40
+ <Labeled label={<FieldTitle source="created_at" />}>
41
+ <DateField source="created_at" showTime={true} />
42
+ </Labeled>
43
+ <Labeled label={<FieldTitle source="updated_at" />}>
44
+ <DateField source="updated_at" showTime={true} />
45
+ </Labeled>
46
+ </SimpleForm>
47
+ </Edit>
48
+ );
49
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./create";
2
+ export * from "./list";
3
+ export * from "./edit";
@@ -0,0 +1,15 @@
1
+ import { List, Datagrid, TextField, DateField } from "react-admin";
2
+ import { PostListActions } from "../listActions/PostListActions";
3
+
4
+ export function DomainList() {
5
+ return (
6
+ <List actions={<PostListActions />}>
7
+ <Datagrid rowClick="edit" bulkActionButtons={false}>
8
+ <TextField source="id" />
9
+ <TextField source="domain" />
10
+ <DateField source="created_at" showTime={true} />
11
+ <DateField source="updated_at" showTime={true} />
12
+ </Datagrid>
13
+ </List>
14
+ );
15
+ }
@@ -0,0 +1,10 @@
1
+ import { CreateButton, ExportButton, TopToolbar } from "react-admin";
2
+
3
+ export function PostListActions() {
4
+ return (
5
+ <TopToolbar>
6
+ <CreateButton />
7
+ <ExportButton />
8
+ </TopToolbar>
9
+ );
10
+ }
@@ -0,0 +1,15 @@
1
+ import { Create, SimpleForm, TextInput, required } from "react-admin";
2
+
3
+ export function TenantsCreate() {
4
+ return (
5
+ <Create>
6
+ <SimpleForm>
7
+ <TextInput source="name" validate={[required()]} />
8
+ <TextInput source="audience" validate={[required()]} />
9
+ <TextInput source="sender_email" validate={[required()]} />
10
+ <TextInput source="sender_name" validate={[required()]} />
11
+ <TextInput source="support_url" label="Support Url" />
12
+ </SimpleForm>
13
+ </Create>
14
+ );
15
+ }