gs-error-reporter 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/dist/cli/detectProject.d.ts +6 -0
- package/dist/cli/detectProject.d.ts.map +1 -0
- package/dist/cli/detectProject.js +36 -0
- package/dist/cli/fileInstaller.d.ts +2 -0
- package/dist/cli/fileInstaller.d.ts.map +1 -0
- package/dist/cli/fileInstaller.js +21 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +60 -0
- package/dist/cli/installEnv.d.ts +2 -0
- package/dist/cli/installEnv.d.ts.map +1 -0
- package/dist/cli/installEnv.js +46 -0
- package/dist/core/client/clientRuntime.d.ts +2 -0
- package/dist/core/client/clientRuntime.d.ts.map +1 -0
- package/dist/core/client/clientRuntime.js +75 -0
- package/dist/core/server/index.d.ts +2 -0
- package/dist/core/server/index.d.ts.map +1 -0
- package/dist/core/server/index.js +17 -0
- package/dist/core/server/serverRuntime.d.ts +3 -0
- package/dist/core/server/serverRuntime.d.ts.map +1 -0
- package/dist/core/server/serverRuntime.js +60 -0
- package/dist/core/shared/buildPayout.d.ts +23 -0
- package/dist/core/shared/buildPayout.d.ts.map +1 -0
- package/dist/core/shared/buildPayout.js +64 -0
- package/dist/core/shared/reporter.d.ts +2 -0
- package/dist/core/shared/reporter.d.ts.map +1 -0
- package/dist/core/shared/reporter.js +23 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/react/GSReporter.d.ts +3 -0
- package/dist/react/GSReporter.d.ts.map +1 -0
- package/dist/react/GSReporter.js +22 -0
- package/package.json +46 -0
- package/templates/.env.example +5 -0
- package/templates/js/gs-error-reporter.js +98 -0
- package/templates/js/instrumentation.js +1 -0
- package/templates/js/route.js +93 -0
- package/templates/ts/gs-error-reporter.ts +100 -0
- package/templates/ts/instrumentation.ts +1 -0
- package/templates/ts/route.ts +99 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const hasTsconfig: boolean;
|
|
2
|
+
export declare const hasSrc: boolean;
|
|
3
|
+
export declare const baseDir: string;
|
|
4
|
+
export declare function hasAppRouter(rootPath: string): boolean;
|
|
5
|
+
export declare function hasPagesRouter(rootPath: string): boolean;
|
|
6
|
+
//# sourceMappingURL=detectProject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectProject.d.ts","sourceRoot":"","sources":["../../src/cli/detectProject.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,WAAW,SAAkD,CAAC;AAI3E,eAAO,MAAM,MAAM,SAAwC,CAAC;AAC5D,eAAO,MAAM,OAAO,QAAsB,CAAC;AAG3C,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,WAa5C;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,WAU9C"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.baseDir = exports.hasSrc = exports.hasTsconfig = void 0;
|
|
7
|
+
exports.hasAppRouter = hasAppRouter;
|
|
8
|
+
exports.hasPagesRouter = hasPagesRouter;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const root = process.cwd();
|
|
12
|
+
// Check for TypeScript
|
|
13
|
+
exports.hasTsconfig = fs_1.default.existsSync(path_1.default.join(root, "tsconfig.json"));
|
|
14
|
+
console.log("Typescript: ", exports.hasTsconfig);
|
|
15
|
+
// Check for "src" directory
|
|
16
|
+
exports.hasSrc = fs_1.default.existsSync(path_1.default.join(root, "src"));
|
|
17
|
+
exports.baseDir = exports.hasSrc ? "src" : "";
|
|
18
|
+
// Check for App Router
|
|
19
|
+
function hasAppRouter(rootPath) {
|
|
20
|
+
const base = fs_1.default.existsSync(path_1.default.join(rootPath, "src")) ? "src" : "";
|
|
21
|
+
const appPath = path_1.default.join(rootPath, base, "app");
|
|
22
|
+
return (fs_1.default.existsSync(appPath) &&
|
|
23
|
+
(fs_1.default.existsSync(path_1.default.join(appPath, "layout.tsx")) ||
|
|
24
|
+
fs_1.default.existsSync(path_1.default.join(appPath, "layout.jsx")) ||
|
|
25
|
+
fs_1.default.existsSync(path_1.default.join(appPath, "layout.js")) ||
|
|
26
|
+
fs_1.default.existsSync(path_1.default.join(appPath, "layout.ts"))));
|
|
27
|
+
}
|
|
28
|
+
// Check for Pages Router
|
|
29
|
+
function hasPagesRouter(rootPath) {
|
|
30
|
+
const base = fs_1.default.existsSync(path_1.default.join(rootPath, "src")) ? "src" : "";
|
|
31
|
+
const pagesPath = path_1.default.join(rootPath, base, "pages");
|
|
32
|
+
return (fs_1.default.existsSync(path_1.default.join(pagesPath, "_app.tsx")) ||
|
|
33
|
+
fs_1.default.existsSync(path_1.default.join(pagesPath, "_app.jsx")) ||
|
|
34
|
+
fs_1.default.existsSync(path_1.default.join(pagesPath, "_app.js")) ||
|
|
35
|
+
fs_1.default.existsSync(path_1.default.join(pagesPath, "_app.ts")));
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileInstaller.d.ts","sourceRoot":"","sources":["../../src/cli/fileInstaller.ts"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,QAenB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.copyTemplateFile = copyTemplateFile;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const root = process.cwd();
|
|
10
|
+
function copyTemplateFile(templatePath, targetPath) {
|
|
11
|
+
// If file already exists → DO NOTHING
|
|
12
|
+
if (fs_1.default.existsSync(targetPath)) {
|
|
13
|
+
console.log(`⚠ Skipped (already exists): ${targetPath}`);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Ensure directory exists
|
|
17
|
+
fs_1.default.mkdirSync(path_1.default.dirname(targetPath), { recursive: true });
|
|
18
|
+
// Copy file
|
|
19
|
+
fs_1.default.copyFileSync(templatePath, targetPath);
|
|
20
|
+
console.log(`✔ Created: ${targetPath}`);
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const detectProject_1 = require("./detectProject");
|
|
12
|
+
const detectProject_2 = require("./detectProject");
|
|
13
|
+
const detectProject_3 = require("./detectProject");
|
|
14
|
+
const detectProject_4 = require("./detectProject");
|
|
15
|
+
const fileInstaller_1 = require("./fileInstaller");
|
|
16
|
+
const installEnv_1 = require("./installEnv");
|
|
17
|
+
console.log(chalk_1.default.cyanBright(`\n!!! Welcome to gs-error-reporter !!!`));
|
|
18
|
+
console.log(chalk_1.default.white(`gs-error-reporter is an error reporting system for Next.js (App Router & Pages Router) with email notifications and CLI setup. (1.0.0)`));
|
|
19
|
+
console.log(chalk_1.default.yellowBright(`\nThis is the CLI system that will generate important files for gs-error-reporter.`));
|
|
20
|
+
console.log(chalk_1.default.redBright(`\nIMPORTANT INFORMATION:`));
|
|
21
|
+
console.log(` * Provide all requested information carefully.`);
|
|
22
|
+
console.log(` * Version 1.0.0 supports Gmail only.\n`);
|
|
23
|
+
const rootPath = process.cwd();
|
|
24
|
+
const packageJsonPath = path_1.default.join(rootPath, "package.json");
|
|
25
|
+
if (!fs_1.default.existsSync(packageJsonPath)) {
|
|
26
|
+
console.error("❌ No package.json found. Run this inside a Next.js project.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf8"));
|
|
30
|
+
if (!packageJson.dependencies?.next && !packageJson.devDependencies?.next) {
|
|
31
|
+
console.error("❌ This is not a Next.js project.");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
(0, installEnv_1.setupEnv)(rootPath);
|
|
35
|
+
async function setup() {
|
|
36
|
+
const templatesDir = path_1.default.join(__dirname, "../../templates", detectProject_1.hasTsconfig ? "ts" : "js");
|
|
37
|
+
const instrumentationTarget = path_1.default.join(rootPath, detectProject_2.baseDir, "instrumentation." + (detectProject_1.hasTsconfig ? "ts" : "js"));
|
|
38
|
+
(0, fileInstaller_1.copyTemplateFile)(path_1.default.join(templatesDir, "instrumentation." + (detectProject_1.hasTsconfig ? "ts" : "js")), instrumentationTarget);
|
|
39
|
+
const AppRouter = (0, detectProject_3.hasAppRouter)(rootPath);
|
|
40
|
+
const PagesRouter = (0, detectProject_4.hasPagesRouter)(rootPath);
|
|
41
|
+
if (AppRouter && PagesRouter) {
|
|
42
|
+
const answers = await inquirer_1.default.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: 'list',
|
|
45
|
+
name: 'router',
|
|
46
|
+
message: 'In What router you want to inmplement SendMail API',
|
|
47
|
+
choices: ['App Router', 'Pages Router']
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
else if (AppRouter && !PagesRouter) {
|
|
52
|
+
const SendMailTarget = path_1.default.join(rootPath, detectProject_2.baseDir, "app", "api", "gs-error-reporter", "route." + (detectProject_1.hasTsconfig ? "ts" : "js"));
|
|
53
|
+
(0, fileInstaller_1.copyTemplateFile)(path_1.default.join(templatesDir, "route." + (detectProject_1.hasTsconfig ? "ts" : "js")), SendMailTarget);
|
|
54
|
+
}
|
|
55
|
+
else if (!AppRouter && PagesRouter) {
|
|
56
|
+
const SendMailTarget = path_1.default.join(rootPath, detectProject_2.baseDir, "pages", "api", "gs-error-reporter." + (detectProject_1.hasTsconfig ? "ts" : "js"));
|
|
57
|
+
(0, fileInstaller_1.copyTemplateFile)(path_1.default.join(templatesDir, "gs-error-reporter." + (detectProject_1.hasTsconfig ? "ts" : "js")), SendMailTarget);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
setup();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installEnv.d.ts","sourceRoot":"","sources":["../../src/cli/installEnv.ts"],"names":[],"mappings":"AAWA,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,QAwC3C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.setupEnv = setupEnv;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const REQUIRED_ENV_VARS = {
|
|
10
|
+
GS_REPORTER_EMAIL: "",
|
|
11
|
+
GS_REPORTER_APP_PASSWORD: "",
|
|
12
|
+
GS_REPORTER_RECEIVER: "",
|
|
13
|
+
BASE_URL: "",
|
|
14
|
+
PROJECT_NAME: ""
|
|
15
|
+
};
|
|
16
|
+
function setupEnv(projectRoot) {
|
|
17
|
+
const envPath = path_1.default.join(projectRoot, ".env");
|
|
18
|
+
// ------------------------------------
|
|
19
|
+
// CASE 1: .env does not exist
|
|
20
|
+
// ------------------------------------
|
|
21
|
+
if (!fs_1.default.existsSync(envPath)) {
|
|
22
|
+
const content = Object.entries(REQUIRED_ENV_VARS)
|
|
23
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
24
|
+
.join("\n");
|
|
25
|
+
fs_1.default.writeFileSync(envPath, content + "\n");
|
|
26
|
+
console.log("✔ Created .env with gs-error-reporter variables");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// ------------------------------------
|
|
30
|
+
// CASE 2: .env exists → append safely
|
|
31
|
+
// ------------------------------------
|
|
32
|
+
const existingContent = fs_1.default.readFileSync(envPath, "utf8");
|
|
33
|
+
const linesToAppend = [];
|
|
34
|
+
for (const key of Object.keys(REQUIRED_ENV_VARS)) {
|
|
35
|
+
const regex = new RegExp(`^${key}=`, "m");
|
|
36
|
+
if (!regex.test(existingContent)) {
|
|
37
|
+
linesToAppend.push(`${key}=`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (linesToAppend.length === 0) {
|
|
41
|
+
console.log("✔ .env already contains required variables");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
fs_1.default.appendFileSync(envPath, "\n# GS Error Reporter\n" + linesToAppend.join("\n") + "\n");
|
|
45
|
+
console.log("✔ Added missing gs-error-reporter variables to .env");
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientRuntime.d.ts","sourceRoot":"","sources":["../../../src/core/client/clientRuntime.ts"],"names":[],"mappings":"AA6EA,wBAAgB,iBAAiB,SAShC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/*
|
|
4
|
+
Browser Runtime Monitor
|
|
5
|
+
NOTE: Do NOT auto-run this file.
|
|
6
|
+
It must be started from a client React component.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.initClientRuntime = initClientRuntime;
|
|
10
|
+
const reporter_1 = require("../shared/reporter");
|
|
11
|
+
const buildPayout_1 = require("../shared/buildPayout");
|
|
12
|
+
let initialized = false;
|
|
13
|
+
let reporting = false;
|
|
14
|
+
async function safeReport(type, error, extra) {
|
|
15
|
+
if (reporting)
|
|
16
|
+
return;
|
|
17
|
+
reporting = true;
|
|
18
|
+
try {
|
|
19
|
+
// Build structured payload
|
|
20
|
+
const payload = (0, buildPayout_1.buildPayload)({
|
|
21
|
+
runtime: "client",
|
|
22
|
+
error,
|
|
23
|
+
extra: {
|
|
24
|
+
errorType: type,
|
|
25
|
+
...extra,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
await (0, reporter_1.reportError)(payload);
|
|
29
|
+
console.log("ERROR CATCHED!");
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
// never break app
|
|
33
|
+
console.log("gs-error-reporter failed to send error report", err);
|
|
34
|
+
}
|
|
35
|
+
reporting = false;
|
|
36
|
+
}
|
|
37
|
+
/* ---------------- Runtime JS errors ---------------- */
|
|
38
|
+
function attachRuntimeErrorListener() {
|
|
39
|
+
window.addEventListener("error", (event) => {
|
|
40
|
+
safeReport("window.error", event.error || event.message, {
|
|
41
|
+
source: event.filename,
|
|
42
|
+
lineno: event.lineno,
|
|
43
|
+
colno: event.colno,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/* ---------------- Promise rejections ---------------- */
|
|
48
|
+
function attachUnhandledRejection() {
|
|
49
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
50
|
+
safeReport("unhandledrejection", event.reason);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/* ---------------- Console patch ---------------- */
|
|
54
|
+
function patchConsole() {
|
|
55
|
+
const originalError = console.error;
|
|
56
|
+
const originalWarn = console.warn;
|
|
57
|
+
console.error = (...args) => {
|
|
58
|
+
originalError(...args);
|
|
59
|
+
safeReport("console.error", args, { source: "client-console" });
|
|
60
|
+
};
|
|
61
|
+
console.warn = (...args) => {
|
|
62
|
+
originalWarn(...args);
|
|
63
|
+
safeReport("console.warn", args, { source: "client-console" });
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/* ---------------- Public initializer ---------------- */
|
|
67
|
+
function initClientRuntime() {
|
|
68
|
+
if (initialized)
|
|
69
|
+
return;
|
|
70
|
+
initialized = true;
|
|
71
|
+
attachRuntimeErrorListener();
|
|
72
|
+
attachUnhandledRejection();
|
|
73
|
+
patchConsole();
|
|
74
|
+
console.log("gs-error-reporter client runtime initialized");
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/server/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const serverRuntime_1 = require("./serverRuntime");
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (!g.__GS_ERROR_REPORTER_SERVER__) {
|
|
6
|
+
g.__GS_ERROR_REPORTER_SERVER__ = true;
|
|
7
|
+
console.log("gs-error-reporter: server monitoring initialized");
|
|
8
|
+
// Attach process listeners
|
|
9
|
+
process.on("uncaughtException", (error) => {
|
|
10
|
+
(0, serverRuntime_1.handleServerError)(error, "uncaughtException");
|
|
11
|
+
});
|
|
12
|
+
process.on("unhandledRejection", (reason) => {
|
|
13
|
+
(0, serverRuntime_1.handleServerError)(reason, "unhandledRejection");
|
|
14
|
+
});
|
|
15
|
+
// Patch console (optional but powerful)
|
|
16
|
+
(0, serverRuntime_1.patchServerConsole)();
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverRuntime.d.ts","sourceRoot":"","sources":["../../../src/core/server/serverRuntime.ts"],"names":[],"mappings":"AAmBA,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,iBAuBjE;AAQD,wBAAgB,kBAAkB,SAkBjC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/*
|
|
4
|
+
Server Error Handler (NOT a runtime starter anymore)
|
|
5
|
+
This file only processes errors sent by server/index.ts
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.handleServerError = handleServerError;
|
|
9
|
+
exports.patchServerConsole = patchServerConsole;
|
|
10
|
+
const reporter_1 = require("../shared/reporter");
|
|
11
|
+
const buildPayout_1 = require("../shared/buildPayout");
|
|
12
|
+
/* ---------------------------------------------------------- */
|
|
13
|
+
/* INTERNAL PROTECTION (ANTI-LOOP) */
|
|
14
|
+
/* ---------------------------------------------------------- */
|
|
15
|
+
let reporting = false;
|
|
16
|
+
/* ---------------------------------------------------------- */
|
|
17
|
+
/* MAIN SERVER ERROR HANDLER */
|
|
18
|
+
/* ---------------------------------------------------------- */
|
|
19
|
+
async function handleServerError(error, source) {
|
|
20
|
+
// Prevent recursive reporting
|
|
21
|
+
if (reporting)
|
|
22
|
+
return;
|
|
23
|
+
reporting = true;
|
|
24
|
+
try {
|
|
25
|
+
const payload = (0, buildPayout_1.buildPayload)({
|
|
26
|
+
runtime: "server",
|
|
27
|
+
error,
|
|
28
|
+
extra: {
|
|
29
|
+
errorType: source,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
await (0, reporter_1.reportError)(payload);
|
|
33
|
+
console.log("gs-error-reporter captured server error:", source);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
// Never allow reporter itself to crash the server
|
|
37
|
+
console.warn("gs-error-reporter failed while reporting server error");
|
|
38
|
+
}
|
|
39
|
+
reporting = false;
|
|
40
|
+
}
|
|
41
|
+
/* ---------------------------------------------------------- */
|
|
42
|
+
/* OPTIONAL: SERVER CONSOLE MONITORING */
|
|
43
|
+
/* ---------------------------------------------------------- */
|
|
44
|
+
let consolePatched = false;
|
|
45
|
+
function patchServerConsole() {
|
|
46
|
+
if (consolePatched)
|
|
47
|
+
return;
|
|
48
|
+
consolePatched = true;
|
|
49
|
+
const originalError = console.error;
|
|
50
|
+
const originalWarn = console.warn;
|
|
51
|
+
console.error = (...args) => {
|
|
52
|
+
originalError(...args);
|
|
53
|
+
handleServerError(args, "console.error");
|
|
54
|
+
};
|
|
55
|
+
console.warn = (...args) => {
|
|
56
|
+
originalWarn(...args);
|
|
57
|
+
handleServerError(args, "console.warn");
|
|
58
|
+
};
|
|
59
|
+
console.log("gs-error-reporter server console patched");
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type RuntimeType = "client" | "server";
|
|
2
|
+
interface BuildPayloadOptions {
|
|
3
|
+
runtime: RuntimeType;
|
|
4
|
+
error: any;
|
|
5
|
+
extra?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
export interface ErrorPayload {
|
|
8
|
+
type: RuntimeType;
|
|
9
|
+
message: string;
|
|
10
|
+
stack?: string;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
method?: string;
|
|
14
|
+
userAgent?: string;
|
|
15
|
+
language?: string;
|
|
16
|
+
nodeVersion?: string;
|
|
17
|
+
platform?: string;
|
|
18
|
+
environment?: string;
|
|
19
|
+
extra?: Record<string, any>;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildPayload(options: BuildPayloadOptions): ErrorPayload;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=buildPayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildPayout.d.ts","sourceRoot":"","sources":["../../../src/core/shared/buildPayout.ts"],"names":[],"mappings":"AAMA,KAAK,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvC,UAAU,mBAAmB;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAElB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAwCD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CA8BvE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/*
|
|
4
|
+
buildPayload.ts
|
|
5
|
+
Converts raw error data into a structured report
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildPayload = buildPayload;
|
|
9
|
+
/* -------------------------------------------------- */
|
|
10
|
+
/* Utility: normalize error */
|
|
11
|
+
/* -------------------------------------------------- */
|
|
12
|
+
function normalizeError(error) {
|
|
13
|
+
if (!error) {
|
|
14
|
+
return {
|
|
15
|
+
message: "Unknown error",
|
|
16
|
+
stack: undefined,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
return {
|
|
21
|
+
message: error.message,
|
|
22
|
+
stack: error.stack,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Sometimes promises reject with string/object
|
|
26
|
+
if (typeof error === "string") {
|
|
27
|
+
return {
|
|
28
|
+
message: error,
|
|
29
|
+
stack: undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// fallback
|
|
33
|
+
return {
|
|
34
|
+
message: JSON.stringify(error),
|
|
35
|
+
stack: undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/* -------------------------------------------------- */
|
|
39
|
+
/* Main Payload Builder */
|
|
40
|
+
/* -------------------------------------------------- */
|
|
41
|
+
function buildPayload(options) {
|
|
42
|
+
const { runtime, error, extra } = options;
|
|
43
|
+
const normalized = normalizeError(error);
|
|
44
|
+
const basePayload = {
|
|
45
|
+
type: runtime,
|
|
46
|
+
message: normalized.message,
|
|
47
|
+
stack: normalized.stack,
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
50
|
+
extra,
|
|
51
|
+
};
|
|
52
|
+
/* ---------------- CLIENT DATA ---------------- */
|
|
53
|
+
if (runtime === "client" && typeof window !== "undefined") {
|
|
54
|
+
basePayload.url = window.location.href;
|
|
55
|
+
basePayload.userAgent = navigator.userAgent;
|
|
56
|
+
basePayload.language = navigator.language;
|
|
57
|
+
}
|
|
58
|
+
/* ---------------- SERVER DATA ---------------- */
|
|
59
|
+
if (runtime === "server" && typeof process !== "undefined") {
|
|
60
|
+
basePayload.nodeVersion = process.version;
|
|
61
|
+
basePayload.platform = process.platform;
|
|
62
|
+
}
|
|
63
|
+
return basePayload;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../../src/core/shared/reporter.ts"],"names":[],"mappings":"AAAA,wBAAsB,WAAW,CAAC,OAAO,EAAE,GAAG,iBAkB7C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reportError = reportError;
|
|
4
|
+
async function reportError(payload) {
|
|
5
|
+
if (payload.type === "server") {
|
|
6
|
+
await fetch(`${process.env.BASE_URL}/api/gs-error-reporter`, {
|
|
7
|
+
method: "POST",
|
|
8
|
+
headers: { "Content-Type": "application/json" },
|
|
9
|
+
body: JSON.stringify({
|
|
10
|
+
payload
|
|
11
|
+
}),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
else if (payload.type === "client") {
|
|
15
|
+
await fetch('/api/gs-error-reporter', {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
payload
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GSReporter = void 0;
|
|
4
|
+
var GSReporter_1 = require("./react/GSReporter");
|
|
5
|
+
Object.defineProperty(exports, "GSReporter", { enumerable: true, get: function () { return GSReporter_1.GSReporter; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GSReporter.d.ts","sourceRoot":"","sources":["../../src/react/GSReporter.tsx"],"names":[],"mappings":"AAMA,OAAO,8BAA8B,CAAC;AAOtC,wBAAgB,UAAU,SAWzB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
"use client";
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.GSReporter = GSReporter;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
// Import server runtime (runs automatically on server)
|
|
8
|
+
require("../core/server/serverRuntime");
|
|
9
|
+
// Import client initializer
|
|
10
|
+
const clientRuntime_1 = require("../core/client/clientRuntime");
|
|
11
|
+
let started = false;
|
|
12
|
+
function GSReporter() {
|
|
13
|
+
(0, react_1.useEffect)(() => {
|
|
14
|
+
// Prevent double init during Fast Refresh
|
|
15
|
+
if (started)
|
|
16
|
+
return;
|
|
17
|
+
started = true;
|
|
18
|
+
// Start browser monitoring
|
|
19
|
+
(0, clientRuntime_1.initClientRuntime)();
|
|
20
|
+
}, []);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gs-error-reporter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Runtime error monitoring and email reporting for Next.js",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "GS",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"sideEffects": true,
|
|
11
|
+
"bin": {
|
|
12
|
+
"gs-email-reporter": "./dist/cli/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"templates",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"prepare": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"next": ">=13",
|
|
25
|
+
"react": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/nodemailer": "^7.0.9",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"require": "./dist/index.js",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./server": {
|
|
37
|
+
"require": "./dist/core/server/index.js",
|
|
38
|
+
"default": "./dist/core/server/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"chalk": "^5.6.2",
|
|
43
|
+
"inquirer": "^13.2.4",
|
|
44
|
+
"nodemailer": "^8.0.1"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
|
|
3
|
+
/* This route receives error reports from gs-error-reporter
|
|
4
|
+
and emails them to the developer.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export default async function handler(req, res) {
|
|
8
|
+
// Ensure we only process POST requests
|
|
9
|
+
if (req.method !== 'POST') {
|
|
10
|
+
return res.status(405).json({ message: 'Method Not Allowed' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// In Pages router, req.body is already parsed if content-type is application/json
|
|
15
|
+
const payload = req.body;
|
|
16
|
+
|
|
17
|
+
/* ---------- EMAIL SUBJECT ---------- */
|
|
18
|
+
|
|
19
|
+
const projectName = process.env.PROJECT_NAME;
|
|
20
|
+
|
|
21
|
+
const subject = `🚨 [${projectName}] [${payload.payload.type?.toUpperCase()}] ${payload.payload.message || "Unknown Error"}`;
|
|
22
|
+
|
|
23
|
+
/* ---------- HTML FORMATTER ---------- */
|
|
24
|
+
|
|
25
|
+
const html = `
|
|
26
|
+
<body style="font-family: Arial, sans-serif; background:#f4f4f7; padding:20px;">
|
|
27
|
+
<div style="max-width:650px; margin:auto; background:white; border-radius:12px; padding:24px; box-shadow:0 4px 12px rgba(0,0,0,0.08);">
|
|
28
|
+
|
|
29
|
+
<h2 style="color:#d32f2f; margin-top:0;">🚨 GS Error Reporter</h2>
|
|
30
|
+
|
|
31
|
+
<h3>🧠 Error Information</h3>
|
|
32
|
+
<p><strong>Runtime:</strong> ${payload.payload.type}</p>
|
|
33
|
+
<p><strong>Message:</strong> ${payload.payload.message}</p>
|
|
34
|
+
<p><strong>Environment:</strong> ${payload.payload.environment}</p>
|
|
35
|
+
<p><strong>Time:</strong> ${payload.payload.timestamp}</p>
|
|
36
|
+
|
|
37
|
+
<hr/>
|
|
38
|
+
|
|
39
|
+
<h3>🌍 Request Context</h3>
|
|
40
|
+
<p><strong>URL:</strong> ${payload.payload.url || "N/A"}</p>
|
|
41
|
+
<p><strong>Method:</strong> ${payload.payload.method || "N/A"}</p>
|
|
42
|
+
|
|
43
|
+
<hr/>
|
|
44
|
+
|
|
45
|
+
<h3>💻 System Information</h3>
|
|
46
|
+
<p><strong>User Agent:</strong> ${payload.payload.userAgent || "Server Runtime"}</p>
|
|
47
|
+
<p><strong>Language:</strong> ${payload.payload.language || "N/A"}</p>
|
|
48
|
+
<p><strong>Node Version:</strong> ${payload.payload.nodeVersion || "N/A"}</p>
|
|
49
|
+
<p><strong>Platform:</strong> ${payload.payload.platform || "N/A"}</p>
|
|
50
|
+
|
|
51
|
+
<hr/>
|
|
52
|
+
|
|
53
|
+
<h3>📄 Stack Trace</h3>
|
|
54
|
+
<pre style="background:#111; color:#0f0; padding:14px; border-radius:8px; font-size:12px; overflow-x:auto;">
|
|
55
|
+
${payload.payload.stack || "No stack trace available"}
|
|
56
|
+
</pre>
|
|
57
|
+
|
|
58
|
+
${payload.payload.extra
|
|
59
|
+
? `
|
|
60
|
+
<hr/>
|
|
61
|
+
<h3>🧩 Extra Data</h3>
|
|
62
|
+
<pre style="background:#f7f7f7; padding:12px; border-radius:8px; font-size:12px;">
|
|
63
|
+
${JSON.stringify(payload.payload.extra, null, 2)}
|
|
64
|
+
</pre>`
|
|
65
|
+
: ""
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
<p style="margin-top:30px; font-size:12px; color:#888; text-align:center;">
|
|
69
|
+
Sent automatically by <b>gs-error-reporter</b>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
</div>
|
|
73
|
+
</body>
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
/* ---------- NODEMAILER ---------- */
|
|
77
|
+
|
|
78
|
+
const transporter = nodemailer.createTransport({
|
|
79
|
+
service: "gmail",
|
|
80
|
+
auth: {
|
|
81
|
+
user: process.env.GS_REPORTER_EMAIL,
|
|
82
|
+
pass: process.env.GS_REPORTER_APP_PASSWORD,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await transporter.sendMail({
|
|
87
|
+
from: process.env.GS_REPORTER_EMAIL,
|
|
88
|
+
to: process.env.GS_REPORTER_RECEIVER,
|
|
89
|
+
subject,
|
|
90
|
+
html,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return res.status(200).json({ success: true });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error("gs-error-reporter mail route failed:", err);
|
|
96
|
+
return res.status(500).json({ success: false });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "gs-error-reporter/server";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
|
|
4
|
+
/* This route receives error reports from gs-error-reporter
|
|
5
|
+
and emails them to the developer.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export async function POST(req) {
|
|
9
|
+
try {
|
|
10
|
+
const payload = await req.json();
|
|
11
|
+
|
|
12
|
+
/* ---------- EMAIL SUBJECT ---------- */
|
|
13
|
+
|
|
14
|
+
const projectName = process.env.PROJECT_NAME;
|
|
15
|
+
|
|
16
|
+
const subject = `🚨 [${projectName}] [${payload.payload.type?.toUpperCase()}] ${payload.payload.message || "Unknown Error"}`;
|
|
17
|
+
|
|
18
|
+
/* ---------- HTML FORMATTER ---------- */
|
|
19
|
+
|
|
20
|
+
const html = `
|
|
21
|
+
<body style="font-family: Arial, sans-serif; background:#f4f4f7; padding:20px;">
|
|
22
|
+
<div style="max-width:650px; margin:auto; background:white; border-radius:12px; padding:24px; box-shadow:0 4px 12px rgba(0,0,0,0.08);">
|
|
23
|
+
|
|
24
|
+
<h2 style="color:#d32f2f; margin-top:0;">🚨 GS Error Reporter</h2>
|
|
25
|
+
|
|
26
|
+
<h3>🧠 Error Information</h3>
|
|
27
|
+
<p><strong>Runtime:</strong> ${payload.payload.type}</p>
|
|
28
|
+
<p><strong>Message:</strong> ${payload.payload.message}</p>
|
|
29
|
+
<p><strong>Environment:</strong> ${payload.payload.environment}</p>
|
|
30
|
+
<p><strong>Time:</strong> ${payload.payload.timestamp}</p>
|
|
31
|
+
|
|
32
|
+
<hr/>
|
|
33
|
+
|
|
34
|
+
<h3>🌍 Request Context</h3>
|
|
35
|
+
<p><strong>URL:</strong> ${payload.payload.url || "N/A"}</p>
|
|
36
|
+
<p><strong>Method:</strong> ${payload.payload.method || "N/A"}</p>
|
|
37
|
+
|
|
38
|
+
<hr/>
|
|
39
|
+
|
|
40
|
+
<h3>💻 System Information</h3>
|
|
41
|
+
<p><strong>User Agent:</strong> ${payload.payload.userAgent || "Server Runtime"}</p>
|
|
42
|
+
<p><strong>Language:</strong> ${payload.payload.language || "N/A"}</p>
|
|
43
|
+
<p><strong>Node Version:</strong> ${payload.payload.nodeVersion || "N/A"}</p>
|
|
44
|
+
<p><strong>Platform:</strong> ${payload.payload.platform || "N/A"}</p>
|
|
45
|
+
|
|
46
|
+
<hr/>
|
|
47
|
+
|
|
48
|
+
<h3>📄 Stack Trace</h3>
|
|
49
|
+
<pre style="background:#111; color:#0f0; padding:14px; border-radius:8px; font-size:12px; overflow-x:auto;">
|
|
50
|
+
${payload.payload.stack || "No stack trace available"}
|
|
51
|
+
</pre>
|
|
52
|
+
|
|
53
|
+
${payload.payload.extra
|
|
54
|
+
? `
|
|
55
|
+
<hr/>
|
|
56
|
+
<h3>🧩 Extra Data</h3>
|
|
57
|
+
<pre style="background:#f7f7f7; padding:12px; border-radius:8px; font-size:12px;">
|
|
58
|
+
${JSON.stringify(payload.payload.extra, null, 2)}
|
|
59
|
+
</pre>`
|
|
60
|
+
: ""
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
<p style="margin-top:30px; font-size:12px; color:#888; text-align:center;">
|
|
64
|
+
Sent automatically by <b>gs-error-reporter</b>
|
|
65
|
+
</p>
|
|
66
|
+
|
|
67
|
+
</div>
|
|
68
|
+
</body>
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
/* ---------- NODEMAILER ---------- */
|
|
72
|
+
|
|
73
|
+
const transporter = nodemailer.createTransport({
|
|
74
|
+
service: "gmail",
|
|
75
|
+
auth: {
|
|
76
|
+
user: process.env.GS_REPORTER_EMAIL,
|
|
77
|
+
pass: process.env.GS_REPORTER_APP_PASSWORD,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await transporter.sendMail({
|
|
82
|
+
from: process.env.GS_REPORTER_EMAIL,
|
|
83
|
+
to: process.env.GS_REPORTER_RECEIVER,
|
|
84
|
+
subject,
|
|
85
|
+
html,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return NextResponse.json({ success: true });
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error("gs-error-reporter mail route failed:", err);
|
|
91
|
+
return NextResponse.json({ success: false }, { status: 500 });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import nodemailer from "nodemailer";
|
|
3
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
4
|
+
|
|
5
|
+
/* This route receives error reports from gs-error-reporter
|
|
6
|
+
and emails them to the developer.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
10
|
+
// Ensure we only process POST requests
|
|
11
|
+
if (req.method !== 'POST') {
|
|
12
|
+
return res.status(405).json({ message: 'Method Not Allowed' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// In Pages router, req.body is already parsed if content-type is application/json
|
|
17
|
+
const payload = req.body;
|
|
18
|
+
|
|
19
|
+
/* ---------- EMAIL SUBJECT ---------- */
|
|
20
|
+
|
|
21
|
+
const projectName = process.env.PROJECT_NAME;
|
|
22
|
+
|
|
23
|
+
const subject = `🚨 [${projectName}] [${payload.payload.type?.toUpperCase()}] ${payload.payload.message || "Unknown Error"}`;
|
|
24
|
+
|
|
25
|
+
/* ---------- HTML FORMATTER ---------- */
|
|
26
|
+
|
|
27
|
+
const html = `
|
|
28
|
+
<body style="font-family: Arial, sans-serif; background:#f4f4f7; padding:20px;">
|
|
29
|
+
<div style="max-width:650px; margin:auto; background:white; border-radius:12px; padding:24px; box-shadow:0 4px 12px rgba(0,0,0,0.08);">
|
|
30
|
+
|
|
31
|
+
<h2 style="color:#d32f2f; margin-top:0;">🚨 GS Error Reporter</h2>
|
|
32
|
+
|
|
33
|
+
<h3>🧠 Error Information</h3>
|
|
34
|
+
<p><strong>Runtime:</strong> ${payload.payload.type}</p>
|
|
35
|
+
<p><strong>Message:</strong> ${payload.payload.message}</p>
|
|
36
|
+
<p><strong>Environment:</strong> ${payload.payload.environment}</p>
|
|
37
|
+
<p><strong>Time:</strong> ${payload.payload.timestamp}</p>
|
|
38
|
+
|
|
39
|
+
<hr/>
|
|
40
|
+
|
|
41
|
+
<h3>🌍 Request Context</h3>
|
|
42
|
+
<p><strong>URL:</strong> ${payload.payload.url || "N/A"}</p>
|
|
43
|
+
<p><strong>Method:</strong> ${payload.payload.method || "N/A"}</p>
|
|
44
|
+
|
|
45
|
+
<hr/>
|
|
46
|
+
|
|
47
|
+
<h3>💻 System Information</h3>
|
|
48
|
+
<p><strong>User Agent:</strong> ${payload.payload.userAgent || "Server Runtime"}</p>
|
|
49
|
+
<p><strong>Language:</strong> ${payload.payload.language || "N/A"}</p>
|
|
50
|
+
<p><strong>Node Version:</strong> ${payload.payload.nodeVersion || "N/A"}</p>
|
|
51
|
+
<p><strong>Platform:</strong> ${payload.payload.platform || "N/A"}</p>
|
|
52
|
+
|
|
53
|
+
<hr/>
|
|
54
|
+
|
|
55
|
+
<h3>📄 Stack Trace</h3>
|
|
56
|
+
<pre style="background:#111; color:#0f0; padding:14px; border-radius:8px; font-size:12px; overflow-x:auto;">
|
|
57
|
+
${payload.payload.stack || "No stack trace available"}
|
|
58
|
+
</pre>
|
|
59
|
+
|
|
60
|
+
${payload.payload.extra
|
|
61
|
+
? `
|
|
62
|
+
<hr/>
|
|
63
|
+
<h3>🧩 Extra Data</h3>
|
|
64
|
+
<pre style="background:#f7f7f7; padding:12px; border-radius:8px; font-size:12px;">
|
|
65
|
+
${JSON.stringify(payload.payload.extra, null, 2)}
|
|
66
|
+
</pre>`
|
|
67
|
+
: ""
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
<p style="margin-top:30px; font-size:12px; color:#888; text-align:center;">
|
|
71
|
+
Sent automatically by <b>gs-error-reporter</b>
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
</div>
|
|
75
|
+
</body>
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
/* ---------- NODEMAILER ---------- */
|
|
79
|
+
|
|
80
|
+
const transporter = nodemailer.createTransport({
|
|
81
|
+
service: "gmail",
|
|
82
|
+
auth: {
|
|
83
|
+
user: process.env.GS_REPORTER_EMAIL,
|
|
84
|
+
pass: process.env.GS_REPORTER_APP_PASSWORD,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await transporter.sendMail({
|
|
89
|
+
from: process.env.GS_REPORTER_EMAIL,
|
|
90
|
+
to: process.env.GS_REPORTER_RECEIVER,
|
|
91
|
+
subject,
|
|
92
|
+
html,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return res.status(200).json({ success: true });
|
|
96
|
+
} catch (err: any) {
|
|
97
|
+
console.error("gs-error-reporter mail route failed:", err);
|
|
98
|
+
return res.status(500).json({ success: false });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "gs-error-reporter/server";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import nodemailer from "nodemailer";
|
|
4
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
This route receives error reports from gs-error-reporter
|
|
8
|
+
and emails them to the developer.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export async function POST(req: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
const payload = await req.json();
|
|
14
|
+
|
|
15
|
+
/* ---------- EMAIL SUBJECT ---------- */
|
|
16
|
+
|
|
17
|
+
const projectName = process.env.PROJECT_NAME;
|
|
18
|
+
|
|
19
|
+
const subject = `🚨 [${projectName}] [${payload.payload.type?.toUpperCase()}] ${payload.payload.message || "Unknown Error"}`;
|
|
20
|
+
|
|
21
|
+
/* ---------- HTML FORMATTER ---------- */
|
|
22
|
+
|
|
23
|
+
const html = `
|
|
24
|
+
<body style="font-family: Arial, sans-serif; background:#f4f4f7; padding:20px;">
|
|
25
|
+
<div style="max-width:650px; margin:auto; background:white; border-radius:12px; padding:24px; box-shadow:0 4px 12px rgba(0,0,0,0.08);">
|
|
26
|
+
|
|
27
|
+
<h2 style="color:#d32f2f; margin-top:0;">🚨 GS Error Reporter</h2>
|
|
28
|
+
|
|
29
|
+
<h3>📦 Project</h3>
|
|
30
|
+
<p><strong>Name:</strong> ${projectName}</p>
|
|
31
|
+
|
|
32
|
+
<h3>🧠 Error Information</h3>
|
|
33
|
+
<p><strong>Runtime:</strong> ${payload.payload.type}</p>
|
|
34
|
+
<p><strong>Message:</strong> ${payload.payload.message}</p>
|
|
35
|
+
<p><strong>Environment:</strong> ${payload.payload.environment}</p>
|
|
36
|
+
<p><strong>Time:</strong> ${payload.payload.timestamp}</p>
|
|
37
|
+
|
|
38
|
+
<hr/>
|
|
39
|
+
|
|
40
|
+
<h3>🌍 Request Context</h3>
|
|
41
|
+
<p><strong>URL:</strong> ${payload.payload.url || "N/A"}</p>
|
|
42
|
+
<p><strong>Method:</strong> ${payload.payload.method || "N/A"}</p>
|
|
43
|
+
|
|
44
|
+
<hr/>
|
|
45
|
+
|
|
46
|
+
<h3>💻 System Information</h3>
|
|
47
|
+
<p><strong>User Agent:</strong> ${payload.payload.userAgent || "Server Runtime"}</p>
|
|
48
|
+
<p><strong>Language:</strong> ${payload.payload.language || "N/A"}</p>
|
|
49
|
+
<p><strong>Node Version:</strong> ${payload.payload.nodeVersion || "N/A"}</p>
|
|
50
|
+
<p><strong>Platform:</strong> ${payload.payload.platform || "N/A"}</p>
|
|
51
|
+
|
|
52
|
+
<hr/>
|
|
53
|
+
|
|
54
|
+
<h3>📄 Stack Trace</h3>
|
|
55
|
+
<pre style="background:#111; color:#0f0; padding:14px; border-radius:8px; font-size:12px; overflow-x:auto;">
|
|
56
|
+
${payload.payload.stack || "No stack trace available"}
|
|
57
|
+
</pre>
|
|
58
|
+
|
|
59
|
+
${payload.payload.extra
|
|
60
|
+
? `
|
|
61
|
+
<hr/>
|
|
62
|
+
<h3>🧩 Extra Data</h3>
|
|
63
|
+
<pre style="background:#f7f7f7; padding:12px; border-radius:8px; font-size:12px;">
|
|
64
|
+
${JSON.stringify(payload.payload.extra, null, 2)}
|
|
65
|
+
</pre>`
|
|
66
|
+
: ""
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
<p style="margin-top:30px; font-size:12px; color:#888; text-align:center;">
|
|
70
|
+
Sent automatically by <b>gs-error-reporter</b>
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
</div>
|
|
74
|
+
</body>
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
/* ---------- NODEMAILER ---------- */
|
|
78
|
+
|
|
79
|
+
const transporter = nodemailer.createTransport({
|
|
80
|
+
service: "gmail",
|
|
81
|
+
auth: {
|
|
82
|
+
user: process.env.GS_REPORTER_EMAIL,
|
|
83
|
+
pass: process.env.GS_REPORTER_APP_PASSWORD,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await transporter.sendMail({
|
|
88
|
+
from: process.env.GS_REPORTER_EMAIL,
|
|
89
|
+
to: process.env.GS_REPORTER_RECEIVER,
|
|
90
|
+
subject,
|
|
91
|
+
html,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return NextResponse.json({ success: true });
|
|
95
|
+
} catch (err: any) {
|
|
96
|
+
console.error("gs-error-reporter mail route failed:", err);
|
|
97
|
+
return NextResponse.json({ success: false }, { status: 500 });
|
|
98
|
+
}
|
|
99
|
+
}
|