iobroker.zeiterfassung 0.0.13 → 0.0.14
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/README.md +6 -1
- package/admin/jsonConfig.json +2 -1
- package/build/lib/web/server.js +28 -8
- package/build/lib/web/server.js.map +3 -3
- package/io-package.json +14 -1
- package/package.json +1 -1
- package/www/sw.js.map +1 -1
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ The adapter is configured in the **instance settings** of the ioBroker admin:
|
|
|
69
69
|
| Setting | Meaning |
|
|
70
70
|
| ----------------------------------- | --------------------------------------------------------------- |
|
|
71
71
|
| Port | port of the HTTP server (web app, API, terminal) |
|
|
72
|
-
| Bind address | interface to listen on (`0.0.0.0` = all)
|
|
72
|
+
| Bind address | interface to listen on, picked from the local addresses (`0.0.0.0` = all) |
|
|
73
73
|
| Instance time zone | fallback time zone (IANA name), e.g. `Europe/Berlin` |
|
|
74
74
|
| Default language for new users | one of the 11 supported languages |
|
|
75
75
|
| Holiday country | country used to generate the public holidays |
|
|
@@ -247,6 +247,11 @@ local SQLite file, access is role-based, and every correction is written to an a
|
|
|
247
247
|
|
|
248
248
|
### **WORK IN PROGRESS**
|
|
249
249
|
|
|
250
|
+
### 0.0.14 (2026-09-17)
|
|
251
|
+
|
|
252
|
+
- (Alex) the **bind address** of the instance is picked from the local addresses of the host now (a list in the
|
|
253
|
+
admin settings instead of a free text field), and a value that carries a port cannot keep the server from starting
|
|
254
|
+
|
|
250
255
|
### 0.0.13 (2026-09-17)
|
|
251
256
|
|
|
252
257
|
- (Alex) graduated break rules can be **per employee** now: the work profile carries its own table, and a rule with
|
package/admin/jsonConfig.json
CHANGED
package/build/lib/web/server.js
CHANGED
|
@@ -28,6 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var server_exports = {};
|
|
30
30
|
__export(server_exports, {
|
|
31
|
+
normalizeBindAddress: () => normalizeBindAddress,
|
|
31
32
|
startWebServer: () => startWebServer
|
|
32
33
|
});
|
|
33
34
|
module.exports = __toCommonJS(server_exports);
|
|
@@ -67,13 +68,31 @@ function parseQuery(requestUrl) {
|
|
|
67
68
|
}
|
|
68
69
|
return query;
|
|
69
70
|
}
|
|
71
|
+
function normalizeBindAddress(value) {
|
|
72
|
+
const raw = (value != null ? value : "").trim();
|
|
73
|
+
if (raw === "") {
|
|
74
|
+
return "127.0.0.1";
|
|
75
|
+
}
|
|
76
|
+
if (raw === "*") {
|
|
77
|
+
return "0.0.0.0";
|
|
78
|
+
}
|
|
79
|
+
const bracketed = /^\[([^\]]+)]:\d+$/.exec(raw);
|
|
80
|
+
if (bracketed) {
|
|
81
|
+
return bracketed[1];
|
|
82
|
+
}
|
|
83
|
+
const withPort = /^(\d{1,3}(?:\.\d{1,3}){3}):\d+$/.exec(raw);
|
|
84
|
+
if (withPort) {
|
|
85
|
+
return withPort[1];
|
|
86
|
+
}
|
|
87
|
+
return raw;
|
|
88
|
+
}
|
|
70
89
|
async function startWebServer(options) {
|
|
71
|
-
var _a, _b, _c, _d, _e
|
|
90
|
+
var _a, _b, _c, _d, _e;
|
|
72
91
|
const maxBodyBytes = (_a = options.maxBodyBytes) != null ? _a : import_router.DEFAULT_MAX_BODY_BYTES;
|
|
73
|
-
const bind = (
|
|
74
|
-
const apiPrefix = normalizePrefix((
|
|
92
|
+
const bind = normalizeBindAddress(options.bind);
|
|
93
|
+
const apiPrefix = normalizePrefix((_b = options.apiPrefix) != null ? _b : "/api");
|
|
75
94
|
const handle = async (request, response) => {
|
|
76
|
-
var _a2, _b2, _c2, _d2, _e2,
|
|
95
|
+
var _a2, _b2, _c2, _d2, _e2, _f, _g, _h, _i, _j;
|
|
77
96
|
try {
|
|
78
97
|
const body = await readBody(request, maxBodyBytes);
|
|
79
98
|
if (body === null) {
|
|
@@ -100,7 +119,7 @@ async function startWebServer(options) {
|
|
|
100
119
|
return;
|
|
101
120
|
}
|
|
102
121
|
const staticRequest = {
|
|
103
|
-
method: (
|
|
122
|
+
method: (_f = request.method) != null ? _f : "GET",
|
|
104
123
|
path: url.pathname,
|
|
105
124
|
query: parseQuery((_g = request.url) != null ? _g : "/"),
|
|
106
125
|
headers,
|
|
@@ -140,13 +159,13 @@ async function startWebServer(options) {
|
|
|
140
159
|
});
|
|
141
160
|
});
|
|
142
161
|
const address = server.address();
|
|
143
|
-
const port = (
|
|
162
|
+
const port = (_c = address == null ? void 0 : address.port) != null ? _c : options.port;
|
|
144
163
|
const stream = options.stream ? (0, import_stream.attachEventStream)({
|
|
145
164
|
...options.stream,
|
|
146
165
|
server,
|
|
147
|
-
path: (
|
|
166
|
+
path: (_d = options.stream.path) != null ? _d : `${apiPrefix}/stream`
|
|
148
167
|
}) : void 0;
|
|
149
|
-
(
|
|
168
|
+
(_e = options.log) == null ? void 0 : _e.info(
|
|
150
169
|
`API listening on http://${bind}:${port}${apiPrefix}${options.staticFiles ? " - web interface is served from disk" : ""}${stream ? " - live events on /stream" : ""}`
|
|
151
170
|
);
|
|
152
171
|
return {
|
|
@@ -164,6 +183,7 @@ async function startWebServer(options) {
|
|
|
164
183
|
}
|
|
165
184
|
// Annotate the CommonJS export names for ESM import in node:
|
|
166
185
|
0 && (module.exports = {
|
|
186
|
+
normalizeBindAddress,
|
|
167
187
|
startWebServer
|
|
168
188
|
});
|
|
169
189
|
//# sourceMappingURL=server.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/web/server.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * HTTP server that mounts the router on a Node http server.\n *\n * This is the adapter's own port (`native.port`): the PWA, the REST API and the kiosk terminal are served on\n * it. The server only translates between Node's request/response objects and the router \u2014 all routing,\n * authentication, RBAC and error handling live in the router and the API.\n *\n * Safety nets: the body is limited while it is being read (a client cannot fill the memory), the bind address\n * defaults to the local machine and a failure to bind never stops the adapter (the API is reported as\n * unavailable instead).\n */\n\nimport * as http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { HttpProblem, PROBLEM_CONTENT_TYPE } from \"./problem\";\nimport { DEFAULT_MAX_BODY_BYTES, type HttpRequest, type Router } from \"./router\";\nimport type { StaticHandler } from \"./static\";\nimport { attachEventStream, type EventStream, type EventStreamOptions } from \"./stream\";\n\n/** Minimal logger used by the server. */\nexport interface ServerLogger {\n\t/** Informational message */\n\tinfo(message: string): void;\n\t/** Warning */\n\twarn(message: string): void;\n\t/** Error */\n\terror(message: string): void;\n}\n\n/** Options of the HTTP server. */\nexport interface WebServerOptions {\n\t/** Router that handles the requests */\n\trouter: Router;\n\t/** Port to listen on (`0` picks a free port) */\n\tport: number;\n\t/** Bind address, default `127.0.0.1` */\n\tbind?: string;\n\t/** Path prefix the API is mounted on (default `/api`) */\n\tapiPrefix?: string;\n\t/** Handler for the files of the web interface (PWA) */\n\tstaticFiles?: StaticHandler;\n\t/** Live event stream (`/api/stream`); without it the endpoint does not exist */\n\tstream?: Omit<EventStreamOptions, \"server\">;\n\t/** Maximum body size in bytes (default 2 MiB) */\n\tmaxBodyBytes?: number;\n\t/** Logger */\n\tlog?: ServerLogger;\n}\n\n/** A running server. */\nexport interface WebServer {\n\t/** Port the server is listening on */\n\treadonly port: number;\n\t/** Base URL of the server, e.g. `http://127.0.0.1:8082` */\n\treadonly url: string;\n\t/** Live event stream, when one is configured */\n\treadonly stream?: EventStream;\n\t/** Stops the server and closes all connections */\n\tclose(): Promise<void>;\n}\n\n/**\n * Normalises a path prefix.\n *\n * @param prefix - configured prefix\n * @returns prefix without trailing slash (`\"\"` disables the prefix)\n */\nfunction normalizePrefix(prefix: string): string {\n\tconst trimmed = prefix.trim();\n\tif (trimmed === \"\" || trimmed === \"/\") {\n\t\treturn \"\";\n\t}\n\treturn trimmed.startsWith(\"/\") ? trimmed.replace(/\\/+$/, \"\") : `/${trimmed.replace(/\\/+$/, \"\")}`;\n}\n\n/**\n * Reads the request body up to a limit.\n *\n * A body that is not text is kept as bytes: an uploaded backup has to arrive byte for byte. Everything else is\n * decoded here, because the endpoints expect a JSON document.\n *\n * @param request - incoming request\n * @param maxBytes - maximum number of bytes\n * @returns body as text or bytes, `null` when the limit is exceeded\n */\nasync function readBody(request: http.IncomingMessage, maxBytes: number): Promise<string | Buffer | null> {\n\tconst chunks: Buffer[] = [];\n\tlet size = 0;\n\n\tfor await (const chunk of request) {\n\t\tconst buffer = chunk as Buffer;\n\t\tsize += buffer.length;\n\t\tif (size > maxBytes) {\n\t\t\treturn null;\n\t\t}\n\t\tchunks.push(buffer);\n\t}\n\n\tconst body = Buffer.concat(chunks);\n\t// the raw upload arrives as bytes; a JSON document is decoded right away\n\treturn (request.headers[\"content-type\"] ?? \"\").toLowerCase().startsWith(\"application/octet-stream\")\n\t\t? body\n\t\t: body.toString(\"utf8\");\n}\n\n/**\n * Builds the query map of a request, repeated names become arrays.\n *\n * @param requestUrl - raw request URL\n * @returns query parameters\n */\nfunction parseQuery(requestUrl: string): Record<string, string | string[]> {\n\tconst url = new URL(requestUrl, \"http://localhost\");\n\tconst query: Record<string, string | string[]> = {};\n\tfor (const key of new Set(url.searchParams.keys())) {\n\t\tconst values = url.searchParams.getAll(key);\n\t\tquery[key] = values.length > 1 ? values : (values[0] ?? \"\");\n\t}\n\treturn query;\n}\n\n/**\n * Starts the HTTP server.\n *\n * @param options - router, port and limits\n * @returns the running server\n */\nexport async function startWebServer(options: WebServerOptions): Promise<WebServer> {\n\tconst maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\tconst bind = options.bind
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA,WAAsB;AAEtB,qBAAkD;AAClD,oBAAsE;AAEtE,oBAA6E;AAkD7E,SAAS,gBAAgB,QAAwB;AAChD,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,YAAY,MAAM,YAAY,KAAK;AACtC,WAAO;AAAA,EACR;AACA,SAAO,QAAQ,WAAW,GAAG,IAAI,QAAQ,QAAQ,QAAQ,EAAE,IAAI,IAAI,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAC/F;AAYA,eAAe,SAAS,SAA+B,UAAmD;AArF1G;AAsFC,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO;AAEX,mBAAiB,SAAS,SAAS;AAClC,UAAM,SAAS;AACf,YAAQ,OAAO;AACf,QAAI,OAAO,UAAU;AACpB,aAAO;AAAA,IACR;AACA,WAAO,KAAK,MAAM;AAAA,EACnB;AAEA,QAAM,OAAO,OAAO,OAAO,MAAM;AAEjC,WAAQ,aAAQ,QAAQ,cAAc,MAA9B,YAAmC,IAAI,YAAY,EAAE,WAAW,0BAA0B,IAC/F,OACA,KAAK,SAAS,MAAM;AACxB;AAQA,SAAS,WAAW,YAAuD;AA/G3E;AAgHC,QAAM,MAAM,IAAI,IAAI,YAAY,kBAAkB;AAClD,QAAM,QAA2C,CAAC;AAClD,aAAW,OAAO,IAAI,IAAI,IAAI,aAAa,KAAK,CAAC,GAAG;AACnD,UAAM,SAAS,IAAI,aAAa,OAAO,GAAG;AAC1C,UAAM,GAAG,IAAI,OAAO,SAAS,IAAI,UAAU,YAAO,CAAC,MAAR,YAAa;AAAA,EACzD;AACA,SAAO;AACR;AAQA,eAAsB,eAAe,SAA+C;
|
|
6
|
-
"names": ["_a", "_b", "_c", "_d", "_e"
|
|
4
|
+
"sourcesContent": ["/**\n * HTTP server that mounts the router on a Node http server.\n *\n * This is the adapter's own port (`native.port`): the PWA, the REST API and the kiosk terminal are served on\n * it. The server only translates between Node's request/response objects and the router \u2014 all routing,\n * authentication, RBAC and error handling live in the router and the API.\n *\n * Safety nets: the body is limited while it is being read (a client cannot fill the memory), the bind address\n * defaults to the local machine and a failure to bind never stops the adapter (the API is reported as\n * unavailable instead).\n */\n\nimport * as http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { HttpProblem, PROBLEM_CONTENT_TYPE } from \"./problem\";\nimport { DEFAULT_MAX_BODY_BYTES, type HttpRequest, type Router } from \"./router\";\nimport type { StaticHandler } from \"./static\";\nimport { attachEventStream, type EventStream, type EventStreamOptions } from \"./stream\";\n\n/** Minimal logger used by the server. */\nexport interface ServerLogger {\n\t/** Informational message */\n\tinfo(message: string): void;\n\t/** Warning */\n\twarn(message: string): void;\n\t/** Error */\n\terror(message: string): void;\n}\n\n/** Options of the HTTP server. */\nexport interface WebServerOptions {\n\t/** Router that handles the requests */\n\trouter: Router;\n\t/** Port to listen on (`0` picks a free port) */\n\tport: number;\n\t/** Bind address, default `127.0.0.1` */\n\tbind?: string;\n\t/** Path prefix the API is mounted on (default `/api`) */\n\tapiPrefix?: string;\n\t/** Handler for the files of the web interface (PWA) */\n\tstaticFiles?: StaticHandler;\n\t/** Live event stream (`/api/stream`); without it the endpoint does not exist */\n\tstream?: Omit<EventStreamOptions, \"server\">;\n\t/** Maximum body size in bytes (default 2 MiB) */\n\tmaxBodyBytes?: number;\n\t/** Logger */\n\tlog?: ServerLogger;\n}\n\n/** A running server. */\nexport interface WebServer {\n\t/** Port the server is listening on */\n\treadonly port: number;\n\t/** Base URL of the server, e.g. `http://127.0.0.1:8082` */\n\treadonly url: string;\n\t/** Live event stream, when one is configured */\n\treadonly stream?: EventStream;\n\t/** Stops the server and closes all connections */\n\tclose(): Promise<void>;\n}\n\n/**\n * Normalises a path prefix.\n *\n * @param prefix - configured prefix\n * @returns prefix without trailing slash (`\"\"` disables the prefix)\n */\nfunction normalizePrefix(prefix: string): string {\n\tconst trimmed = prefix.trim();\n\tif (trimmed === \"\" || trimmed === \"/\") {\n\t\treturn \"\";\n\t}\n\treturn trimmed.startsWith(\"/\") ? trimmed.replace(/\\/+$/, \"\") : `/${trimmed.replace(/\\/+$/, \"\")}`;\n}\n\n/**\n * Reads the request body up to a limit.\n *\n * A body that is not text is kept as bytes: an uploaded backup has to arrive byte for byte. Everything else is\n * decoded here, because the endpoints expect a JSON document.\n *\n * @param request - incoming request\n * @param maxBytes - maximum number of bytes\n * @returns body as text or bytes, `null` when the limit is exceeded\n */\nasync function readBody(request: http.IncomingMessage, maxBytes: number): Promise<string | Buffer | null> {\n\tconst chunks: Buffer[] = [];\n\tlet size = 0;\n\n\tfor await (const chunk of request) {\n\t\tconst buffer = chunk as Buffer;\n\t\tsize += buffer.length;\n\t\tif (size > maxBytes) {\n\t\t\treturn null;\n\t\t}\n\t\tchunks.push(buffer);\n\t}\n\n\tconst body = Buffer.concat(chunks);\n\t// the raw upload arrives as bytes; a JSON document is decoded right away\n\treturn (request.headers[\"content-type\"] ?? \"\").toLowerCase().startsWith(\"application/octet-stream\")\n\t\t? body\n\t\t: body.toString(\"utf8\");\n}\n\n/**\n * Builds the query map of a request, repeated names become arrays.\n *\n * @param requestUrl - raw request URL\n * @returns query parameters\n */\nfunction parseQuery(requestUrl: string): Record<string, string | string[]> {\n\tconst url = new URL(requestUrl, \"http://localhost\");\n\tconst query: Record<string, string | string[]> = {};\n\tfor (const key of new Set(url.searchParams.keys())) {\n\t\tconst values = url.searchParams.getAll(key);\n\t\tquery[key] = values.length > 1 ? values : (values[0] ?? \"\");\n\t}\n\treturn query;\n}\n\n/**\n * Normalises the bind address of the instance settings.\n *\n * The admin offers the local addresses of the host in a list, and a value may come back in the `host:port` form\n * (`[::1]:8082` for IPv6) or as a wildcard. `listen` expects the address alone and the port comes from the\n * instance, so such a value cannot make the server fail to start. An empty value means \"this machine only\".\n *\n * @param value - raw value of the instance settings\n * @returns the address to bind to\n */\nexport function normalizeBindAddress(value: string | null | undefined): string {\n\tconst raw = (value ?? \"\").trim();\n\tif (raw === \"\") {\n\t\treturn \"127.0.0.1\";\n\t}\n\tif (raw === \"*\") {\n\t\treturn \"0.0.0.0\";\n\t}\n\t// [::1]:8082 or 192.168.1.5:8082 - the port belongs to the instance, not to the address\n\tconst bracketed = /^\\[([^\\]]+)]:\\d+$/.exec(raw);\n\tif (bracketed) {\n\t\treturn bracketed[1];\n\t}\n\tconst withPort = /^(\\d{1,3}(?:\\.\\d{1,3}){3}):\\d+$/.exec(raw);\n\tif (withPort) {\n\t\treturn withPort[1];\n\t}\n\treturn raw;\n}\n\n/**\n * Starts the HTTP server.\n *\n * @param options - router, port and limits\n * @returns the running server\n */\nexport async function startWebServer(options: WebServerOptions): Promise<WebServer> {\n\tconst maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n\tconst bind = normalizeBindAddress(options.bind);\n\tconst apiPrefix = normalizePrefix(options.apiPrefix ?? \"/api\");\n\n\tconst handle = async (request: http.IncomingMessage, response: http.ServerResponse): Promise<void> => {\n\t\ttry {\n\t\t\tconst body = await readBody(request, maxBodyBytes);\n\t\t\tif (body === null) {\n\t\t\t\tconst details = new HttpProblem(413, \"payload_too_large\", `body exceeds ${maxBodyBytes} bytes`);\n\t\t\t\tresponse.writeHead(413, { \"content-type\": PROBLEM_CONTENT_TYPE });\n\t\t\t\tresponse.end(JSON.stringify(details.toProblem(request.url ?? \"/\")));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst url = new URL(request.url ?? \"/\", \"http://localhost\");\n\t\t\tconst headers = request.headers;\n\t\t\tconst remoteAddress = request.socket.remoteAddress ?? null;\n\n\t\t\t// the API lives below the prefix; everything else is a file of the web interface\n\t\t\tif (apiPrefix !== \"\" && url.pathname.startsWith(`${apiPrefix}/`)) {\n\t\t\t\tconst routerRequest: HttpRequest = {\n\t\t\t\t\tmethod: request.method ?? \"GET\",\n\t\t\t\t\tpath: url.pathname.slice(apiPrefix.length),\n\t\t\t\t\tquery: parseQuery(request.url ?? \"/\"),\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t\tremoteAddress,\n\t\t\t\t};\n\t\t\t\tconst result = await options.router.handle(routerRequest);\n\t\t\t\tresponse.writeHead(result.status, result.headers);\n\t\t\t\tresponse.end(result.body);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst staticRequest: HttpRequest = {\n\t\t\t\tmethod: request.method ?? \"GET\",\n\t\t\t\tpath: url.pathname,\n\t\t\t\tquery: parseQuery(request.url ?? \"/\"),\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t\tremoteAddress,\n\t\t\t};\n\t\t\tconst file = options.staticFiles?.(staticRequest) ?? null;\n\t\t\tif (file) {\n\t\t\t\tresponse.writeHead(file.status, file.headers);\n\t\t\t\tresponse.end(file.body);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresponse.writeHead(404, { \"content-type\": PROBLEM_CONTENT_TYPE });\n\t\t\tresponse.end(\n\t\t\t\tJSON.stringify(\n\t\t\t\t\tnew HttpProblem(404, \"not_found\", `no route for ${url.pathname}`).toProblem(url.pathname),\n\t\t\t\t),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// the router never throws, so this is an unexpected failure of the transport itself\n\t\t\toptions.log?.error(`request failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\tif (!response.headersSent) {\n\t\t\t\tresponse.writeHead(500, { \"content-type\": PROBLEM_CONTENT_TYPE });\n\t\t\t}\n\t\t\tresponse.end();\n\t\t}\n\t};\n\n\tconst server = http.createServer((request, response) => {\n\t\tvoid handle(request, response);\n\t});\n\tserver.on(\"clientError\", (_error, socket) => socket.destroy());\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst onError = (error: Error): void => reject(error);\n\t\tserver.once(\"error\", onError);\n\t\tserver.listen(options.port, bind, () => {\n\t\t\tserver.off(\"error\", onError);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tconst address = server.address() as AddressInfo | null;\n\tconst port = address?.port ?? options.port;\n\tconst stream = options.stream\n\t\t? attachEventStream({\n\t\t\t\t...options.stream,\n\t\t\t\tserver,\n\t\t\t\tpath: options.stream.path ?? `${apiPrefix}/stream`,\n\t\t\t})\n\t\t: undefined;\n\toptions.log?.info(\n\t\t`API listening on http://${bind}:${port}${apiPrefix}${\n\t\t\toptions.staticFiles ? \" - web interface is served from disk\" : \"\"\n\t\t}${stream ? \" - live events on /stream\" : \"\"}`,\n\t);\n\n\treturn {\n\t\tport,\n\t\turl: `http://${bind === \"0.0.0.0\" ? \"127.0.0.1\" : bind}:${port}`,\n\t\tstream,\n\t\tclose: async (): Promise<void> => {\n\t\t\tawait stream?.close();\n\t\t\tawait new Promise<void>(resolve => {\n\t\t\t\tserver.close(() => resolve());\n\t\t\t\t// keep-alive connections would delay the shutdown\n\t\t\t\tserver.closeAllConnections();\n\t\t\t});\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["_a", "_b", "_c", "_d", "_e"]
|
|
7
7
|
}
|
package/io-package.json
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "zeiterfassung",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.14",
|
|
5
5
|
"nogit": true,
|
|
6
6
|
"news": {
|
|
7
|
+
"0.0.14": {
|
|
8
|
+
"en": "the bind address of the instance is picked from the local addresses of the host now: a list in the adapter settings instead of a free text field",
|
|
9
|
+
"de": "die Bind-Adresse der Instanz wird jetzt aus den lokalen Adressen des Hosts gewählt: eine Auswahlliste in den Adaptereinstellungen statt eines Freitextfelds",
|
|
10
|
+
"ru": "адрес привязки экземпляра теперь выбирается из локальных адресов хоста: список в настройках адаптера вместо поля ввода",
|
|
11
|
+
"pt": "o endereço de ligação da instância passa a ser escolhido entre os endereços locais do host: uma lista nas definições do adaptador em vez de um campo de texto",
|
|
12
|
+
"nl": "het bindadres van de instantie wordt nu gekozen uit de lokale adressen van de host: een lijst in de adapterinstellingen in plaats van een tekstveld",
|
|
13
|
+
"fr": "l'adresse de liaison de l'instance est désormais choisie parmi les adresses locales de l'hôte : une liste dans les réglages de l'adaptateur au lieu d'un champ de texte",
|
|
14
|
+
"it": "l'indirizzo di binding dell'istanza viene ora scelto tra gli indirizzi locali dell'host: un elenco nelle impostazioni dell'adattatore al posto di un campo di testo",
|
|
15
|
+
"es": "la dirección de enlace de la instancia se elige ahora entre las direcciones locales del host: una lista en los ajustes del adaptador en lugar de un campo de texto",
|
|
16
|
+
"pl": "adres nasłuchu instancji jest teraz wybierany spośród lokalnych adresów hosta: lista w ustawieniach adaptera zamiast pola tekstowego",
|
|
17
|
+
"uk": "адреса прив'язки екземпляра тепер вибирається з локальних адрес хоста: список у налаштуваннях адаптера замість поля введення",
|
|
18
|
+
"zh-cn": "实例的绑定地址现在从主机的本地地址中选择:适配器设置中为列表而不是文本输入框"
|
|
19
|
+
},
|
|
7
20
|
"0.0.13": {
|
|
8
21
|
"en": "break rules per employee, an enforced edit window for own punches (corrections right in the month view) and a CSV export of the raw punches in the monthly report",
|
|
9
22
|
"de": "Pausenregeln je Mitarbeiter, ein wirksames Bearbeitungsfenster für eigene Stempel (Korrektur direkt in der Monatsansicht) und ein CSV-Export der Rohstempel im Monatsbericht",
|
package/package.json
CHANGED
package/www/sw.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sw.js","sources":["../../../../../../tmp/
|
|
1
|
+
{"version":3,"file":"sw.js","sources":["../../../../../../tmp/5f38c10634d381eca819da255254fb77/sw.js"],"sourcesContent":["import {clientsClaim as workbox_core_clientsClaim} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {registerRoute as workbox_routing_registerRoute} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-routing/registerRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from '/home/runner/work/ioBroker.zeiterfassung/ioBroker.zeiterfassung/src-pwa/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\nself.skipWaiting();\nworkbox_core_clientsClaim();\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"person.png\",\n \"revision\": \"771d3283ece4739e203e02aac535855e\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"a1fa3b4e63a622e66623a9b9088f0d97\"\n },\n {\n \"url\": \"icon-512.png\",\n \"revision\": \"d8e3715779f4cd7e67e1f82f215a2f22\"\n },\n {\n \"url\": \"icon-192.png\",\n \"revision\": \"940ef37caef09c52272ddab844b9ea9b\"\n },\n {\n \"url\": \"favicon.svg\",\n \"revision\": \"fc9ba5a0c15ccb2a3acaf4d49f794dff\"\n },\n {\n \"url\": \"assets/workbox-window.prod.es5-Bd17z0YL.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-CkR_2Zgj.js\",\n \"revision\": null\n },\n {\n \"url\": \"icon-192.png\",\n \"revision\": \"940ef37caef09c52272ddab844b9ea9b\"\n },\n {\n \"url\": \"icon-512.png\",\n \"revision\": \"d8e3715779f4cd7e67e1f82f215a2f22\"\n },\n {\n \"url\": \"manifest.webmanifest\",\n \"revision\": \"6f40f1ce0ce138d5af307fb0dbb1e6f9\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();workbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\"), {\n allowlist: [/^\\/$/], denylist: [/^\\/api\\//],}));\n\n\n"],"names":["self","skipWaiting","workbox_core_clientsClaim","workbox_precaching_precacheAndRoute","url","revision","workbox_precaching_cleanupOutdatedCaches","workbox_routing_registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL","allowlist","denylist"],"mappings":"inBAoBAA,KAAKC,cACLC,EAAAA,eAMAC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,6CACPC,SAAY,MAEd,CACED,IAAO,2BACPC,SAAY,MAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,eACPC,SAAY,oCAEd,CACED,IAAO,uBACPC,SAAY,qCAEb,CAAA,GACHC,EAAAA,wBAA2CC,EAAAA,cAA8B,IAAIC,EAAAA,gBAAgCC,0BAA2C,cAAe,CACrKC,UAAW,CAAC,QAAUC,SAAU,CAAC"}
|