instbyte 1.9.1 → 1.9.3

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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "instbyte",
3
- "version": "1.9.1",
3
+ "version": "1.9.3",
4
4
  "description": "A self-hosted LAN sharing utility for fast, frictionless file, link, and snippet exchange across devices — no cloud required.",
5
- "main": "server/server.js",
5
+ "main": "bin/instbyte.js",
6
6
  "bin": {
7
7
  "instbyte": "bin/instbyte.js"
8
8
  },
@@ -12,8 +12,10 @@
12
12
  "client/"
13
13
  ],
14
14
  "scripts": {
15
- "start": "node server/server.js",
16
- "dev": "nodemon server/server.js"
15
+ "start": "node bin/instbyte.js",
16
+ "dev": "nodemon bin/instbyte.js",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest"
17
19
  },
18
20
  "keywords": [
19
21
  "lan",
@@ -44,5 +46,10 @@
44
46
  "sharp": "^0.33.2",
45
47
  "socket.io": "^4.6.1",
46
48
  "sqlite3": "^5.1.6"
49
+ },
50
+ "devDependencies": {
51
+ "nodemon": "^3.1.0",
52
+ "supertest": "^7.2.2",
53
+ "vitest": "^2.1.8"
47
54
  }
48
55
  }
package/server/cleanup.js CHANGED
@@ -1,26 +1,27 @@
1
- const db = require("./db");
2
- const fs = require("fs");
3
- const path = require("path");
4
- const config = require("./config");
5
-
6
- setInterval(() => {
7
- if (config.storage.retention === null) return;
8
- const cutoff = Date.now() - config.storage.retention;
9
-
10
- db.all(
11
- `SELECT * FROM items WHERE created_at < ? AND pinned = 0`,
12
- [cutoff],
13
- (err, rows) => {
14
- rows.forEach((item) => {
15
- if (item.filename) {
16
- const uploadsDir = process.env.INSTBYTE_UPLOADS
17
- || path.join(__dirname, "../uploads");
18
- const filePath = path.join(uploadsDir, item.filename);
19
- if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
20
- }
21
-
22
- db.run(`DELETE FROM items WHERE id=?`, [item.id]);
23
- });
24
- }
25
- );
26
- }, 10 * 60 * 1000);
1
+ const db = require("./db");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const config = require("./config");
5
+
6
+ setInterval(() => {
7
+ if (config.storage.retention === null) return;
8
+ const cutoff = Date.now() - config.storage.retention;
9
+
10
+ db.all(
11
+ `SELECT * FROM items WHERE created_at < ? AND pinned = 0`,
12
+ [cutoff],
13
+ (err, rows) => {
14
+ if (err || !rows) return;
15
+ rows.forEach((item) => {
16
+ if (item.filename) {
17
+ const uploadsDir = process.env.INSTBYTE_UPLOADS
18
+ || path.join(__dirname, "../uploads");
19
+ const filePath = path.join(uploadsDir, item.filename);
20
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
21
+ }
22
+
23
+ db.run(`DELETE FROM items WHERE id=?`, [item.id]);
24
+ });
25
+ }
26
+ );
27
+ }, 10 * 60 * 1000);
package/server/config.js CHANGED
@@ -1,70 +1,70 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- // These are the defaults — what you get with zero config file
5
- const defaults = {
6
- server: {
7
- port: 3000,
8
- },
9
- auth: {
10
- passphrase: "" // empty = no password required
11
- },
12
- storage: {
13
- maxFileSize: 2 * 1024 * 1024 * 1024, // 2GB in bytes
14
- retention: 24 * 60 * 60 * 1000, // 24 hours in ms
15
- },
16
- branding: {
17
- appName: "Instbyte",
18
- logoPath: "",
19
- faviconPath: "",
20
- primaryColor: "#111827"
21
- }
22
- };
23
-
24
- function parseFileSize(val) {
25
- if (typeof val === "number") return val;
26
- const units = { KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3 };
27
- const match = String(val).match(/^(\d+(\.\d+)?)\s*(KB|MB|GB)$/i);
28
- if (!match) return defaults.storage.maxFileSize;
29
- return parseFloat(match[1]) * units[match[3].toUpperCase()];
30
- }
31
-
32
- function parseRetention(val) {
33
- if (String(val).toLowerCase() === "never") return null;
34
- if (typeof val === "number") return val;
35
- const units = { h: 3600000, d: 86400000 };
36
- const match = String(val).match(/^(\d+)(h|d)$/i);
37
- if (!match) return defaults.storage.retention;
38
- return parseInt(match[1]) * units[match[2].toLowerCase()];
39
- }
40
-
41
- function loadConfig() {
42
- const configPath = path.join(process.cwd(), "instbyte.config.json");
43
- let userConfig = {};
44
-
45
- if (fs.existsSync(configPath)) {
46
- try {
47
- const raw = fs.readFileSync(configPath, "utf-8");
48
- userConfig = JSON.parse(raw);
49
- console.log("Config loaded from instbyte.config.json");
50
- } catch (e) {
51
- console.warn("Warning: instbyte.config.json is invalid JSON, using defaults.");
52
- }
53
- }
54
-
55
- // Deep merge user config over defaults
56
- const config = {
57
- server: { ...defaults.server, ...(userConfig.server || {}) },
58
- auth: { ...defaults.auth, ...(userConfig.auth || {}) },
59
- storage: { ...defaults.storage, ...(userConfig.storage || {}) },
60
- branding: { ...defaults.branding, ...(userConfig.branding || {}) }
61
- };
62
-
63
- // Parse human-readable values like "500MB" or "48h"
64
- config.storage.maxFileSize = parseFileSize(config.storage.maxFileSize);
65
- config.storage.retention = parseRetention(config.storage.retention);
66
-
67
- return config;
68
- }
69
-
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ // These are the defaults — what you get with zero config file
5
+ const defaults = {
6
+ server: {
7
+ port: 3000,
8
+ },
9
+ auth: {
10
+ passphrase: "" // empty = no password required
11
+ },
12
+ storage: {
13
+ maxFileSize: 2 * 1024 * 1024 * 1024, // 2GB in bytes
14
+ retention: 24 * 60 * 60 * 1000, // 24 hours in ms
15
+ },
16
+ branding: {
17
+ appName: "Instbyte",
18
+ logoPath: "",
19
+ faviconPath: "",
20
+ primaryColor: "#111827"
21
+ }
22
+ };
23
+
24
+ function parseFileSize(val) {
25
+ if (typeof val === "number") return val;
26
+ const units = { KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3 };
27
+ const match = String(val).match(/^(\d+(\.\d+)?)\s*(KB|MB|GB)$/i);
28
+ if (!match) return defaults.storage.maxFileSize;
29
+ return parseFloat(match[1]) * units[match[3].toUpperCase()];
30
+ }
31
+
32
+ function parseRetention(val) {
33
+ if (String(val).toLowerCase() === "never") return null;
34
+ if (typeof val === "number") return val;
35
+ const units = { h: 3600000, d: 86400000 };
36
+ const match = String(val).match(/^(\d+)(h|d)$/i);
37
+ if (!match) return defaults.storage.retention;
38
+ return parseInt(match[1]) * units[match[2].toLowerCase()];
39
+ }
40
+
41
+ function loadConfig() {
42
+ const configPath = path.join(process.cwd(), "instbyte.config.json");
43
+ let userConfig = {};
44
+
45
+ if (fs.existsSync(configPath)) {
46
+ try {
47
+ const raw = fs.readFileSync(configPath, "utf-8");
48
+ userConfig = JSON.parse(raw);
49
+ console.log("Config loaded from instbyte.config.json");
50
+ } catch (e) {
51
+ console.warn("Warning: instbyte.config.json is invalid JSON, using defaults.");
52
+ }
53
+ }
54
+
55
+ // Deep merge user config over defaults
56
+ const config = {
57
+ server: { ...defaults.server, ...(userConfig.server || {}) },
58
+ auth: { ...defaults.auth, ...(userConfig.auth || {}) },
59
+ storage: { ...defaults.storage, ...(userConfig.storage || {}) },
60
+ branding: { ...defaults.branding, ...(userConfig.branding || {}) }
61
+ };
62
+
63
+ // Parse human-readable values like "500MB" or "48h"
64
+ config.storage.maxFileSize = parseFileSize(config.storage.maxFileSize);
65
+ config.storage.retention = parseRetention(config.storage.retention);
66
+
67
+ return config;
68
+ }
69
+
70
70
  module.exports = loadConfig();
package/server/db.js CHANGED
@@ -1,60 +1,60 @@
1
- const sqlite3 = require("sqlite3").verbose();
2
- const path = require("path");
3
-
4
- const dbPath = process.env.INSTBYTE_DATA
5
- ? path.join(process.env.INSTBYTE_DATA, "db.sqlite")
6
- : path.join(__dirname, "../db.sqlite");
7
-
8
- const db = new sqlite3.Database(dbPath);
9
- db.configure("busyTimeout", 5000);
10
-
11
- db.serialize(() => {
12
-
13
- // ITEMS TABLE
14
- db.run(`
15
- CREATE TABLE IF NOT EXISTS items (
16
- id INTEGER PRIMARY KEY AUTOINCREMENT,
17
- type TEXT,
18
- content TEXT,
19
- filename TEXT,
20
- size INTEGER DEFAULT 0,
21
- channel TEXT,
22
- uploader TEXT,
23
- pinned INTEGER DEFAULT 0,
24
- created_at INTEGER
25
- )
26
- `);
27
-
28
- // CHANNELS TABLE
29
- db.run(`
30
- CREATE TABLE IF NOT EXISTS channels (
31
- id INTEGER PRIMARY KEY AUTOINCREMENT,
32
- name TEXT UNIQUE,
33
- pinned INTEGER DEFAULT 0
34
- )
35
- `);
36
-
37
- db.run(`ALTER TABLE items ADD COLUMN size INTEGER DEFAULT 0`, () => { });
38
- db.run(`ALTER TABLE channels ADD COLUMN pinned INTEGER DEFAULT 0`, () => { });
39
-
40
- db.run(`ALTER TABLE items ADD COLUMN title TEXT DEFAULT ''`, () => { });
41
- db.run(`ALTER TABLE items ADD COLUMN edited_at INTEGER DEFAULT NULL`, () => { });
42
-
43
- // Insert default channels if empty
44
- db.get("SELECT COUNT(*) as count FROM channels", (err, row) => {
45
- if (err) {
46
- console.error("Channel count error:", err);
47
- return;
48
- }
49
-
50
- if (row.count === 0) {
51
- const defaults = ["general", "projects", "assets", "temp"];
52
- defaults.forEach(name => {
53
- db.run("INSERT INTO channels (name) VALUES (?)", [name]);
54
- });
55
- }
56
- });
57
-
58
- });
59
-
60
- module.exports = db;
1
+ const sqlite3 = require("sqlite3").verbose();
2
+ const path = require("path");
3
+
4
+ const dbPath = process.env.INSTBYTE_DATA
5
+ ? path.join(process.env.INSTBYTE_DATA, "db.sqlite")
6
+ : path.join(__dirname, "../db.sqlite");
7
+
8
+ const db = new sqlite3.Database(dbPath);
9
+ db.configure("busyTimeout", 5000);
10
+
11
+ db.serialize(() => {
12
+
13
+ // ITEMS TABLE
14
+ db.run(`
15
+ CREATE TABLE IF NOT EXISTS items (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ type TEXT,
18
+ content TEXT,
19
+ filename TEXT,
20
+ size INTEGER DEFAULT 0,
21
+ channel TEXT,
22
+ uploader TEXT,
23
+ pinned INTEGER DEFAULT 0,
24
+ created_at INTEGER
25
+ )
26
+ `);
27
+
28
+ // CHANNELS TABLE
29
+ db.run(`
30
+ CREATE TABLE IF NOT EXISTS channels (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ name TEXT UNIQUE,
33
+ pinned INTEGER DEFAULT 0
34
+ )
35
+ `);
36
+
37
+ db.run(`ALTER TABLE items ADD COLUMN size INTEGER DEFAULT 0`, () => { });
38
+ db.run(`ALTER TABLE channels ADD COLUMN pinned INTEGER DEFAULT 0`, () => { });
39
+
40
+ db.run(`ALTER TABLE items ADD COLUMN title TEXT DEFAULT ''`, () => { });
41
+ db.run(`ALTER TABLE items ADD COLUMN edited_at INTEGER DEFAULT NULL`, () => { });
42
+
43
+ // Insert default channels if empty
44
+ db.get("SELECT COUNT(*) as count FROM channels", (err, row) => {
45
+ if (err) {
46
+ console.error("Channel count error:", err);
47
+ return;
48
+ }
49
+
50
+ if (row.count === 0) {
51
+ const defaults = ["general", "projects", "assets", "temp"];
52
+ defaults.forEach(name => {
53
+ db.run("INSERT INTO channels (name) VALUES (?)", [name]);
54
+ });
55
+ }
56
+ });
57
+
58
+ });
59
+
60
+ module.exports = db;