Compare commits

...
6 Commits
8 changed files with 73 additions and 51 deletions

No files matched your search

+7
View File
@@ -1,5 +1,12 @@
# Change Log # Change Log
## 1.2.12 (2024-03-28)
- Fix broken XMPP support in the previous release.
- Fix regression causing CSV downloads to be buffered in memory before being
streamed to the client.
## 1.2.11 (2024-03-21) ## 1.2.11 (2024-03-21)
- Resolved an issue from the previous release that caused incompatibility with - Resolved an issue from the previous release that caused incompatibility with
+18
View File
@@ -0,0 +1,18 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
sphinx:
configuration: docs/conf.py
formats: all
python:
install:
- requirements: docs/requirements.txt
+2 -2
View File
@@ -19,7 +19,7 @@ import json
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
project = 'GenieACS Documentation' project = 'GenieACS Documentation'
copyright = '2019, GenieACS Inc.' copyright = '2024, GenieACS Inc.'
author = 'GenieACS Inc.' author = 'GenieACS Inc.'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
@@ -58,7 +58,7 @@ html_theme_options = {
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static'] # html_static_path = ['_static']
master_doc = 'index' master_doc = 'index'
+1
View File
@@ -0,0 +1 @@
sphinx_rtd_theme==2.0.0
+26 -24
View File
@@ -1,4 +1,4 @@
import { PassThrough } from "node:stream"; import { Readable } from "node:stream";
import Router from "koa-router"; import Router from "koa-router";
import { ObjectId } from "mongodb"; import { ObjectId } from "mongodb";
import * as db from "./db.ts"; import * as db from "./db.ts";
@@ -88,10 +88,9 @@ router.get(`/devices/:id.csv`, async (ctx) => {
.replace(/[:.]/g, "")}.csv`, .replace(/[:.]/g, "")}.csv`,
); );
ctx.body = new PassThrough(); const lines: string[] = [
ctx.body.write( "Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp,Notification,Notification timestamp,Access list,Access list timestamp",
"Parameter,Object,Object timestamp,Writable,Writable timestamp,Value,Value type,Value timestamp,Notification,Notification timestamp,Access list,Access list timestamp\n", ];
);
for (const k of Object.keys(device).sort()) { for (const k of Object.keys(device).sort()) {
const p = device[k]; const p = device[k];
@@ -118,9 +117,9 @@ router.get(`/devices/:id.csv`, async (ctx) => {
p.accessList ? p.accessList.join(", ") : "", p.accessList ? p.accessList.join(", ") : "",
new Date(p.accessListTimestamp).toJSON(), new Date(p.accessListTimestamp).toJSON(),
]; ];
ctx.body.write(row.map((r) => (r != null ? r : "")).join(",") + "\n"); lines.push(row.map((r) => (r != null ? r : "")).join(","));
} }
ctx.body.end(); ctx.body = lines.join("\n");
logger.accessInfo(log); logger.accessInfo(log);
}); });
@@ -201,16 +200,17 @@ for (const [resource, flags] of Object.entries(resources)) {
]); ]);
} }
ctx.body = new PassThrough();
ctx.type = "application/json";
let c = 0;
ctx.body.write("[\n");
for await (const obj of db.query(resource, filter, options))
ctx.body.write((c++ ? "," : "") + JSON.stringify(obj) + "\n");
ctx.body.end("]");
logger.accessInfo(log); logger.accessInfo(log);
ctx.type = "application/json";
ctx.body = Readable.from(
(async function* () {
let c = 0;
yield "[\n";
for await (const obj of db.query(resource, filter, options))
yield (c++ ? "," : "") + JSON.stringify(obj) + "\n";
yield "]";
})(),
);
}); });
// CSV download // CSV download
@@ -260,15 +260,16 @@ for (const [resource, flags] of Object.entries(resources)) {
]); ]);
} }
ctx.body = new PassThrough(); logger.accessInfo(log);
ctx.type = "text/csv"; ctx.type = "text/csv";
ctx.attachment( ctx.attachment(
`${resource}-${new Date(now).toISOString().replace(/[:.]/g, "")}.csv`, `${resource}-${new Date(now).toISOString().replace(/[:.]/g, "")}.csv`,
); );
ctx.body.write( ctx.body = Readable.from(
Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) + "\n", (async function* () {
); yield Object.keys(columns).map((k) => `"${k.replace(/"/, '""')}"`) +
"\n";
for await (const obj of db.query(resource, filter, options)) { for await (const obj of db.query(resource, filter, options)) {
const arr = Object.values(columns).map((exp) => { const arr = Object.values(columns).map((exp) => {
const v = evaluate(exp, obj, null, (e) => { const v = evaluate(exp, obj, null, (e) => {
@@ -278,7 +279,8 @@ for (const [resource, flags] of Object.entries(resources)) {
if (e[1] === "Tags") { if (e[1] === "Tags") {
const tags = []; const tags = [];
for (const p in obj) for (const p in obj)
if (p.startsWith("Tags.")) tags.push(decodeTag(p.slice(5))); if (p.startsWith("Tags."))
tags.push(decodeTag(p.slice(5)));
return tags.join(", "); return tags.join(", ");
} }
@@ -310,10 +312,10 @@ for (const [resource, flags] of Object.entries(resources)) {
if (typeof v === "string") return `"${v.replace(/"/g, '""')}"`; if (typeof v === "string") return `"${v.replace(/"/g, '""')}"`;
return v; return v;
}); });
ctx.body.write(arr.join(",") + "\n"); yield arr.join(",") + "\n";
} }
ctx.body.end(); })(),
logger.accessInfo(log); );
}); });
router.head(`/${resource}/:id`, async (ctx) => { router.head(`/${resource}/:id`, async (ctx) => {
+16 -22
View File
@@ -321,16 +321,19 @@ export default class XmppClient extends EventEmitter {
this._iqStanzaCallbacks = new Map(); this._iqStanzaCallbacks = new Map();
} }
static connect(opts: XmppClientOptions): Promise<XmppClient> { static async connect(opts: XmppClientOptions): Promise<XmppClient> {
function connectSocket(host: string, port: number): Promise<net.Socket> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let socket = new net.Socket(); const socket = new net.Socket();
socket.on("error", reject); socket.on("error", reject);
socket.connect(port, host, () => {
socket.connect(
opts.port || 5222,
opts.host,
void async function () {
socket.removeListener("error", reject); socket.removeListener("error", reject);
resolve(socket);
});
});
}
let socket = await connectSocket(opts.host, opts.port || 5222);
try { try {
let status = 1; let status = 1;
while (status) { while (status) {
@@ -338,15 +341,13 @@ export default class XmppClient extends EventEmitter {
socket = await upgradeTls(socket, opts.host); socket = await upgradeTls(socket, opts.host);
status = await xmppStream( status = await xmppStream(
socket, socket,
init( init(socket, opts.host, opts.username, opts.password, opts.resource),
socket,
opts.host,
opts.username,
opts.password,
opts.resource,
),
); );
} }
} catch (err) {
socket.destroy();
throw err;
}
const client = new XmppClient(); const client = new XmppClient();
client._socket = socket; client._socket = socket;
@@ -357,14 +358,7 @@ export default class XmppClient extends EventEmitter {
socket.on("error", client._onError.bind(client)); socket.on("error", client._onError.bind(client));
if (opts.timeout) if (opts.timeout)
socket.setTimeout(opts.timeout, client.close.bind(client)); socket.setTimeout(opts.timeout, client.close.bind(client));
resolve(client); return client;
} catch (err) {
socket.destroy();
reject(err);
}
},
);
});
} }
close(): void { close(): void {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "genieacs", "name": "genieacs",
"version": "1.2.11", "version": "1.2.12",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "genieacs", "name": "genieacs",
"version": "1.2.11", "version": "1.2.12",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@breejs/later": "^4.2.0", "@breejs/later": "^4.2.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "genieacs", "name": "genieacs",
"version": "1.2.11", "version": "1.2.12",
"description": "A TR-069 Auto Configuration Server (ACS)", "description": "A TR-069 Auto Configuration Server (ACS)",
"repository": { "repository": {
"type": "git", "type": "git",