instbyte 1.12.0 → 1.12.2
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 +8 -4
- package/package.json +1 -1
- package/server/cleanup.js +33 -20
- package/server/seen.js +5 -0
- package/server/server.js +19 -11
package/README.md
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
<h1 align="center">Instbyte</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/instbyte"><img src="https://img.shields.io/npm/v/instbyte" alt="npm version"></a>
|
|
9
|
-
<a href="https://github.com/mohitgauniyal/instbyte/
|
|
10
|
-
<img src="https://img.shields.io/
|
|
11
|
-
|
|
8
|
+
<a href="https://www.npmjs.com/package/instbyte"><img src="https://img.shields.io/npm/v/instbyte?style=flat-square" alt="npm version"></a>
|
|
9
|
+
<a href="https://github.com/mohitgauniyal/instbyte/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/mohitgauniyal/instbyte/ci.yml?branch=main&style=flat-square&label=build" alt="build status"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/instbyte"><img src="https://img.shields.io/node/v/instbyte?style=flat-square" alt="node version"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/instbyte"><img src="https://img.shields.io/npm/dm/instbyte?style=flat-square" alt="npm downloads"></a>
|
|
12
|
+
<a href="https://github.com/mohitgauniyal/instbyte"><img src="https://img.shields.io/github/languages/code-size/mohitgauniyal/instbyte?style=flat-square" alt="code size"></a>
|
|
13
|
+
<a href="https://github.com/mohitgauniyal/instbyte/commits/main"><img src="https://img.shields.io/github/last-commit/mohitgauniyal/instbyte?style=flat-square" alt="last commit"></a>
|
|
14
|
+
<a href="https://github.com/mohitgauniyal/instbyte/blob/main/LICENSE"><img src="https://img.shields.io/github/license/mohitgauniyal/instbyte?style=flat-square" alt="license"></a>
|
|
15
|
+
<a href="https://github.com/mohitgauniyal/instbyte/issues"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" alt="PRs Welcome"></a>
|
|
12
16
|
</p>
|
|
13
17
|
|
|
14
18
|
---
|
package/package.json
CHANGED
package/server/cleanup.js
CHANGED
|
@@ -2,26 +2,39 @@ const db = require("./db");
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const config = require("./config");
|
|
5
|
+
const seenBy = require("./seen");
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// One retention pass: delete expired, unpinned items along with their files
|
|
8
|
+
// and any stale in-memory seenBy entries. Returns a promise so tests can await
|
|
9
|
+
// a single pass; the interval below just fires it and ignores the result.
|
|
10
|
+
function purgeExpired() {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
if (config.storage.retention === null) return resolve();
|
|
13
|
+
const cutoff = Date.now() - config.storage.retention;
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
db.all(
|
|
16
|
+
`SELECT * FROM items WHERE created_at < ? AND pinned = 0`,
|
|
17
|
+
[cutoff],
|
|
18
|
+
(err, rows) => {
|
|
19
|
+
if (err || !rows) return resolve();
|
|
20
|
+
rows.forEach((item) => {
|
|
21
|
+
if (item.filename) {
|
|
22
|
+
const uploadsDir = process.env.INSTBYTE_UPLOADS
|
|
23
|
+
|| path.join(__dirname, "../uploads");
|
|
24
|
+
const filePath = path.join(uploadsDir, item.filename);
|
|
25
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
26
|
+
}
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
+
db.run(`DELETE FROM items WHERE id=?`, [item.id]);
|
|
29
|
+
seenBy.delete(item.id); // drop stale in-memory seen tracking
|
|
30
|
+
});
|
|
31
|
+
resolve();
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const interval = setInterval(purgeExpired, 10 * 60 * 1000);
|
|
38
|
+
interval.unref(); // don't keep the process alive just for cleanup
|
|
39
|
+
|
|
40
|
+
module.exports = { purgeExpired };
|
package/server/seen.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Shared in-memory "seen by" tracking — item id → Set of viewer names.
|
|
2
|
+
// Kept in its own module so both the request handlers (server.js) and the
|
|
3
|
+
// retention job (cleanup.js) mutate the same Map without a circular require.
|
|
4
|
+
// Resets on server restart, no DB needed.
|
|
5
|
+
module.exports = new Map();
|
package/server/server.js
CHANGED
|
@@ -121,7 +121,8 @@ const MAGIC_NUMBERS = {
|
|
|
121
121
|
webp: [Buffer.from([0x52, 0x49, 0x46, 0x46])],
|
|
122
122
|
pdf: [Buffer.from([0x25, 0x50, 0x44, 0x46])],
|
|
123
123
|
zip: [Buffer.from([0x50, 0x4B, 0x03, 0x04]), Buffer.from([0x50, 0x4B, 0x05, 0x06])],
|
|
124
|
-
mp4
|
|
124
|
+
// mp4 is handled separately in checkMagicNumber — its signature is the
|
|
125
|
+
// "ftyp" marker at bytes 4-7, not a fixed sequence at the start.
|
|
125
126
|
};
|
|
126
127
|
|
|
127
128
|
// Extension → magic group mapping
|
|
@@ -147,18 +148,25 @@ function checkMagicNumber(filePath, ext) {
|
|
|
147
148
|
const group = EXT_TO_MAGIC[ext.toLowerCase()];
|
|
148
149
|
if (!group) return true; // no check defined for this type — allow through
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
if (!signatures) return true;
|
|
152
|
-
|
|
151
|
+
let buf;
|
|
153
152
|
try {
|
|
154
153
|
const fd = fs.openSync(filePath, 'r');
|
|
155
|
-
|
|
154
|
+
buf = Buffer.alloc(8);
|
|
156
155
|
fs.readSync(fd, buf, 0, 8, 0);
|
|
157
156
|
fs.closeSync(fd);
|
|
158
|
-
return signatures.some(sig => buf.slice(0, sig.length).equals(sig));
|
|
159
157
|
} catch (e) {
|
|
160
158
|
return false; // can't read → reject
|
|
161
159
|
}
|
|
160
|
+
|
|
161
|
+
// MP4: bytes 0-3 are the atom size (which varies between files), so the real
|
|
162
|
+
// file-type signature is the "ftyp" marker at bytes 4-7. Check that instead.
|
|
163
|
+
if (group === 'mp4') {
|
|
164
|
+
return buf.slice(4, 8).toString('latin1') === 'ftyp';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const signatures = MAGIC_NUMBERS[group];
|
|
168
|
+
if (!signatures) return true;
|
|
169
|
+
return signatures.some(sig => buf.slice(0, sig.length).equals(sig));
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
function hexToHsl(hex) {
|
|
@@ -684,7 +692,7 @@ app.delete("/channels/:name", (req, res) => {
|
|
|
684
692
|
db.all("SELECT * FROM items WHERE channel=?", [name], (err, rows) => {
|
|
685
693
|
rows.forEach(item => {
|
|
686
694
|
if (item.filename) {
|
|
687
|
-
const filePath = path.join(
|
|
695
|
+
const filePath = path.join(UPLOADS_DIR, item.filename);
|
|
688
696
|
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
689
697
|
}
|
|
690
698
|
});
|
|
@@ -967,9 +975,9 @@ app.get("/logo-dynamic.png", (req, res) => {
|
|
|
967
975
|
/* ============================
|
|
968
976
|
SOCKET CONNECTION LOGGING
|
|
969
977
|
============================ */
|
|
970
|
-
// in-memory seen tracking — item id → Set of
|
|
971
|
-
//
|
|
972
|
-
const seenBy =
|
|
978
|
+
// in-memory seen tracking — item id → Set of viewer names.
|
|
979
|
+
// Shared with cleanup.js so expired items get purged from it too.
|
|
980
|
+
const seenBy = require("./seen");
|
|
973
981
|
|
|
974
982
|
// Broadcast state — null when IDLE, populated when LIVE
|
|
975
983
|
let currentBroadcast = null;
|
|
@@ -1072,7 +1080,7 @@ function getLocalIP() {
|
|
|
1072
1080
|
const preferred =
|
|
1073
1081
|
candidates.find(c => c.address.startsWith("192.168.")) ||
|
|
1074
1082
|
candidates.find(c => c.address.startsWith("10.")) ||
|
|
1075
|
-
candidates.find(c => c.address.startsWith("172.
|
|
1083
|
+
candidates.find(c => c.address.startsWith("172.")) ||
|
|
1076
1084
|
candidates[0];
|
|
1077
1085
|
|
|
1078
1086
|
return preferred ? preferred.address : "localhost";
|