instbyte 1.6.0
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/LICENSE +21 -0
- package/README.md +200 -0
- package/bin/instbyte.js +28 -0
- package/client/assets/favicon-16.png +0 -0
- package/client/assets/favicon.png +0 -0
- package/client/assets/logo.png +0 -0
- package/client/css/app.css +813 -0
- package/client/index.html +81 -0
- package/client/js/app.js +946 -0
- package/package.json +47 -0
- package/server/cleanup.js +27 -0
- package/server/config.js +69 -0
- package/server/db.js +56 -0
- package/server/server.js +668 -0
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "instbyte",
|
|
3
|
+
"version": "1.6.0",
|
|
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",
|
|
6
|
+
"bin": {
|
|
7
|
+
"instbyte": "bin/instbyte.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"server/",
|
|
12
|
+
"client/"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node server/server.js",
|
|
16
|
+
"dev": "nodemon server/server.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"lan",
|
|
20
|
+
"file-sharing",
|
|
21
|
+
"local-network",
|
|
22
|
+
"realtime",
|
|
23
|
+
"self-hosted",
|
|
24
|
+
"developer-tools",
|
|
25
|
+
"team-collaboration",
|
|
26
|
+
"socket-io",
|
|
27
|
+
"sqlite"
|
|
28
|
+
],
|
|
29
|
+
"author": "your name",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/mohitgauniyal/instbyte.git"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"cookie-parser": "^1.4.6",
|
|
40
|
+
"express": "^4.18.2",
|
|
41
|
+
"express-rate-limit": "^7.1.5",
|
|
42
|
+
"multer": "^1.4.5-lts.1",
|
|
43
|
+
"sharp": "^0.33.2",
|
|
44
|
+
"socket.io": "^4.6.1",
|
|
45
|
+
"sqlite3": "^5.1.6"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const db = require("./db");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const config = require("./config");
|
|
5
|
+
|
|
6
|
+
const DAY = config.storage.retention;
|
|
7
|
+
|
|
8
|
+
setInterval(() => {
|
|
9
|
+
const cutoff = Date.now() - DAY;
|
|
10
|
+
|
|
11
|
+
db.all(
|
|
12
|
+
`SELECT * FROM items WHERE created_at < ?`,
|
|
13
|
+
[cutoff],
|
|
14
|
+
(err, rows) => {
|
|
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
ADDED
|
@@ -0,0 +1,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 (typeof val === "number") return val;
|
|
34
|
+
const units = { h: 3600000, d: 86400000 };
|
|
35
|
+
const match = String(val).match(/^(\d+)(h|d)$/i);
|
|
36
|
+
if (!match) return defaults.storage.retention;
|
|
37
|
+
return parseInt(match[1]) * units[match[2].toLowerCase()];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function loadConfig() {
|
|
41
|
+
const configPath = path.join(process.cwd(), "instbyte.config.json");
|
|
42
|
+
let userConfig = {};
|
|
43
|
+
|
|
44
|
+
if (fs.existsSync(configPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
47
|
+
userConfig = JSON.parse(raw);
|
|
48
|
+
console.log("Config loaded from instbyte.config.json");
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.warn("Warning: instbyte.config.json is invalid JSON, using defaults.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Deep merge user config over defaults
|
|
55
|
+
const config = {
|
|
56
|
+
server: { ...defaults.server, ...(userConfig.server || {}) },
|
|
57
|
+
auth: { ...defaults.auth, ...(userConfig.auth || {}) },
|
|
58
|
+
storage: { ...defaults.storage, ...(userConfig.storage || {}) },
|
|
59
|
+
branding: { ...defaults.branding, ...(userConfig.branding || {}) }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Parse human-readable values like "500MB" or "48h"
|
|
63
|
+
config.storage.maxFileSize = parseFileSize(config.storage.maxFileSize);
|
|
64
|
+
config.storage.retention = parseRetention(config.storage.retention);
|
|
65
|
+
|
|
66
|
+
return config;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = loadConfig();
|
package/server/db.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
|
|
10
|
+
db.serialize(() => {
|
|
11
|
+
|
|
12
|
+
// ITEMS TABLE
|
|
13
|
+
db.run(`
|
|
14
|
+
CREATE TABLE IF NOT EXISTS items (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
type TEXT,
|
|
17
|
+
content TEXT,
|
|
18
|
+
filename TEXT,
|
|
19
|
+
size INTEGER DEFAULT 0,
|
|
20
|
+
channel TEXT,
|
|
21
|
+
uploader TEXT,
|
|
22
|
+
pinned INTEGER DEFAULT 0,
|
|
23
|
+
created_at INTEGER
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
// CHANNELS TABLE
|
|
28
|
+
db.run(`
|
|
29
|
+
CREATE TABLE IF NOT EXISTS channels (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
name TEXT UNIQUE,
|
|
32
|
+
pinned INTEGER DEFAULT 0
|
|
33
|
+
)
|
|
34
|
+
`);
|
|
35
|
+
|
|
36
|
+
db.run(`ALTER TABLE items ADD COLUMN size INTEGER DEFAULT 0`, () => { });
|
|
37
|
+
db.run(`ALTER TABLE channels ADD COLUMN pinned INTEGER DEFAULT 0`, () => { });
|
|
38
|
+
|
|
39
|
+
// Insert default channels if empty
|
|
40
|
+
db.get("SELECT COUNT(*) as count FROM channels", (err, row) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
console.error("Channel count error:", err);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (row.count === 0) {
|
|
47
|
+
const defaults = ["general", "projects", "assets", "temp"];
|
|
48
|
+
defaults.forEach(name => {
|
|
49
|
+
db.run("INSERT INTO channels (name) VALUES (?)", [name]);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
module.exports = db;
|