@oneuptime/common 9.5.10 → 9.5.13
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.
- package/Models/DatabaseModels/GlobalConfig.ts +19 -0
- package/Server/DatabaseConfig.ts +7 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/ProjectService.ts +14 -0
- package/Types/Monitor/MonitorType.ts +50 -0
- package/UI/Components/CardSelect/CardSelect.tsx +133 -67
- package/UI/Components/Forms/Types/Field.ts +7 -2
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +56 -1
- package/UI/esbuild-config.js +52 -3
- package/UI/index.d.ts +26 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/DatabaseConfig.js +9 -0
- package/build/dist/Server/DatabaseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +12 -2
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +44 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js +55 -20
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +55 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/package.json +1 -1
|
@@ -58,6 +58,25 @@ export default class GlobalConfig extends GlobalConfigModel {
|
|
|
58
58
|
})
|
|
59
59
|
public disableSignup?: boolean = undefined;
|
|
60
60
|
|
|
61
|
+
@ColumnAccessControl({
|
|
62
|
+
create: [],
|
|
63
|
+
read: [],
|
|
64
|
+
update: [],
|
|
65
|
+
})
|
|
66
|
+
@TableColumn({
|
|
67
|
+
type: TableColumnType.Boolean,
|
|
68
|
+
title: "Disable User Project Creation",
|
|
69
|
+
description: "Only master admins can create projects when enabled.",
|
|
70
|
+
defaultValue: false,
|
|
71
|
+
})
|
|
72
|
+
@Column({
|
|
73
|
+
type: ColumnType.Boolean,
|
|
74
|
+
nullable: true,
|
|
75
|
+
default: false,
|
|
76
|
+
unique: true,
|
|
77
|
+
})
|
|
78
|
+
public disableUserProjectCreation?: boolean = undefined;
|
|
79
|
+
|
|
61
80
|
// SMTP Settings.
|
|
62
81
|
|
|
63
82
|
@ColumnAccessControl({
|
package/Server/DatabaseConfig.ts
CHANGED
|
@@ -80,4 +80,11 @@ export default class DatabaseConfig {
|
|
|
80
80
|
"disableSignup",
|
|
81
81
|
)) as boolean;
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
@CaptureSpan()
|
|
85
|
+
public static async shouldDisableUserProjectCreation(): Promise<boolean> {
|
|
86
|
+
return (await DatabaseConfig.getFromGlobalConfig(
|
|
87
|
+
"disableUserProjectCreation",
|
|
88
|
+
)) as boolean;
|
|
89
|
+
}
|
|
83
90
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1770834237091 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1770834237091";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "GlobalConfig" ADD "disableUserProjectCreation" boolean DEFAULT false`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_disableUserProjectCreation" UNIQUE ("disableUserProjectCreation")`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_disableUserProjectCreation"`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "disableUserProjectCreation"`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -258,6 +258,7 @@ import { MigrationName1770728946893 } from "./1770728946893-MigrationName";
|
|
|
258
258
|
import { MigrationName1770732721195 } from "./1770732721195-MigrationName";
|
|
259
259
|
import { MigrationName1770833704656 } from "./1770833704656-MigrationName";
|
|
260
260
|
import { MigrationName1770834237090 } from "./1770834237090-MigrationName";
|
|
261
|
+
import { MigrationName1770834237091 } from "./1770834237091-MigrationName";
|
|
261
262
|
|
|
262
263
|
export default [
|
|
263
264
|
InitialMigration,
|
|
@@ -520,4 +521,5 @@ export default [
|
|
|
520
521
|
MigrationName1770732721195,
|
|
521
522
|
MigrationName1770833704656,
|
|
522
523
|
MigrationName1770834237090,
|
|
524
|
+
MigrationName1770834237091,
|
|
523
525
|
];
|
|
@@ -124,6 +124,7 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
124
124
|
select: {
|
|
125
125
|
name: true,
|
|
126
126
|
email: true,
|
|
127
|
+
isMasterAdmin: true,
|
|
127
128
|
companyPhoneNumber: true,
|
|
128
129
|
companyName: true,
|
|
129
130
|
utmCampaign: true,
|
|
@@ -142,6 +143,15 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
142
143
|
throw new BadDataException("User not found.");
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
// Check if project creation is restricted to admins only
|
|
147
|
+
const shouldDisableProjectCreation: boolean =
|
|
148
|
+
await DatabaseConfig.shouldDisableUserProjectCreation();
|
|
149
|
+
if (shouldDisableProjectCreation && !user.isMasterAdmin) {
|
|
150
|
+
throw new NotAuthorizedException(
|
|
151
|
+
"Project creation is restricted to admin users only on this OneUptime Server. Please contact your server admin.",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
145
155
|
if (IsBillingEnabled) {
|
|
146
156
|
if (!data.data.paymentProviderPlanId) {
|
|
147
157
|
throw new BadDataException("Plan required to create the project.");
|
|
@@ -1313,6 +1323,8 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
1313
1323
|
paymentProviderSubscriptionId: true,
|
|
1314
1324
|
paymentProviderMeteredSubscriptionId: true,
|
|
1315
1325
|
name: true,
|
|
1326
|
+
createdAt: true,
|
|
1327
|
+
planName: true,
|
|
1316
1328
|
createdByUser: {
|
|
1317
1329
|
name: true,
|
|
1318
1330
|
email: true,
|
|
@@ -1341,6 +1353,8 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
1341
1353
|
let slackMessage: string = `*Project Deleted:*
|
|
1342
1354
|
*Project Name:* ${project.name?.toString() || "N/A"}
|
|
1343
1355
|
*Project ID:* ${project._id?.toString() || "N/A"}
|
|
1356
|
+
*Project Created Date:* ${project.createdAt ? new Date(project.createdAt).toUTCString() : "N/A"}
|
|
1357
|
+
*Project Plan Name:* ${project.planName?.toString() || "N/A"}
|
|
1344
1358
|
`;
|
|
1345
1359
|
|
|
1346
1360
|
if (subscriptionStatus) {
|
|
@@ -40,7 +40,57 @@ export interface MonitorTypeProps {
|
|
|
40
40
|
icon: IconProp;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export interface MonitorTypeCategory {
|
|
44
|
+
label: string;
|
|
45
|
+
monitorTypes: Array<MonitorType>;
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
export class MonitorTypeHelper {
|
|
49
|
+
public static getMonitorTypeCategories(): Array<MonitorTypeCategory> {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
label: "Basic Monitoring",
|
|
53
|
+
monitorTypes: [
|
|
54
|
+
MonitorType.Website,
|
|
55
|
+
MonitorType.API,
|
|
56
|
+
MonitorType.Ping,
|
|
57
|
+
MonitorType.IP,
|
|
58
|
+
MonitorType.Port,
|
|
59
|
+
MonitorType.DNS,
|
|
60
|
+
MonitorType.SSLCertificate,
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: "Synthetic Monitoring",
|
|
65
|
+
monitorTypes: [
|
|
66
|
+
MonitorType.SyntheticMonitor,
|
|
67
|
+
MonitorType.CustomJavaScriptCode,
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: "Inbound Monitoring",
|
|
72
|
+
monitorTypes: [MonitorType.IncomingRequest, MonitorType.IncomingEmail],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: "Infrastructure",
|
|
76
|
+
monitorTypes: [MonitorType.Server, MonitorType.SNMP],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: "Telemetry",
|
|
80
|
+
monitorTypes: [
|
|
81
|
+
MonitorType.Logs,
|
|
82
|
+
MonitorType.Metrics,
|
|
83
|
+
MonitorType.Traces,
|
|
84
|
+
MonitorType.Exceptions,
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: "Other",
|
|
89
|
+
monitorTypes: [MonitorType.Manual],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
44
94
|
public static isTelemetryMonitor(monitorType: MonitorType): boolean {
|
|
45
95
|
return (
|
|
46
96
|
monitorType === MonitorType.Logs ||
|
|
@@ -9,8 +9,22 @@ export interface CardSelectOption {
|
|
|
9
9
|
icon: IconProp;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export interface
|
|
12
|
+
export interface CardSelectOptionGroup {
|
|
13
|
+
label: string;
|
|
13
14
|
options: Array<CardSelectOption>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isCardSelectOptionGroup(
|
|
18
|
+
option: CardSelectOption | CardSelectOptionGroup,
|
|
19
|
+
): option is CardSelectOptionGroup {
|
|
20
|
+
return (
|
|
21
|
+
(option as CardSelectOptionGroup).label !== undefined &&
|
|
22
|
+
Array.isArray((option as CardSelectOptionGroup).options)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ComponentProps {
|
|
27
|
+
options: Array<CardSelectOption | CardSelectOptionGroup>;
|
|
14
28
|
value?: string | undefined;
|
|
15
29
|
onChange: (value: string) => void;
|
|
16
30
|
error?: string | undefined;
|
|
@@ -18,80 +32,132 @@ export interface ComponentProps {
|
|
|
18
32
|
dataTestId?: string | undefined;
|
|
19
33
|
}
|
|
20
34
|
|
|
35
|
+
interface RenderGroup {
|
|
36
|
+
label: string | null;
|
|
37
|
+
options: Array<CardSelectOption>;
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
const CardSelect: FunctionComponent<ComponentProps> = (
|
|
22
41
|
props: ComponentProps,
|
|
23
42
|
): ReactElement => {
|
|
43
|
+
// Normalize options into render groups
|
|
44
|
+
const groups: Array<RenderGroup> = [];
|
|
45
|
+
let ungroupedOptions: Array<CardSelectOption> = [];
|
|
46
|
+
|
|
47
|
+
for (const option of props.options) {
|
|
48
|
+
if (isCardSelectOptionGroup(option)) {
|
|
49
|
+
// Flush any accumulated ungrouped options first
|
|
50
|
+
if (ungroupedOptions.length > 0) {
|
|
51
|
+
groups.push({ label: null, options: ungroupedOptions });
|
|
52
|
+
ungroupedOptions = [];
|
|
53
|
+
}
|
|
54
|
+
groups.push({ label: option.label, options: option.options });
|
|
55
|
+
} else {
|
|
56
|
+
ungroupedOptions.push(option);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (ungroupedOptions.length > 0) {
|
|
61
|
+
groups.push({ label: null, options: ungroupedOptions });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let cardIndex: number = 0;
|
|
65
|
+
|
|
24
66
|
return (
|
|
25
67
|
<div data-testid={props.dataTestId}>
|
|
26
|
-
<div
|
|
27
|
-
|
|
28
|
-
aria-label="Select an option"
|
|
29
|
-
className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"
|
|
30
|
-
>
|
|
31
|
-
{props.options.map((option: CardSelectOption, index: number) => {
|
|
32
|
-
const isSelected: boolean = props.value === option.value;
|
|
33
|
-
|
|
68
|
+
<div role="radiogroup" aria-label="Select an option">
|
|
69
|
+
{groups.map((group: RenderGroup, groupIndex: number) => {
|
|
34
70
|
return (
|
|
35
|
-
<div
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
onKeyDown={(e: React.KeyboardEvent) => {
|
|
42
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
43
|
-
e.preventDefault();
|
|
44
|
-
props.onChange(option.value);
|
|
45
|
-
}
|
|
46
|
-
}}
|
|
47
|
-
className={`relative flex cursor-pointer rounded-lg border p-4 shadow-sm focus:outline-none transition-all duration-200 hover:border-indigo-400 hover:shadow-md ${
|
|
48
|
-
isSelected
|
|
49
|
-
? "border-indigo-500 bg-indigo-50/50"
|
|
50
|
-
: "border-gray-200 bg-white"
|
|
51
|
-
}`}
|
|
52
|
-
role="radio"
|
|
53
|
-
aria-checked={isSelected}
|
|
54
|
-
data-testid={`card-select-option-${option.value}`}
|
|
55
|
-
>
|
|
56
|
-
<div className="flex w-full items-start">
|
|
57
|
-
<div
|
|
58
|
-
className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg ${
|
|
59
|
-
isSelected ? "bg-indigo-100" : "bg-gray-100"
|
|
60
|
-
}`}
|
|
61
|
-
>
|
|
62
|
-
<Icon
|
|
63
|
-
icon={option.icon}
|
|
64
|
-
size={SizeProp.Large}
|
|
65
|
-
className={`h-5 w-5 ${
|
|
66
|
-
isSelected ? "text-indigo-600" : "text-gray-600"
|
|
67
|
-
}`}
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
<div className="ml-4 flex-1">
|
|
71
|
-
<span
|
|
72
|
-
className={`block text-sm font-semibold ${
|
|
73
|
-
isSelected ? "text-gray-900" : "text-gray-900"
|
|
74
|
-
}`}
|
|
71
|
+
<div key={groupIndex} className={groupIndex > 0 ? "mt-8" : ""}>
|
|
72
|
+
{group.label && (
|
|
73
|
+
<div className="relative mb-4">
|
|
74
|
+
<div
|
|
75
|
+
className="absolute inset-0 flex items-center"
|
|
76
|
+
aria-hidden="true"
|
|
75
77
|
>
|
|
76
|
-
|
|
77
|
-
</
|
|
78
|
-
<
|
|
79
|
-
className=
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
>
|
|
83
|
-
{option.description}
|
|
84
|
-
</span>
|
|
85
|
-
</div>
|
|
86
|
-
{isSelected && (
|
|
87
|
-
<div className="flex-shrink-0 ml-2">
|
|
88
|
-
<Icon
|
|
89
|
-
icon={IconProp.CheckCircle}
|
|
90
|
-
size={SizeProp.Large}
|
|
91
|
-
className="h-5 w-5 text-indigo-500"
|
|
92
|
-
/>
|
|
78
|
+
<div className="w-full border-t border-gray-200"></div>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="relative flex justify-start">
|
|
81
|
+
<span className="bg-white pr-3 text-xs font-semibold uppercase tracking-wider text-gray-400">
|
|
82
|
+
{group.label}
|
|
83
|
+
</span>
|
|
93
84
|
</div>
|
|
94
|
-
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
88
|
+
{group.options.map((option: CardSelectOption) => {
|
|
89
|
+
const isSelected: boolean = props.value === option.value;
|
|
90
|
+
const currentIndex: number = cardIndex++;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
key={option.value}
|
|
95
|
+
tabIndex={
|
|
96
|
+
props.tabIndex
|
|
97
|
+
? props.tabIndex + currentIndex
|
|
98
|
+
: currentIndex
|
|
99
|
+
}
|
|
100
|
+
onClick={() => {
|
|
101
|
+
props.onChange(option.value);
|
|
102
|
+
}}
|
|
103
|
+
onKeyDown={(e: React.KeyboardEvent) => {
|
|
104
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
props.onChange(option.value);
|
|
107
|
+
}
|
|
108
|
+
}}
|
|
109
|
+
className={`relative flex cursor-pointer rounded-lg border p-4 shadow-sm focus:outline-none transition-all duration-200 hover:border-indigo-400 hover:shadow-md ${
|
|
110
|
+
isSelected
|
|
111
|
+
? "border-indigo-500 bg-indigo-50/50"
|
|
112
|
+
: "border-gray-200 bg-white"
|
|
113
|
+
}`}
|
|
114
|
+
role="radio"
|
|
115
|
+
aria-checked={isSelected}
|
|
116
|
+
data-testid={`card-select-option-${option.value}`}
|
|
117
|
+
>
|
|
118
|
+
<div className="flex w-full items-start">
|
|
119
|
+
<div
|
|
120
|
+
className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg ${
|
|
121
|
+
isSelected ? "bg-indigo-100" : "bg-gray-100"
|
|
122
|
+
}`}
|
|
123
|
+
>
|
|
124
|
+
<Icon
|
|
125
|
+
icon={option.icon}
|
|
126
|
+
size={SizeProp.Large}
|
|
127
|
+
className={`h-5 w-5 ${
|
|
128
|
+
isSelected ? "text-indigo-600" : "text-gray-600"
|
|
129
|
+
}`}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="ml-4 flex-1">
|
|
133
|
+
<span
|
|
134
|
+
className={`block text-sm font-semibold ${
|
|
135
|
+
isSelected ? "text-gray-900" : "text-gray-900"
|
|
136
|
+
}`}
|
|
137
|
+
>
|
|
138
|
+
{option.title}
|
|
139
|
+
</span>
|
|
140
|
+
<span
|
|
141
|
+
className={`mt-1 block text-sm ${
|
|
142
|
+
isSelected ? "text-gray-600" : "text-gray-500"
|
|
143
|
+
}`}
|
|
144
|
+
>
|
|
145
|
+
{option.description}
|
|
146
|
+
</span>
|
|
147
|
+
</div>
|
|
148
|
+
{isSelected && (
|
|
149
|
+
<div className="flex-shrink-0 ml-2">
|
|
150
|
+
<Icon
|
|
151
|
+
icon={IconProp.CheckCircle}
|
|
152
|
+
size={SizeProp.Large}
|
|
153
|
+
className="h-5 w-5 text-indigo-500"
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
95
161
|
</div>
|
|
96
162
|
</div>
|
|
97
163
|
);
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
CategoryCheckboxOption,
|
|
4
4
|
CheckboxCategory,
|
|
5
5
|
} from "../../CategoryCheckbox/CategoryCheckboxTypes";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CardSelectOption,
|
|
8
|
+
CardSelectOptionGroup,
|
|
9
|
+
} from "../../CardSelect/CardSelect";
|
|
7
10
|
import { DropdownOption, DropdownOptionGroup } from "../../Dropdown/Dropdown";
|
|
8
11
|
import { RadioButton } from "../../RadioButtons/GroupRadioButtons";
|
|
9
12
|
import FormFieldSchemaType from "./FormFieldSchemaType";
|
|
@@ -51,7 +54,9 @@ export default interface Field<TEntity> {
|
|
|
51
54
|
stepId?: string | undefined;
|
|
52
55
|
required?: boolean | ((item: FormValues<TEntity>) => boolean) | undefined;
|
|
53
56
|
dropdownOptions?: Array<DropdownOption | DropdownOptionGroup> | undefined;
|
|
54
|
-
cardSelectOptions?:
|
|
57
|
+
cardSelectOptions?:
|
|
58
|
+
| Array<CardSelectOption | CardSelectOptionGroup>
|
|
59
|
+
| undefined;
|
|
55
60
|
fetchDropdownOptions?:
|
|
56
61
|
| ((
|
|
57
62
|
item: FormValues<TEntity>,
|
|
@@ -8,8 +8,63 @@ import React, {
|
|
|
8
8
|
import ReactMarkdown from "react-markdown";
|
|
9
9
|
// https://github.com/remarkjs/remark-gfm
|
|
10
10
|
import remarkGfm from "remark-gfm";
|
|
11
|
-
import
|
|
11
|
+
import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism-light";
|
|
12
12
|
import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
13
|
+
import javascript from "react-syntax-highlighter/dist/esm/languages/prism/javascript";
|
|
14
|
+
import typescript from "react-syntax-highlighter/dist/esm/languages/prism/typescript";
|
|
15
|
+
import jsx from "react-syntax-highlighter/dist/esm/languages/prism/jsx";
|
|
16
|
+
import tsx from "react-syntax-highlighter/dist/esm/languages/prism/tsx";
|
|
17
|
+
import python from "react-syntax-highlighter/dist/esm/languages/prism/python";
|
|
18
|
+
import bash from "react-syntax-highlighter/dist/esm/languages/prism/bash";
|
|
19
|
+
import json from "react-syntax-highlighter/dist/esm/languages/prism/json";
|
|
20
|
+
import yaml from "react-syntax-highlighter/dist/esm/languages/prism/yaml";
|
|
21
|
+
import sql from "react-syntax-highlighter/dist/esm/languages/prism/sql";
|
|
22
|
+
import go from "react-syntax-highlighter/dist/esm/languages/prism/go";
|
|
23
|
+
import java from "react-syntax-highlighter/dist/esm/languages/prism/java";
|
|
24
|
+
import css from "react-syntax-highlighter/dist/esm/languages/prism/css";
|
|
25
|
+
import markup from "react-syntax-highlighter/dist/esm/languages/prism/markup";
|
|
26
|
+
import markdown from "react-syntax-highlighter/dist/esm/languages/prism/markdown";
|
|
27
|
+
import docker from "react-syntax-highlighter/dist/esm/languages/prism/docker";
|
|
28
|
+
import rust from "react-syntax-highlighter/dist/esm/languages/prism/rust";
|
|
29
|
+
import c from "react-syntax-highlighter/dist/esm/languages/prism/c";
|
|
30
|
+
import cpp from "react-syntax-highlighter/dist/esm/languages/prism/cpp";
|
|
31
|
+
import csharp from "react-syntax-highlighter/dist/esm/languages/prism/csharp";
|
|
32
|
+
import ruby from "react-syntax-highlighter/dist/esm/languages/prism/ruby";
|
|
33
|
+
import php from "react-syntax-highlighter/dist/esm/languages/prism/php";
|
|
34
|
+
import graphql from "react-syntax-highlighter/dist/esm/languages/prism/graphql";
|
|
35
|
+
import http from "react-syntax-highlighter/dist/esm/languages/prism/http";
|
|
36
|
+
|
|
37
|
+
SyntaxHighlighter.registerLanguage("javascript", javascript);
|
|
38
|
+
SyntaxHighlighter.registerLanguage("js", javascript);
|
|
39
|
+
SyntaxHighlighter.registerLanguage("typescript", typescript);
|
|
40
|
+
SyntaxHighlighter.registerLanguage("ts", typescript);
|
|
41
|
+
SyntaxHighlighter.registerLanguage("jsx", jsx);
|
|
42
|
+
SyntaxHighlighter.registerLanguage("tsx", tsx);
|
|
43
|
+
SyntaxHighlighter.registerLanguage("python", python);
|
|
44
|
+
SyntaxHighlighter.registerLanguage("bash", bash);
|
|
45
|
+
SyntaxHighlighter.registerLanguage("shell", bash);
|
|
46
|
+
SyntaxHighlighter.registerLanguage("json", json);
|
|
47
|
+
SyntaxHighlighter.registerLanguage("yaml", yaml);
|
|
48
|
+
SyntaxHighlighter.registerLanguage("yml", yaml);
|
|
49
|
+
SyntaxHighlighter.registerLanguage("sql", sql);
|
|
50
|
+
SyntaxHighlighter.registerLanguage("go", go);
|
|
51
|
+
SyntaxHighlighter.registerLanguage("java", java);
|
|
52
|
+
SyntaxHighlighter.registerLanguage("css", css);
|
|
53
|
+
SyntaxHighlighter.registerLanguage("markup", markup);
|
|
54
|
+
SyntaxHighlighter.registerLanguage("html", markup);
|
|
55
|
+
SyntaxHighlighter.registerLanguage("xml", markup);
|
|
56
|
+
SyntaxHighlighter.registerLanguage("markdown", markdown);
|
|
57
|
+
SyntaxHighlighter.registerLanguage("md", markdown);
|
|
58
|
+
SyntaxHighlighter.registerLanguage("docker", docker);
|
|
59
|
+
SyntaxHighlighter.registerLanguage("dockerfile", docker);
|
|
60
|
+
SyntaxHighlighter.registerLanguage("rust", rust);
|
|
61
|
+
SyntaxHighlighter.registerLanguage("c", c);
|
|
62
|
+
SyntaxHighlighter.registerLanguage("cpp", cpp);
|
|
63
|
+
SyntaxHighlighter.registerLanguage("csharp", csharp);
|
|
64
|
+
SyntaxHighlighter.registerLanguage("ruby", ruby);
|
|
65
|
+
SyntaxHighlighter.registerLanguage("php", php);
|
|
66
|
+
SyntaxHighlighter.registerLanguage("graphql", graphql);
|
|
67
|
+
SyntaxHighlighter.registerLanguage("http", http);
|
|
13
68
|
import mermaid from "mermaid";
|
|
14
69
|
|
|
15
70
|
// Initialize mermaid
|
package/UI/esbuild-config.js
CHANGED
|
@@ -39,6 +39,54 @@ function createRefractorCompatibilityPlugin() {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// Plugin to force mermaid to use its pre-bundled CJS build (no dynamic imports)
|
|
43
|
+
function createMermaidPlugin() {
|
|
44
|
+
const candidateRoots = [
|
|
45
|
+
path.resolve(__dirname, '../node_modules/mermaid'),
|
|
46
|
+
path.resolve(__dirname, '../../node_modules/mermaid'),
|
|
47
|
+
];
|
|
48
|
+
const mermaidRoot = candidateRoots.find((p) => fs.existsSync(p));
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
name: 'mermaid-prebundled',
|
|
52
|
+
setup(build) {
|
|
53
|
+
if (!mermaidRoot) return;
|
|
54
|
+
const bundlePath = path.join(mermaidRoot, 'dist', 'mermaid.min.js');
|
|
55
|
+
|
|
56
|
+
// Intercept bare "mermaid" imports and serve the pre-bundled CJS file
|
|
57
|
+
// with an ESM export appended. The CJS file declares a local var
|
|
58
|
+
// __esbuild_esm_mermaid_nm and assigns .mermaid on it, so we inline
|
|
59
|
+
// the file contents and export from the same scope.
|
|
60
|
+
build.onResolve({ filter: /^mermaid$/ }, () => {
|
|
61
|
+
return { path: 'mermaid-wrapper', namespace: 'mermaid-ns' };
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
build.onLoad({ filter: /^mermaid-wrapper$/, namespace: 'mermaid-ns' }, () => {
|
|
65
|
+
let cjsSource = fs.readFileSync(bundlePath, 'utf8');
|
|
66
|
+
// The CJS bundle ends with a line that tries globalThis.__esbuild_esm_mermaid_nm
|
|
67
|
+
// which fails because the var is local-scoped when bundled. Strip it and
|
|
68
|
+
// expose the local var on globalThis ourselves before that line.
|
|
69
|
+
cjsSource = cjsSource.replace(
|
|
70
|
+
/globalThis\["mermaid"\]\s*=\s*globalThis\.__esbuild_esm_mermaid_nm\["mermaid"\]\.default;?\s*$/,
|
|
71
|
+
''
|
|
72
|
+
);
|
|
73
|
+
const contents = cjsSource + `
|
|
74
|
+
;globalThis.__esbuild_esm_mermaid_nm = typeof __esbuild_esm_mermaid_nm !== "undefined" ? __esbuild_esm_mermaid_nm : {};
|
|
75
|
+
var _mermaid_export = __esbuild_esm_mermaid_nm.mermaid;
|
|
76
|
+
if (_mermaid_export && _mermaid_export.default) { _mermaid_export = _mermaid_export.default; }
|
|
77
|
+
export default _mermaid_export;
|
|
78
|
+
export { _mermaid_export as mermaid };
|
|
79
|
+
`;
|
|
80
|
+
return {
|
|
81
|
+
contents,
|
|
82
|
+
loader: 'js',
|
|
83
|
+
resolveDir: path.dirname(bundlePath),
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
42
90
|
// CSS Plugin to handle CSS/SCSS files
|
|
43
91
|
function createCSSPlugin() {
|
|
44
92
|
return {
|
|
@@ -161,12 +209,13 @@ function createConfig(options) {
|
|
|
161
209
|
entryPoints: [entryPoint],
|
|
162
210
|
bundle: true,
|
|
163
211
|
outdir,
|
|
164
|
-
format: 'esm',
|
|
212
|
+
format: 'esm',
|
|
165
213
|
platform: 'browser',
|
|
166
214
|
target: 'es2017',
|
|
167
215
|
sourcemap: isDev ? 'inline' : false,
|
|
168
216
|
minify: false,
|
|
169
|
-
|
|
217
|
+
treeShaking: true,
|
|
218
|
+
splitting: true,
|
|
170
219
|
publicPath,
|
|
171
220
|
define: {
|
|
172
221
|
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
@@ -177,7 +226,7 @@ function createConfig(options) {
|
|
|
177
226
|
'react': path.resolve('./node_modules/react'),
|
|
178
227
|
...additionalAlias,
|
|
179
228
|
},
|
|
180
|
-
plugins: [createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
|
|
229
|
+
plugins: [createMermaidPlugin(), createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
|
|
181
230
|
loader: {
|
|
182
231
|
'.tsx': 'tsx',
|
|
183
232
|
'.ts': 'ts',
|
package/UI/index.d.ts
CHANGED
|
@@ -2,3 +2,29 @@ declare module "*.png";
|
|
|
2
2
|
declare module "*.svg";
|
|
3
3
|
declare module "*.jpg";
|
|
4
4
|
declare module "*.gif";
|
|
5
|
+
|
|
6
|
+
declare module "react-syntax-highlighter/dist/esm/prism-light";
|
|
7
|
+
declare module "react-syntax-highlighter/dist/esm/styles/prism";
|
|
8
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/javascript";
|
|
9
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/typescript";
|
|
10
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/jsx";
|
|
11
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/tsx";
|
|
12
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/python";
|
|
13
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/bash";
|
|
14
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/json";
|
|
15
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/yaml";
|
|
16
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/sql";
|
|
17
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/go";
|
|
18
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/java";
|
|
19
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/css";
|
|
20
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/markup";
|
|
21
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/markdown";
|
|
22
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/docker";
|
|
23
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/rust";
|
|
24
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/c";
|
|
25
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/cpp";
|
|
26
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/csharp";
|
|
27
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/ruby";
|
|
28
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/php";
|
|
29
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/graphql";
|
|
30
|
+
declare module "react-syntax-highlighter/dist/esm/languages/prism/http";
|
|
@@ -33,6 +33,7 @@ let GlobalConfig = class GlobalConfig extends GlobalConfigModel {
|
|
|
33
33
|
constructor() {
|
|
34
34
|
super(...arguments);
|
|
35
35
|
this.disableSignup = undefined;
|
|
36
|
+
this.disableUserProjectCreation = undefined;
|
|
36
37
|
// SMTP Settings.
|
|
37
38
|
this.isSMTPSecure = undefined;
|
|
38
39
|
this.smtpUsername = undefined;
|
|
@@ -86,6 +87,26 @@ __decorate([
|
|
|
86
87
|
}),
|
|
87
88
|
__metadata("design:type", Boolean)
|
|
88
89
|
], GlobalConfig.prototype, "disableSignup", void 0);
|
|
90
|
+
__decorate([
|
|
91
|
+
ColumnAccessControl({
|
|
92
|
+
create: [],
|
|
93
|
+
read: [],
|
|
94
|
+
update: [],
|
|
95
|
+
}),
|
|
96
|
+
TableColumn({
|
|
97
|
+
type: TableColumnType.Boolean,
|
|
98
|
+
title: "Disable User Project Creation",
|
|
99
|
+
description: "Only master admins can create projects when enabled.",
|
|
100
|
+
defaultValue: false,
|
|
101
|
+
}),
|
|
102
|
+
Column({
|
|
103
|
+
type: ColumnType.Boolean,
|
|
104
|
+
nullable: true,
|
|
105
|
+
default: false,
|
|
106
|
+
unique: true,
|
|
107
|
+
}),
|
|
108
|
+
__metadata("design:type", Boolean)
|
|
109
|
+
], GlobalConfig.prototype, "disableUserProjectCreation", void 0);
|
|
89
110
|
__decorate([
|
|
90
111
|
ColumnAccessControl({
|
|
91
112
|
create: [],
|