@explita/tunnel-server-plugin 0.0.0-stage → 0.1.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/CHANGELOG.md +44 -0
- package/LICENSE +21 -0
- package/README.md +63 -2
- package/bin/cli.js +100 -0
- package/dist/generated/prisma/browser.d.ts +25 -0
- package/dist/generated/prisma/browser.d.ts.map +1 -0
- package/dist/generated/prisma/browser.js +18 -0
- package/dist/generated/prisma/browser.js.map +1 -0
- package/dist/generated/prisma/client.d.ts +44 -0
- package/dist/generated/prisma/client.d.ts.map +1 -0
- package/dist/generated/prisma/client.js +35 -0
- package/dist/generated/prisma/client.js.map +1 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +278 -0
- package/dist/generated/prisma/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/prisma/commonInputTypes.js +11 -0
- package/dist/generated/prisma/commonInputTypes.js.map +1 -0
- package/dist/generated/prisma/enums.d.ts +2 -0
- package/dist/generated/prisma/enums.d.ts.map +1 -0
- package/dist/generated/prisma/enums.js +11 -0
- package/dist/generated/prisma/enums.js.map +1 -0
- package/dist/generated/prisma/internal/class.d.ts +166 -0
- package/dist/generated/prisma/internal/class.d.ts.map +1 -0
- package/dist/generated/prisma/internal/class.js +50 -0
- package/dist/generated/prisma/internal/class.js.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +867 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +128 -0
- package/dist/generated/prisma/internal/prismaNamespace.js.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +92 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +99 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/generated/prisma/models/DailyMetric.d.ts +1152 -0
- package/dist/generated/prisma/models/DailyMetric.d.ts.map +1 -0
- package/dist/generated/prisma/models/DailyMetric.js +2 -0
- package/dist/generated/prisma/models/DailyMetric.js.map +1 -0
- package/dist/generated/prisma/models/Developer.d.ts +1134 -0
- package/dist/generated/prisma/models/Developer.d.ts.map +1 -0
- package/dist/generated/prisma/models/Developer.js +2 -0
- package/dist/generated/prisma/models/Developer.js.map +1 -0
- package/dist/generated/prisma/models/SystemMetric.d.ts +1048 -0
- package/dist/generated/prisma/models/SystemMetric.d.ts.map +1 -0
- package/dist/generated/prisma/models/SystemMetric.js +2 -0
- package/dist/generated/prisma/models/SystemMetric.js.map +1 -0
- package/dist/generated/prisma/models/Token.d.ts +1214 -0
- package/dist/generated/prisma/models/Token.d.ts.map +1 -0
- package/dist/generated/prisma/models/Token.js +2 -0
- package/dist/generated/prisma/models/Token.js.map +1 -0
- package/dist/generated/prisma/models.d.ts +6 -0
- package/dist/generated/prisma/models.d.ts.map +1 -0
- package/dist/generated/prisma/models.js +2 -0
- package/dist/generated/prisma/models.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/issue.d.ts +3 -0
- package/dist/issue.d.ts.map +1 -0
- package/dist/issue.js +102 -0
- package/dist/issue.js.map +1 -0
- package/dist/lib/cache.d.ts +2 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +6 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/mailer.d.ts +28 -0
- package/dist/lib/mailer.d.ts.map +1 -0
- package/dist/lib/mailer.js +116 -0
- package/dist/lib/mailer.js.map +1 -0
- package/dist/lib/metrics.d.ts +27 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +93 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/prisma.d.ts +3 -0
- package/dist/lib/prisma.d.ts.map +1 -0
- package/dist/lib/prisma.js +20 -0
- package/dist/lib/prisma.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/request.d.ts +4 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +29 -0
- package/dist/request.js.map +1 -0
- package/dist/validate.d.ts +2 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +48 -0
- package/dist/validate.js.map +1 -0
- package/dist/verify.d.ts +3 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +53 -0
- package/dist/verify.js.map +1 -0
- package/package.json +52 -4
- package/prisma/schema.prisma +62 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
2
|
+
import { PrismaClient } from "../generated/prisma/client.js";
|
|
3
|
+
let prismaInstance = null;
|
|
4
|
+
export function getPrisma() {
|
|
5
|
+
if (prismaInstance)
|
|
6
|
+
return prismaInstance;
|
|
7
|
+
const connectionString = process.env.DATABASE_URL;
|
|
8
|
+
if (!connectionString)
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
const adapter = new PrismaPg({ connectionString });
|
|
12
|
+
prismaInstance = new PrismaClient({ adapter });
|
|
13
|
+
return prismaInstance;
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
console.warn("[tunnel-auth] Warning: failed to initialize Prisma database client:", err);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/lib/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,IAAI,cAAc,GAAwB,IAAI,CAAC;AAE/C,MAAM,UAAU,SAAS;IACvB,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAE1C,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAClD,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACnD,cAAc,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/C,OAAO,cAAc,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,qEAAqE,EACrE,GAAG,CACJ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,aAAa;IAC3B,OAAO,YAAY,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { type SendOtpOptions } from "./lib/mailer.js";
|
|
3
|
+
export declare function requestOtp(request: FastifyRequest, reply: FastifyReply, options?: SendOtpOptions): Promise<never>;
|
|
4
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5D,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE/D,wBAAsB,UAAU,CAC9B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,cAAc,kBA8BzB"}
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseRequestBody } from "@explita/infra-protocol";
|
|
2
|
+
import { cache } from "./lib/cache.js";
|
|
3
|
+
import { sendOTP } from "./lib/mailer.js";
|
|
4
|
+
export async function requestOtp(request, reply, options) {
|
|
5
|
+
const parsed = parseRequestBody(request.body);
|
|
6
|
+
const email = parsed.email?.trim().toLowerCase();
|
|
7
|
+
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
8
|
+
return reply.code(400).send({
|
|
9
|
+
success: false,
|
|
10
|
+
error: "A valid email address is required",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
// Generate 6-digit OTP and store in cache with 10-minute TTL
|
|
14
|
+
const otp = String(Math.floor(100000 + Math.random() * 900000));
|
|
15
|
+
await cache.set(`otp:${email}`, otp, { ttl: "10m" });
|
|
16
|
+
const sent = await sendOTP(email, otp, options);
|
|
17
|
+
if (!sent) {
|
|
18
|
+
return reply.code(500).send({
|
|
19
|
+
success: false,
|
|
20
|
+
error: "Failed to send OTP",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return reply.code(200).send({
|
|
24
|
+
success: true,
|
|
25
|
+
email,
|
|
26
|
+
message: "Verification code sent to your email",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,OAAO,EAAuB,MAAM,iBAAiB,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAuB,EACvB,KAAmB,EACnB,OAAwB;IAExB,MAAM,MAAM,GAAG,gBAAgB,CAAqB,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEjD,IAAI,CAAC,KAAK,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,mCAAmC;SAC3C,CAAC,CAAC;IACL,CAAC;IAED,6DAA6D;IAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAErD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAEhD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,oBAAoB;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAC1B,OAAO,EAAE,IAAI;QACb,KAAK;QACL,OAAO,EAAE,sCAAsC;KAChD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAkDnE"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { cache } from "./lib/cache.js";
|
|
2
|
+
import { getPrisma } from "./lib/prisma.js";
|
|
3
|
+
export async function validateToken(token) {
|
|
4
|
+
if (!token || !process.env.DATABASE_URL)
|
|
5
|
+
return false;
|
|
6
|
+
// 1. Fast path: check Redis / in-memory cache
|
|
7
|
+
try {
|
|
8
|
+
const tokenExists = await cache.get(`token:verified:${token}`);
|
|
9
|
+
if (tokenExists) {
|
|
10
|
+
await cache.expire(`token:verified:${token}`, "7d").catch(() => { });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Ignore cache error and proceed to database fallback
|
|
16
|
+
}
|
|
17
|
+
// 2. Resilient fallback: check PostgreSQL in case Redis flushed or restarted
|
|
18
|
+
const prisma = getPrisma();
|
|
19
|
+
if (prisma) {
|
|
20
|
+
try {
|
|
21
|
+
const dbToken = await prisma.token.findFirst({
|
|
22
|
+
where: {
|
|
23
|
+
token,
|
|
24
|
+
revokedAt: null,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
if (dbToken) {
|
|
28
|
+
// Re-warm cache for subsequent requests
|
|
29
|
+
await cache
|
|
30
|
+
.set(`token:verified:${token}`, token, { ttl: "7d" })
|
|
31
|
+
.catch(() => { });
|
|
32
|
+
// Asynchronously update last active timestamp without blocking handshake
|
|
33
|
+
prisma.token
|
|
34
|
+
.update({
|
|
35
|
+
where: { id: dbToken.id },
|
|
36
|
+
data: { lastUsedAt: new Date() },
|
|
37
|
+
})
|
|
38
|
+
.catch(() => { });
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error("[tunnel-auth] Error validating token against database:", error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEtD,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC;QAC/D,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,KAAK,CAAC,MAAM,CAAC,kBAAkB,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACpE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;IAED,6EAA6E;IAC7E,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC3C,KAAK,EAAE;oBACL,KAAK;oBACL,SAAS,EAAE,IAAI;iBAChB;aACF,CAAC,CAAC;YAEH,IAAI,OAAO,EAAE,CAAC;gBACZ,wCAAwC;gBACxC,MAAM,KAAK;qBACR,GAAG,CAAC,kBAAkB,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;qBACpD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAEnB,yEAAyE;gBACzE,MAAM,CAAC,KAAK;qBACT,MAAM,CAAC;oBACN,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE;oBACzB,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE;iBACjC,CAAC;qBACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAEnB,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,wDAAwD,EACxD,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM5D,wBAAsB,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,kBA2D3E"}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { parseRequestBody } from "@explita/infra-protocol";
|
|
2
|
+
import { cache } from "./lib/cache.js";
|
|
3
|
+
import { getPrisma } from "./lib/prisma.js";
|
|
4
|
+
import { generateToken } from "./lib/utils.js";
|
|
5
|
+
export async function verifyOtp(request, reply) {
|
|
6
|
+
const parsed = parseRequestBody(request.body);
|
|
7
|
+
const email = parsed.email?.trim().toLowerCase();
|
|
8
|
+
const otp = String(parsed.otp || "").trim();
|
|
9
|
+
if (!email || !otp) {
|
|
10
|
+
return reply.code(400).send({
|
|
11
|
+
success: false,
|
|
12
|
+
error: "Email and OTP code are both required",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const storedOtp = await cache.get(`otp:${email}`);
|
|
16
|
+
if (!storedOtp || String(storedOtp).trim() !== otp) {
|
|
17
|
+
return reply.code(400).send({
|
|
18
|
+
success: false,
|
|
19
|
+
error: "Invalid or expired verification code",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// Clear consumed OTP
|
|
23
|
+
await cache.del(`otp:${email}`);
|
|
24
|
+
const token = generateToken();
|
|
25
|
+
const prisma = getPrisma();
|
|
26
|
+
if (prisma) {
|
|
27
|
+
try {
|
|
28
|
+
const developer = await prisma.developer.upsert({
|
|
29
|
+
where: { email },
|
|
30
|
+
create: { email },
|
|
31
|
+
update: {},
|
|
32
|
+
});
|
|
33
|
+
await prisma.token.create({
|
|
34
|
+
data: {
|
|
35
|
+
token,
|
|
36
|
+
developerId: developer.id,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error("[tunnel-auth] Failed to persist developer/token in database:", error);
|
|
42
|
+
// Continue: cache still grants access even if DB write momentarily fails
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
await cache.set(`token:verified:${token}`, token, { ttl: "7d" });
|
|
46
|
+
return reply.code(200).send({
|
|
47
|
+
success: true,
|
|
48
|
+
email,
|
|
49
|
+
token,
|
|
50
|
+
message: "Verification successful",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAuB,EAAE,KAAmB;IAC1E,MAAM,MAAM,GAAG,gBAAgB,CAC7B,OAAO,CAAC,IAAI,CACb,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE5C,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,sCAAsC;SAC9C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,GAAG,CAAS,OAAO,KAAK,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,sCAAsC;SAC9C,CAAC,CAAC;IACL,CAAC;IAED,qBAAqB;IACrB,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IAEhC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;gBAC9C,KAAK,EAAE,EAAE,KAAK,EAAE;gBAChB,MAAM,EAAE,EAAE,KAAK,EAAE;gBACjB,MAAM,EAAE,EAAE;aACX,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;gBACxB,IAAI,EAAE;oBACJ,KAAK;oBACL,WAAW,EAAE,SAAS,CAAC,EAAE;iBAC1B;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,8DAA8D,EAC9D,KAAK,CACN,CAAC;YACF,yEAAyE;QAC3E,CAAC;IACH,CAAC;IAED,MAAM,KAAK,CAAC,GAAG,CAAC,kBAAkB,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAC1B,OAAO,EAAE,IAAI;QACb,KAAK;QACL,KAAK;QACL,OAAO,EAAE,yBAAyB;KACnC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explita/tunnel-server-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Explita Tunnel all-in-one database, auth, and metrics telemetry plugin",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"tunnel-plugin": "./bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clean": "rimraf dist",
|
|
20
|
+
"db:gen": "npx prisma generate",
|
|
21
|
+
"db:push": "pnpm db:gen && npx prisma db push",
|
|
22
|
+
"build": "pnpm clean && pnpm db:gen && tsc -p tsconfig.json",
|
|
23
|
+
"prepack": "pnpm build",
|
|
24
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@explita/cache": "^0.2.0",
|
|
28
|
+
"@explita/env-loader": "^0.2.0",
|
|
29
|
+
"@prisma/adapter-pg": "^7.10.0",
|
|
30
|
+
"@prisma/client": "^7.10.0",
|
|
31
|
+
"nodemailer": "^6.10.0",
|
|
32
|
+
"prisma": "^7.10.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@explita/infra-protocol": "^0.1.0",
|
|
36
|
+
"@types/node": "^26.6.3",
|
|
37
|
+
"@types/nodemailer": "^6.4.17",
|
|
38
|
+
"fastify": "^5",
|
|
39
|
+
"rimraf": "^6.1.3",
|
|
40
|
+
"typescript": "^7"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@explita/infra-protocol": "^0.1.0",
|
|
44
|
+
"fastify": "^5"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"bin",
|
|
49
|
+
"prisma",
|
|
50
|
+
"README.md",
|
|
51
|
+
"CHANGELOG.md",
|
|
52
|
+
"LICENSE"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client"
|
|
3
|
+
output = "../src/generated/prisma"
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
datasource db {
|
|
7
|
+
provider = "postgresql"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Developer Account (identified by verified email)
|
|
11
|
+
model Developer {
|
|
12
|
+
id String @id @default(nanoid(10))
|
|
13
|
+
email String @unique
|
|
14
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
15
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
16
|
+
tokens Token[]
|
|
17
|
+
|
|
18
|
+
@@index([email])
|
|
19
|
+
@@map("developers")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Multi-device developer tokens
|
|
23
|
+
model Token {
|
|
24
|
+
id String @id @default(nanoid(10))
|
|
25
|
+
developerId String @map("developer_id")
|
|
26
|
+
token String @unique
|
|
27
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
28
|
+
lastUsedAt DateTime? @map("last_used_at")
|
|
29
|
+
revokedAt DateTime? @map("revoked_at")
|
|
30
|
+
|
|
31
|
+
developer Developer @relation(fields: [developerId], references: [id], onDelete: Cascade)
|
|
32
|
+
|
|
33
|
+
@@index([token])
|
|
34
|
+
@@index([developerId])
|
|
35
|
+
@@map("tokens")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Anonymous daily usage aggregates (Zero-Logging compliant)
|
|
39
|
+
model DailyMetric {
|
|
40
|
+
id String @id @default(nanoid(10))
|
|
41
|
+
date String @unique
|
|
42
|
+
tunnelsOpened Int @default(0) @map("tunnels_opened")
|
|
43
|
+
requestsRouted BigInt @default(0) @map("requests_routed")
|
|
44
|
+
bytesTransferred BigInt @default(0) @map("bytes_transferred")
|
|
45
|
+
activeDevelopers Int @default(0) @map("active_developers")
|
|
46
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
47
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
48
|
+
|
|
49
|
+
@@index([date])
|
|
50
|
+
@@map("daily_metrics")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Global lifetime totals
|
|
54
|
+
model SystemMetric {
|
|
55
|
+
id String @id @default(nanoid(10))
|
|
56
|
+
totalTunnels BigInt @default(0) @map("total_tunnels")
|
|
57
|
+
totalRequests BigInt @default(0) @map("total_requests")
|
|
58
|
+
totalBytes BigInt @default(0) @map("total_bytes")
|
|
59
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
60
|
+
|
|
61
|
+
@@map("system_metrics")
|
|
62
|
+
}
|