@gov-cy/govcy-express-services 0.1.1
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/LICENSE +21 -0
- package/README.md +1157 -0
- package/package.json +72 -0
- package/src/auth/cyLoginAuth.mjs +123 -0
- package/src/index.mjs +188 -0
- package/src/middleware/cyLoginAuth.mjs +131 -0
- package/src/middleware/govcyConfigSiteData.mjs +38 -0
- package/src/middleware/govcyCsrf.mjs +36 -0
- package/src/middleware/govcyFormsPostHandler.mjs +83 -0
- package/src/middleware/govcyHeadersControl.mjs +22 -0
- package/src/middleware/govcyHttpErrorHandler.mjs +63 -0
- package/src/middleware/govcyLanguageMiddleware.mjs +19 -0
- package/src/middleware/govcyLogger.mjs +15 -0
- package/src/middleware/govcyManifestHandler.mjs +46 -0
- package/src/middleware/govcyPDFRender.mjs +30 -0
- package/src/middleware/govcyPageHandler.mjs +110 -0
- package/src/middleware/govcyPageRender.mjs +14 -0
- package/src/middleware/govcyRequestTimer.mjs +29 -0
- package/src/middleware/govcyReviewPageHandler.mjs +102 -0
- package/src/middleware/govcyReviewPostHandler.mjs +147 -0
- package/src/middleware/govcyRoutePageHandler.mjs +37 -0
- package/src/middleware/govcyServiceEligibilityHandler.mjs +101 -0
- package/src/middleware/govcySessionData.mjs +9 -0
- package/src/middleware/govcySuccessPageHandler.mjs +112 -0
- package/src/public/img/Certificate_A4.svg +30 -0
- package/src/public/js/govcyForms.js +21 -0
- package/src/resources/govcyResources.mjs +430 -0
- package/src/standalone.mjs +7 -0
- package/src/utils/govcyApiRequest.mjs +114 -0
- package/src/utils/govcyConstants.mjs +4 -0
- package/src/utils/govcyDataLayer.mjs +311 -0
- package/src/utils/govcyEnvVariables.mjs +45 -0
- package/src/utils/govcyFormHandling.mjs +148 -0
- package/src/utils/govcyLoadConfigData.mjs +135 -0
- package/src/utils/govcyLogger.mjs +30 -0
- package/src/utils/govcyNotification.mjs +85 -0
- package/src/utils/govcyPdfMaker.mjs +27 -0
- package/src/utils/govcyReviewSummary.mjs +205 -0
- package/src/utils/govcySubmitData.mjs +530 -0
- package/src/utils/govcyUtils.mjs +13 -0
- package/src/utils/govcyValidator.mjs +352 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module govcyDataLayer
|
|
3
|
+
* @fileoverview This utility provides functions for storing and retrieving data from the session store.
|
|
4
|
+
* It includes functions to initialize the data layer, store page validation errors, store form data,
|
|
5
|
+
* retrieve validation errors, and clear site data.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize the data layer
|
|
11
|
+
*
|
|
12
|
+
* @param {object} store The session store
|
|
13
|
+
* @param {string} siteId The site id
|
|
14
|
+
* @param {string} pageUrl The page url
|
|
15
|
+
*/
|
|
16
|
+
export function initializeSiteData(store, siteId, pageUrl = null) {
|
|
17
|
+
if (!store.siteData) store.siteData = {};
|
|
18
|
+
if (!store.siteData[siteId]) store.siteData[siteId] = {};
|
|
19
|
+
if (!store.siteData[siteId].inputData) store.siteData[siteId].inputData = {};
|
|
20
|
+
if (!store.siteData[siteId].submissionData) store.siteData[siteId].submissionData = {};
|
|
21
|
+
|
|
22
|
+
if (pageUrl && !store.siteData[siteId].inputData[pageUrl]) {
|
|
23
|
+
store.siteData[siteId].inputData[pageUrl] = { formData: {} };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// export function getSubmissionData(store, siteId, pageUrl) {
|
|
28
|
+
// return store.siteData?.[siteId]?.inputData?.[pageUrl]?.formData || {};
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Store the page errors in the data layer
|
|
33
|
+
*
|
|
34
|
+
* The following is an example of the data that will be stored:
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"errors": {
|
|
38
|
+
"Iban": {
|
|
39
|
+
"id": "Iban",
|
|
40
|
+
"message": {
|
|
41
|
+
"en": "Enter your IBAN",
|
|
42
|
+
"el": "Εισαγάγετε το IBAN σας"
|
|
43
|
+
},
|
|
44
|
+
"pageUrl": ""
|
|
45
|
+
},
|
|
46
|
+
"Swift": {
|
|
47
|
+
"id": "Swift",
|
|
48
|
+
"message": {
|
|
49
|
+
"en": "Enter your SWIFT",
|
|
50
|
+
"el": "Εισαγάγετε το SWIFT σας"
|
|
51
|
+
},
|
|
52
|
+
"pageUrl": ""
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
* @param {object} store The session store
|
|
57
|
+
* @param {string} siteId The site id
|
|
58
|
+
* @param {string} pageUrl The page url
|
|
59
|
+
* @param {object} validationErrors The validation errors
|
|
60
|
+
* @param {object} formData The form data that produced the errors
|
|
61
|
+
*/
|
|
62
|
+
export function storePageValidationErrors(store, siteId, pageUrl, validationErrors, formData) {
|
|
63
|
+
// Ensure session structure is initialized
|
|
64
|
+
initializeSiteData(store, siteId, pageUrl);
|
|
65
|
+
|
|
66
|
+
// Store the validation errors
|
|
67
|
+
store.siteData[siteId].inputData[pageUrl]["validationErrors"] = {
|
|
68
|
+
errors: validationErrors,
|
|
69
|
+
formData: formData,
|
|
70
|
+
errorSummary: []
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Stores the page's form data in the data layer
|
|
76
|
+
*
|
|
77
|
+
* The following is an example of the data that will be stored:
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
field1: [
|
|
81
|
+
"value1",
|
|
82
|
+
"value2"
|
|
83
|
+
],
|
|
84
|
+
field2: "value2",
|
|
85
|
+
_csrf: "1234567890"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
*
|
|
89
|
+
* @param {object} store The session store
|
|
90
|
+
* @param {string} siteId The site id
|
|
91
|
+
* @param {string} pageUrl The page url
|
|
92
|
+
* @param {object} formData The form data to be stored
|
|
93
|
+
*/
|
|
94
|
+
export function storePageData(store, siteId, pageUrl, formData) {
|
|
95
|
+
// Ensure session structure is initialized
|
|
96
|
+
initializeSiteData(store, siteId, pageUrl);
|
|
97
|
+
|
|
98
|
+
store.siteData[siteId].inputData[pageUrl]["formData"] = formData;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Stores the site validation errors in the data layer
|
|
103
|
+
*
|
|
104
|
+
* The following is an example of the data that will be stored:
|
|
105
|
+
*
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"errors": {
|
|
109
|
+
"bank-detailsIban": {
|
|
110
|
+
"id": "Iban",
|
|
111
|
+
"message": {
|
|
112
|
+
"en": "Enter your IBAN",
|
|
113
|
+
"el": "Εισαγάγετε το IBAN σας"
|
|
114
|
+
},
|
|
115
|
+
"pageUrl": "bank-details"
|
|
116
|
+
},
|
|
117
|
+
"bank-detailsSwift": {
|
|
118
|
+
"id": "Swift",
|
|
119
|
+
"message": {
|
|
120
|
+
"en": "Enter your SWIFT",
|
|
121
|
+
"el": "Εισαγάγετε το SWIFT σας"
|
|
122
|
+
},
|
|
123
|
+
"pageUrl": "bank-details"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"errorSummary": []
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
* @param {object} store The session store
|
|
130
|
+
* @param {string} siteId The site id
|
|
131
|
+
* @param {object} validationErrors The validation errors to be stored
|
|
132
|
+
*/
|
|
133
|
+
export function storeSiteValidationErrors(store, siteId, validationErrors) {
|
|
134
|
+
initializeSiteData(store, siteId); // Ensure the structure exists
|
|
135
|
+
|
|
136
|
+
store.siteData[siteId]["submissionErrors"] = {
|
|
137
|
+
errors: validationErrors,
|
|
138
|
+
errorSummary: []
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Stores the submitted site's input data in the data layer and clears the input data
|
|
145
|
+
*
|
|
146
|
+
* Check NOTES.md for sample of the data
|
|
147
|
+
*
|
|
148
|
+
* @param {object} store The session store
|
|
149
|
+
* @param {string} siteId The site id
|
|
150
|
+
* @param {object} submissionData The submission data to be stored
|
|
151
|
+
*/
|
|
152
|
+
export function storeSiteSubmissionData(store, siteId, submissionData) {
|
|
153
|
+
initializeSiteData(store, siteId); // Ensure the structure exists
|
|
154
|
+
|
|
155
|
+
// let rawData = getSiteInputData(store, siteId);
|
|
156
|
+
// Store the submission data
|
|
157
|
+
store.siteData[siteId].submissionData = submissionData;
|
|
158
|
+
|
|
159
|
+
// Clear validation errors from the session
|
|
160
|
+
store.siteData[siteId].inputData = {};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Store eligibility result for a site and endpoint
|
|
166
|
+
* @param {object} store - session store
|
|
167
|
+
* @param {string} siteId
|
|
168
|
+
* @param {string} endpointKey - unique key for the eligibility endpoint (e.g. resolved URL)
|
|
169
|
+
* @param {object} result - API response
|
|
170
|
+
*/
|
|
171
|
+
export function storeSiteEligibilityResult(store, siteId, endpointKey, result) {
|
|
172
|
+
|
|
173
|
+
initializeSiteData(store, siteId); // Ensure the structure exists
|
|
174
|
+
|
|
175
|
+
if (!store.siteData[siteId].eligibility) store.siteData[siteId].eligibility = {};
|
|
176
|
+
store.siteData[siteId].eligibility[endpointKey] = {
|
|
177
|
+
result,
|
|
178
|
+
timestamp: Date.now()
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get eligibility result for a site and endpoint
|
|
184
|
+
* @param {object} store - session store
|
|
185
|
+
* @param {string} siteId
|
|
186
|
+
* @param {string} endpointKey
|
|
187
|
+
* @param {number} maxAgeMs - max age in ms (optional)
|
|
188
|
+
* @returns {object|null}
|
|
189
|
+
*/
|
|
190
|
+
export function getSiteEligibilityResult(store, siteId, endpointKey, maxAgeMs = null) {
|
|
191
|
+
const entry = store?.siteData?.[siteId]?.eligibility?.[endpointKey];
|
|
192
|
+
if (!entry) return null;
|
|
193
|
+
if (maxAgeMs === 0) return null; // 0 Never caches
|
|
194
|
+
if (maxAgeMs && Date.now() - entry.timestamp > maxAgeMs) return null; // Expired
|
|
195
|
+
return entry.result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get the page validation errors from the store and clear them
|
|
200
|
+
*
|
|
201
|
+
* @param {object} store The session store
|
|
202
|
+
* @param {string} siteId The site id
|
|
203
|
+
* @param {string} pageUrl The page url
|
|
204
|
+
* @returns The validation errors for the page or null if none exist. Also clears the errors from the store.
|
|
205
|
+
*/
|
|
206
|
+
export function getPageValidationErrors(store, siteId, pageUrl) {
|
|
207
|
+
const validationErrors = store?.siteData?.[siteId]?.inputData?.[pageUrl]?.validationErrors || null;
|
|
208
|
+
|
|
209
|
+
if (validationErrors) {
|
|
210
|
+
// Clear validation errors from the session
|
|
211
|
+
delete store.siteData[siteId].inputData[pageUrl].validationErrors;
|
|
212
|
+
return validationErrors;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get the posted page data from the store
|
|
220
|
+
*
|
|
221
|
+
* @param {object} store The session store
|
|
222
|
+
* @param {string} siteId The site id
|
|
223
|
+
* @param {string} pageUrl The page url
|
|
224
|
+
* @returns The form data for the page or an empty object if none exist.
|
|
225
|
+
*/
|
|
226
|
+
export function getPageData(store, siteId, pageUrl) {
|
|
227
|
+
return store?.siteData?.[siteId]?.inputData?.[pageUrl]?.formData || {};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the site validation errors from the store and clear them
|
|
232
|
+
*
|
|
233
|
+
* @param {object} store The session store
|
|
234
|
+
* @param {string} siteId |The site id
|
|
235
|
+
* @returns The validation errors for the site or null if none exist. Also clears the errors from the store.
|
|
236
|
+
*/
|
|
237
|
+
export function getSiteSubmissionErrors(store, siteId) {
|
|
238
|
+
const validationErrors = store?.siteData?.[siteId]?.submissionErrors || null;
|
|
239
|
+
|
|
240
|
+
if (validationErrors) {
|
|
241
|
+
// Clear validation errors from the session
|
|
242
|
+
delete store.siteData[siteId].submissionErrors;
|
|
243
|
+
return validationErrors;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get the site's input data from the store
|
|
251
|
+
*
|
|
252
|
+
* @param {object} store The session store
|
|
253
|
+
* @param {string} siteId |The site id
|
|
254
|
+
* @returns The site input data or null if none exist.
|
|
255
|
+
*/
|
|
256
|
+
export function getSiteInputData(store, siteId) {
|
|
257
|
+
const inputData = store?.siteData?.[siteId]?.inputData || {};
|
|
258
|
+
|
|
259
|
+
if (inputData) {
|
|
260
|
+
return inputData;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get the site's input data from the store
|
|
268
|
+
*
|
|
269
|
+
* @param {object} store The session store
|
|
270
|
+
* @param {string} siteId |The site id
|
|
271
|
+
* @returns The site input data or null if none exist.
|
|
272
|
+
*/
|
|
273
|
+
export function getSiteSubmissionData(store, siteId) {
|
|
274
|
+
initializeSiteData(store, siteId); // Ensure the structure exists
|
|
275
|
+
|
|
276
|
+
const submission = store?.siteData?.[siteId]?.submissionData || {};
|
|
277
|
+
|
|
278
|
+
if (submission) {
|
|
279
|
+
return submission;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get the value of a specific form data element from the store
|
|
287
|
+
*
|
|
288
|
+
* @param {object} store The session store
|
|
289
|
+
* @param {string} siteId The site id
|
|
290
|
+
* @param {string} pageUrl The page url
|
|
291
|
+
* @param {string} elementName The element name
|
|
292
|
+
* @returns The value of the form data for the element or an empty string if none exist.
|
|
293
|
+
*/
|
|
294
|
+
export function getFormDataValue(store, siteId, pageUrl, elementName) {
|
|
295
|
+
return store?.siteData?.[siteId]?.inputData?.[pageUrl]?.formData?.[elementName] || "";
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Get the user object from the session store
|
|
300
|
+
*
|
|
301
|
+
* @param {object} store The session store
|
|
302
|
+
* @returns The user object from the store or null if it doesn't exist.
|
|
303
|
+
*/
|
|
304
|
+
export function getUser(store){
|
|
305
|
+
return store.user || null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function clearSiteData(store, siteId) {
|
|
309
|
+
delete store?.siteData[siteId];
|
|
310
|
+
}
|
|
311
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module govcyEnvVariables
|
|
3
|
+
* @fileoverview This module manages environment variables and settings for the application.
|
|
4
|
+
* It loads environment variables from a .env file in non-production environments,
|
|
5
|
+
* and provides functions to check the current environment and retrieve environment variables.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as dotenv from 'dotenv';
|
|
9
|
+
|
|
10
|
+
// Load environment variables from .env file only in non-production environments
|
|
11
|
+
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'staging') {
|
|
12
|
+
dotenv.config();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if the current environment is production or staging
|
|
17
|
+
*
|
|
18
|
+
* @returns {boolean} true if the environment is production or staging, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
export function isProdOrStaging() {
|
|
21
|
+
// Determine environment settings
|
|
22
|
+
const ENV = whatsIsMyEnvironment();
|
|
23
|
+
return ENV === 'production' || ENV === 'staging';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the value of an environment variable
|
|
28
|
+
*
|
|
29
|
+
* @param {string} key The environment variable key
|
|
30
|
+
* @param {string} defaultValue The default value to return if the key is not found
|
|
31
|
+
* @returns The value of the environment variable or the default value
|
|
32
|
+
*/
|
|
33
|
+
export function getEnvVariable(key, defaultValue = undefined) {
|
|
34
|
+
return process.env[key] || defaultValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Return the current environment (development, staging, production)
|
|
39
|
+
*
|
|
40
|
+
* @returns {string} The current environment (development, staging, production)
|
|
41
|
+
*/
|
|
42
|
+
export function whatsIsMyEnvironment() {
|
|
43
|
+
// Determine environment
|
|
44
|
+
return process.env.NODE_ENV || 'development';
|
|
45
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module govcyFormHandling
|
|
3
|
+
* @fileoverview This module provides utility functions for handling form data in a web application.
|
|
4
|
+
* It includes functions to populate form data with session data, handle conditional elements,
|
|
5
|
+
* and show error summary when there are validation errors.
|
|
6
|
+
*/
|
|
7
|
+
import { ALLOWED_FORM_ELEMENTS } from "./govcyConstants.mjs";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper function to populate form data with session data
|
|
12
|
+
* @param {Array} formElements The form elements
|
|
13
|
+
* @param {*} theData The data either from session or request
|
|
14
|
+
*/
|
|
15
|
+
export function populateFormData(formElements, theData, validationErrors) {
|
|
16
|
+
const inputElements = ALLOWED_FORM_ELEMENTS;
|
|
17
|
+
|
|
18
|
+
// Recursively populate form data with session data
|
|
19
|
+
formElements.forEach(element => {
|
|
20
|
+
if (inputElements.includes(element.element)) {
|
|
21
|
+
const fieldName = element.params.name;
|
|
22
|
+
|
|
23
|
+
// Handle `dateInput` separately
|
|
24
|
+
if (element.element === "dateInput") {
|
|
25
|
+
element.params.dayValue = theData[`${fieldName}_day`] || "";
|
|
26
|
+
element.params.monthValue = theData[`${fieldName}_month`] || "";
|
|
27
|
+
element.params.yearValue = theData[`${fieldName}_year`] || "";
|
|
28
|
+
//Handle `datePicker` separately
|
|
29
|
+
} else if (element.element === "datePicker") {
|
|
30
|
+
const val = theData[fieldName];
|
|
31
|
+
|
|
32
|
+
// Check if the value exists and matches the D/M/YYYY or DD/MM/YYYY pattern
|
|
33
|
+
if (val && /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
|
|
34
|
+
const [day, month, year] = val.split("/").map(Number); // Convert parts to numbers
|
|
35
|
+
const date = new Date(year, month - 1, day); // Month is zero-based in JS
|
|
36
|
+
|
|
37
|
+
// Check if the date is valid (e.g., not 31/02/2020)
|
|
38
|
+
if (
|
|
39
|
+
date.getFullYear() === year &&
|
|
40
|
+
date.getMonth() === month - 1 &&
|
|
41
|
+
date.getDate() === day
|
|
42
|
+
) {
|
|
43
|
+
// Format it as YYYY-MM-DD for the <input type="date"> value
|
|
44
|
+
const yyyy = year;
|
|
45
|
+
const mm = String(month).padStart(2, '0');
|
|
46
|
+
const dd = String(day).padStart(2, '0');
|
|
47
|
+
element.params.value = `${yyyy}-${mm}-${dd}`;
|
|
48
|
+
} else {
|
|
49
|
+
// Invalid date (e.g., 31/02/2020)
|
|
50
|
+
element.params.value = "";
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// Invalid format (not matching D/M/YYYY or DD/MM/YYYY)
|
|
54
|
+
element.params.value = "";
|
|
55
|
+
}
|
|
56
|
+
// Handle all other input elements (textInput, checkboxes, radios, etc.)
|
|
57
|
+
} else {
|
|
58
|
+
element.params.value = theData[fieldName] || "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// if there are validation errors, populate the error message
|
|
62
|
+
if (validationErrors?.errors?.[fieldName]) {
|
|
63
|
+
element.params.error = validationErrors.errors[fieldName].message;
|
|
64
|
+
//populate the error summary
|
|
65
|
+
const elementId = (element.element === "checkboxes" || element.element === "radios") // if checkboxes or radios
|
|
66
|
+
? `${element.params.id}-option-1` // use the id of the first option
|
|
67
|
+
: (element.element === "dateInput") //if dateInput
|
|
68
|
+
? `${element.params.id}_day` // use the id of the day input
|
|
69
|
+
: element.params.id; // else use the id of the element
|
|
70
|
+
validationErrors.errorSummary.push({
|
|
71
|
+
link: `#${elementId}`,
|
|
72
|
+
text: validationErrors.errors[fieldName].message
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle conditional elements inside radios
|
|
78
|
+
if (element.element === "radios" && element.params.items) {
|
|
79
|
+
element.params.items.forEach(item => {
|
|
80
|
+
if (item.conditionalElements) {
|
|
81
|
+
populateFormData(item.conditionalElements, theData, validationErrors);
|
|
82
|
+
|
|
83
|
+
// Check if any conditional element has an error and add to the parent "conditionalHasErrors": true
|
|
84
|
+
if (item.conditionalElements.some(condEl => condEl.params?.error)) {
|
|
85
|
+
item.conditionalHasErrors = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Filters form data based on the form definition, including conditionals.
|
|
96
|
+
*
|
|
97
|
+
* @param {Array} elements - The form elements (including conditional ones).
|
|
98
|
+
* @param {Object} formData - The submitted form data.
|
|
99
|
+
* @returns {Object} filteredData - The filtered form data.
|
|
100
|
+
*/
|
|
101
|
+
export function getFormData(elements, formData) {
|
|
102
|
+
const filteredData = {};
|
|
103
|
+
elements.forEach(element => {
|
|
104
|
+
const { name } = element.params || {};
|
|
105
|
+
|
|
106
|
+
// Check if the element is allowed and has a name
|
|
107
|
+
if (ALLOWED_FORM_ELEMENTS.includes(element.element) && name) {
|
|
108
|
+
// Handle conditional elements (e.g., checkboxes, radios, select)
|
|
109
|
+
if (["checkboxes", "radios", "select"].includes(element.element)) {
|
|
110
|
+
const value = formData[name];
|
|
111
|
+
filteredData[name] = value !== undefined && value !== null ? value : "";
|
|
112
|
+
// Process conditional elements inside items
|
|
113
|
+
if (element.element === "radios" && element.params.items) {
|
|
114
|
+
element.params.items.forEach(item => {
|
|
115
|
+
if (item.conditionalElements) {
|
|
116
|
+
Object.assign(
|
|
117
|
+
filteredData,
|
|
118
|
+
getFormData(item.conditionalElements, formData)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
// Handle dateInput
|
|
126
|
+
else if (element.element === "dateInput") {
|
|
127
|
+
const day = formData[`${name}_day`];
|
|
128
|
+
const month = formData[`${name}_month`];
|
|
129
|
+
const year = formData[`${name}_year`];
|
|
130
|
+
filteredData[`${name}_day`] = day !== undefined && day !== null ? day : "";
|
|
131
|
+
filteredData[`${name}_month`] = month !== undefined && month !== null ? month : "";
|
|
132
|
+
filteredData[`${name}_year`] = year !== undefined && year !== null ? year : "";
|
|
133
|
+
// Handle other elements (e.g., textInput, textArea, datePicker)
|
|
134
|
+
} else {
|
|
135
|
+
filteredData[name] = formData[name] !== undefined && formData[name] !== null ? formData[name] : "";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Always process conditional elements directly attached to the current element
|
|
139
|
+
// if (element.conditionalElements) {
|
|
140
|
+
// Object.assign(filteredData, getFormData(element.conditionalElements, formData));
|
|
141
|
+
// }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
return filteredData;
|
|
148
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module govcyLoadConfigData
|
|
3
|
+
* @fileoverview This module provides functions to load and manipulate configuration data for services.
|
|
4
|
+
* It includes functions to load service data from JSON files, handle language settings, and check for staging environments.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { whatsIsMyEnvironment } from './govcyEnvVariables.mjs';
|
|
10
|
+
import { logger } from "./govcyLogger.mjs";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load JSON data from `/data` by siteId
|
|
15
|
+
* @param {string} siteId The siteId
|
|
16
|
+
* @returns {Array} services - Array of JSON data
|
|
17
|
+
*/
|
|
18
|
+
function loadConfigDataById(siteId) {
|
|
19
|
+
// const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
// const __dirname = path.dirname(__filename);
|
|
21
|
+
// const dataPath = path.join(__dirname, '..', '..', 'data'); // Adjust if needed
|
|
22
|
+
const dataPath = path.join(process.cwd(), 'data'); // Adjust if needed
|
|
23
|
+
const filePath = path.join(dataPath, `${siteId}.json`);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(filePath)) {
|
|
27
|
+
logger.debug(`Service data for '${siteId}' not found.`);
|
|
28
|
+
return null; // Return null so caller can handle 404 response
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
logger.error(`Error loading ${siteId}.json:`, error.message);
|
|
34
|
+
logger.debug(`Error loading ${siteId}.json:`, error);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load service by siteId and return a deep copy
|
|
42
|
+
*
|
|
43
|
+
* @param {string} siteId The siteId
|
|
44
|
+
* @param {string} lang The desired language
|
|
45
|
+
* @returns {Array} services - Array of JSON data
|
|
46
|
+
*/
|
|
47
|
+
export function getServiceConfigData(siteId,lang) {
|
|
48
|
+
//Load data from source
|
|
49
|
+
const service = loadConfigDataById(siteId);
|
|
50
|
+
if (!service) {
|
|
51
|
+
const error = new Error('Service not found');
|
|
52
|
+
error.status = 404;
|
|
53
|
+
throw error; // Let Express catch this
|
|
54
|
+
}
|
|
55
|
+
//Handle deep copy: Deep copy to avoid mutations
|
|
56
|
+
let serviceCopy = JSON.parse(JSON.stringify(service));
|
|
57
|
+
|
|
58
|
+
//Handle lang: Set language based on provided `lang` parameter
|
|
59
|
+
if (Array.isArray(serviceCopy.site.languages)) {
|
|
60
|
+
if (serviceCopy.site.languages.length === 1) {
|
|
61
|
+
// If there's only one language, set it to that language
|
|
62
|
+
serviceCopy.site.lang = serviceCopy.site.languages[0].code;
|
|
63
|
+
serviceCopy.site.languages = undefined;
|
|
64
|
+
} else if (serviceCopy.site.languages.some(l => l.code === lang)) {
|
|
65
|
+
// If the provided lang exists in the languages array, set it
|
|
66
|
+
serviceCopy.site.lang = lang;
|
|
67
|
+
} else if (!serviceCopy.site.lang) {
|
|
68
|
+
// Default to 'el' if no language is set
|
|
69
|
+
serviceCopy.site.lang = "el";
|
|
70
|
+
}
|
|
71
|
+
} else if (!serviceCopy.site.lang) {
|
|
72
|
+
// Default to 'el' if languages is not defined and no language is set
|
|
73
|
+
serviceCopy.site.lang = "el";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//Handle TESTING banner: check if staging and set isTesting
|
|
77
|
+
serviceCopy.site.isTesting = (whatsIsMyEnvironment() === "staging");
|
|
78
|
+
|
|
79
|
+
// Add manifest path
|
|
80
|
+
serviceCopy.site.manifest = `/${siteId}/manifest.json`;
|
|
81
|
+
|
|
82
|
+
return serviceCopy;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Load page by pageUrl and return a deep copy
|
|
87
|
+
*
|
|
88
|
+
* @param {object} service The service object containing page configurations
|
|
89
|
+
* @param {string} pageUrl The page URL
|
|
90
|
+
* @returns The page configuration object
|
|
91
|
+
*/
|
|
92
|
+
export function getPageConfigData(service, pageUrl) {
|
|
93
|
+
|
|
94
|
+
// Find the page by pageUrl
|
|
95
|
+
let page = service.pages.find(p => p.pageData.url === pageUrl);
|
|
96
|
+
|
|
97
|
+
if (!page) {
|
|
98
|
+
const error = new Error('Page not found');
|
|
99
|
+
error.status = 404;
|
|
100
|
+
throw error; // Let Express catch this
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return page;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get a list of available site configs with their titles.
|
|
108
|
+
* @returns {Array<{filename: string, title: string}>}
|
|
109
|
+
*/
|
|
110
|
+
export function listAvailableSiteConfigs() {
|
|
111
|
+
const dataPath = path.join(process.cwd(), 'data');
|
|
112
|
+
let result = [];
|
|
113
|
+
try {
|
|
114
|
+
const files = fs.readdirSync(dataPath)
|
|
115
|
+
.filter(f => f.endsWith('.json'));
|
|
116
|
+
|
|
117
|
+
for (const file of files) {
|
|
118
|
+
const siteId = path.basename(file, '.json');
|
|
119
|
+
try {
|
|
120
|
+
const config = getServiceConfigData(siteId);
|
|
121
|
+
result.push({
|
|
122
|
+
filename: siteId,
|
|
123
|
+
title: config.site?.title || {el: "Unknown Service", en: "Unknown Service", tr: "Unknown Service"}
|
|
124
|
+
});
|
|
125
|
+
} catch (e) {
|
|
126
|
+
// Skip files that can't be loaded
|
|
127
|
+
logger.debug(`Skipping ${file}: ${e.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
logger.error('Error reading data directory:', err.message);
|
|
132
|
+
return result; // Return empty array if there's an error
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs a message to the console with a given level.
|
|
3
|
+
* Falls back to console.log if an unknown level is used.
|
|
4
|
+
*
|
|
5
|
+
* @param {'info'|'warn'|'error'|'debug'} level - The log level.
|
|
6
|
+
* @param {...any} args - The content to log.
|
|
7
|
+
*/
|
|
8
|
+
export function logger(level, ...args) {
|
|
9
|
+
const timestamp = new Date().toISOString();
|
|
10
|
+
|
|
11
|
+
const logMethods = {
|
|
12
|
+
info: console.log,
|
|
13
|
+
warn: console.warn,
|
|
14
|
+
error: console.error,
|
|
15
|
+
debug: (...msg) => {
|
|
16
|
+
if (process.env.DEBUG === 'true') {
|
|
17
|
+
console.debug(...msg);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const logFn = logMethods[level] || console.log;
|
|
23
|
+
|
|
24
|
+
logFn(`[${level.toUpperCase()}] [${timestamp}]`, ...args);
|
|
25
|
+
}
|
|
26
|
+
// Attach shortcut functions to the main logger function
|
|
27
|
+
logger.info = (...args) => logger('info', ...args);
|
|
28
|
+
logger.warn = (...args) => logger('warn', ...args);
|
|
29
|
+
logger.error = (...args) => logger('error', ...args);
|
|
30
|
+
logger.debug = (...args) => logger('debug', ...args);
|