From 039e466f8b126c7d22db7fb8bec678a9504e8cbe Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 10 Apr 2026 22:29:05 -0500 Subject: [PATCH 1/4] Create dynamic sitemap generation from package npm build --- cda-gui/package.json | 2 +- cda-gui/scripts/generate-sitemap.mjs | 36 +++++++++++++++++++++++++ cda-gui/src/main.jsx | 26 +++++++++++------- cda-gui/src/route-paths.js | 40 ++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 cda-gui/scripts/generate-sitemap.mjs create mode 100644 cda-gui/src/route-paths.js diff --git a/cda-gui/package.json b/cda-gui/package.json index 950620627..0816912ae 100644 --- a/cda-gui/package.json +++ b/cda-gui/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "vite build && node scripts/generate-sitemap.mjs", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "prepare": "cd .. && husky cda-gui/.husky", diff --git a/cda-gui/scripts/generate-sitemap.mjs b/cda-gui/scripts/generate-sitemap.mjs new file mode 100644 index 000000000..2d7dfc536 --- /dev/null +++ b/cda-gui/scripts/generate-sitemap.mjs @@ -0,0 +1,36 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { sitemapPaths } from "../src/route-paths.js"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const projectDir = path.resolve(__dirname, ".."); +const outputPath = path.join(projectDir, "dist", "sitemap.xml"); + +const siteOrigin = (process.env.SITE_ORIGIN ?? "https://cwms-data.usace.army.mil").replace( + /\/+$/, + "", +); +const siteBasePath = (process.env.SITE_BASE_PATH ?? "/cwms-data").replace(/\/+$/, ""); + +const urls = sitemapPaths.map((routePath) => { + const normalizedPath = routePath ? `/${routePath}` : ""; + return `${siteOrigin}${siteBasePath}${normalizedPath}`; +}); + +const xml = ` + +${urls + .map( + (url) => ` + ${url} + `, + ) + .join("\n")} + +`; + +await mkdir(path.dirname(outputPath), { recursive: true }); +await writeFile(outputPath, xml, "utf8"); diff --git a/cda-gui/src/main.jsx b/cda-gui/src/main.jsx index fce0af57c..56bf70029 100644 --- a/cda-gui/src/main.jsx +++ b/cda-gui/src/main.jsx @@ -21,8 +21,18 @@ import ErrorFallback from "./pages/ErrorFallback"; import FilterExpressions from "./pages/rsql"; import Timestamps from "./pages/timestamps"; import LegacyFormat from "./pages/legacy-format/index.jsx"; +import { routePaths } from "./route-paths"; const queryClient = new QueryClient(); +const routeComponents = { + home: Home, + "swagger-ui": SwaggerUI, + "data-query": DataQuery, + regexp: Regexp, + "filter-expressions": FilterExpressions, + timestamps: Timestamps, + "legacy-format": LegacyFormat, +}; const router = createBrowserRouter( [ @@ -31,16 +41,12 @@ const router = createBrowserRouter( element: , errorElement: , children: [ - { index: true, element: }, - { - path: "swagger-ui", - element: , - }, - { path: "data-query", element: }, - { path: "regexp", element: }, - { path: "filter-expressions", element: }, - { path: "timestamps", element: }, - { path: "legacy-format", element: }, + ...routePaths.map(({ id, index, path }) => { + const Component = routeComponents[id]; + return index + ? { index: true, element: } + : { path, element: }; + }), { path: "*", element: }, ], }, diff --git a/cda-gui/src/route-paths.js b/cda-gui/src/route-paths.js new file mode 100644 index 000000000..f24a09e49 --- /dev/null +++ b/cda-gui/src/route-paths.js @@ -0,0 +1,40 @@ +// Routes are defined here to allow building a sitemap dynamically +export const routePaths = [ + { + id: "home", + index: true, + sitemapPath: "", + }, + { + id: "swagger-ui", + path: "swagger-ui", + sitemapPath: "swagger-ui", + }, + { + id: "data-query", + path: "data-query", + sitemapPath: "data-query", + }, + { + id: "regexp", + path: "regexp", + sitemapPath: "regexp", + }, + { + id: "filter-expressions", + path: "filter-expressions", + sitemapPath: "filter-expressions", + }, + { + id: "timestamps", + path: "timestamps", + sitemapPath: "timestamps", + }, + { + id: "legacy-format", + path: "legacy-format", + sitemapPath: "legacy-format", + }, +]; + +export const sitemapPaths = routePaths.map(({ sitemapPath }) => sitemapPath); From 4fe780f6a68b1b6dd9a8817fc641cc373cf3d000 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 10 Apr 2026 22:35:49 -0500 Subject: [PATCH 2/4] Create robots.txt at root --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1b4d0a8a2..e922de01b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,8 @@ RUN mkdir /download && \ rm -rf /download && \ rm -rf /usr/local/tomcat/webapps/* && \ mkdir /usr/local/tomcat/webapps/ROOT && \ - echo "Nothing to see here" > /usr/local/tomcat/webapps/ROOT/index.html + echo "Nothing to see here" > /usr/local/tomcat/webapps/ROOT/index.html && \ + printf "User-agent: *\nAllow: /cwms-data/\nDisallow: /cwms-data/auth/\nDisallow: /cwms-data/catalog/\nDisallow: /cwms-data/timeseries/\nDisallow: /cwms-data/swagger-docs\nDisallow: /auth/\n" > /usr/local/tomcat/webapps/ROOT/robots.txt CMD ["/usr/local/tomcat/bin/catalina.sh","run"] FROM tomcat_base AS api From 4af234abb63141fc9a9f88de48ac21d3e896b282 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 10 Apr 2026 22:45:57 -0500 Subject: [PATCH 3/4] ensure statemap does not become stale during build --- cda-gui/build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cda-gui/build.gradle b/cda-gui/build.gradle index 8280658cc..196d5b8e2 100644 --- a/cda-gui/build.gradle +++ b/cda-gui/build.gradle @@ -7,8 +7,10 @@ task buildGui(type:NpxTask) { dependsOn npmInstall command = "npm" args = ["run", "build", "--prod"] - inputs.files('package.json', 'package-lock.json', 'vite.config.js') + inputs.files('package.json', 'package-lock.json', 'vite.config.js', 'index.html') inputs.dir('src') + inputs.dir('public') + inputs.dir('scripts') inputs.dir(fileTree("node_modules").exclude(".cache")) outputs.dir('dist') } @@ -38,4 +40,4 @@ sourceSets { } } } -} \ No newline at end of file +} From ab11cd77006db09f5bee58a3f6bf7ad3bcd0ba39 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Fri, 10 Apr 2026 23:13:47 -0500 Subject: [PATCH 4/4] add robots .txt and a 302 JSP redirect for the root page --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e922de01b..5ca26703e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,12 +33,13 @@ RUN mkdir /download && \ rm -rf /download && \ rm -rf /usr/local/tomcat/webapps/* && \ mkdir /usr/local/tomcat/webapps/ROOT && \ - echo "Nothing to see here" > /usr/local/tomcat/webapps/ROOT/index.html && \ - printf "User-agent: *\nAllow: /cwms-data/\nDisallow: /cwms-data/auth/\nDisallow: /cwms-data/catalog/\nDisallow: /cwms-data/timeseries/\nDisallow: /cwms-data/swagger-docs\nDisallow: /auth/\n" > /usr/local/tomcat/webapps/ROOT/robots.txt + printf "<%% response.sendRedirect(\"/cwms-data/\"); %%>\n" > /usr/local/tomcat/webapps/ROOT/index.jsp && \ + printf "User-agent: *\nAllow: /cwms-data/\nDisallow: /cwms-data/auth/\nDisallow: /cwms-data/catalog/\nDisallow: /cwms-data/timeseries/\nDisallow: /cwms-data/swagger-docs\nDisallow: /auth/\nSitemap: https://cwms-data.usace.army.mil/sitemap.xml\n" > /usr/local/tomcat/webapps/ROOT/robots.txt CMD ["/usr/local/tomcat/bin/catalina.sh","run"] FROM tomcat_base AS api +COPY --from=builder /builddir/cda-gui/dist/sitemap.xml /usr/local/tomcat/webapps/ROOT/sitemap.xml COPY --from=builder /builddir/cwms-data-api/build/docker/cda/ /usr/local/tomcat COPY --from=builder /builddir/cwms-data-api/build/docker/context.xml /usr/local/tomcat/conf COPY --from=builder /builddir/cwms-data-api/build/docker/server.xml /usr/local/tomcat/conf