98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import { Router } from "express";
|
|
import crypto from "crypto";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
|
|
const router = Router();
|
|
|
|
const LICENSE_FILE = path.join(process.cwd(), "license.json");
|
|
const SECRET = process.env.LICENSE_SECRET || "";
|
|
|
|
function validateKey(key: string): { valid: boolean; expiry?: string; error?: string } {
|
|
// Format: DENTAL-{24-char-signature}-{YYYY-MM-DD}
|
|
const parts = key.trim().split("-");
|
|
if (parts.length !== 5 || parts[0] !== "DENTAL") {
|
|
return { valid: false, error: "Invalid license key format" };
|
|
}
|
|
|
|
const signature = parts[1];
|
|
const expiryStr = `${parts[2]}-${parts[3]}-${parts[4]}`;
|
|
|
|
// Verify signature
|
|
const payload = `DENTAL:${expiryStr}`;
|
|
const expectedSig = crypto
|
|
.createHmac("sha256", SECRET)
|
|
.update(payload)
|
|
.digest("hex")
|
|
.substring(0, 24)
|
|
.toUpperCase();
|
|
|
|
if (signature !== expectedSig) {
|
|
return { valid: false, error: "Invalid license key" };
|
|
}
|
|
|
|
// Check expiry
|
|
const expiry = new Date(expiryStr);
|
|
if (isNaN(expiry.getTime())) {
|
|
return { valid: false, error: "Invalid expiry date in key" };
|
|
}
|
|
|
|
if (new Date() > expiry) {
|
|
return { valid: false, expiry: expiryStr, error: "License key has expired" };
|
|
}
|
|
|
|
return { valid: true, expiry: expiryStr };
|
|
}
|
|
|
|
function loadLicense(): { key: string; expiry: string } | null {
|
|
try {
|
|
if (!fs.existsSync(LICENSE_FILE)) return null;
|
|
return JSON.parse(fs.readFileSync(LICENSE_FILE, "utf8"));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function saveLicense(key: string, expiry: string) {
|
|
fs.writeFileSync(LICENSE_FILE, JSON.stringify({ key, expiry }, null, 2));
|
|
}
|
|
|
|
// GET /api/license/status
|
|
router.get("/status", (req, res) => {
|
|
const stored = loadLicense();
|
|
if (!stored) {
|
|
return res.json({ activated: false, expired: false, expiry: null, daysLeft: null });
|
|
}
|
|
|
|
const result = validateKey(stored.key);
|
|
if (!result.valid && result.expiry) {
|
|
return res.json({ activated: true, expired: true, expiry: stored.expiry, daysLeft: 0 });
|
|
}
|
|
if (!result.valid) {
|
|
return res.json({ activated: false, expired: false, expiry: null, daysLeft: null });
|
|
}
|
|
|
|
const expiry = new Date(stored.expiry);
|
|
const daysLeft = Math.ceil((expiry.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
|
|
return res.json({ activated: true, expired: false, expiry: stored.expiry, daysLeft });
|
|
});
|
|
|
|
// POST /api/license/activate
|
|
router.post("/activate", (req, res) => {
|
|
const { key } = req.body;
|
|
if (!key) {
|
|
return res.status(400).json({ error: "License key is required" });
|
|
}
|
|
|
|
const result = validateKey(key);
|
|
if (!result.valid) {
|
|
return res.status(400).json({ error: result.error });
|
|
}
|
|
|
|
saveLicense(key.trim(), result.expiry!);
|
|
return res.json({ success: true, expiry: result.expiry });
|
|
});
|
|
|
|
export default router;
|