dev-_007haste-server 1.0.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/README ADDED
@@ -0,0 +1,7 @@
1
+ npm init -y
2
+ npm install express
3
+ npm start
4
+
5
+
6
+ +++ bashrc
7
+ alias haste='node DATA/haste.server/server.js'
package/cert/cert.pem ADDED
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDiTCCAnGgAwIBAgIUaYtIq6l74H/ECqm2HYrHGmwh4AwwDQYJKoZIhvcNAQEL
3
+ BQAwVDELMAkGA1UEBhMCVUExEzARBgNVBAgMClNvbWUtU3RhdGUxCzAJBgNVBAoM
4
+ AkRQMQswCQYDVQQLDAJoYTEWMBQGCSqGSIb3DQEJARYHYWFALmlvYTAeFw0yNTA3
5
+ MTUwODU4NDRaFw0yNjA3MTUwODU4NDRaMFQxCzAJBgNVBAYTAlVBMRMwEQYDVQQI
6
+ DApTb21lLVN0YXRlMQswCQYDVQQKDAJEUDELMAkGA1UECwwCaGExFjAUBgkqhkiG
7
+ 9w0BCQEWB2FhQC5pb2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr
8
+ 1niYQMRY3Jt19Z0sleSwhxXv9xjyqiwgD8KKCAu34DghLAIqh1P3tEHi8FFfUxgM
9
+ cO2XVYmTe06xhYJF+zLkfDHIBeuYP0svrMXA5Q9h7xieX50c+ac5A5aDibqjaiQq
10
+ M+e28X5DGC8+nI5BaPtBEm76ugerf5SxYXhmPurU7zuE9I7+E7atRaALSYwAtjGO
11
+ pGw533s0/EpZQEGy8/Snpnm0d0/DQZE88Ma5IxMFgjQgol6yAgiqDXoinbTEbFFE
12
+ xTtW6jb5ZzbRN7AlDy6+V4upfRgIUYzOuM9dKoaN5T5nFjkzz//XIsTJDE1t731Q
13
+ XHT8OU6SIBx70CxmjF6jAgMBAAGjUzBRMB0GA1UdDgQWBBRfU7K2lhkLt7Q7d7Rw
14
+ NSkOK3j5pDAfBgNVHSMEGDAWgBRfU7K2lhkLt7Q7d7RwNSkOK3j5pDAPBgNVHRMB
15
+ Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCm/MJeroEBsHsWN3K7F9+e2UCe
16
+ X7LyIQqhHzuoJgFXQt2/fz7QSDGGScT8ntnAmdSjl29fQKt1PepthNLevbiYcg3X
17
+ qQoEbyNUN1ur2Y8r+6siKT+XjqYqIehIiTNiz3vmLmMYKr8AW/R7Jx2s/jWpYb4d
18
+ RSd2tmflMgphSYUI6J3ja1wNngl29Zi/xeuJu7fM6wxT8puXpy2wFnpugksFMUKE
19
+ pItSinifPXPrAnTK0VcdRwsVTtO6SkOJjHouYqlowI2AoSBUUAhkgaii7ksiieF3
20
+ VePVr0x0VkGuf0ziVH1J5W3Blp64OO8ZhClBE97mv59lVSxPWTDa/TfCg/55
21
+ -----END CERTIFICATE-----
package/cert/key.pem ADDED
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCr1niYQMRY3Jt1
3
+ 9Z0sleSwhxXv9xjyqiwgD8KKCAu34DghLAIqh1P3tEHi8FFfUxgMcO2XVYmTe06x
4
+ hYJF+zLkfDHIBeuYP0svrMXA5Q9h7xieX50c+ac5A5aDibqjaiQqM+e28X5DGC8+
5
+ nI5BaPtBEm76ugerf5SxYXhmPurU7zuE9I7+E7atRaALSYwAtjGOpGw533s0/EpZ
6
+ QEGy8/Snpnm0d0/DQZE88Ma5IxMFgjQgol6yAgiqDXoinbTEbFFExTtW6jb5ZzbR
7
+ N7AlDy6+V4upfRgIUYzOuM9dKoaN5T5nFjkzz//XIsTJDE1t731QXHT8OU6SIBx7
8
+ 0CxmjF6jAgMBAAECggEAIkgCvgkpwrqcLx/Fb/glbAw/FaT7rqnBbNIIEwaA4/mQ
9
+ 6RlBA2AFIeUtwcsEyNoocY7z712KuUf/zo2ejDyNjiSyv79inXh/FO9Icm+gLn8H
10
+ L81OaFHsCQVMuUg4WlKaDWtWflpWQKdqAXk1Lhp2CajWSCHl1tJ4hRy3atqBeeI+
11
+ GqWd994RNUt5Jp0uK+EFXlNwnnMXRU8vdntOHpnDBIJ6VEEwa1T3W0PDQzAgFEqX
12
+ ko2uSIOGERF0IbWChZFLJALKij9VAVtz/hEpASeochAkGgMdsXWHvmPobtGs17Ww
13
+ JZthw2Sw+PqkGIqvDrprX2sujzHXR/m+4C+/r7ugAQKBgQDpN1Baq6JgRm6JB/FE
14
+ C7nikLz1hSWdhqMrs0qagWTv+nifRB8DjqReoagMZFDdRUtegikcYSYV9uoOSN2G
15
+ DbEvwqHfHVv46VnO35Aq/WkvBgOBMfOYqeFeZdBwC76oBpvQEpLdZu0n7sB0Xakh
16
+ q5667RC2VQ7XoNUbzxhxv04gAQKBgQC8oBlDAeY4LDk++W5LFrBfQY6hPvKYT4rK
17
+ ApF1smrpBik0xuD5IHLkJQSZLuWMxDSG02xPXLf81KY44+PzZ3bt6Kkt78zGvOWB
18
+ NjEHnuXY/v29Bk+YXIZXjJRlkcdusvCoWZf1mz3dNetEAwyifpaegC9sLJswDm/7
19
+ iUZ4dA3+owKBgGB1MlTuHDC5sMoYcN92AiIFP0JFGbO3lhFjDH4u+nKPJAirgjBU
20
+ dQN21Sya6R87o9qo9xv3ymrXPyM9W/IwA5L/azf+yqV+zlhT+yrsuO5sDFMJN/ly
21
+ efYzIizVjrusUBGVPQ05nukfRJZcjrGQbAxgNMNVFth0evAPjcquF0ABAoGBAI5h
22
+ FtBylZGfKBL1+p/pX3Z3qK7kMgBwQiYb7Cp4Y6TDhsUUvequ6Kp3mtH6CUHJNJD0
23
+ 9BOvvB23ckUNGBq55ZbNgS5wjRUSrvZqrnW/JmY2i9dr9Rbf+HQLZFSKxowzu96H
24
+ ymGVLgxVT+IFnzrk7NJ1ldZEkVuj+jlXNH9lss6bAoGBAMNISITTIr/n/S5i6ap9
25
+ 5GU1mdVMc8d7jBF5ckWm+xE+U3AF7lJDA2bZ/ml9HANFj0J2zKUxjhruXpCbGaGd
26
+ DpvoMMc+ualpkszIpIC19Av9oX0SKnFaHa5nUmAG/A6nua51DFAqNvvepoISdmyy
27
+ WeSifK0ywhJQWXUJMjngCJrI
28
+ -----END PRIVATE KEY-----
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "dev-_007haste-server",
3
+ "version": "1.0.0",
4
+ "description": "Simple hastebin-like server with HTTPS",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "start": "node server.js"
8
+ },
9
+ "dependencies": {
10
+ "express": "^5.1.0"
11
+ },
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "author": "aa.arsenenko",
16
+ "license": "ISC"
17
+ }
18
+
@@ -0,0 +1,84 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Paste</title>
5
+ <style>
6
+ html, body {
7
+ margin: 0; padding: 0; height: 100%;
8
+ background: #1d1f21; color: #c5c8c6;
9
+ font-family: monospace;
10
+ }
11
+ textarea {
12
+ width: 100%; height: 100%;
13
+ border: none; resize: none; outline: none;
14
+ background: #1d1f21; color: #c5c8c6;
15
+ font-size: 16px; padding: 1em;
16
+ box-sizing: border-box;
17
+ }
18
+ #controls {
19
+ position: fixed; top: 10px; right: 10px;
20
+ z-index: 10;
21
+ display: flex; gap: 8px;
22
+ }
23
+ #password, #ttl {
24
+ padding: 5px; font-size: 14px;
25
+ background: #373b41; border: none;
26
+ color: #c5c8c6; border-radius: 3px;
27
+ }
28
+ #saveBtn {
29
+ background: #373b41; color: #fff;
30
+ border: none; padding: 8px 16px;
31
+ cursor: pointer; font-size: 14px;
32
+ border-radius: 3px;
33
+ }
34
+ #saveBtn:hover { background: #4e5257; }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <div id="controls">
39
+ <input type="password" id="password" placeholder="(optional) password" />
40
+ <select id="ttl" title="Select paste expiration">
41
+ <option value="none" selected>No expiration</option>
42
+ <option value="1m">1 minute</option>
43
+ <option value="10m">10 minutes</option>
44
+ <option value="1h">1 hour</option>
45
+ <option value="1d">1 day</option>
46
+ <option value="7d">7 days</option>
47
+ </select>
48
+
49
+ <button id="saveBtn" onclick="save()">Save</button>
50
+ </div>
51
+
52
+ <textarea id="text" placeholder="Paste your code or text here..."></textarea>
53
+
54
+ <script>
55
+ const textarea = document.getElementById('text');
56
+
57
+ window.addEventListener('keydown', function (e) {
58
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
59
+ e.preventDefault();
60
+ save();
61
+ }
62
+ });
63
+
64
+ async function save() {
65
+ const text = textarea.value;
66
+ const password = document.getElementById('password').value;
67
+ const ttl = document.getElementById('ttl').value;
68
+
69
+ const res = await fetch('/save', {
70
+ method: 'POST',
71
+ headers: {'Content-Type': 'application/json'},
72
+ body: JSON.stringify({ text, password, ttl })
73
+ });
74
+
75
+ if (res.ok) {
76
+ const data = await res.json();
77
+ window.location.href = data.url;
78
+ } else {
79
+ alert('Save failed');
80
+ }
81
+ }
82
+ </script>
83
+ </body>
84
+ </html>
@@ -0,0 +1,52 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>View Paste</title>
5
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
6
+ <style>
7
+ html, body {
8
+ margin: 0;
9
+ padding: 0;
10
+ background: #1d1f21;
11
+ color: #c5c8c6;
12
+ font-family: monospace;
13
+ height: 100%;
14
+ }
15
+ pre {
16
+ margin: 0;
17
+ padding: 1em;
18
+ white-space: pre-wrap;
19
+ word-wrap: break-word;
20
+ font-size: 16px;
21
+ overflow: auto;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <pre><code id="output" class="plaintext">Loading...</code></pre>
27
+
28
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
29
+ <script>
30
+ const params = new URLSearchParams(location.search);
31
+ const id = params.get('id');
32
+
33
+ function fetchPaste(password = '') {
34
+ fetch(`/data/${id}?password=${encodeURIComponent(password)}`)
35
+ .then(res => res.json())
36
+ .then(data => {
37
+ if (data.error) throw new Error(data.error);
38
+ const el = document.getElementById('output');
39
+ el.textContent = data.text;
40
+ hljs.highlightElement(el);
41
+ })
42
+ .catch(err => {
43
+ const pass = prompt("Enter password for this paste:");
44
+ if (pass !== null) fetchPaste(pass);
45
+ else document.getElementById('output').textContent = 'Access denied.';
46
+ });
47
+ }
48
+
49
+ fetchPaste();
50
+ </script>
51
+ </body>
52
+ </html>
package/server.js ADDED
@@ -0,0 +1,119 @@
1
+ const express = require('express');
2
+ const https = require('https');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const crypto = require('crypto');
6
+
7
+ const app = express();
8
+ const PORT = 3000;
9
+ const DATA_DIR = path.join(__dirname, 'data');
10
+
11
+ if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR);
12
+
13
+ app.use(express.json());
14
+ app.use(express.static(path.join(__dirname, 'public')));
15
+
16
+ // Загружаем SSL сертификат и ключ
17
+ const SSL_KEY_PATH = path.join(__dirname, 'cert', 'key.pem');
18
+ const SSL_CERT_PATH = path.join(__dirname, 'cert', 'cert.pem');
19
+
20
+ const sslOptions = {
21
+ key: fs.readFileSync(SSL_KEY_PATH),
22
+ cert: fs.readFileSync(SSL_CERT_PATH)
23
+ };
24
+
25
+ // Удаляем просроченные пасты
26
+ function cleanupExpired() {
27
+ const files = fs.readdirSync(DATA_DIR);
28
+ const now = Date.now();
29
+
30
+ for (const file of files) {
31
+ if (!file.endsWith('.json')) continue;
32
+
33
+ const filePath = path.join(DATA_DIR, file);
34
+ let data;
35
+
36
+ try {
37
+ data = JSON.parse(fs.readFileSync(filePath));
38
+ } catch (err) {
39
+ console.error(`Ошибка чтения файла ${file}:`, err);
40
+ continue;
41
+ }
42
+
43
+ if (data.expireAt && now > data.expireAt) {
44
+ try {
45
+ fs.unlinkSync(filePath);
46
+ console.log(`[CLEANUP] Удалена просроченная паста: ${file}`);
47
+ } catch (err) {
48
+ console.error(`Не удалось удалить файл ${file}:`, err);
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ // Запуск очистки каждую минуту
55
+ setInterval(cleanupExpired, 60 * 1000);
56
+ cleanupExpired();
57
+
58
+ app.post('/save', (req, res) => {
59
+ const { text, password, ttl } = req.body;
60
+ if (!text) return res.status(400).send('No text provided');
61
+
62
+ const id = crypto.randomBytes(3).toString('hex');
63
+ const filePath = path.join(DATA_DIR, `${id}.json`);
64
+
65
+ let expireAt = null;
66
+ if (ttl && ttl !== 'none') {
67
+ const num = parseInt(ttl.slice(0, -1));
68
+ const unit = ttl.slice(-1);
69
+ const multipliers = { m: 60 * 1000, h: 60 * 60 * 1000, d: 24 * 60 * 60 * 1000 };
70
+
71
+ if (!multipliers[unit] || isNaN(num)) {
72
+ return res.status(400).send('Invalid TTL format');
73
+ }
74
+
75
+ expireAt = Date.now() + multipliers[unit] * num;
76
+ }
77
+
78
+ const data = {
79
+ text,
80
+ password: password || null,
81
+ expireAt
82
+ };
83
+
84
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
85
+
86
+ const url = `/view.html?id=${id}`;
87
+ console.log(`[NEW] https://192.168.88.234:${PORT}${url}`);
88
+ res.json({ url });
89
+ });
90
+
91
+ app.get('/data/:id', (req, res) => {
92
+ const filePath = path.join(DATA_DIR, `${req.params.id}.json`);
93
+ if (!fs.existsSync(filePath)) return res.status(404).send('Not found');
94
+
95
+ let data;
96
+ try {
97
+ data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
98
+ } catch (err) {
99
+ return res.status(500).send('Error reading paste');
100
+ }
101
+
102
+ const now = Date.now();
103
+
104
+ if (data.expireAt && now > data.expireAt) {
105
+ fs.unlinkSync(filePath);
106
+ return res.status(404).send('Paste expired');
107
+ }
108
+
109
+ const given = req.query.password || '';
110
+ if (data.password && data.password !== given) {
111
+ return res.status(403).json({ error: 'Incorrect password' });
112
+ }
113
+
114
+ res.json({ text: data.text });
115
+ });
116
+
117
+ https.createServer(sslOptions, app).listen(PORT, () => {
118
+ console.log(`HTTPS Server running at https://192.168.88.234:${PORT}`);
119
+ });