@geode/opengeodeweb-front 10.34.0-rc.9 → 10.34.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/{server → .build/server}/utils/app_config.js +10 -9
- package/{server → .build/server}/utils/cleanup.js +31 -17
- package/{server → .build/server}/utils/cloud.js +1 -4
- package/{server → .build/server}/utils/microservices.js +21 -9
- package/{server → .build/server}/utils/path.js +4 -4
- package/{server → .build/server}/utils/scripts.js +9 -9
- package/.build/server/utils/server_config.js +51 -0
- package/.build/server/utils/stream.js +6 -0
- package/{server → .build/server}/utils/ws_client.js +18 -3
- package/{shared → .build/shared}/scripts.js +10 -10
- package/.build/shared/utils/call_raw.js +31 -0
- package/{shared → .build/shared}/utils/call_schema.js +2 -2
- package/.build/shared/utils/fetch_raw.js +52 -0
- package/{shared → .build/shared}/utils/fetch_schema.js +2 -2
- package/{shared → .build/shared}/utils/file.js +3 -1
- package/{shared → .build/shared}/utils/response_handlers/load.js +10 -7
- package/{shared → .build/shared}/utils/validate_schema.js +1 -1
- package/app/components/ActionButton.vue +6 -6
- package/app/components/CameraManager/List.vue +5 -5
- package/app/components/CameraManager/Saver.vue +3 -5
- package/app/components/CameraManager.vue +6 -4
- package/app/components/CameraOrientation.vue +12 -12
- package/app/components/ClippingPlaneCard.vue +4 -2
- package/app/components/ClippingPlanes.vue +8 -8
- package/app/components/CrsSelector.vue +9 -10
- package/app/components/DeleteDialog.vue +4 -2
- package/app/components/DragAndDrop.vue +10 -12
- package/app/components/DragAndDropInternal/DragAndDropInline.vue +4 -2
- package/app/components/ExtensionSelector.vue +4 -4
- package/app/components/FeedBack/ErrorBanner.vue +3 -1
- package/app/components/FeedBack/Snackers.vue +4 -3
- package/app/components/FileSelector.vue +10 -8
- package/app/components/FileUploader.vue +11 -11
- package/app/components/GlassCard.vue +1 -1
- package/app/components/HybridRenderingView.vue +7 -5
- package/app/components/HybridViewerTooltip.vue +7 -7
- package/app/components/Launcher.vue +1 -1
- package/app/components/Loading/EcoMessages.vue +9 -3
- package/app/components/Loading.vue +2 -2
- package/app/components/MissingFilesSelector.vue +12 -10
- package/app/components/ObjectSelector.vue +10 -9
- package/app/components/PackagesVersions.vue +6 -5
- package/app/components/Recaptcha.vue +3 -3
- package/app/components/RemoteRenderingView.vue +6 -7
- package/app/components/Ruler.vue +3 -3
- package/app/components/Screenshot.vue +6 -6
- package/app/components/SearchBar.vue +4 -2
- package/app/components/ShrinkFilter.vue +7 -7
- package/app/components/Step.vue +6 -5
- package/app/components/Stepper.vue +4 -3
- package/app/components/ToolPanel.vue +6 -3
- package/app/components/ViewToolbar.vue +25 -28
- package/app/components/Viewer/ContextMenu/CenterButton.vue +7 -5
- package/app/components/Viewer/ContextMenu/CircularItems.vue +3 -3
- package/app/components/Viewer/ContextMenu/ContextMenu.vue +22 -19
- package/app/components/Viewer/ContextMenu/ContextMenuItem.vue +10 -8
- package/app/components/Viewer/ContextMenu/InfoCard.vue +11 -12
- package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +13 -17
- package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +11 -15
- package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +12 -16
- package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +8 -12
- package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +11 -15
- package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +11 -15
- package/app/components/Viewer/Generic/Model/BlocksOptions.vue +67 -38
- package/app/components/Viewer/Generic/Model/CornersOptions.vue +53 -23
- package/app/components/Viewer/Generic/Model/EdgesOptions.vue +1 -3
- package/app/components/Viewer/Generic/Model/LinesOptions.vue +67 -38
- package/app/components/Viewer/Generic/Model/ModelStyleCard.vue +31 -15
- package/app/components/Viewer/Generic/Model/ModelStyleOptions.vue +2 -3
- package/app/components/Viewer/Generic/Model/PointsOptions.vue +6 -6
- package/app/components/Viewer/Generic/Model/SurfacesOptions.vue +69 -38
- package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +64 -46
- package/app/components/Viewer/ObjectTree/Base/Controls.vue +5 -5
- package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +13 -9
- package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +5 -5
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +10 -10
- package/app/components/Viewer/ObjectTree/Box.vue +6 -4
- package/app/components/Viewer/ObjectTree/Layout.vue +23 -21
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +14 -14
- package/app/components/Viewer/ObjectTree/Views/{ModelCollections.vue → ModelTree.vue} +59 -24
- package/app/components/Viewer/Options/AttributeColorBar.vue +4 -2
- package/app/components/Viewer/Options/AttributeRangeSelector.vue +4 -2
- package/app/components/Viewer/Options/AttributeSelector.vue +7 -9
- package/app/components/Viewer/Options/ColorMapList.vue +15 -13
- package/app/components/Viewer/Options/ColorMapPicker.vue +4 -4
- package/app/components/Viewer/Options/ColorPicker.vue +223 -41
- package/app/components/Viewer/Options/ColoringTypeSelector.vue +16 -16
- package/app/components/Viewer/Options/ColormapQuickPicker.vue +7 -5
- package/app/components/Viewer/Options/OptionsSection.vue +1 -1
- package/app/components/Viewer/Options/TextureItem.vue +8 -6
- package/app/components/Viewer/Options/TexturesSelector.vue +1 -1
- package/app/components/Viewer/OverlappingObjectsPicker.vue +8 -9
- package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +2 -6
- package/app/components/Viewer/Solid/SpecificPolyhedraOptions.vue +12 -16
- package/app/components/Viewer/Surface/PolygonsOptions.vue +12 -16
- package/app/components/Viewer/Ui.vue +2 -2
- package/app/components/Wrapper.vue +1 -4
- package/app/components/ZScaling.vue +5 -3
- package/app/components/csv-preview/CsvPreviewer.vue +38 -37
- package/app/components/csv-preview/CsvSettings.vue +6 -4
- package/app/components/csv-preview/CsvTable.vue +1 -1
- package/app/composables/batch_style.ts +5 -6
- package/app/composables/clipping_planes_widget.ts +73 -181
- package/app/composables/clipping_planes_widget_entries.ts +189 -0
- package/app/composables/copy_to_clipboard.ts +7 -0
- package/app/composables/global_attribute_style.ts +163 -83
- package/app/composables/hover_highlight.ts +46 -14
- package/app/composables/model_collections.ts +26 -13
- package/app/composables/model_components.ts +26 -13
- package/app/composables/project_manager.ts +76 -19
- package/app/composables/responsive_middle_truncate.ts +13 -13
- package/app/composables/stepper_tree.ts +34 -11
- package/app/composables/tree_filter.ts +66 -44
- package/app/composables/tree_keyboard_nav.ts +25 -11
- package/app/composables/tree_scroll.ts +44 -14
- package/app/composables/use_adaptive_styles.ts +66 -23
- package/app/composables/use_overlapping_picker.ts +66 -16
- package/app/composables/use_quick_colormap.ts +16 -5
- package/app/composables/virtual_tree.ts +75 -43
- package/app/composables/vtk_implicit_plane_widget.d.ts +2 -0
- package/app/plugins/auto_store_register.ts +2 -7
- package/app/plugins/click_outside_ignore_snackbars.client.ts +39 -0
- package/app/stores/app.ts +57 -192
- package/app/stores/app_helpers/extension.ts +179 -0
- package/app/stores/back.ts +37 -21
- package/app/stores/camera_manager.ts +22 -20
- package/app/stores/cloud.ts +17 -13
- package/app/stores/data.ts +88 -47
- package/app/stores/data_helpers/collections.ts +36 -38
- package/app/stores/data_helpers/mesh.ts +45 -36
- package/app/stores/data_style.ts +50 -28
- package/app/stores/feedback.ts +6 -6
- package/app/stores/hybrid_viewer.ts +41 -52
- package/app/stores/infra.ts +39 -37
- package/app/stores/menu.ts +23 -14
- package/app/stores/treeview.ts +72 -42
- package/app/stores/viewer.ts +39 -27
- package/app/utils/attributes.ts +3 -3
- package/app/utils/clipping_planes.ts +111 -14
- package/app/utils/color_picker.ts +57 -0
- package/app/utils/colormap.ts +58 -35
- package/app/utils/default_styles/index.ts +2 -2
- package/app/utils/default_styles/meshes.ts +107 -13
- package/app/utils/default_styles/models.ts +83 -14
- package/app/utils/extension.ts +47 -20
- package/app/utils/import_workflow.ts +21 -13
- package/app/utils/name_cleaner.ts +1 -1
- package/app/utils/string.ts +1 -1
- package/app/utils/treeview.ts +2 -2
- package/internal/database/base_database.ts +7 -3
- package/internal/database/database.ts +36 -26
- package/internal/database/extended_database.ts +1 -1
- package/internal/stores/data_style/mesh/cells/cell.ts +70 -34
- package/internal/stores/data_style/mesh/cells/color.ts +12 -6
- package/internal/stores/data_style/mesh/cells/common.ts +18 -8
- package/internal/stores/data_style/mesh/cells/index.ts +122 -42
- package/internal/stores/data_style/mesh/cells/textures.ts +12 -6
- package/internal/stores/data_style/mesh/cells/vertex.ts +75 -34
- package/internal/stores/data_style/mesh/cells/visibility.ts +15 -4
- package/internal/stores/data_style/mesh/edges/color.ts +12 -6
- package/internal/stores/data_style/mesh/edges/common.ts +14 -6
- package/internal/stores/data_style/mesh/edges/edge.ts +71 -39
- package/internal/stores/data_style/mesh/edges/index.ts +117 -42
- package/internal/stores/data_style/mesh/edges/vertex.ts +76 -39
- package/internal/stores/data_style/mesh/edges/visibility.ts +15 -4
- package/internal/stores/data_style/mesh/edges/width.ts +12 -4
- package/internal/stores/data_style/mesh/index.ts +30 -11
- package/internal/stores/data_style/mesh/points/color.ts +12 -6
- package/internal/stores/data_style/mesh/points/common.ts +21 -8
- package/internal/stores/data_style/mesh/points/index.ts +101 -50
- package/internal/stores/data_style/mesh/points/size.ts +12 -4
- package/internal/stores/data_style/mesh/points/vertex.ts +75 -34
- package/internal/stores/data_style/mesh/points/visibility.ts +17 -7
- package/internal/stores/data_style/mesh/polygons/color.ts +12 -6
- package/internal/stores/data_style/mesh/polygons/common.ts +23 -6
- package/internal/stores/data_style/mesh/polygons/index.ts +130 -70
- package/internal/stores/data_style/mesh/polygons/polygon.ts +77 -43
- package/internal/stores/data_style/mesh/polygons/textures.ts +12 -6
- package/internal/stores/data_style/mesh/polygons/vertex.ts +79 -39
- package/internal/stores/data_style/mesh/polygons/visibility.ts +16 -6
- package/internal/stores/data_style/mesh/polyhedra/color.ts +12 -6
- package/internal/stores/data_style/mesh/polyhedra/common.ts +14 -6
- package/internal/stores/data_style/mesh/polyhedra/index.ts +115 -56
- package/internal/stores/data_style/mesh/polyhedra/polyhedron.ts +80 -39
- package/internal/stores/data_style/mesh/polyhedra/vertex.ts +79 -39
- package/internal/stores/data_style/mesh/polyhedra/visibility.ts +16 -6
- package/internal/stores/data_style/model/blocks/color.ts +43 -13
- package/internal/stores/data_style/model/blocks/common.ts +21 -6
- package/internal/stores/data_style/model/blocks/index.ts +89 -59
- package/internal/stores/data_style/model/blocks/polyhedron.ts +101 -44
- package/internal/stores/data_style/model/blocks/vertex.ts +107 -42
- package/internal/stores/data_style/model/blocks/visibility.ts +17 -4
- package/internal/stores/data_style/model/color.ts +171 -83
- package/internal/stores/data_style/model/common.ts +70 -21
- package/internal/stores/data_style/model/corners/color.ts +43 -11
- package/internal/stores/data_style/model/corners/common.ts +25 -6
- package/internal/stores/data_style/model/corners/index.ts +66 -41
- package/internal/stores/data_style/model/corners/vertex.ts +105 -74
- package/internal/stores/data_style/model/corners/visibility.ts +19 -4
- package/internal/stores/data_style/model/edges/common.ts +12 -4
- package/internal/stores/data_style/model/edges/index.ts +2 -1
- package/internal/stores/data_style/model/edges/visibility.ts +24 -7
- package/internal/stores/data_style/model/index.ts +24 -11
- package/internal/stores/data_style/model/lines/color.ts +41 -11
- package/internal/stores/data_style/model/lines/common.ts +17 -6
- package/internal/stores/data_style/model/lines/edge.ts +105 -42
- package/internal/stores/data_style/model/lines/index.ts +86 -59
- package/internal/stores/data_style/model/lines/vertex.ts +105 -42
- package/internal/stores/data_style/model/lines/visibility.ts +17 -4
- package/internal/stores/data_style/model/points/common.ts +8 -4
- package/internal/stores/data_style/model/points/index.ts +10 -3
- package/internal/stores/data_style/model/points/size.ts +12 -4
- package/internal/stores/data_style/model/points/visibility.ts +16 -6
- package/internal/stores/data_style/model/selection.ts +80 -58
- package/internal/stores/data_style/model/surfaces/color.ts +41 -11
- package/internal/stores/data_style/model/surfaces/common.ts +23 -6
- package/internal/stores/data_style/model/surfaces/index.ts +89 -59
- package/internal/stores/data_style/model/surfaces/polygon.ts +107 -67
- package/internal/stores/data_style/model/surfaces/vertex.ts +111 -67
- package/internal/stores/data_style/model/surfaces/visibility.ts +18 -5
- package/internal/stores/data_style/model/visibility.ts +82 -20
- package/internal/stores/data_style/state.ts +77 -65
- package/internal/stores/hybrid_viewer/brightness.ts +49 -31
- package/internal/stores/hybrid_viewer/camera.ts +191 -177
- package/internal/stores/hybrid_viewer/camera_animation.ts +11 -10
- package/internal/stores/hybrid_viewer/core.ts +64 -0
- package/internal/stores/hybrid_viewer/filters.ts +10 -16
- package/internal/stores/hybrid_viewer/highlight.ts +91 -130
- package/internal/stores/hybrid_viewer/ruler.ts +24 -15
- package/internal/stores/hybrid_viewer/scene.ts +96 -117
- package/internal/stores/hybrid_viewer/viewport.ts +74 -65
- package/internal/stores/hybrid_viewer/vtk_types.ts +13 -29
- package/internal/utils/api_fetch.ts +54 -23
- package/internal/utils/upload_file.ts +74 -53
- package/internal/utils/viewer_call.ts +22 -13
- package/internal/utils/ws_client.ts +24 -13
- package/nuxt.config.ts +58 -1
- package/package.json +20 -8
- package/server/api/cloud/extensions/run.post.ts +6 -10
- package/server/api/local/app/project_folder_path.post.ts +4 -4
- package/server/api/local/app/run_back.post.ts +5 -7
- package/server/api/local/app/run_viewer.post.ts +5 -7
- package/server/api/local/extensions/kill.post.ts +4 -3
- package/server/api/local/extensions/run.post.ts +7 -10
- package/server/api/local/extensions/upload.put.ts +44 -54
- package/server/api/microservice/app/get_is_app_ready.get.ts +4 -5
- package/server/api/microservice/app/set_app_base_url.post.ts +5 -6
- package/server/api/microservice/app/set_back_base_url.post.ts +5 -6
- package/server/api/microservice/app/set_is_app_ready.post.ts +5 -6
- package/server/api/microservice/app/set_viewer_base_url.post.ts +5 -6
- package/server/api/microservice/extensions/download.post.ts +5 -4
- package/server/api/serverless/run_cloud.post.ts +53 -11
- package/server/utils/app_config.ts +29 -22
- package/server/utils/cleanup.ts +60 -30
- package/server/utils/cloud.ts +9 -8
- package/server/utils/extension.ts +23 -9
- package/server/utils/microservices.ts +33 -24
- package/server/utils/path.ts +4 -4
- package/server/utils/scripts.ts +26 -23
- package/server/utils/server.ts +2 -2
- package/server/utils/server_config.ts +41 -27
- package/server/utils/stream.ts +6 -0
- package/server/utils/ws_client.ts +42 -8
- package/shared/scripts.ts +15 -10
- package/shared/utils/call_raw.ts +23 -16
- package/shared/utils/call_schema.ts +5 -8
- package/shared/utils/fetch_raw.ts +87 -47
- package/shared/utils/fetch_schema.ts +6 -9
- package/shared/utils/file.ts +8 -2
- package/shared/utils/response_handlers/load.ts +26 -11
- package/shared/utils/types.ts +9 -9
- package/shared/utils/validate_schema.ts +1 -1
- package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +0 -276
- package/server/types/vendor.d.ts +0 -64
- package/server/utils/server_config.js +0 -43
- package/shared/utils/call_raw.js +0 -30
- package/shared/utils/fetch_raw.js +0 -40
- /package/{opengeodeweb_front_schemas.js → .build/opengeodeweb_front_schemas.js} +0 -0
- /package/{server → .build/server}/utils/server.js +0 -0
- /package/{shared → .build/shared}/app_mode.js +0 -0
- /package/{shared → .build/shared}/utils/parse_boolean.js +0 -0
- /package/{shared → .build/shared}/utils/types.js +0 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
1
|
+
import type { JsonRpcSchema, RequestHandlersWithValidation } from "@ogw_shared/utils/types.js";
|
|
3
2
|
import { endRequestLog, startRequestLog } from "@ogw_front/utils/log";
|
|
3
|
+
import type { Microservice } from "./api_fetch.js";
|
|
4
|
+
import type { RpcClient } from "@ogw_shared/utils/call_raw.js";
|
|
4
5
|
import { callSchema } from "@ogw_shared/utils/call_schema";
|
|
5
6
|
import { useFeedbackStore } from "@ogw_front/stores/feedback";
|
|
6
|
-
import type { JsonRpcSchema, RequestHandlersWithValidation } from "@ogw_shared/utils/types.js";
|
|
7
|
-
import type { RpcClient } from "@ogw_shared/utils/call_raw.js";
|
|
8
|
-
import type { Microservice } from "./api_fetch.js";
|
|
9
7
|
|
|
10
8
|
interface ViewerMicroservice extends Microservice {
|
|
11
9
|
client: RpcClient;
|
|
@@ -23,7 +21,17 @@ interface RpcErrorLike {
|
|
|
23
21
|
message?: string;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
function
|
|
24
|
+
function toRpcErrorLike(value: unknown): RpcErrorLike {
|
|
25
|
+
if (typeof value !== "object" || value === null) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
code: "code" in value && typeof value.code === "number" ? value.code : undefined,
|
|
30
|
+
message: "message" in value && typeof value.message === "string" ? value.message : undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function viewer_call(
|
|
27
35
|
microservice: ViewerMicroservice,
|
|
28
36
|
{ schema, params = {}, timeout }: ViewerCallParams,
|
|
29
37
|
{
|
|
@@ -31,12 +39,13 @@ function viewer_call(
|
|
|
31
39
|
response_function,
|
|
32
40
|
response_error_function,
|
|
33
41
|
}: RequestHandlersWithValidation = {},
|
|
34
|
-
) {
|
|
42
|
+
): Promise<unknown> {
|
|
35
43
|
const feedbackStore = useFeedbackStore();
|
|
36
44
|
const { client } = microservice;
|
|
45
|
+
microservice.start_request();
|
|
37
46
|
|
|
38
47
|
const requestStartingTime = startRequestLog(microservice, schema);
|
|
39
|
-
|
|
48
|
+
const result = await callSchema(
|
|
40
49
|
{
|
|
41
50
|
schema,
|
|
42
51
|
params,
|
|
@@ -46,7 +55,7 @@ function viewer_call(
|
|
|
46
55
|
{
|
|
47
56
|
request_error_function(error: unknown) {
|
|
48
57
|
microservice.stop_request();
|
|
49
|
-
const typedError = error
|
|
58
|
+
const typedError = toRpcErrorLike(error);
|
|
50
59
|
feedbackStore.add_error(
|
|
51
60
|
typedError.code ?? 0,
|
|
52
61
|
schema.$id,
|
|
@@ -54,7 +63,7 @@ function viewer_call(
|
|
|
54
63
|
typedError.message ?? "",
|
|
55
64
|
);
|
|
56
65
|
if (request_error_function) {
|
|
57
|
-
request_error_function(error);
|
|
66
|
+
void request_error_function(error);
|
|
58
67
|
}
|
|
59
68
|
},
|
|
60
69
|
response_function(data: unknown) {
|
|
@@ -66,8 +75,7 @@ function viewer_call(
|
|
|
66
75
|
},
|
|
67
76
|
response_error_function(response: unknown) {
|
|
68
77
|
microservice.stop_request();
|
|
69
|
-
|
|
70
|
-
const typedResponse = response as RpcErrorLike;
|
|
78
|
+
const typedResponse = toRpcErrorLike(response);
|
|
71
79
|
feedbackStore.add_error(
|
|
72
80
|
typedResponse.code ?? 0,
|
|
73
81
|
schema.$id,
|
|
@@ -75,7 +83,7 @@ function viewer_call(
|
|
|
75
83
|
typedResponse.message ?? "",
|
|
76
84
|
);
|
|
77
85
|
if (response_error_function) {
|
|
78
|
-
response_error_function(response);
|
|
86
|
+
void response_error_function(response);
|
|
79
87
|
}
|
|
80
88
|
},
|
|
81
89
|
validation_error_function({ code, name, error }) {
|
|
@@ -84,6 +92,7 @@ function viewer_call(
|
|
|
84
92
|
},
|
|
85
93
|
},
|
|
86
94
|
);
|
|
95
|
+
return result;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
export { viewer_call };
|
|
@@ -1,32 +1,43 @@
|
|
|
1
|
-
// Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
3
1
|
// Third party imports
|
|
4
|
-
import vtkWSLinkClient, {
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import vtkWSLinkClient, {
|
|
3
|
+
type vtkWSLinkClient as VtkWSLinkClient,
|
|
4
|
+
newInstance,
|
|
5
|
+
} from "@kitware/vtk.js/IO/Core/WSLinkClient";
|
|
7
6
|
import SmartConnect from "wslink/src/SmartConnect";
|
|
8
|
-
import _ from "lodash";
|
|
9
7
|
|
|
10
8
|
interface WsClientCallbacks {
|
|
11
9
|
onConnectionClose?: () => void;
|
|
12
10
|
}
|
|
13
11
|
|
|
12
|
+
// Wslink's connection event shape is untyped upstream; this describes the fields we read.
|
|
13
|
+
interface WsConnectionEvent {
|
|
14
|
+
response?: { error?: string };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isVtkWSLinkClient(value: unknown): value is VtkWSLinkClient {
|
|
18
|
+
return (
|
|
19
|
+
typeof value === "object" &&
|
|
20
|
+
value !== null &&
|
|
21
|
+
"connect" in value &&
|
|
22
|
+
"onConnectionError" in value &&
|
|
23
|
+
"onConnectionClose" in value
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
async function initWebSocketClient(
|
|
15
28
|
baseUrl: string,
|
|
16
29
|
initialClient: unknown = {},
|
|
17
30
|
{ onConnectionClose }: WsClientCallbacks = {},
|
|
18
31
|
): Promise<VtkWSLinkClient> {
|
|
19
32
|
vtkWSLinkClient.setSmartConnectClass(SmartConnect);
|
|
20
|
-
const client = (
|
|
33
|
+
const client = isVtkWSLinkClient(initialClient) ? initialClient : newInstance();
|
|
21
34
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const message = httpReq?.response?.error || `Connection error`;
|
|
35
|
+
client.onConnectionError((httpReq: WsConnectionEvent) => {
|
|
36
|
+
const message = httpReq.response?.error ?? "Connection error";
|
|
25
37
|
console.error(message);
|
|
26
38
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const message = httpReq?.response?.error || `Connection close`;
|
|
39
|
+
client.onConnectionClose((httpReq: WsConnectionEvent) => {
|
|
40
|
+
const message = httpReq.response?.error ?? "Connection close";
|
|
30
41
|
onConnectionClose?.();
|
|
31
42
|
console.error(message);
|
|
32
43
|
});
|
package/nuxt.config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Node imports
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
// Third party imports
|
|
@@ -9,6 +10,28 @@ import package_json from "./package.json";
|
|
|
9
10
|
|
|
10
11
|
const __dirname = import.meta.dirname;
|
|
11
12
|
|
|
13
|
+
// Oxlint's type-aware linter auto-discovers each file's nearest tsconfig.json
|
|
14
|
+
// By walking up directories, and any "extends" on that discovered file makes
|
|
15
|
+
// Its whole type-aware resolution collapse: every symbol coming through the
|
|
16
|
+
// Aliases normally only defined in .nuxt/tsconfig.json (@ogw_shared, etc.,
|
|
17
|
+
// But also Nuxt's own #app/#imports/defineStore auto-imports) becomes an
|
|
18
|
+
// `error` type, even though tsc/vue-tsc resolve the exact same "extends"
|
|
19
|
+
// Chain correctly (oxc-project/oxc#22345). The only fix is for the root
|
|
20
|
+
// Tsconfig.json to be fully self-contained: no "extends", with its own copy
|
|
21
|
+
// Of .nuxt/tsconfig.json's compilerOptions.paths (re-relativized here, since
|
|
22
|
+
// They're written relative to .nuxt/) and a deliberately project-wide
|
|
23
|
+
// "include" (unlike .nuxt/tsconfig.json's own include, which only covers
|
|
24
|
+
// Nuxt's conventional folders and would otherwise silently drop internal/,
|
|
25
|
+
// Tests/, etc. from the program). Regenerated on every Nuxt prepare so it
|
|
26
|
+
// Can never drift from what Nuxt actually resolves.
|
|
27
|
+
function remap_path_to_root(target: string, build_dir: string): string {
|
|
28
|
+
const relative = path
|
|
29
|
+
.relative(__dirname, path.resolve(build_dir, target))
|
|
30
|
+
.split(path.sep)
|
|
31
|
+
.join("/");
|
|
32
|
+
return relative.startsWith("./") || relative.startsWith("../") ? relative : `./${relative}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
12
35
|
// Nuxt requires this file's config object as the default export; the shared org lint config's no-default-export override only lists *.config.js, not *.config.ts, since this file predates the TypeScript migration.
|
|
13
36
|
// oxlint-disable-next-line import/no-default-export
|
|
14
37
|
export default defineNuxtConfig({
|
|
@@ -17,7 +40,7 @@ export default defineNuxtConfig({
|
|
|
17
40
|
COMMAND_BACK: "opengeodeweb-back",
|
|
18
41
|
COMMAND_VIEWER: "opengeodeweb-viewer",
|
|
19
42
|
NUXT_ROOT_PATH: __dirname,
|
|
20
|
-
MODE: process.env.MODE
|
|
43
|
+
MODE: process.env.MODE ?? "CLOUD",
|
|
21
44
|
PROJECT: package_json.name,
|
|
22
45
|
},
|
|
23
46
|
},
|
|
@@ -108,4 +131,38 @@ export default defineNuxtConfig({
|
|
|
108
131
|
],
|
|
109
132
|
},
|
|
110
133
|
},
|
|
134
|
+
typescript: {
|
|
135
|
+
tsConfig: {
|
|
136
|
+
compilerOptions: {
|
|
137
|
+
types: ["node", "ws", "busboy"],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
hooks: {
|
|
143
|
+
"prepare:types": ({ tsConfig }) => {
|
|
144
|
+
const paths = tsConfig.compilerOptions?.paths;
|
|
145
|
+
if (!paths) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const build_dir = path.resolve(__dirname, ".nuxt");
|
|
149
|
+
const root_paths = Object.fromEntries(
|
|
150
|
+
Object.entries(paths).map(([alias, targets]) => [
|
|
151
|
+
alias,
|
|
152
|
+
targets.map((target) => remap_path_to_root(target, build_dir)),
|
|
153
|
+
]),
|
|
154
|
+
);
|
|
155
|
+
fs.writeFileSync(
|
|
156
|
+
path.resolve(__dirname, "tsconfig.json"),
|
|
157
|
+
`${JSON.stringify(
|
|
158
|
+
{
|
|
159
|
+
compilerOptions: { ...tsConfig.compilerOptions, paths: root_paths },
|
|
160
|
+
include: ["**/*", "./.nuxt/nuxt.d.ts"],
|
|
161
|
+
},
|
|
162
|
+
undefined,
|
|
163
|
+
2,
|
|
164
|
+
)}\n`,
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
},
|
|
111
168
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geode/opengeodeweb-front",
|
|
3
|
-
"version": "10.34.0
|
|
3
|
+
"version": "10.34.0",
|
|
4
4
|
"description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
|
|
5
5
|
"homepage": "https://github.com/Geode-solutions/OpenGeodeWeb-Front",
|
|
6
6
|
"bugs": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"internal",
|
|
22
22
|
"server",
|
|
23
23
|
"shared",
|
|
24
|
+
".build",
|
|
24
25
|
"nuxt.config.ts",
|
|
25
26
|
"opengeodeweb_front_schemas.json",
|
|
26
27
|
"opengeodeweb_front_schemas.js",
|
|
@@ -32,6 +33,14 @@
|
|
|
32
33
|
"main": "./nuxt.config.ts",
|
|
33
34
|
"exports": {
|
|
34
35
|
".": "./nuxt.config.ts",
|
|
36
|
+
"./server/utils/*.js": {
|
|
37
|
+
"types": "./server/utils/*.ts",
|
|
38
|
+
"default": "./.build/server/utils/*.js"
|
|
39
|
+
},
|
|
40
|
+
"./shared/*.js": {
|
|
41
|
+
"types": "./shared/*.ts",
|
|
42
|
+
"default": "./.build/shared/*.js"
|
|
43
|
+
},
|
|
35
44
|
"./*": "./*"
|
|
36
45
|
},
|
|
37
46
|
"publishConfig": {
|
|
@@ -53,13 +62,15 @@
|
|
|
53
62
|
"prepublishOnly": "npm run build"
|
|
54
63
|
},
|
|
55
64
|
"dependencies": {
|
|
56
|
-
"@geode/opengeodeweb-back": "
|
|
57
|
-
"@geode/opengeodeweb-viewer": "
|
|
58
|
-
"@google-cloud/run": "
|
|
65
|
+
"@geode/opengeodeweb-back": "latest",
|
|
66
|
+
"@geode/opengeodeweb-viewer": "latest",
|
|
67
|
+
"@google-cloud/run": "4.1.0",
|
|
59
68
|
"@kitware/vtk.js": "33.3.0",
|
|
60
69
|
"@mdi/font": "7.4.47",
|
|
61
70
|
"@pinia/nuxt": "1.0.1",
|
|
62
|
-
"@types/
|
|
71
|
+
"@types/busboy": "1.5.4",
|
|
72
|
+
"@types/node": "22.20.3",
|
|
73
|
+
"@types/ws": "8.18.1",
|
|
63
74
|
"@vueuse/components": "13.1.0",
|
|
64
75
|
"@vueuse/core": "13.1.0",
|
|
65
76
|
"@vueuse/nuxt": "13.1.0",
|
|
@@ -69,8 +80,8 @@
|
|
|
69
80
|
"conf": "15.1.0",
|
|
70
81
|
"dexie": "4.2.1",
|
|
71
82
|
"get-port-please": "3.2.0",
|
|
72
|
-
"google-auth-library": "
|
|
73
|
-
"googleapis": "
|
|
83
|
+
"google-auth-library": "11.0.2",
|
|
84
|
+
"googleapis": "181.0.0",
|
|
74
85
|
"js-file-download": "0.4.12",
|
|
75
86
|
"jszip": "3.10.1",
|
|
76
87
|
"lodash": "4.18.1",
|
|
@@ -89,7 +100,8 @@
|
|
|
89
100
|
"vuetify": "4.2.0",
|
|
90
101
|
"vuetify-nuxt-module": "1.0.0-rc.5",
|
|
91
102
|
"ws": "8.21.2",
|
|
92
|
-
"wslink": "1.12.4"
|
|
103
|
+
"wslink": "1.12.4",
|
|
104
|
+
"zod": "4.4.3"
|
|
93
105
|
},
|
|
94
106
|
"devDependencies": {
|
|
95
107
|
"@nuxt/test-utils": "4.1.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
|
|
4
4
|
// Third party imports
|
|
5
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
5
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
6
6
|
|
|
7
7
|
// Local imports
|
|
8
8
|
import {
|
|
@@ -25,15 +25,13 @@ interface RunExtensionsBody {
|
|
|
25
25
|
projectName: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export default defineEventHandler(async (event) => {
|
|
28
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
29
29
|
try {
|
|
30
30
|
console.log("NITRO: runExtensions", event);
|
|
31
31
|
const { projectFolderPath, projectName } = await readBody<RunExtensionsBody>(event);
|
|
32
32
|
const extensionsConfig = extensionsConf(projectName);
|
|
33
33
|
const extensionsArray = await Promise.all(
|
|
34
|
-
Object.
|
|
35
|
-
// Safe: extensionId comes from Object.keys(extensionsConfig) itself.
|
|
36
|
-
const extensionPath = extensionsConfig[extensionId]!.path;
|
|
34
|
+
Object.entries(extensionsConfig).map(async ([extensionId, { path: extensionPath }]) => {
|
|
37
35
|
const unzippedExtensionPath = await unzipFile(
|
|
38
36
|
extensionPath,
|
|
39
37
|
extensionFolderPath(projectFolderPath, extensionId),
|
|
@@ -49,12 +47,10 @@ export default defineEventHandler(async (event) => {
|
|
|
49
47
|
const port = await runExtension(id, backendExecutable, unzippedExtensionPath, {
|
|
50
48
|
projectFolderPath,
|
|
51
49
|
});
|
|
52
|
-
|
|
50
|
+
addMicroserviceMetadatas(projectFolderPath, {
|
|
53
51
|
type: "back",
|
|
54
52
|
name,
|
|
55
|
-
|
|
56
|
-
// (pre-existing bug: addMicroserviceMetadatas/URLs then embed "undefined").
|
|
57
|
-
port: port as number,
|
|
53
|
+
port,
|
|
58
54
|
});
|
|
59
55
|
return {
|
|
60
56
|
id,
|
|
@@ -74,7 +70,7 @@ export default defineEventHandler(async (event) => {
|
|
|
74
70
|
console.error("Error running extensions:", error);
|
|
75
71
|
throw createError({
|
|
76
72
|
statusCode: 500,
|
|
77
|
-
statusMessage:
|
|
73
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
78
74
|
});
|
|
79
75
|
}
|
|
80
76
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Node imports
|
|
2
2
|
|
|
3
3
|
// Third party imports
|
|
4
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
4
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
7
7
|
import {
|
|
@@ -13,11 +13,11 @@ interface ProjectFolderPathBody {
|
|
|
13
13
|
PROJECT: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export default defineEventHandler(async (event) => {
|
|
16
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
17
17
|
try {
|
|
18
18
|
const { PROJECT } = await readBody<ProjectFolderPathBody>(event);
|
|
19
19
|
const projectFolderPath = generateProjectFolderPath(PROJECT);
|
|
20
|
-
|
|
20
|
+
createPath(projectFolderPath);
|
|
21
21
|
|
|
22
22
|
return {
|
|
23
23
|
statusCode: 200,
|
|
@@ -27,7 +27,7 @@ export default defineEventHandler(async (event) => {
|
|
|
27
27
|
console.log(error);
|
|
28
28
|
throw createError({
|
|
29
29
|
statusCode: 500,
|
|
30
|
-
statusMessage:
|
|
30
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Node imports
|
|
2
2
|
|
|
3
3
|
// Third party imports
|
|
4
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
4
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
7
7
|
import {
|
|
@@ -15,16 +15,14 @@ interface RunBackBody {
|
|
|
15
15
|
args: { projectFolderPath: string; [key: string]: unknown };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export default defineEventHandler(async (event) => {
|
|
18
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
19
19
|
try {
|
|
20
20
|
const { COMMAND_BACK, NUXT_ROOT_PATH, args } = await readBody<RunBackBody>(event);
|
|
21
21
|
const port = await runBack(COMMAND_BACK, NUXT_ROOT_PATH, args);
|
|
22
|
-
|
|
22
|
+
addMicroserviceMetadatas(args.projectFolderPath, {
|
|
23
23
|
type: "back",
|
|
24
24
|
name: COMMAND_BACK,
|
|
25
|
-
|
|
26
|
-
// (pre-existing bug: addMicroserviceMetadatas/URLs then embed "undefined").
|
|
27
|
-
port: port as number,
|
|
25
|
+
port,
|
|
28
26
|
});
|
|
29
27
|
|
|
30
28
|
return {
|
|
@@ -35,7 +33,7 @@ export default defineEventHandler(async (event) => {
|
|
|
35
33
|
console.log(error);
|
|
36
34
|
throw createError({
|
|
37
35
|
statusCode: 500,
|
|
38
|
-
statusMessage:
|
|
36
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
39
37
|
});
|
|
40
38
|
}
|
|
41
39
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Node imports
|
|
2
2
|
|
|
3
3
|
// Third party imports
|
|
4
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
4
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
7
7
|
import {
|
|
@@ -15,16 +15,14 @@ interface RunViewerBody {
|
|
|
15
15
|
args: { projectFolderPath: string; [key: string]: unknown };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export default defineEventHandler(async (event) => {
|
|
18
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
19
19
|
try {
|
|
20
20
|
const { COMMAND_VIEWER, NUXT_ROOT_PATH, args } = await readBody<RunViewerBody>(event);
|
|
21
21
|
const port = await runViewer(COMMAND_VIEWER, NUXT_ROOT_PATH, args);
|
|
22
|
-
|
|
22
|
+
addMicroserviceMetadatas(args.projectFolderPath, {
|
|
23
23
|
type: "viewer",
|
|
24
24
|
name: COMMAND_VIEWER,
|
|
25
|
-
|
|
26
|
-
// (pre-existing bug: addMicroserviceMetadatas/URLs then embed "undefined").
|
|
27
|
-
port: port as number,
|
|
25
|
+
port,
|
|
28
26
|
});
|
|
29
27
|
|
|
30
28
|
return {
|
|
@@ -35,7 +33,7 @@ export default defineEventHandler(async (event) => {
|
|
|
35
33
|
console.log(error);
|
|
36
34
|
throw createError({
|
|
37
35
|
statusCode: 500,
|
|
38
|
-
statusMessage:
|
|
36
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
39
37
|
});
|
|
40
38
|
}
|
|
41
39
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Node imports
|
|
2
2
|
|
|
3
3
|
// Third party imports
|
|
4
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
4
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
5
5
|
|
|
6
6
|
// Local imports
|
|
7
7
|
import {
|
|
@@ -19,7 +19,7 @@ interface KillExtensionBody {
|
|
|
19
19
|
extensionId: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export default defineEventHandler(async (event) => {
|
|
22
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
23
23
|
try {
|
|
24
24
|
const body = await readBody<KillExtensionBody>(event);
|
|
25
25
|
const { projectFolderPath, projectName, extensionId } = body;
|
|
@@ -43,7 +43,8 @@ export default defineEventHandler(async (event) => {
|
|
|
43
43
|
console.error("Error killing extension:", error);
|
|
44
44
|
throw createError({
|
|
45
45
|
statusCode: 500,
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
|
|
4
4
|
// Third party imports
|
|
5
|
-
import { createError, defineEventHandler, readBody } from "h3";
|
|
5
|
+
import { type H3Event, createError, defineEventHandler, readBody } from "h3";
|
|
6
6
|
|
|
7
7
|
// Local imports
|
|
8
8
|
import {
|
|
@@ -25,15 +25,13 @@ interface RunExtensionsBody {
|
|
|
25
25
|
projectName: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export default defineEventHandler(async (event) => {
|
|
28
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
29
29
|
try {
|
|
30
30
|
console.log("NITRO: runExtensions", event);
|
|
31
31
|
const { projectFolderPath, projectName } = await readBody<RunExtensionsBody>(event);
|
|
32
32
|
const extensionsConfig = extensionsConf(projectName);
|
|
33
33
|
const extensionsArray = await Promise.all(
|
|
34
|
-
Object.
|
|
35
|
-
// Safe: extensionId comes from Object.keys(extensionsConfig) itself.
|
|
36
|
-
const extensionPath = extensionsConfig[extensionId]!.path;
|
|
34
|
+
Object.entries(extensionsConfig).map(async ([extensionId, { path: extensionPath }]) => {
|
|
37
35
|
const unzippedExtensionPath = await unzipFile(
|
|
38
36
|
extensionPath,
|
|
39
37
|
extensionFolderPath(projectFolderPath, extensionId),
|
|
@@ -49,12 +47,10 @@ export default defineEventHandler(async (event) => {
|
|
|
49
47
|
const port = await runBack(backendExecutable, unzippedExtensionPath, {
|
|
50
48
|
projectFolderPath,
|
|
51
49
|
});
|
|
52
|
-
|
|
50
|
+
addMicroserviceMetadatas(projectFolderPath, {
|
|
53
51
|
type: "back",
|
|
54
52
|
name,
|
|
55
|
-
|
|
56
|
-
// (pre-existing bug: addMicroserviceMetadatas/URLs then embed "undefined").
|
|
57
|
-
port: port as number,
|
|
53
|
+
port,
|
|
58
54
|
});
|
|
59
55
|
return {
|
|
60
56
|
id,
|
|
@@ -74,7 +70,8 @@ export default defineEventHandler(async (event) => {
|
|
|
74
70
|
console.error("Error running extensions:", error);
|
|
75
71
|
throw createError({
|
|
76
72
|
statusCode: 500,
|
|
77
|
-
|
|
73
|
+
|
|
74
|
+
statusMessage: error instanceof Error ? error.message : String(error),
|
|
78
75
|
});
|
|
79
76
|
}
|
|
80
77
|
});
|
|
@@ -1,81 +1,71 @@
|
|
|
1
|
-
// Not auto-fixable (eslint's sort-imports core rule has no autofixer) and this file's import order doesn't match its syntax-kind-then-alphabetical requirement - left as-is rather than manually reordered across the codebase for a purely cosmetic rule.
|
|
2
|
-
// oxlint-disable eslint/sort-imports
|
|
3
|
-
/// <reference path="../../../types/vendor.d.ts" />
|
|
4
1
|
// Node imports
|
|
5
|
-
import { finished, pipeline } from "node:stream/promises";
|
|
6
2
|
import { Readable } from "node:stream";
|
|
7
|
-
import type { ReadableStream as NodeReadableStream } from "node:stream/web";
|
|
8
3
|
import fs from "node:fs";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
9
5
|
|
|
10
6
|
// Third party imports
|
|
11
|
-
import { createError, defineEventHandler,
|
|
12
|
-
import busboy from "busboy";
|
|
7
|
+
import { type H3Event, createError, defineEventHandler, getQuery, getRequestWebStream } from "h3";
|
|
13
8
|
|
|
14
9
|
// Local imports
|
|
15
10
|
import {
|
|
16
11
|
registerExtensionFile,
|
|
17
12
|
targetExtensionFilePath,
|
|
18
13
|
} from "@geode/opengeodeweb-front/server/utils/app_config.ts";
|
|
14
|
+
import { BYTES_PER_KIBIBYTE } from "@ogw_shared/utils/file.js";
|
|
15
|
+
import { toNodeWebStream } from "@geode/opengeodeweb-front/server/utils/stream.ts";
|
|
19
16
|
|
|
17
|
+
const CODE_200 = 200;
|
|
20
18
|
const CODE_201 = 201;
|
|
21
|
-
const BYTES_PER_KIBIBYTE = 1024;
|
|
22
19
|
const MAX_FILE_MEGABYTES = 500;
|
|
23
20
|
const FILE_SIZE_LIMIT = MAX_FILE_MEGABYTES * BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE;
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
fileSize: FILE_SIZE_LIMIT,
|
|
33
|
-
files: 1,
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
let projectName = "";
|
|
37
|
-
busboyInstance.on("field", (name, value) => {
|
|
38
|
-
console.log(`Field ${name}: ${value}`);
|
|
39
|
-
if (name === "projectName") {
|
|
40
|
-
projectName = value;
|
|
41
|
-
}
|
|
42
|
-
});
|
|
22
|
+
function requiredQueryString(event: H3Event, key: string): string {
|
|
23
|
+
const value = getQuery(event)[key];
|
|
24
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
25
|
+
throw createError({ statusCode: 400, message: `Missing "${key}" query parameter` });
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const writePromise = (async () => {
|
|
52
|
-
const writeStream = fs.createWriteStream(targetPath);
|
|
53
|
-
await pipeline(fileStream, writeStream);
|
|
54
|
-
savedFiles.push(targetPath);
|
|
55
|
-
console.log("File written:", targetPath);
|
|
56
|
-
})();
|
|
57
|
-
writePromises.push(writePromise);
|
|
58
|
-
fileStream.on("limit", () => busboyInstance.destroy(new Error("File too large")));
|
|
59
|
-
});
|
|
30
|
+
function requiredQueryInt(event: H3Event, key: string): number {
|
|
31
|
+
const parsed = Math.trunc(Number(requiredQueryString(event, key)));
|
|
32
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
33
|
+
throw createError({ statusCode: 400, message: `Invalid "${key}" query parameter` });
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
60
37
|
|
|
61
|
-
|
|
62
|
-
|
|
38
|
+
// The file is sent as a raw (non-multipart) body, split into sequential chunks the client uploads in order (see OpenGeodeWeb-Front's upload_file.ts and OpenGeodeWeb-Back's upload_file route, which use the same protocol). Each chunk is appended to a `.part` file; the last one triggers the existing zip-metadata registration.
|
|
39
|
+
export default defineEventHandler(async (event: H3Event) => {
|
|
40
|
+
const projectName = requiredQueryString(event, "projectName");
|
|
41
|
+
const filename = requiredQueryString(event, "filename");
|
|
42
|
+
const totalChunks = requiredQueryInt(event, "total_chunks");
|
|
43
|
+
const chunkIndex = requiredQueryInt(event, "chunk_index");
|
|
44
|
+
if (chunkIndex >= totalChunks) {
|
|
45
|
+
throw createError({ statusCode: 400, message: "chunk_index must be less than total_chunks" });
|
|
46
|
+
}
|
|
63
47
|
|
|
64
48
|
const webStream = getRequestWebStream(event);
|
|
65
49
|
if (!webStream) {
|
|
66
50
|
throw createError({ statusCode: 400, message: "No request body received" });
|
|
67
51
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
52
|
+
|
|
53
|
+
const targetPath = targetExtensionFilePath(projectName, filename);
|
|
54
|
+
const partPath = `${targetPath}.part`;
|
|
55
|
+
const writeStream = fs.createWriteStream(partPath, { flags: chunkIndex === 0 ? "w" : "a" });
|
|
56
|
+
await pipeline(Readable.fromWeb(toNodeWebStream(webStream)), writeStream);
|
|
57
|
+
|
|
58
|
+
const { size } = await fs.promises.stat(partPath);
|
|
59
|
+
if (size > FILE_SIZE_LIMIT) {
|
|
60
|
+
await fs.promises.unlink(partPath);
|
|
61
|
+
throw createError({ statusCode: 400, message: "File too large" });
|
|
75
62
|
}
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
|
|
64
|
+
if (chunkIndex < totalChunks - 1) {
|
|
65
|
+
return { statusCode: CODE_200 };
|
|
78
66
|
}
|
|
79
|
-
|
|
67
|
+
|
|
68
|
+
await fs.promises.rename(partPath, targetPath);
|
|
69
|
+
await registerExtensionFile(projectName, targetPath);
|
|
80
70
|
return { statusCode: CODE_201 };
|
|
81
71
|
});
|