-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
105 lines (95 loc) · 4.73 KB
/
Copy pathserver.js
File metadata and controls
105 lines (95 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const cds = require("@sap/cds");
const express = require("express");
const engine = require("abap2UI5/engine");
const z2ui5_cl_util_http = require("abap2UI5/z2ui5_cl_util_http");
/**
* The CAP platform wiring for the abap2UI5 core package (linked as
* `abap2UI5` from the vendored ./core). The z2ui5 roundtrip itself is a CDS REST action
* (see z2ui5-service.cds + z2ui5-service.js — `srv.on('z2ui5', …)` on the
* rootService). This file contributes everything CDS can't express:
*
* GET /rest/root/z2ui5 → bootstrap HTML via engine.bootstrap_html()
* (mirrors abap _http_get)
* HEAD /rest/root/z2ui5 → CSRF-prefetch + sap-terminate ack. CDS REST
* actions don't expose HEAD, so we register it here.
* /resources → the local UI5 runtime
*
* plus the two platform ports of this app:
* draft store → the CDS entity cap2ui5.z2ui5_t_01 (db/schema.cds),
* expired rows cleaned hourly (srv/draft-retention.js)
* app discovery → this project's srv/app/ (custom apps)
*/
// Draft persistence: the CDS-backed store. The core package is platform
// neutral and only knows the injected contract load(id)/save(entry).
engine.set_store({
load: async (id) => {
const { z2ui5_t_01 } = cds.entities("cap2ui5");
return SELECT.one.from(z2ui5_t_01).where({ id });
},
save: async (entry) => {
const { z2ui5_t_01 } = cds.entities("cap2ui5");
await INSERT.into(z2ui5_t_01).entries(entry);
},
});
// App discovery: custom apps of THIS project live in srv/app/ — outside the
// core package, so they must be registered (the bundled samples inside the
// package are found without registration).
engine.register_app_dir(require("path").join(__dirname, "app"));
// Retention: the store above is append-only, so expired draft chains are
// pruned on a timer once the database is connected.
cds.on("served", () => require("./draft-retention").start());
cds.on("bootstrap", (app) => {
// Readiness probe — mta.yaml declares
// readiness-health-check-http-endpoint: /health for the abap2UI5-srv
// module, so CF polls this route to decide the instance is up. It must stay
// public (the probe carries no auth) and cheap; a bare 200 is enough since
// the process answering at all is the signal CF needs.
app.get("/health", (_req, res) => res.status(200).json({ status: "UP" }));
// Serve the local UI5 runtime at /resources (must be registered before the
// CDS services so it is not shadowed by the OData/REST routing) — the app
// bootstraps from `/resources/sap-ui-core.js` (see patch-frontend.js /
// z2ui5_cl_ui5_user_exit.js) instead of a public CDN, so the whole stack runs
// offline, served from the pinned `openui5-dist` dependency. OpenUI5 ships
// only the open-source libraries; sample apps that use commercial SAPUI5
// libs (sap.suite.*, sap.gantt, sap.ui.comp, …) still require the SAPUI5
// CDN. The trailing handler answers a plain 404 for files the dist doesn't
// ship (e.g. locale message bundles UI5 probes for and then falls back on)
// instead of letting the miss bubble up as a logged error.
app.use(
"/resources",
express.static(engine.ui5_resources_dir(), { maxAge: "1h" }),
(_req, res) => res.status(404).end(),
);
// Auth boundary: the DATA endpoints — the POST z2ui5 roundtrip action and
// the AdminService OData entities — are restricted to authenticated users
// (@requires in z2ui5-service.cds). The GET/HEAD routes below are
// deliberately left public: they serve only the static UI5 bootstrap shell
// and the CSRF/terminate ack, carry no user data, and keeping them open
// preserves the offline/dev flow. In BTP the approuter authenticates before
// the frontend can reach them anyway.
app.get("/rest/root/z2ui5", (req, res) => {
// The engine call renders arbitrary app HTML — never let a failure
// escape as an unhandled express error (raw stack trace to the client).
try {
const reqInfo = z2ui5_cl_util_http.factory_cloud(req, res).get_req_info();
const { html, headers } = engine.bootstrap_html(reqInfo);
// Apply ABAP's t_security_header (cache-control, X-Frame-Options, …).
for (const h of headers) {
res.set(h.n, h.v);
}
res.set("Content-Type", "text/html; charset=utf-8");
res.status(200).send(html);
} catch (e) {
console.error("GET /rest/root/z2ui5 bootstrap failed:", e);
res
.status(500)
.set("Content-Type", "text/plain; charset=utf-8")
.send(`z2ui5 bootstrap failed: ${e.message}`);
}
});
app.head("/rest/root/z2ui5", (_req, res) => {
res.set("X-CSRF-Token", "disabled");
res.status(200).end();
});
});
module.exports = cds.server;