implemented a lot of features

This commit is contained in:
Daan Meijer committed 2026-09-29 00:32:01 +02:00
1 parent 62f1767df3
commit bcb9be5da9
141 files changed
+10705 -385

No files matched your search

+68 -21
View File
@@ -1,5 +1,6 @@
import nntpConfig from '#config/nntp'
import { NNTP } from "nntp-js";
import { parseGroupResponse, type GroupInfo } from '#lib/nntp/group'
class NntpService {
private readonly poolSize: number;
@@ -14,15 +15,20 @@ class NntpService {
this.idleConnections = [];
this.waiters = [];
this.createdCount = 0;
console.log(`NNTP Pool initialized with size ${this.poolSize}`)
}
private async _createConnection() {
// This connection logic is based on the older, working pool implementation.
const conn = new NNTP(nntpConfig.host, nntpConfig.port);
await conn.connect();
if (nntpConfig.user) {
await conn.login(nntpConfig.user, nntpConfig.password?.release());
try {
await conn.connect();
if (nntpConfig.user) {
await conn.login(nntpConfig.user, nntpConfig.password?.release());
}
} catch (error) {
// Free the slot, otherwise a failed connect shrinks the pool for good.
this.createdCount--;
this._destroy(conn);
throw error;
}
this.allConnections.add(conn);
return conn;
@@ -30,38 +36,79 @@ class NntpService {
public async acquire() {
if (this.idleConnections.length > 0) {
console.log('Reusing existing connection from pool.');
return this.idleConnections.pop();
}
if (this.createdCount < this.poolSize) {
this.createdCount++;
console.log(`Creating new connection (${this.createdCount}/${this.poolSize}).`);
console.log(`Creating new connection (${this.createdCount}/${this.poolSize}).`);
return this._createConnection();
}
console.log(`Pool maxed out at ${this.poolSize}. Waiting for a connection to become available.`);
return new Promise(resolve => this.waiters.push(resolve));
}
public release(conn: any) {
if (this.waiters.length > 0) {
console.log('Releasing connection directly to a waiting task.');
const resolve = this.waiters.shift();
if(resolve) resolve(conn);
/**
* Returns a connection to the pool. Pass `failed` when a command on it
* threw: the connection may be dead (e.g. closed by the server while idle),
* so it is discarded instead of being handed to the next job.
*/
public release(conn: any, failed = false) {
if (failed) {
this.discard(conn);
return;
}
const resolve = this.waiters.shift();
if (resolve) {
resolve(conn);
} else {
console.log('Returning connection to the idle pool.');
this.idleConnections.push(conn);
}
}
public async shutdown() {
console.log('Shutting down all connections in the pool.');
const shutdownPromises: Promise<any>[] = [];
for (const conn of this.allConnections) {
shutdownPromises.push(conn.quit());
/** Closes a broken connection and frees its slot for a new one. */
public discard(conn: any) {
this.allConnections.delete(conn);
this.createdCount--;
this._destroy(conn);
// Someone is waiting for a slot: open a fresh connection for them.
const resolve = this.waiters.shift();
if (resolve) {
this.createdCount++;
this._createConnection().then(resolve, () => this.waiters.unshift(resolve));
}
await Promise.all(shutdownPromises);
}
private _destroy(conn: any) {
try {
conn.sock?.destroy();
} catch {
// already closed
}
}
/** Selects a group and returns its correctly parsed article range. */
public async selectGroup(conn: any, name: string): Promise<GroupInfo> {
const { resp } = await conn.group(name)
return parseGroupResponse(resp)
}
/** Fetches a raw (still dot-stuffed) article body as a Buffer. */
public async fetchBody(conn: any, messageId: string): Promise<Buffer> {
const id = messageId.startsWith('<') ? messageId : `<${messageId}>`
const { data } = await conn.body(id)
return Buffer.from(data.buffer, data.byteOffset, data.byteLength)
}
public async shutdown() {
console.log('Shutting down all connections in the pool.');
// A connection the server already closed may never answer QUIT.
const quit = (conn: any) =>
Promise.race([conn.quit(), new Promise((resolve) => setTimeout(resolve, 2000).unref())])
.catch(() => {})
.finally(() => this._destroy(conn));
await Promise.all([...this.allConnections].map(quit));
this.allConnections.clear();
this.idleConnections.length = 0;
this.waiters.length = 0;
@@ -69,4 +116,4 @@ class NntpService {
}
}
export default new NntpService();
export default new NntpService(nntpConfig.poolSize);