krsyer-server-monitor-pro 1.0.8 → 1.0.10

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.
@@ -12,24 +12,63 @@ app.use(express.urlencoded({ extended: true }));
12
12
  app.use(cors());
13
13
 
14
14
  // --- Database (Simple JSON File Persistence) ---
15
- const DB_FILE = path.join(__dirname, 'licenses.json');
15
+ const mysql = require('mysql2/promise');
16
+
17
+ // --- Database (MySQL) ---
18
+ const dbConfig = {
19
+ host: process.env.DB_HOST || 'localhost',
20
+ user: process.env.DB_USER || 'root',
21
+ password: process.env.DB_PASSWORD || '',
22
+ waitForConnections: true,
23
+ connectionLimit: 10,
24
+ queueLimit: 0
25
+ };
26
+
27
+ const DB_NAME = process.env.DB_NAME || 'license_portal';
28
+
29
+ let pool;
16
30
 
17
- // Helper to load licenses
18
- const getLicenses = () => {
31
+ // Init DB
32
+ (async () => {
19
33
  try {
20
- if (fs.existsSync(DB_FILE)) {
21
- return JSON.parse(fs.readFileSync(DB_FILE, 'utf8'));
22
- }
23
- } catch (e) { console.error("DB Read Error", e); }
24
- return {};
25
- };
34
+ // 1. Create DB if not exists
35
+ const output = await mysql.createConnection({
36
+ host: dbConfig.host,
37
+ user: dbConfig.user,
38
+ password: dbConfig.password
39
+ });
40
+ await output.query(`CREATE DATABASE IF NOT EXISTS ${DB_NAME}`);
41
+ await output.end();
42
+
43
+ // 2. Init Pool
44
+ pool = mysql.createPool({
45
+ ...dbConfig,
46
+ database: DB_NAME
47
+ });
48
+
49
+ // 3. Create Table
50
+ const conn = await pool.getConnection();
51
+ await conn.query(`
52
+ CREATE TABLE IF NOT EXISTS licenses (
53
+ id INT AUTO_INCREMENT PRIMARY KEY,
54
+ license_key VARCHAR(255) NOT NULL UNIQUE,
55
+ order_id VARCHAR(255),
56
+ customer_email VARCHAR(255),
57
+ customer_phone VARCHAR(50),
58
+ plan VARCHAR(50) DEFAULT 'Pro',
59
+ active BOOLEAN DEFAULT TRUE,
60
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
61
+ expiry_date DATETIME,
62
+ INDEX idx_key (license_key)
63
+ )
64
+ `);
65
+ console.log('MySQL Database initialized');
66
+ conn.release();
67
+ } catch (err) {
68
+ console.error('MySQL Init Error:', err);
69
+ }
70
+ })();
26
71
 
27
- // Helper to save licenses
28
- const saveLicense = (key, data) => {
29
- const licenses = getLicenses();
30
- licenses[key] = data;
31
- fs.writeFileSync(DB_FILE, JSON.stringify(licenses, null, 2));
32
- };
33
72
 
34
73
  // --- Cashfree Init Helper ---
35
74
  const getCashfree = () => {
@@ -37,8 +76,6 @@ const getCashfree = () => {
37
76
  cf.XClientId = process.env.CASHFREE_APP_ID;
38
77
  cf.XClientSecret = process.env.CASHFREE_SECRET_KEY;
39
78
  cf.XEnvironment = CFEnvironment.PRODUCTION;
40
- // VERY IMPORTANT: Set version config if needed, but instance method PGCreateOrder logic
41
- // internally uses this.XApiVersion if available.
42
79
  return cf;
43
80
  };
44
81
 
@@ -55,6 +92,9 @@ app.post('/api/buy', async (req, res) => {
55
92
  const { email, phone } = req.body;
56
93
  const orderId = `order_${Date.now()}`;
57
94
 
95
+ // Store temp order info? Not strictly needed if we trust Cashfree return
96
+ // But helpful for reconciliation. For now, rely on Return URL params.
97
+
58
98
  const request = {
59
99
  order_amount: 1.00,
60
100
  order_currency: "INR",
@@ -70,13 +110,10 @@ app.post('/api/buy', async (req, res) => {
70
110
  };
71
111
 
72
112
  const cf = getCashfree();
73
- // FIXED: For SDK v5+, remove the API Version string argument.
74
- // It should be: await cf.PGCreateOrder(request)
75
113
  const response = await cf.PGCreateOrder(request);
76
114
  res.json(response.data);
77
115
 
78
116
  } catch (error) {
79
- // Detailed error logging
80
117
  console.error("Cashfree Order Create Error:", error.response?.data || error.message);
81
118
  res.status(500).json({ error: "Failed to create order" });
82
119
  }
@@ -87,13 +124,17 @@ app.get('/verify', async (req, res) => {
87
124
  const { order_id } = req.query;
88
125
  try {
89
126
  const cf = getCashfree();
90
- // FIXED: Remove version string argument here too
91
127
  const response = await cf.PGOrderFetchPayments(order_id);
92
128
  const payments = response.data;
93
-
94
129
  const isPaid = payments.some(p => p.payment_status === 'SUCCESS');
95
130
 
96
131
  if (isPaid) {
132
+ // Get Customer Details from Order Fetch?
133
+ // To simplify, we get email from stored order or just query Cashfree Order details again
134
+ const orderDetails = await cf.PGOrderFetch(order_id);
135
+ const email = orderDetails.data.customer_details.customer_email;
136
+ const phone = orderDetails.data.customer_details.customer_phone;
137
+
97
138
  // Generate License
98
139
  const licenseKey = 'PRO-' + Math.random().toString(36).substring(2, 10).toUpperCase() + '-' + Date.now().toString(36).toUpperCase();
99
140
 
@@ -101,13 +142,11 @@ app.get('/verify', async (req, res) => {
101
142
  const expiryDate = new Date();
102
143
  expiryDate.setFullYear(expiryDate.getFullYear() + 1);
103
144
 
104
- // Save to JSON DB
105
- saveLicense(licenseKey, {
106
- active: true,
107
- orderId: order_id,
108
- created: new Date().toISOString(),
109
- expiry: expiryDate.toISOString()
110
- });
145
+ // Save to MySQL
146
+ await pool.query(
147
+ 'INSERT INTO licenses (license_key, order_id, customer_email, customer_phone, expiry_date, active) VALUES (?, ?, ?, ?, ?, ?)',
148
+ [licenseKey, order_id, email, phone, expiryDate, true]
149
+ );
111
150
 
112
151
  // Show Success Page
113
152
  res.send(`
@@ -141,34 +180,41 @@ app.get('/verify', async (req, res) => {
141
180
  });
142
181
 
143
182
  // 4. API Validation Endpoint
144
- app.get('/api/validate', (req, res) => {
183
+ app.get('/api/validate', async (req, res) => {
145
184
  let { key } = req.query;
146
185
  if (!key) return res.json({ valid: false });
147
186
 
148
187
  key = key.trim();
149
188
 
150
- const licenses = getLicenses();
151
- const license = licenses[key];
152
-
153
189
  // Special Demo Key
154
190
  if (key === 'DEMO-123') {
155
191
  return res.json({ valid: true, plan: 'Pro (Demo)' });
156
192
  }
157
193
 
158
- if (license && license.active) {
159
- // Check Expiry
160
- const now = new Date();
161
- const expiry = new Date(license.expiry);
194
+ try {
195
+ const [rows] = await pool.query('SELECT * FROM licenses WHERE license_key = ?', [key]);
196
+ const license = rows[0];
162
197
 
163
- if (expiry && now > expiry) {
164
- return res.json({ valid: false, message: 'Subscription Expired' });
165
- }
198
+ if (license && license.active) {
199
+ // Check Expiry
200
+ const now = new Date();
201
+ const expiry = new Date(license.expiry_date);
166
202
 
167
- return res.json({ valid: true, plan: 'Pro (Yearly)', expiry: license.expiry });
203
+ if (expiry && now > expiry) {
204
+ return res.json({ valid: false, message: 'Subscription Expired' });
205
+ }
206
+
207
+ return res.json({
208
+ valid: true,
209
+ plan: license.plan,
210
+ expiry: license.expiry_date
211
+ });
212
+ }
213
+ } catch (e) {
214
+ console.error("Validation Error", e);
168
215
  }
169
216
 
170
- // Invalid Key
171
- res.json({ valid: false, message: 'Invalid Key' });
217
+ return res.json({ valid: false, message: 'Invalid Key' });
172
218
  });
173
219
 
174
220
  const PORT = process.env.PORT || 3011;
@@ -4,109 +4,262 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Buy Server Monitor Pro</title>
7
+ <title>Server Monitor Pro - Professional Server Management</title>
8
8
  <script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap"
10
+ rel="stylesheet">
9
11
  <style>
12
+ :root {
13
+ --primary: #8b5cf6;
14
+ --primary-hover: #7c3aed;
15
+ --secondary: #06b6d4;
16
+ --bg-dark: #0f172a;
17
+ --card-bg: #1e293b;
18
+ --text-main: #f8fafc;
19
+ --text-muted: #94a3b8;
20
+ --accent: #f43f5e;
21
+ --success: #10b981;
22
+ }
23
+
10
24
  body {
11
- font-family: 'Inter', sans-serif;
12
- background: #0f172a;
13
- color: white;
14
- display: flex;
15
- justify-content: center;
16
- align-items: center;
17
- height: 100vh;
25
+ font-family: 'Space Grotesk', sans-serif;
26
+ background-color: var(--bg-dark);
27
+ color: var(--text-main);
18
28
  margin: 0;
29
+ padding: 0;
30
+ line-height: 1.6;
19
31
  }
20
32
 
21
- .card {
22
- background: #1e293b;
23
- padding: 2rem;
24
- border-radius: 16px;
25
- border: 1px solid #334155;
26
- width: 400px;
33
+ .container {
34
+ max-width: 1200px;
35
+ margin: 0 auto;
36
+ padding: 0 20px;
37
+ }
38
+
39
+ /* Hero Section */
40
+ .hero {
41
+ padding: 80px 0 40px;
27
42
  text-align: center;
28
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
43
+ background: radial-gradient(circle at center, rgba(139, 92, 246, 0.15) 0%, transparent 70%);
29
44
  }
30
45
 
31
46
  h1 {
32
- font-size: 1.5rem;
33
- margin-bottom: 0.5rem;
47
+ font-size: 3.5rem;
48
+ margin-bottom: 1rem;
49
+ background: linear-gradient(135deg, #fff 0%, #cbd5e1 100%);
50
+ -webkit-background-clip: text;
51
+ -webkit-text-fill-color: transparent;
52
+ font-weight: 700;
34
53
  }
35
54
 
36
- p {
37
- color: #94a3b8;
55
+ .tagline {
56
+ font-size: 1.25rem;
57
+ color: var(--secondary);
38
58
  margin-bottom: 2rem;
59
+ font-weight: 500;
60
+ }
61
+
62
+ /* Installation Section */
63
+ .install-section {
64
+ background: var(--card-bg);
65
+ border-radius: 16px;
66
+ padding: 40px;
67
+ margin: 40px auto;
68
+ max-width: 800px;
69
+ border: 1px solid rgba(255, 255, 255, 0.05);
70
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
71
+ }
72
+
73
+ .section-title {
74
+ text-align: center;
75
+ font-size: 1.8rem;
76
+ margin-bottom: 30px;
77
+ color: #fff;
78
+ }
79
+
80
+ .code-block {
81
+ background: #000;
82
+ padding: 20px;
83
+ border-radius: 8px;
84
+ font-family: 'Courier New', monospace;
85
+ color: #4ade80;
86
+ margin-bottom: 15px;
87
+ position: relative;
88
+ border-left: 4px solid var(--primary);
89
+ overflow-x: auto;
90
+ }
91
+
92
+ .code-comment {
93
+ color: #64748b;
94
+ display: block;
95
+ margin-bottom: 5px;
96
+ }
97
+
98
+ /* Checkout Card */
99
+ .card {
100
+ background: white;
101
+ padding: 2.5rem;
102
+ border-radius: 24px;
103
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
104
+ text-align: center;
105
+ width: 100%;
106
+ max-width: 420px;
107
+ margin: 0 auto 60px;
108
+ border: 1px solid rgba(255, 255, 255, 0.1);
109
+ position: relative;
110
+ background: linear-gradient(145deg, #1e293b, #0f172a);
111
+ }
112
+
113
+ .price-tag {
114
+ font-size: 3rem;
115
+ font-weight: 700;
116
+ color: var(--text-main);
117
+ margin: 1.5rem 0;
118
+ }
119
+
120
+ .price-sub {
121
+ font-size: 1rem;
122
+ color: var(--text-muted);
123
+ font-weight: 400;
39
124
  }
40
125
 
41
126
  input {
42
127
  width: 100%;
43
- padding: 12px;
44
- margin-bottom: 1rem;
45
- background: #0f172a;
128
+ padding: 14px;
129
+ margin: 10px 0;
46
130
  border: 1px solid #334155;
47
- color: white;
48
- border-radius: 8px;
131
+ border-radius: 12px;
49
132
  box-sizing: border-box;
133
+ background: #0f172a;
134
+ color: white;
135
+ font-family: inherit;
136
+ transition: all 0.3s;
137
+ }
138
+
139
+ input:focus {
140
+ outline: none;
141
+ border-color: var(--primary);
142
+ box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2);
50
143
  }
51
144
 
52
145
  button {
53
146
  width: 100%;
54
- padding: 12px;
55
- background: #3b82f6;
147
+ padding: 16px;
148
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
56
149
  color: white;
57
150
  border: none;
58
- border-radius: 8px;
59
- font-weight: bold;
151
+ border-radius: 12px;
152
+ font-size: 1.1rem;
153
+ font-weight: 600;
60
154
  cursor: pointer;
61
- transition: 0.2s;
155
+ transition: transform 0.1s, box-shadow 0.3s;
156
+ margin-top: 10px;
62
157
  }
63
158
 
64
159
  button:hover {
65
- background: #2563eb;
160
+ transform: translateY(-2px);
161
+ box-shadow: 0 10px 20px rgba(139, 92, 246, 0.3);
66
162
  }
67
163
 
68
- .price {
69
- font-size: 3rem;
70
- font-weight: 800;
71
- margin: 1rem 0;
72
- color: #fbbf24;
164
+ .secure-badge {
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ gap: 8px;
169
+ margin-top: 20px;
170
+ color: var(--text-muted);
171
+ font-size: 0.9rem;
73
172
  }
74
173
 
75
- .sub {
76
- font-size: 1rem;
77
- color: #94a3b8;
78
- font-weight: normal;
174
+ /* Responsive */
175
+ @media (max-width: 768px) {
176
+ h1 {
177
+ font-size: 2.5rem;
178
+ }
179
+
180
+ .install-section {
181
+ padding: 20px;
182
+ }
79
183
  }
80
184
  </style>
81
185
  </head>
82
186
 
83
187
  <body>
84
188
 
85
- <div class="card">
86
- <h1>Get Server Monitor Pro</h1>
87
- <p>Annual License for unlimited servers</p>
189
+ <div class="hero">
190
+ <div class="container">
191
+ <h1>Server Monitor Pro</h1>
192
+ <p class="tagline">The ultimate realtime monitoring solution for your infrastructure.</p>
193
+ </div>
194
+ </div>
195
+
196
+ <div class="container">
197
+ <!-- Installation Guide -->
198
+ <div class="install-section">
199
+ <h2 class="section-title">🚀 Installation Guide</h2>
200
+
201
+ <div class="code-block">
202
+ <span class="code-comment"># 1. Install the package globally</span>
203
+ sudo npm install -g krsyer-server-monitor-pro@latest
204
+ </div>
205
+
206
+ <div class="code-block">
207
+ <span class="code-comment"># 2. Setup the system service</span>
208
+ sudo npx server-monitor setup
209
+ </div>
88
210
 
89
- <div class="price">₹1 <span class="sub">/ year</span></div>
211
+ <div class="code-block">
212
+ <span class="code-comment"># 3. Start the monitor</span>
213
+ sudo systemctl restart krsyer-monitor
214
+ </div>
90
215
 
91
- <input type="email" id="email" placeholder="Email Address" required>
92
- <input type="tel" id="phone" placeholder="Phone Number" required>
216
+ <p style="text-align: center; color: var(--text-muted); margin-top: 20px;">
217
+ Once installed, visit your server IP on port 3000 to activate.
218
+ </p>
219
+ </div>
93
220
 
94
- <button onclick="buy()" id="btn">Buy Now</button>
221
+ <!-- Checkout / License Section -->
222
+ <div class="card">
223
+ <h2 style="margin-top: 0; margin-bottom: 10px;">Get Your License</h2>
224
+ <p style="color: var(--text-muted); margin-bottom: 20px;">Unlimited servers, 1 year updates</p>
225
+
226
+ <div class="price-tag">₹1 <span class="price-sub">/ year</span></div>
227
+
228
+ <input type="email" id="email" placeholder="Email Address" required>
229
+ <input type="tel" id="phone" placeholder="Phone Number" required>
230
+
231
+ <button id="buyBtn">Buy License Now</button>
232
+
233
+ <div class="secure-badge">
234
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
235
+ <path
236
+ d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z" />
237
+ </svg>
238
+ Secured by Cashfree Payments
239
+ </div>
240
+ </div>
95
241
  </div>
96
242
 
97
243
  <script>
98
- const cashfree = Cashfree({ mode: "production" });
244
+ const cashfree = Cashfree({
245
+ mode: "production"
246
+ });
99
247
 
100
- async function buy() {
248
+ document.getElementById('buyBtn').addEventListener('click', async () => {
101
249
  const email = document.getElementById('email').value;
102
250
  const phone = document.getElementById('phone').value;
103
- if (!email || !phone) return alert("Please fill details");
251
+ const btn = document.getElementById('buyBtn');
104
252
 
105
- const btn = document.getElementById('btn');
106
- btn.innerText = "Processing...";
253
+ if (!email || !phone) {
254
+ alert('Please fill in all details');
255
+ return;
256
+ }
257
+
258
+ btn.innerText = 'Processing...';
107
259
  btn.disabled = true;
108
260
 
109
261
  try {
262
+ // Create Order
110
263
  const res = await fetch('/api/buy', {
111
264
  method: 'POST',
112
265
  headers: { 'Content-Type': 'application/json' },
@@ -115,24 +268,25 @@
115
268
  const data = await res.json();
116
269
 
117
270
  if (data.payment_session_id) {
271
+ // Redirect to Payment
118
272
  cashfree.checkout({
119
273
  paymentSessionId: data.payment_session_id,
120
274
  redirectTarget: "_self"
275
+ // returnUrl: `${window.location.origin}/verify?order_id=${data.order_id}` // Handled by Cashfree self redirect if desired, but better to use returnUrl
121
276
  });
122
277
  } else {
123
- alert("Failed to init payment: " + (data.error || "Unknown"));
278
+ alert('Error creating order');
279
+ btn.innerText = 'Buy License Now';
124
280
  btn.disabled = false;
125
- btn.innerText = "Buy Now";
126
281
  }
127
282
  } catch (e) {
128
283
  console.error(e);
129
- alert("Error initiating payment");
284
+ alert('Connection Error');
285
+ btn.innerText = 'Buy License Now';
130
286
  btn.disabled = false;
131
- btn.innerText = "Buy Now";
132
287
  }
133
- }
288
+ });
134
289
  </script>
135
-
136
290
  </body>
137
291
 
138
292
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "krsyer-server-monitor-pro",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "API to get server details like IP address, health, applications running, etc.",
5
5
  "main": "lib/server.js",
6
6
  "bin": {