create-hhmi-example 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.
- package/README.md +78 -0
- package/copy-template.js +76 -0
- package/index.js +254 -0
- package/package.json +17 -0
- package/template/hhmiExample.Server/Program.cs +167 -0
- package/template/hhmiExample.Server/Properties/launchSettings.json +44 -0
- package/template/hhmiExample.Server/appsettings.Development.json +8 -0
- package/template/hhmiExample.Server/appsettings.json +9 -0
- package/template/hhmiExample.Server/hhmiExample.Server.csproj +50 -0
- package/template/hhmiExample.Server/hhmiExample.Server.http +6 -0
- package/template/hhmiExample.sln +33 -0
- package/template/hhmiexample.client/eslint.config.js +23 -0
- package/template/hhmiexample.client/hhmiexample.client.esproj +12 -0
- package/template/hhmiexample.client/index.html +13 -0
- package/template/hhmiexample.client/package-lock.json +6490 -0
- package/template/hhmiexample.client/package.json +42 -0
- package/template/hhmiexample.client/prompts/README.md +12 -0
- package/template/hhmiexample.client/prompts/REQUIREMENTS.md +113 -0
- package/template/hhmiexample.client/public/favicon.ico +0 -0
- package/template/hhmiexample.client/public/vite.svg +1 -0
- package/template/hhmiexample.client/src/App.css +11 -0
- package/template/hhmiexample.client/src/App.tsx +147 -0
- package/template/hhmiexample.client/src/assets/logo-black.png +0 -0
- package/template/hhmiexample.client/src/assets/logo-white.png +0 -0
- package/template/hhmiexample.client/src/assets/react.svg +1 -0
- package/template/hhmiexample.client/src/components/AppFrame/AppFrame.tsx +796 -0
- package/template/hhmiexample.client/src/components/AppFrame/Theme.tsx +98 -0
- package/template/hhmiexample.client/src/components/AppFrame/UserSettingPage.tsx +91 -0
- package/template/hhmiexample.client/src/components/AppFrame/UserSettings.tsx +146 -0
- package/template/hhmiexample.client/src/components/AppFrame/modules/ExampleConfig.tsx +86 -0
- package/template/hhmiexample.client/src/components/AppFrame/modules/index.ts +8 -0
- package/template/hhmiexample.client/src/components/AppFrame/types.ts +48 -0
- package/template/hhmiexample.client/src/components/Global/HHMIControls.tsx +567 -0
- package/template/hhmiexample.client/src/components/Global/Quill.tsx +60 -0
- package/template/hhmiexample.client/src/index.css +11 -0
- package/template/hhmiexample.client/src/main.tsx +17 -0
- package/template/hhmiexample.client/src/pages/Example/ExampleConfigurationPage.tsx +24 -0
- package/template/hhmiexample.client/src/pages/Example/ExampleHomePage.tsx +23 -0
- package/template/hhmiexample.client/src/pages/LandingPage.tsx +36 -0
- package/template/hhmiexample.client/src/pages/NotAuthorizedPage.tsx +18 -0
- package/template/hhmiexample.client/src/services/AppService.ts +297 -0
- package/template/hhmiexample.client/src/types/IExampleUser.ts +19 -0
- package/template/hhmiexample.client/src/types/IMessageLocation.ts +8 -0
- package/template/hhmiexample.client/src/vite-env.d.ts +4 -0
- package/template/hhmiexample.client/tsconfig.app.json +27 -0
- package/template/hhmiexample.client/tsconfig.json +11 -0
- package/template/hhmiexample.client/tsconfig.node.json +25 -0
- package/template/hhmiexample.client/vite.config.ts +61 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { makeStyles, Text } from "@fluentui/react-components";
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles({
|
|
4
|
+
container: {
|
|
5
|
+
display: "flex",
|
|
6
|
+
flexDirection: "column",
|
|
7
|
+
gap: "16px",
|
|
8
|
+
padding: "16px",
|
|
9
|
+
},
|
|
10
|
+
title: {
|
|
11
|
+
fontWeight: "600",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default function ExampleHomePage() {
|
|
16
|
+
const styles = useStyles();
|
|
17
|
+
return (
|
|
18
|
+
<div className={styles.container}>
|
|
19
|
+
<Text className={styles.title} as="h1">Home</Text>
|
|
20
|
+
<Text>This is the default module home. Replace with your own content.</Text>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useNavigate } from "react-router";
|
|
2
|
+
import { type IHHMIHomeIcon } from "../components/Global/HHMIControls";
|
|
3
|
+
import { HHMIHomeIcons } from "../components/Global/HHMIControls";
|
|
4
|
+
import { makeStyles } from "@fluentui/react-components";
|
|
5
|
+
import { ContentViewRegular } from '@fluentui/react-icons';
|
|
6
|
+
import type { IExampleUser } from "../types/IExampleUser";
|
|
7
|
+
|
|
8
|
+
const useStyles = makeStyles({
|
|
9
|
+
title: {
|
|
10
|
+
textAlign: 'center',
|
|
11
|
+
margin: '40px 0'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default function LandingPage(props: { currentUser: IExampleUser }) {
|
|
16
|
+
console.log("LandingPage props.currentUser", props.currentUser);
|
|
17
|
+
|
|
18
|
+
const styles = useStyles();
|
|
19
|
+
const navigate = useNavigate();
|
|
20
|
+
|
|
21
|
+
const icons: IHHMIHomeIcon[] = [
|
|
22
|
+
{
|
|
23
|
+
key: "Example",
|
|
24
|
+
text: 'Example',
|
|
25
|
+
icon: <div style={{ paddingBottom: '10px' }}><ContentViewRegular /></div>,
|
|
26
|
+
onClick: () => { navigate("/example"); }
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div>
|
|
32
|
+
<h2 className={styles.title}>Welcome to the Example App</h2>
|
|
33
|
+
<HHMIHomeIcons icons={icons} />
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { makeStyles } from "@fluentui/react-components";
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles({
|
|
4
|
+
notAuthorizedContainer: {
|
|
5
|
+
padding: '40px',
|
|
6
|
+
height: '100vh',
|
|
7
|
+
width: '100vw'
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default function NotAuthorizedPage() {
|
|
12
|
+
const styles = useStyles();
|
|
13
|
+
return (
|
|
14
|
+
<div className={styles.notAuthorizedContainer}>
|
|
15
|
+
<p>You do not have access to this site. Please contact the administrator to request access.</p>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
//import * as aiHelper from './AppInsights';
|
|
2
|
+
import type { IExampleUser, IExampleUserSettings } from "../types/IExampleUser";
|
|
3
|
+
|
|
4
|
+
export async function AuthMe() {
|
|
5
|
+
const authMeUrl = "/.auth/me";
|
|
6
|
+
const authMe = await fetch(authMeUrl, {
|
|
7
|
+
method: 'GET',
|
|
8
|
+
headers: {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
'Access-Control-Allow-Origin': '*'
|
|
11
|
+
},
|
|
12
|
+
credentials: 'same-origin'
|
|
13
|
+
});
|
|
14
|
+
return authMe;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function AuthRefresh() {
|
|
18
|
+
const refreshUrl = "/.auth/refresh";
|
|
19
|
+
const refreshResult = await fetch(refreshUrl, {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Access-Control-Allow-Origin': '*'
|
|
24
|
+
},
|
|
25
|
+
credentials: 'same-origin'
|
|
26
|
+
});
|
|
27
|
+
return refreshResult;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function getADUser(): Promise<IAuthMeResponse | null> {
|
|
31
|
+
try {
|
|
32
|
+
const authMeBlob1 = await AuthMe();
|
|
33
|
+
console.log("authMeBlob1", authMeBlob1);
|
|
34
|
+
|
|
35
|
+
const refreshResult = await AuthRefresh();
|
|
36
|
+
console.log("refresh-result", refreshResult);
|
|
37
|
+
|
|
38
|
+
const authMeBlob2 = await AuthMe();
|
|
39
|
+
console.log("authMeBlob2", authMeBlob2);
|
|
40
|
+
|
|
41
|
+
let authMe: IAuthMeResponse | null = null;
|
|
42
|
+
const authMeResponse: IAuthMeResponse[] = await authMeBlob2.json();
|
|
43
|
+
|
|
44
|
+
if (authMeResponse.length > 0) {
|
|
45
|
+
authMe = authMeResponse[0];
|
|
46
|
+
window.sessionStorage.setItem("AUTHME-IDTOKEN", authMe.id_token);
|
|
47
|
+
window.sessionStorage.setItem("AUTHME-ACCESSTOKEN", authMe.access_token);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return authMe;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.log("Authentication error:", error);
|
|
53
|
+
if (process.env.NODE_ENV === 'production') {
|
|
54
|
+
console.log("Production environment - redirecting to login");
|
|
55
|
+
window.location.href = "/.auth/login/aad?post_login_redirect_url=" + window.location.href;
|
|
56
|
+
} else {
|
|
57
|
+
console.log("Development environment - continuing without authentication");
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function HeartBeat(): Promise<string> {
|
|
64
|
+
return fetch("/api/HeartBeat", {
|
|
65
|
+
method: 'GET',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'Access-Control-Allow-Origin': '*',
|
|
69
|
+
}
|
|
70
|
+
}).then(blob => blob.text()).then(data => {
|
|
71
|
+
return data;
|
|
72
|
+
}).catch((e) => {
|
|
73
|
+
console.log(e);
|
|
74
|
+
return e;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function getCollabEnvironment(): Promise<string> {
|
|
79
|
+
return fetch("/api/Environment", {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
'Access-Control-Allow-Origin': '*',
|
|
84
|
+
}
|
|
85
|
+
}).then(blob => blob.text()).then(env => {
|
|
86
|
+
console.log("getCollabEnvironment - " + env);
|
|
87
|
+
return env;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function syncData<T>(params: ISyncData): Promise<T | T[]> {
|
|
92
|
+
return fetch("/api/SyncData", {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
'Access-Control-Allow-Origin': '*',
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(params)
|
|
99
|
+
}).then(blob => blob.json())
|
|
100
|
+
.then(data => {
|
|
101
|
+
console.log("syncData response", data);
|
|
102
|
+
if (params.IsArray === false) {
|
|
103
|
+
if (data.singleOutput) {
|
|
104
|
+
return data.singleOutput as T;
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
return data.arrayOutput as T[];
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.catch((e) => {
|
|
111
|
+
console.log(e);
|
|
112
|
+
return e;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function getExampleUser(userId: string): Promise<IExampleUser | null> {
|
|
117
|
+
const getUserParams: ISyncData = {
|
|
118
|
+
StoredProcedure: "example.p_Get_Example_User",
|
|
119
|
+
IsArray: false,
|
|
120
|
+
Parameters: [
|
|
121
|
+
{ ParameterName: "@UserId", ParameterValue: userId }
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return syncData<IExampleUser>(getUserParams).then((user) => {
|
|
126
|
+
console.log("syncData - getExampleUser", user);
|
|
127
|
+
|
|
128
|
+
if (user && typeof user === 'object' && !Array.isArray(user)) {
|
|
129
|
+
const hasValidUserId = user.UserId && typeof user.UserId === 'string' && user.UserId.trim() !== '';
|
|
130
|
+
|
|
131
|
+
if (hasValidUserId) {
|
|
132
|
+
return user as IExampleUser;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return null;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function manageExampleUserSettings(user: IExampleUserSettings, currentUserId: string): Promise<IExampleUserSettings> {
|
|
141
|
+
const userSettings = await syncData<IExampleUserSettings>({
|
|
142
|
+
StoredProcedure: "example.p_Example_Manage_UserSettings",
|
|
143
|
+
IsArray: false,
|
|
144
|
+
Parameters: [
|
|
145
|
+
{ ParameterName: "@UserId", ParameterValue: user.UserId },
|
|
146
|
+
{ ParameterName: "@IsDarkTheme", ParameterValue: user.IsDarkTheme },
|
|
147
|
+
{ ParameterName: "@EditorId", ParameterValue: currentUserId }
|
|
148
|
+
]
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
console.log("syncData - manageExampleUserSettings", userSettings);
|
|
152
|
+
|
|
153
|
+
return userSettings as IExampleUserSettings;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface IPutFileResponse {
|
|
157
|
+
azureStorageContainer: string;
|
|
158
|
+
azureStorageFileName: string;
|
|
159
|
+
fileExtension: string;
|
|
160
|
+
fileId: string;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export async function putFile(content: string, fileExtension: string, fileId: string): Promise<IPutFileResponse> {
|
|
164
|
+
const url: string = "/api/SyncFiles";
|
|
165
|
+
const blob = await fetch(url, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: {
|
|
168
|
+
"Content-Type": "application/json",
|
|
169
|
+
"Access-Control-Allow-Origin": "*",
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify({ Content: content })
|
|
172
|
+
});
|
|
173
|
+
const data = await blob.json();
|
|
174
|
+
console.log("putFile returned", data.singleOutput);
|
|
175
|
+
data.singleOutput.fileExtension = fileExtension;
|
|
176
|
+
data.singleOutput.fileId = fileId;
|
|
177
|
+
return data.singleOutput;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function searchDistributionLists(mail: string): Promise<IDistributionList[]> {
|
|
181
|
+
const accessToken = window.sessionStorage.getItem("AUTHME-ACCESSTOKEN");
|
|
182
|
+
if (mail !== null && mail.trim().length > 0 && accessToken !== null) {
|
|
183
|
+
const url = `https://graph.microsoft.com/v1.0/groups?$select=mailEnabled,mail,displayName,description&$filter=mailEnabled eq true and (startsWith(displayName, '${mail}') or startsWith(mail, '${mail}'))`;
|
|
184
|
+
console.log("Get Users URL", url);
|
|
185
|
+
return fetch(url, {
|
|
186
|
+
method: 'GET',
|
|
187
|
+
headers: {
|
|
188
|
+
'Content-Type': 'application/json',
|
|
189
|
+
'Access-Control-Allow-Origin': '*',
|
|
190
|
+
'Authorization': 'Bearer ' + accessToken,
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
.then(blob => blob.json())
|
|
194
|
+
.then(data => {
|
|
195
|
+
const lists: IDistributionList[] = data.value;
|
|
196
|
+
console.log("searchDistributionLists returned Lists", lists);
|
|
197
|
+
if (lists === null || lists === undefined) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
return lists;
|
|
201
|
+
})
|
|
202
|
+
.catch(e => {
|
|
203
|
+
console.log(e);
|
|
204
|
+
return e;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Stub for template: returns empty array. Replace with real implementation (e.g. syncData to dbo.p_SearchForWorker) per project. */
|
|
211
|
+
export async function searchUserByEmailWDHCM(_filter: string): Promise<IUserQueryResult[]> {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Stub for template: returns empty array. Replace with real implementation per project if needed. */
|
|
216
|
+
export async function getAllActiveJaneliaLabs(): Promise<IJaneliaLabSupOrg[]> {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface IJaneliaLabSupOrg {
|
|
221
|
+
SupOrgCode: string;
|
|
222
|
+
LocationCode: string;
|
|
223
|
+
SupOrgName: string;
|
|
224
|
+
SupOrgSubType: string;
|
|
225
|
+
SuperiorSupOrgCode: string;
|
|
226
|
+
LabCoordinatorId: string;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface IAuthMeResponse {
|
|
230
|
+
access_token: string;
|
|
231
|
+
expires_on: string;
|
|
232
|
+
id_token: string;
|
|
233
|
+
provider_name: string;
|
|
234
|
+
refresh_token: string;
|
|
235
|
+
user_claims: IAuthMeUserClaim[];
|
|
236
|
+
user_id: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
interface IAuthMeUserClaim {
|
|
240
|
+
typ: string;
|
|
241
|
+
val: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface IDistributionList {
|
|
245
|
+
description: string;
|
|
246
|
+
displayName: string;
|
|
247
|
+
mail: string;
|
|
248
|
+
mailEnabled: boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface ISyncData {
|
|
252
|
+
StoredProcedure: string;
|
|
253
|
+
IsArray: boolean;
|
|
254
|
+
Parameters: ISyncDataParameter[]
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface ISyncDataParameter {
|
|
258
|
+
ParameterName: string;
|
|
259
|
+
ParameterValue: string | number | Date | boolean | null | undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export interface IWDHCMSearchUser {
|
|
263
|
+
Building: string;
|
|
264
|
+
BusinessTitle: string;
|
|
265
|
+
Email: string;
|
|
266
|
+
EmployeeId: string;
|
|
267
|
+
LocationCode: string;
|
|
268
|
+
LocationName: string;
|
|
269
|
+
NameFirstPreferred: string;
|
|
270
|
+
NameLastPreferred: string;
|
|
271
|
+
NameMiddlePreferred: string;
|
|
272
|
+
Phone1: string;
|
|
273
|
+
Phone1Type: string;
|
|
274
|
+
Phone2: string;
|
|
275
|
+
Phone2Type: string;
|
|
276
|
+
PhotoURL: string;
|
|
277
|
+
SecondaryLocation: string;
|
|
278
|
+
SecondaryWorkspace: string;
|
|
279
|
+
UserIdO365: string;
|
|
280
|
+
WorkSpaceName: string;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface IUserQueryResult {
|
|
284
|
+
displayName: string;
|
|
285
|
+
givenName: string;
|
|
286
|
+
id: string;
|
|
287
|
+
jobTitle: string;
|
|
288
|
+
mail: string;
|
|
289
|
+
officeLocation: string;
|
|
290
|
+
surname: string;
|
|
291
|
+
userPrincipalName: string;
|
|
292
|
+
photoURL: string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface IArrayOutput {
|
|
296
|
+
jsonData: string;
|
|
297
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface IExampleUserSettings {
|
|
2
|
+
UserId: string;
|
|
3
|
+
IsDarkTheme: boolean;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface IExampleUser {
|
|
7
|
+
UserId: string;
|
|
8
|
+
Name: string;
|
|
9
|
+
Title: string;
|
|
10
|
+
Email: string;
|
|
11
|
+
SupOrgCode: string;
|
|
12
|
+
IsEnabled: boolean;
|
|
13
|
+
IsDeveloper: boolean;
|
|
14
|
+
IsAppManager: boolean;
|
|
15
|
+
IsPowerUser: boolean;
|
|
16
|
+
IsSuperUser: boolean;
|
|
17
|
+
PhotoURL: string;
|
|
18
|
+
IsDarkTheme: boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"forceConsistentCasingInFileNames": true,
|
|
7
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
"types": ["react", "react-dom", "vite/client", "vite-plugin-svgr/client", "node"],
|
|
18
|
+
|
|
19
|
+
"strict": true,
|
|
20
|
+
"noUnusedLocals": true,
|
|
21
|
+
"noUnusedParameters": true,
|
|
22
|
+
"erasableSyntaxOnly": true,
|
|
23
|
+
"noFallthroughCasesInSwitch": true,
|
|
24
|
+
"noUncheckedSideEffectImports": true
|
|
25
|
+
},
|
|
26
|
+
"include": ["src"]
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"types": ["node"],
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"erasableSyntaxOnly": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"noUncheckedSideEffectImports": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["vite.config.ts"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { fileURLToPath, URL } from 'node:url';
|
|
2
|
+
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
import plugin from '@vitejs/plugin-react';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import child_process from 'child_process';
|
|
8
|
+
import { env } from 'process';
|
|
9
|
+
import svgr from 'vite-plugin-svgr';
|
|
10
|
+
|
|
11
|
+
const baseFolder =
|
|
12
|
+
env.APPDATA !== undefined && env.APPDATA !== ''
|
|
13
|
+
? `${env.APPDATA}/ASP.NET/https`
|
|
14
|
+
: `${env.HOME}/.aspnet/https`;
|
|
15
|
+
|
|
16
|
+
const certificateName = "hhmiexample.client";
|
|
17
|
+
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
|
|
18
|
+
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(baseFolder)) {
|
|
21
|
+
fs.mkdirSync(baseFolder, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
|
|
25
|
+
if (0 !== child_process.spawnSync('dotnet', [
|
|
26
|
+
'dev-certs',
|
|
27
|
+
'https',
|
|
28
|
+
'--export-path',
|
|
29
|
+
certFilePath,
|
|
30
|
+
'--format',
|
|
31
|
+
'Pem',
|
|
32
|
+
'--no-password',
|
|
33
|
+
], { stdio: 'inherit', }).status) {
|
|
34
|
+
throw new Error("Could not create certificate.");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
|
|
39
|
+
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'https://localhost:7289';
|
|
40
|
+
|
|
41
|
+
export default defineConfig({
|
|
42
|
+
plugins: [plugin(), svgr()],
|
|
43
|
+
resolve: {
|
|
44
|
+
alias: {
|
|
45
|
+
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
server: {
|
|
49
|
+
proxy: {
|
|
50
|
+
'^/weatherforecast': {
|
|
51
|
+
target,
|
|
52
|
+
secure: false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
port: parseInt(env.DEV_SERVER_PORT || '61636'),
|
|
56
|
+
https: {
|
|
57
|
+
key: fs.readFileSync(keyFilePath),
|
|
58
|
+
cert: fs.readFileSync(certFilePath),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|