Skip to content

Commit 3eaf8e9

Browse files
authored
1688 create sitemapxml and robotstxt (#1690)
React router now uses a small shared route manifest so the GUI build can generate sitemap.xml automatically from the app routes. i.e. `cda-gui/src/route-paths.js` Sitemap is packaged into the WAR and also copied to Tomcat ROOT in the Docker image so it’s available at /sitemap.xml. Tomcat root app was updated to replace the placeholder page. / now returns a real HTTP 302 redirect to /cwms-data/, and /robots.txt is served from root with rules that allow the GUI while blocking raw API/query surfaces. Confirmed war builds with sitemap.xml in it Verified the container root returns 302 Location: /cwms-data/
1 parent 86cf055 commit 3eaf8e9

File tree

6 files changed

+100
-14
lines changed

6 files changed

+100
-14
lines changed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ RUN mkdir /download && \
3333
rm -rf /download && \
3434
rm -rf /usr/local/tomcat/webapps/* && \
3535
mkdir /usr/local/tomcat/webapps/ROOT && \
36-
echo "<html><body>Nothing to see here</body></html>" > /usr/local/tomcat/webapps/ROOT/index.html
36+
printf "<%% response.sendRedirect(\"/cwms-data/\"); %%>\n" > /usr/local/tomcat/webapps/ROOT/index.jsp && \
37+
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
3738
CMD ["/usr/local/tomcat/bin/catalina.sh","run"]
3839

3940
FROM tomcat_base AS api
4041

42+
COPY --from=builder /builddir/cda-gui/dist/sitemap.xml /usr/local/tomcat/webapps/ROOT/sitemap.xml
4143
COPY --from=builder /builddir/cwms-data-api/build/docker/cda/ /usr/local/tomcat
4244
COPY --from=builder /builddir/cwms-data-api/build/docker/context.xml /usr/local/tomcat/conf
4345
COPY --from=builder /builddir/cwms-data-api/build/docker/server.xml /usr/local/tomcat/conf

cda-gui/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ task buildGui(type:NpxTask) {
77
dependsOn npmInstall
88
command = "npm"
99
args = ["run", "build", "--prod"]
10-
inputs.files('package.json', 'package-lock.json', 'vite.config.js')
10+
inputs.files('package.json', 'package-lock.json', 'vite.config.js', 'index.html')
1111
inputs.dir('src')
12+
inputs.dir('public')
13+
inputs.dir('scripts')
1214
inputs.dir(fileTree("node_modules").exclude(".cache"))
1315
outputs.dir('dist')
1416
}
@@ -38,4 +40,4 @@ sourceSets {
3840
}
3941
}
4042
}
41-
}
43+
}

cda-gui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
8-
"build": "vite build",
8+
"build": "vite build && node scripts/generate-sitemap.mjs",
99
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
1010
"preview": "vite preview",
1111
"prepare": "cd .. && husky cda-gui/.husky",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { mkdir, writeFile } from "node:fs/promises";
2+
import path from "node:path";
3+
import { fileURLToPath } from "node:url";
4+
5+
import { sitemapPaths } from "../src/route-paths.js";
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = path.dirname(__filename);
9+
const projectDir = path.resolve(__dirname, "..");
10+
const outputPath = path.join(projectDir, "dist", "sitemap.xml");
11+
12+
const siteOrigin = (process.env.SITE_ORIGIN ?? "https://cwms-data.usace.army.mil").replace(
13+
/\/+$/,
14+
"",
15+
);
16+
const siteBasePath = (process.env.SITE_BASE_PATH ?? "/cwms-data").replace(/\/+$/, "");
17+
18+
const urls = sitemapPaths.map((routePath) => {
19+
const normalizedPath = routePath ? `/${routePath}` : "";
20+
return `${siteOrigin}${siteBasePath}${normalizedPath}`;
21+
});
22+
23+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
24+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
25+
${urls
26+
.map(
27+
(url) => ` <url>
28+
<loc>${url}</loc>
29+
</url>`,
30+
)
31+
.join("\n")}
32+
</urlset>
33+
`;
34+
35+
await mkdir(path.dirname(outputPath), { recursive: true });
36+
await writeFile(outputPath, xml, "utf8");

cda-gui/src/main.jsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,18 @@ import ErrorFallback from "./pages/ErrorFallback";
2121
import FilterExpressions from "./pages/rsql";
2222
import Timestamps from "./pages/timestamps";
2323
import LegacyFormat from "./pages/legacy-format/index.jsx";
24+
import { routePaths } from "./route-paths";
2425

2526
const queryClient = new QueryClient();
27+
const routeComponents = {
28+
home: Home,
29+
"swagger-ui": SwaggerUI,
30+
"data-query": DataQuery,
31+
regexp: Regexp,
32+
"filter-expressions": FilterExpressions,
33+
timestamps: Timestamps,
34+
"legacy-format": LegacyFormat,
35+
};
2636

2737
const router = createBrowserRouter(
2838
[
@@ -31,16 +41,12 @@ const router = createBrowserRouter(
3141
element: <Layout />,
3242
errorElement: <ErrorFallback />,
3343
children: [
34-
{ index: true, element: <Home /> },
35-
{
36-
path: "swagger-ui",
37-
element: <SwaggerUI />,
38-
},
39-
{ path: "data-query", element: <DataQuery /> },
40-
{ path: "regexp", element: <Regexp /> },
41-
{ path: "filter-expressions", element: <FilterExpressions /> },
42-
{ path: "timestamps", element: <Timestamps /> },
43-
{ path: "legacy-format", element: <LegacyFormat /> },
44+
...routePaths.map(({ id, index, path }) => {
45+
const Component = routeComponents[id];
46+
return index
47+
? { index: true, element: <Component /> }
48+
: { path, element: <Component /> };
49+
}),
4450
{ path: "*", element: <NotFound /> },
4551
],
4652
},

cda-gui/src/route-paths.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Routes are defined here to allow building a sitemap dynamically
2+
export const routePaths = [
3+
{
4+
id: "home",
5+
index: true,
6+
sitemapPath: "",
7+
},
8+
{
9+
id: "swagger-ui",
10+
path: "swagger-ui",
11+
sitemapPath: "swagger-ui",
12+
},
13+
{
14+
id: "data-query",
15+
path: "data-query",
16+
sitemapPath: "data-query",
17+
},
18+
{
19+
id: "regexp",
20+
path: "regexp",
21+
sitemapPath: "regexp",
22+
},
23+
{
24+
id: "filter-expressions",
25+
path: "filter-expressions",
26+
sitemapPath: "filter-expressions",
27+
},
28+
{
29+
id: "timestamps",
30+
path: "timestamps",
31+
sitemapPath: "timestamps",
32+
},
33+
{
34+
id: "legacy-format",
35+
path: "legacy-format",
36+
sitemapPath: "legacy-format",
37+
},
38+
];
39+
40+
export const sitemapPaths = routePaths.map(({ sitemapPath }) => sitemapPath);

0 commit comments

Comments
 (0)