@ngn-net/nestjs-telescope 0.1.1
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.md +110 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +7 -0
- package/dist/controllers/telescope.controller.d.ts +17 -0
- package/dist/controllers/telescope.controller.js +126 -0
- package/dist/enums/entry-type.enum.d.ts +14 -0
- package/dist/enums/entry-type.enum.js +19 -0
- package/dist/guards/telescope-jwt.guard.d.ts +7 -0
- package/dist/guards/telescope-jwt.guard.js +44 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +25 -0
- package/dist/interfaces/entry.interface.d.ts +10 -0
- package/dist/interfaces/entry.interface.js +2 -0
- package/dist/interfaces/telescope-options.interface.d.ts +13 -0
- package/dist/interfaces/telescope-options.interface.js +2 -0
- package/dist/storage/entities/telescope-entry.entity.d.ts +10 -0
- package/dist/storage/entities/telescope-entry.entity.js +56 -0
- package/dist/storage/telescope-repository.service.d.ts +18 -0
- package/dist/storage/telescope-repository.service.js +77 -0
- package/dist/telescope.module.d.ts +4 -0
- package/dist/telescope.module.js +84 -0
- package/dist/telescope.service.d.ts +13 -0
- package/dist/telescope.service.js +62 -0
- package/dist/watchers/cache.watcher.d.ts +7 -0
- package/dist/watchers/cache.watcher.js +62 -0
- package/dist/watchers/event.watcher.d.ts +13 -0
- package/dist/watchers/event.watcher.js +51 -0
- package/dist/watchers/exception.watcher.d.ts +8 -0
- package/dist/watchers/exception.watcher.js +44 -0
- package/dist/watchers/http-request.watcher.d.ts +8 -0
- package/dist/watchers/http-request.watcher.js +52 -0
- package/dist/watchers/log.watcher.d.ts +7 -0
- package/dist/watchers/log.watcher.js +63 -0
- package/dist/watchers/mail.watcher.d.ts +12 -0
- package/dist/watchers/mail.watcher.js +90 -0
- package/dist/watchers/query.watcher.d.ts +10 -0
- package/dist/watchers/query.watcher.js +52 -0
- package/dist/watchers/queue.watcher.d.ts +10 -0
- package/dist/watchers/queue.watcher.js +66 -0
- package/dist/watchers/redis.watcher.d.ts +7 -0
- package/dist/watchers/redis.watcher.js +74 -0
- package/dist/watchers/schedule.watcher.d.ts +9 -0
- package/dist/watchers/schedule.watcher.js +92 -0
- package/package.json +49 -0
- package/ui/index.html +1268 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @ngn-net/nestjs-telescope
|
|
2
|
+
|
|
3
|
+
A full-featured developer assistant and application monitoring suite for NestJS, inspired by Laravel Telescope. It monitors incoming HTTP requests, database queries, cache operations, background queue jobs, application events, outgoing mail, logs, unhandled exceptions, cron job schedules, and Redis commands.
|
|
4
|
+
|
|
5
|
+
All telemetry records are persisted to a database table and accessible via a modern, high-fidelity dark-themed single-page application dashboard.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **HTTP Request Watcher**: Captures request methods, URLs, IP addresses, payloads, headers, durations, and response status/bodies.
|
|
12
|
+
- **Database Query Watcher**: Hooks into TypeORM entity cycles (`INSERT`, `UPDATE`, `REMOVE`) to record query queries, parameters, and affected data.
|
|
13
|
+
- **Cache Watcher**: Wraps Cache Manager operations (`GET`, `SET`, `DEL`) to trace cache activity.
|
|
14
|
+
- **Queue Watcher**: Tracks BullMQ background jobs and captures completion values and job failures.
|
|
15
|
+
- **Event Watcher**: Intercepts `EventEmitter2` events and lists firing metrics.
|
|
16
|
+
- **Mail Watcher**: Proxies Nodemailer transports to log outgoing mail contents.
|
|
17
|
+
- **Log Watcher**: Integrates console logs (`log`, `warn`, `error`, `debug`) directly into the dashboard.
|
|
18
|
+
- **Exception Watcher**: Hooks into HTTP streams to catch and log request failures, statuses, and stack traces.
|
|
19
|
+
- **Schedule Watcher**: Monitors cron execution runtimes and statuses from `@nestjs/schedule`.
|
|
20
|
+
- **Redis Watcher**: Hooks into `ioredis` to report Redis command durations and arguments.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Install the package alongside its peer dependencies:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @ngn-net/nestjs-telescope @nestjs/typeorm typeorm @nestjs/jwt @nestjs/passport passport-jwt
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Ensure you have TypeORM initialized in your NestJS application.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Import the `TelescopeModule` in your root `AppModule`:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Module } from '@nestjs/common';
|
|
42
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
43
|
+
import { TelescopeModule, EntryType } from '@ngn-net/nestjs-telescope';
|
|
44
|
+
|
|
45
|
+
@Module({
|
|
46
|
+
imports: [
|
|
47
|
+
TypeOrmModule.forRoot({
|
|
48
|
+
type: 'postgres', // or mysql, sqlite, etc.
|
|
49
|
+
host: 'localhost',
|
|
50
|
+
port: 5432,
|
|
51
|
+
username: 'db_user',
|
|
52
|
+
password: 'db_password',
|
|
53
|
+
database: 'app_db',
|
|
54
|
+
autoLoadEntities: true,
|
|
55
|
+
synchronize: true, // Telescope creates telescope_entries table automatically
|
|
56
|
+
}),
|
|
57
|
+
TelescopeModule.forRoot({
|
|
58
|
+
path: 'telescope', // Access at /telescope
|
|
59
|
+
jwtSecret: 'your-secret-key', // Secret key for authentication
|
|
60
|
+
maxEntries: 1000, // Maximum entries to retain in database
|
|
61
|
+
enabledEntryTypes: [
|
|
62
|
+
EntryType.REQUEST,
|
|
63
|
+
EntryType.QUERY,
|
|
64
|
+
EntryType.CACHE,
|
|
65
|
+
EntryType.JOB,
|
|
66
|
+
EntryType.EVENT,
|
|
67
|
+
EntryType.LOG,
|
|
68
|
+
EntryType.EXCEPTION,
|
|
69
|
+
EntryType.SCHEDULED_TASK,
|
|
70
|
+
EntryType.REDIS,
|
|
71
|
+
EntryType.MAIL,
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
],
|
|
75
|
+
})
|
|
76
|
+
export class AppModule {}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Authentication Configuration
|
|
80
|
+
|
|
81
|
+
To secure the Telescope dashboard, configure the dashboard access password in your environment variables:
|
|
82
|
+
|
|
83
|
+
```env
|
|
84
|
+
TELESCOPE_PASSWORD=my-secure-password
|
|
85
|
+
TELESCOPE_JWT_SECRET=super-secret-token
|
|
86
|
+
```
|
|
87
|
+
*If not set, the password defaults to `password`.*
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Dashboard Access
|
|
92
|
+
|
|
93
|
+
1. Run your NestJS application.
|
|
94
|
+
2. Open your browser and navigate to `http://localhost:3000/telescope`.
|
|
95
|
+
3. Enter the password configured via `TELESCOPE_PASSWORD`.
|
|
96
|
+
4. Monitor requests, logs, queries, and errors in real-time.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Development & Test
|
|
101
|
+
|
|
102
|
+
To build the package:
|
|
103
|
+
```bash
|
|
104
|
+
npm run build
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To run Jest tests:
|
|
108
|
+
```bash
|
|
109
|
+
npm run test
|
|
110
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TELESCOPE_DB_CONNECTION = exports.TELESCOPE_JWT_SECRET = exports.TELESCOPE_PATH = void 0;
|
|
4
|
+
// src/constants.ts
|
|
5
|
+
exports.TELESCOPE_PATH = process.env.TELESCOPE_PATH || 'telescope';
|
|
6
|
+
exports.TELESCOPE_JWT_SECRET = process.env.TELESCOPE_JWT_SECRET || 'change-me';
|
|
7
|
+
exports.TELESCOPE_DB_CONNECTION = process.env.TELESCOPE_DB_CONNECTION || 'default';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TelescopeRepository } from '../storage/telescope-repository.service';
|
|
2
|
+
import { JwtService } from '@nestjs/jwt';
|
|
3
|
+
import { Request, Response } from 'express';
|
|
4
|
+
export declare class TelescopeController {
|
|
5
|
+
private readonly repo;
|
|
6
|
+
private readonly jwtService;
|
|
7
|
+
constructor(repo: TelescopeRepository, jwtService: JwtService);
|
|
8
|
+
login(body: any): Promise<{
|
|
9
|
+
token: string;
|
|
10
|
+
}>;
|
|
11
|
+
getEntries(type?: string, search?: string, limit?: string): Promise<import("..").TelescopeEntry[]>;
|
|
12
|
+
getEntry(uuid: string): Promise<import("..").TelescopeEntry>;
|
|
13
|
+
clearEntries(): Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
serveStatic(req: Request, res: Response): void | Response<any, Record<string, any>>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TelescopeController = void 0;
|
|
16
|
+
// src/controllers/telescope.controller.ts
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const telescope_repository_service_1 = require("../storage/telescope-repository.service");
|
|
19
|
+
const jwt_1 = require("@nestjs/jwt");
|
|
20
|
+
const telescope_jwt_guard_1 = require("../guards/telescope-jwt.guard");
|
|
21
|
+
const constants_1 = require("../constants");
|
|
22
|
+
const path_1 = require("path");
|
|
23
|
+
const fs_1 = require("fs");
|
|
24
|
+
let TelescopeController = class TelescopeController {
|
|
25
|
+
repo;
|
|
26
|
+
jwtService;
|
|
27
|
+
constructor(repo, jwtService) {
|
|
28
|
+
this.repo = repo;
|
|
29
|
+
this.jwtService = jwtService;
|
|
30
|
+
}
|
|
31
|
+
async login(body) {
|
|
32
|
+
const password = process.env.TELESCOPE_PASSWORD || 'password';
|
|
33
|
+
if (body.password === password) {
|
|
34
|
+
const payload = { username: 'telescope' };
|
|
35
|
+
return {
|
|
36
|
+
token: await this.jwtService.signAsync(payload),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
throw new common_1.UnauthorizedException('Invalid credentials');
|
|
40
|
+
}
|
|
41
|
+
async getEntries(type, search, limit) {
|
|
42
|
+
const limitNum = limit ? parseInt(limit, 10) : 100;
|
|
43
|
+
return await this.repo.queryEntries({ type, search, limit: limitNum });
|
|
44
|
+
}
|
|
45
|
+
async getEntry(uuid) {
|
|
46
|
+
const entry = await this.repo.findByUuid(uuid);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
throw new common_1.UnauthorizedException('Entry not found');
|
|
49
|
+
}
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
async clearEntries() {
|
|
53
|
+
await this.repo.clearAll();
|
|
54
|
+
return { success: true };
|
|
55
|
+
}
|
|
56
|
+
// Fallback wildcard to serve static assets and UI dashboard
|
|
57
|
+
serveStatic(req, res) {
|
|
58
|
+
const rawPath = req.path;
|
|
59
|
+
// Strip TELESCOPE_PATH prefix to find the asset inside the ui directory
|
|
60
|
+
// e.g. /telescope/assets/main.js -> /assets/main.js
|
|
61
|
+
let assetPath = rawPath.replace(new RegExp(`^/${constants_1.TELESCOPE_PATH}`), '');
|
|
62
|
+
if (assetPath.startsWith('/')) {
|
|
63
|
+
assetPath = assetPath.substring(1);
|
|
64
|
+
}
|
|
65
|
+
// Default to index.html if empty or route is not file-like (support client SPA routing)
|
|
66
|
+
if (!assetPath || !assetPath.includes('.')) {
|
|
67
|
+
assetPath = 'index.html';
|
|
68
|
+
}
|
|
69
|
+
const filePath = (0, path_1.join)(__dirname, '../../ui', assetPath);
|
|
70
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
71
|
+
return res.sendFile(filePath);
|
|
72
|
+
}
|
|
73
|
+
// Fall back to index.html
|
|
74
|
+
const fallbackPath = (0, path_1.join)(__dirname, '../../ui/index.html');
|
|
75
|
+
if ((0, fs_1.existsSync)(fallbackPath)) {
|
|
76
|
+
return res.sendFile(fallbackPath);
|
|
77
|
+
}
|
|
78
|
+
return res.status(404).send('Telescope UI files not found. Ensure build copy succeeded.');
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.TelescopeController = TelescopeController;
|
|
82
|
+
__decorate([
|
|
83
|
+
(0, common_1.Post)('api/login'),
|
|
84
|
+
__param(0, (0, common_1.Body)()),
|
|
85
|
+
__metadata("design:type", Function),
|
|
86
|
+
__metadata("design:paramtypes", [Object]),
|
|
87
|
+
__metadata("design:returntype", Promise)
|
|
88
|
+
], TelescopeController.prototype, "login", null);
|
|
89
|
+
__decorate([
|
|
90
|
+
(0, common_1.UseGuards)(telescope_jwt_guard_1.JwtAuthGuard),
|
|
91
|
+
(0, common_1.Get)('api/entries'),
|
|
92
|
+
__param(0, (0, common_1.Query)('type')),
|
|
93
|
+
__param(1, (0, common_1.Query)('search')),
|
|
94
|
+
__param(2, (0, common_1.Query)('limit')),
|
|
95
|
+
__metadata("design:type", Function),
|
|
96
|
+
__metadata("design:paramtypes", [String, String, String]),
|
|
97
|
+
__metadata("design:returntype", Promise)
|
|
98
|
+
], TelescopeController.prototype, "getEntries", null);
|
|
99
|
+
__decorate([
|
|
100
|
+
(0, common_1.UseGuards)(telescope_jwt_guard_1.JwtAuthGuard),
|
|
101
|
+
(0, common_1.Get)('api/entries/:uuid'),
|
|
102
|
+
__param(0, (0, common_1.Param)('uuid')),
|
|
103
|
+
__metadata("design:type", Function),
|
|
104
|
+
__metadata("design:paramtypes", [String]),
|
|
105
|
+
__metadata("design:returntype", Promise)
|
|
106
|
+
], TelescopeController.prototype, "getEntry", null);
|
|
107
|
+
__decorate([
|
|
108
|
+
(0, common_1.UseGuards)(telescope_jwt_guard_1.JwtAuthGuard),
|
|
109
|
+
(0, common_1.Delete)('api/entries'),
|
|
110
|
+
__metadata("design:type", Function),
|
|
111
|
+
__metadata("design:paramtypes", []),
|
|
112
|
+
__metadata("design:returntype", Promise)
|
|
113
|
+
], TelescopeController.prototype, "clearEntries", null);
|
|
114
|
+
__decorate([
|
|
115
|
+
(0, common_1.Get)('*'),
|
|
116
|
+
__param(0, (0, common_1.Req)()),
|
|
117
|
+
__param(1, (0, common_1.Res)()),
|
|
118
|
+
__metadata("design:type", Function),
|
|
119
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
120
|
+
__metadata("design:returntype", void 0)
|
|
121
|
+
], TelescopeController.prototype, "serveStatic", null);
|
|
122
|
+
exports.TelescopeController = TelescopeController = __decorate([
|
|
123
|
+
(0, common_1.Controller)(constants_1.TELESCOPE_PATH),
|
|
124
|
+
__metadata("design:paramtypes", [telescope_repository_service_1.TelescopeRepository,
|
|
125
|
+
jwt_1.JwtService])
|
|
126
|
+
], TelescopeController);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare enum EntryType {
|
|
2
|
+
REQUEST = "request",
|
|
3
|
+
QUERY = "query",
|
|
4
|
+
CACHE = "cache",
|
|
5
|
+
JOB = "job",
|
|
6
|
+
EVENT = "event",
|
|
7
|
+
MAIL = "mail",
|
|
8
|
+
LOG = "log",
|
|
9
|
+
EXCEPTION = "exception",
|
|
10
|
+
SCHEDULED_TASK = "scheduled_task",
|
|
11
|
+
REDIS = "redis",
|
|
12
|
+
GATE = "gate",
|
|
13
|
+
DUMP = "dump"
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EntryType = void 0;
|
|
4
|
+
// src/enums/entry-type.enum.ts
|
|
5
|
+
var EntryType;
|
|
6
|
+
(function (EntryType) {
|
|
7
|
+
EntryType["REQUEST"] = "request";
|
|
8
|
+
EntryType["QUERY"] = "query";
|
|
9
|
+
EntryType["CACHE"] = "cache";
|
|
10
|
+
EntryType["JOB"] = "job";
|
|
11
|
+
EntryType["EVENT"] = "event";
|
|
12
|
+
EntryType["MAIL"] = "mail";
|
|
13
|
+
EntryType["LOG"] = "log";
|
|
14
|
+
EntryType["EXCEPTION"] = "exception";
|
|
15
|
+
EntryType["SCHEDULED_TASK"] = "scheduled_task";
|
|
16
|
+
EntryType["REDIS"] = "redis";
|
|
17
|
+
EntryType["GATE"] = "gate";
|
|
18
|
+
EntryType["DUMP"] = "dump";
|
|
19
|
+
})(EntryType || (exports.EntryType = EntryType = {}));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { JwtService } from '@nestjs/jwt';
|
|
3
|
+
export declare class JwtAuthGuard implements CanActivate {
|
|
4
|
+
private readonly jwtService;
|
|
5
|
+
constructor(jwtService: JwtService);
|
|
6
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.JwtAuthGuard = void 0;
|
|
13
|
+
// src/guards/telescope-jwt.guard.ts
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const jwt_1 = require("@nestjs/jwt");
|
|
16
|
+
let JwtAuthGuard = class JwtAuthGuard {
|
|
17
|
+
jwtService;
|
|
18
|
+
constructor(jwtService) {
|
|
19
|
+
this.jwtService = jwtService;
|
|
20
|
+
}
|
|
21
|
+
async canActivate(context) {
|
|
22
|
+
const request = context.switchToHttp().getRequest();
|
|
23
|
+
const authHeader = request.headers['authorization'];
|
|
24
|
+
if (!authHeader)
|
|
25
|
+
throw new common_1.UnauthorizedException('Missing Authorization header');
|
|
26
|
+
const [type, token] = authHeader.split(' ');
|
|
27
|
+
if (type !== 'Bearer' || !token)
|
|
28
|
+
throw new common_1.UnauthorizedException('Invalid Authorization format');
|
|
29
|
+
try {
|
|
30
|
+
const payload = await this.jwtService.verifyAsync(token);
|
|
31
|
+
// attach payload for downstream use if needed
|
|
32
|
+
request.user = payload;
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new common_1.UnauthorizedException('Invalid token');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.JwtAuthGuard = JwtAuthGuard;
|
|
41
|
+
exports.JwtAuthGuard = JwtAuthGuard = __decorate([
|
|
42
|
+
(0, common_1.Injectable)(),
|
|
43
|
+
__metadata("design:paramtypes", [jwt_1.JwtService])
|
|
44
|
+
], JwtAuthGuard);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './telescope.module';
|
|
2
|
+
export * from './telescope.service';
|
|
3
|
+
export * from './storage/telescope-repository.service';
|
|
4
|
+
export * from './storage/entities/telescope-entry.entity';
|
|
5
|
+
export * from './enums/entry-type.enum';
|
|
6
|
+
export * from './interfaces/telescope-options.interface';
|
|
7
|
+
export * from './interfaces/entry.interface';
|
|
8
|
+
export * from './guards/telescope-jwt.guard';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// src/index.ts
|
|
18
|
+
__exportStar(require("./telescope.module"), exports);
|
|
19
|
+
__exportStar(require("./telescope.service"), exports);
|
|
20
|
+
__exportStar(require("./storage/telescope-repository.service"), exports);
|
|
21
|
+
__exportStar(require("./storage/entities/telescope-entry.entity"), exports);
|
|
22
|
+
__exportStar(require("./enums/entry-type.enum"), exports);
|
|
23
|
+
__exportStar(require("./interfaces/telescope-options.interface"), exports);
|
|
24
|
+
__exportStar(require("./interfaces/entry.interface"), exports);
|
|
25
|
+
__exportStar(require("./guards/telescope-jwt.guard"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EntryType } from '../enums/entry-type.enum';
|
|
2
|
+
export interface TelescopeOptions {
|
|
3
|
+
/** Base path for UI (default: /telescope) */
|
|
4
|
+
path?: string;
|
|
5
|
+
/** JWT secret used for UI authentication */
|
|
6
|
+
jwtSecret?: string;
|
|
7
|
+
/** Which entry types to enable */
|
|
8
|
+
enabledEntryTypes?: EntryType[];
|
|
9
|
+
/** Max number of entries to retain (pruning) */
|
|
10
|
+
maxEntries?: number;
|
|
11
|
+
/** Additional middleware to apply to UI routes */
|
|
12
|
+
uiMiddleware?: any[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TelescopeEntry = void 0;
|
|
13
|
+
// src/storage/entities/telescope-entry.entity.ts
|
|
14
|
+
const typeorm_1 = require("typeorm");
|
|
15
|
+
const entry_type_enum_1 = require("../../enums/entry-type.enum");
|
|
16
|
+
let TelescopeEntry = class TelescopeEntry {
|
|
17
|
+
id;
|
|
18
|
+
uuid;
|
|
19
|
+
batchId;
|
|
20
|
+
type;
|
|
21
|
+
content;
|
|
22
|
+
recordedAt;
|
|
23
|
+
familyHash;
|
|
24
|
+
};
|
|
25
|
+
exports.TelescopeEntry = TelescopeEntry;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, typeorm_1.PrimaryGeneratedColumn)(),
|
|
28
|
+
__metadata("design:type", Number)
|
|
29
|
+
], TelescopeEntry.prototype, "id", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, typeorm_1.Column)({ type: 'uuid' }),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], TelescopeEntry.prototype, "uuid", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], TelescopeEntry.prototype, "batchId", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, typeorm_1.Column)({ type: 'enum', enum: entry_type_enum_1.EntryType }),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], TelescopeEntry.prototype, "type", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, typeorm_1.Column)({ type: 'jsonb' }),
|
|
44
|
+
__metadata("design:type", Object)
|
|
45
|
+
], TelescopeEntry.prototype, "content", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
(0, typeorm_1.CreateDateColumn)({ type: 'timestamp' }),
|
|
48
|
+
__metadata("design:type", Date)
|
|
49
|
+
], TelescopeEntry.prototype, "recordedAt", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
52
|
+
__metadata("design:type", String)
|
|
53
|
+
], TelescopeEntry.prototype, "familyHash", void 0);
|
|
54
|
+
exports.TelescopeEntry = TelescopeEntry = __decorate([
|
|
55
|
+
(0, typeorm_1.Entity)({ name: 'telescope_entries' })
|
|
56
|
+
], TelescopeEntry);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Repository, DeleteResult } from 'typeorm';
|
|
2
|
+
import { TelescopeEntry } from './entities/telescope-entry.entity';
|
|
3
|
+
import { EntryType } from '../enums/entry-type.enum';
|
|
4
|
+
export declare class TelescopeRepository {
|
|
5
|
+
private readonly entryRepo;
|
|
6
|
+
constructor(entryRepo: Repository<TelescopeEntry>);
|
|
7
|
+
save(entry: Partial<TelescopeEntry>): Promise<TelescopeEntry>;
|
|
8
|
+
findAll(limit?: number): Promise<TelescopeEntry[]>;
|
|
9
|
+
findByType(type: EntryType, limit?: number): Promise<TelescopeEntry[]>;
|
|
10
|
+
prune(maxEntries: number): Promise<void>;
|
|
11
|
+
queryEntries(filters: {
|
|
12
|
+
type?: string;
|
|
13
|
+
search?: string;
|
|
14
|
+
limit?: number;
|
|
15
|
+
}): Promise<TelescopeEntry[]>;
|
|
16
|
+
findByUuid(uuid: string): Promise<TelescopeEntry | null>;
|
|
17
|
+
clearAll(): Promise<DeleteResult>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TelescopeRepository = void 0;
|
|
16
|
+
// src/storage/telescope-repository.service.ts
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
19
|
+
const typeorm_2 = require("typeorm");
|
|
20
|
+
const telescope_entry_entity_1 = require("./entities/telescope-entry.entity");
|
|
21
|
+
let TelescopeRepository = class TelescopeRepository {
|
|
22
|
+
entryRepo;
|
|
23
|
+
constructor(entryRepo) {
|
|
24
|
+
this.entryRepo = entryRepo;
|
|
25
|
+
}
|
|
26
|
+
async save(entry) {
|
|
27
|
+
const ent = this.entryRepo.create(entry);
|
|
28
|
+
return await this.entryRepo.save(ent);
|
|
29
|
+
}
|
|
30
|
+
async findAll(limit = 100) {
|
|
31
|
+
return await this.entryRepo.find({ order: { recordedAt: 'DESC' }, take: limit });
|
|
32
|
+
}
|
|
33
|
+
async findByType(type, limit = 100) {
|
|
34
|
+
return await this.entryRepo.find({ where: { type }, order: { recordedAt: 'DESC' }, take: limit });
|
|
35
|
+
}
|
|
36
|
+
async prune(maxEntries) {
|
|
37
|
+
const count = await this.entryRepo.count();
|
|
38
|
+
if (count <= maxEntries)
|
|
39
|
+
return;
|
|
40
|
+
const excess = count - maxEntries;
|
|
41
|
+
const oldest = await this.entryRepo.find({ order: { recordedAt: 'ASC' }, take: excess });
|
|
42
|
+
const ids = oldest.map(e => e.id);
|
|
43
|
+
await this.entryRepo.delete(ids);
|
|
44
|
+
}
|
|
45
|
+
async queryEntries(filters) {
|
|
46
|
+
const limit = filters.limit || 100;
|
|
47
|
+
const findOptions = {
|
|
48
|
+
order: { recordedAt: 'DESC' },
|
|
49
|
+
take: limit,
|
|
50
|
+
};
|
|
51
|
+
if (filters.type) {
|
|
52
|
+
findOptions.where = { type: filters.type };
|
|
53
|
+
}
|
|
54
|
+
let entries = await this.entryRepo.find(findOptions);
|
|
55
|
+
if (filters.search) {
|
|
56
|
+
const searchLower = filters.search.toLowerCase();
|
|
57
|
+
entries = entries.filter(entry => {
|
|
58
|
+
const uuidMatch = entry.uuid.toLowerCase().includes(searchLower);
|
|
59
|
+
const contentMatch = JSON.stringify(entry.content).toLowerCase().includes(searchLower);
|
|
60
|
+
return uuidMatch || contentMatch;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return entries;
|
|
64
|
+
}
|
|
65
|
+
async findByUuid(uuid) {
|
|
66
|
+
return await this.entryRepo.findOne({ where: { uuid } });
|
|
67
|
+
}
|
|
68
|
+
async clearAll() {
|
|
69
|
+
return await this.entryRepo.delete({});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.TelescopeRepository = TelescopeRepository;
|
|
73
|
+
exports.TelescopeRepository = TelescopeRepository = __decorate([
|
|
74
|
+
(0, common_1.Injectable)(),
|
|
75
|
+
__param(0, (0, typeorm_1.InjectRepository)(telescope_entry_entity_1.TelescopeEntry)),
|
|
76
|
+
__metadata("design:paramtypes", [typeorm_2.Repository])
|
|
77
|
+
], TelescopeRepository);
|