team-chat 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +458 -2
- package/agents/rules.md +52 -0
- package/agents/runtimes/codex.md +14 -0
- package/bin/bridge +3 -0
- package/bin/runner +3 -0
- package/cli/lib/agents.js +97 -0
- package/cli/lib/docs.js +151 -0
- package/cli/lib/local.js +41 -0
- package/cli/lib/server.js +52 -0
- package/cli/lib/util.js +107 -0
- package/cli/team-chat.js +383 -0
- package/host/bridge/attempts.js +43 -0
- package/host/bridge/bridge.js +326 -0
- package/host/bridge/config.js +105 -0
- package/host/bridge/knowledge-outbox.js +96 -0
- package/host/bridge/launcher.js +126 -0
- package/host/bridge/main.js +87 -0
- package/host/bridge/progress.js +74 -0
- package/host/bridge/prompt.js +97 -0
- package/host/bridge/run-archive.js +200 -0
- package/host/bridge/thread.js +44 -0
- package/host/bridge/triage.js +97 -0
- package/host/bridge/workdir.js +41 -0
- package/host/knowledge/card.js +43 -0
- package/host/knowledge/errors.js +50 -0
- package/host/knowledge/lesson.js +153 -0
- package/host/knowledge/process.js +160 -0
- package/host/knowledge/scrub.js +29 -0
- package/host/knowledge/shell.js +74 -0
- package/host/knowledge/stack.js +229 -0
- package/host/knowledge/transcript.js +82 -0
- package/host/lib/chat-api.js +120 -0
- package/host/lib/log.js +6 -0
- package/host/lib/pid-lock.js +65 -0
- package/host/lib/shell-words.js +45 -0
- package/host/runner/bridge-process.js +82 -0
- package/host/runner/config.js +81 -0
- package/host/runner/main.js +69 -0
- package/host/runner/retention.js +40 -0
- package/host/runner/runner.js +161 -0
- package/package.json +61 -5
- package/server/dist/src/app.js +25 -0
- package/server/dist/src/app.module.js +50 -0
- package/server/dist/src/attachments/attachment-store.js +111 -0
- package/server/dist/src/attachments/attachments.controller.js +67 -0
- package/server/dist/src/attachments/mime.js +84 -0
- package/server/dist/src/attachments/uploads.interceptor.js +72 -0
- package/server/dist/src/auth/participant.guard.js +53 -0
- package/server/dist/src/bridges/bridge-restart.service.js +80 -0
- package/server/dist/src/bridges/bridges.controller.js +47 -0
- package/server/dist/src/chats/chats.controller.js +83 -0
- package/server/dist/src/chats/chats.service.js +128 -0
- package/server/dist/src/collector/collector.controller.js +71 -0
- package/server/dist/src/collector/collector.module.js +23 -0
- package/server/dist/src/collector/collector.service.js +111 -0
- package/server/dist/src/common/clock.js +20 -0
- package/server/dist/src/common/errors.js +37 -0
- package/server/dist/src/common/format.js +14 -0
- package/server/dist/src/common/http.js +17 -0
- package/server/dist/src/common/slug.js +28 -0
- package/server/dist/src/common/tokens.js +19 -0
- package/server/dist/src/common/validation.js +41 -0
- package/server/dist/src/config/config.js +39 -0
- package/server/dist/src/conversation.module.js +41 -0
- package/server/dist/src/db/database.js +73 -0
- package/server/dist/src/db/database.module.js +51 -0
- package/server/dist/src/db/schema.js +212 -0
- package/server/dist/src/decisions/decisions.controller.js +55 -0
- package/server/dist/src/decisions/decisions.service.js +109 -0
- package/server/dist/src/documents/documents.controller.js +87 -0
- package/server/dist/src/documents/documents.service.js +189 -0
- package/server/dist/src/health/health.controller.js +29 -0
- package/server/dist/src/knowledge/knowledge.controller.js +83 -0
- package/server/dist/src/knowledge/knowledge.module.js +25 -0
- package/server/dist/src/knowledge/knowledge.service.js +189 -0
- package/server/dist/src/local/docs-sync.service.js +97 -0
- package/server/dist/src/local/local.controller.js +197 -0
- package/server/dist/src/local/local.guard.js +55 -0
- package/server/dist/src/local/local.module.js +30 -0
- package/server/dist/src/local/setup.service.js +154 -0
- package/server/dist/src/local/transfer.service.js +151 -0
- package/server/dist/src/main.js +33 -0
- package/server/dist/src/mcp/chat-tools.js +282 -0
- package/server/dist/src/mcp/mcp.controller.js +58 -0
- package/server/dist/src/mcp/mcp.module.js +24 -0
- package/server/dist/src/mentions/mention-state.js +80 -0
- package/server/dist/src/mentions/mentions.controller.js +100 -0
- package/server/dist/src/mentions/mentions.service.js +203 -0
- package/server/dist/src/messages/message.presenter.js +102 -0
- package/server/dist/src/messages/messages.service.js +149 -0
- package/server/dist/src/messages/thread.controller.js +179 -0
- package/server/dist/src/participants/directory.service.js +68 -0
- package/server/dist/src/participants/participants.module.js +24 -0
- package/server/dist/src/participants/participants.service.js +84 -0
- package/server/dist/src/settings/settings.service.js +42 -0
- package/server/dist/src/sharing/consent.js +16 -0
- package/server/dist/src/sharing/sharing.module.js +22 -0
- package/server/dist/src/sharing/sharing.service.js +175 -0
- package/server/dist/src/team/runner.service.js +75 -0
- package/server/dist/src/team/team.controller.js +178 -0
- package/server/dist/src/team/team.module.js +25 -0
- package/server/dist/src/team/team.service.js +280 -0
- package/server/dist/src/web/template.js +57 -0
- package/server/dist/src/web/web-login.service.js +74 -0
- package/server/dist/src/web/web.controller.js +203 -0
- package/server/dist/src/web/web.module.js +25 -0
- package/server/drizzle/0000_init.sql +169 -0
- package/server/drizzle/0001_seed_roles.sql +25 -0
- package/server/drizzle/0002_knowledge.sql +17 -0
- package/server/drizzle/0003_sharing.sql +30 -0
- package/server/drizzle/meta/0000_snapshot.json +1224 -0
- package/server/drizzle/meta/0001_snapshot.json +1224 -0
- package/server/drizzle/meta/0002_snapshot.json +1359 -0
- package/server/drizzle/meta/0003_snapshot.json +1545 -0
- package/server/drizzle/meta/_journal.json +34 -0
- package/server/package.json +1 -0
- package/server/views/chat.html +865 -0
- package/server/views/documents.html +204 -0
- package/server/views/login.html +31 -0
- package/server/views/setup.html +60 -0
- package/server/views/team.html +369 -0
|
@@ -0,0 +1,71 @@
|
|
|
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.CollectorController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const errors_1 = require("../common/errors");
|
|
18
|
+
const http_1 = require("../common/http");
|
|
19
|
+
const config_1 = require("../config/config");
|
|
20
|
+
const collector_service_1 = require("./collector.service");
|
|
21
|
+
/** Пути сборщика есть только у сервера-сборщика (CHAT_COLLECTOR=1); у обычного чата их нет вовсе. */
|
|
22
|
+
let CollectorEnabled = class CollectorEnabled {
|
|
23
|
+
config;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.config = config;
|
|
26
|
+
}
|
|
27
|
+
canActivate() {
|
|
28
|
+
if (!this.config.collector)
|
|
29
|
+
throw (0, errors_1.notFound)();
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
CollectorEnabled = __decorate([
|
|
34
|
+
(0, common_1.Injectable)(),
|
|
35
|
+
__param(0, (0, common_1.Inject)(config_1.SERVER_CONFIG)),
|
|
36
|
+
__metadata("design:paramtypes", [Object])
|
|
37
|
+
], CollectorEnabled);
|
|
38
|
+
/** Приём опыта от установок. Управление собранным — служебными путями этой машины. */
|
|
39
|
+
let CollectorController = class CollectorController {
|
|
40
|
+
collector;
|
|
41
|
+
constructor(collector) {
|
|
42
|
+
this.collector = collector;
|
|
43
|
+
}
|
|
44
|
+
register() {
|
|
45
|
+
return this.collector.register();
|
|
46
|
+
}
|
|
47
|
+
async receive(request, body) {
|
|
48
|
+
return this.collector.receive(await this.collector.installation((0, http_1.bearerToken)(request)), body);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
exports.CollectorController = CollectorController;
|
|
52
|
+
__decorate([
|
|
53
|
+
(0, common_1.Post)('installations'),
|
|
54
|
+
__metadata("design:type", Function),
|
|
55
|
+
__metadata("design:paramtypes", []),
|
|
56
|
+
__metadata("design:returntype", void 0)
|
|
57
|
+
], CollectorController.prototype, "register", null);
|
|
58
|
+
__decorate([
|
|
59
|
+
(0, common_1.Post)('cards'),
|
|
60
|
+
(0, common_1.HttpCode)(200),
|
|
61
|
+
__param(0, (0, common_1.Req)()),
|
|
62
|
+
__param(1, (0, common_1.Body)()),
|
|
63
|
+
__metadata("design:type", Function),
|
|
64
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
65
|
+
__metadata("design:returntype", Promise)
|
|
66
|
+
], CollectorController.prototype, "receive", null);
|
|
67
|
+
exports.CollectorController = CollectorController = __decorate([
|
|
68
|
+
(0, common_1.Controller)('api/collect'),
|
|
69
|
+
(0, common_1.UseGuards)(CollectorEnabled),
|
|
70
|
+
__metadata("design:paramtypes", [collector_service_1.CollectorService])
|
|
71
|
+
], CollectorController);
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CollectorModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const collector_controller_1 = require("./collector.controller");
|
|
12
|
+
const collector_service_1 = require("./collector.service");
|
|
13
|
+
/** Сборщик опыта — сервер автора пакета (CHAT_COLLECTOR=1). */
|
|
14
|
+
let CollectorModule = class CollectorModule {
|
|
15
|
+
};
|
|
16
|
+
exports.CollectorModule = CollectorModule;
|
|
17
|
+
exports.CollectorModule = CollectorModule = __decorate([
|
|
18
|
+
(0, common_1.Module)({
|
|
19
|
+
providers: [collector_service_1.CollectorService],
|
|
20
|
+
controllers: [collector_controller_1.CollectorController],
|
|
21
|
+
exports: [collector_service_1.CollectorService],
|
|
22
|
+
})
|
|
23
|
+
], CollectorModule);
|
|
@@ -0,0 +1,111 @@
|
|
|
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.CollectorService = exports.MAX_BATCH = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
18
|
+
const zod_1 = require("zod");
|
|
19
|
+
const clock_1 = require("../common/clock");
|
|
20
|
+
const errors_1 = require("../common/errors");
|
|
21
|
+
const format_1 = require("../common/format");
|
|
22
|
+
const tokens_1 = require("../common/tokens");
|
|
23
|
+
const validation_1 = require("../common/validation");
|
|
24
|
+
const database_module_1 = require("../db/database.module");
|
|
25
|
+
const schema_1 = require("../db/schema");
|
|
26
|
+
const knowledge_service_1 = require("../knowledge/knowledge.service");
|
|
27
|
+
/** Карточек за раз: пачка укладывается в предел тела запроса (2 МБ) даже из самых больших. */
|
|
28
|
+
exports.MAX_BATCH = 20;
|
|
29
|
+
/** Карточек от одной установки — с запасом на годы работы; больше — что-то пошло не так. */
|
|
30
|
+
const MAX_PER_INSTALLATION = 20_000;
|
|
31
|
+
const uploadSchema = zod_1.z.object({
|
|
32
|
+
cards: zod_1.z.array(zod_1.z.object({
|
|
33
|
+
ref: zod_1.z.string().regex(/^[0-9a-f]{64}$/, { error: 'ref — sha256 записи запуска' }),
|
|
34
|
+
card: knowledge_service_1.cardSchema,
|
|
35
|
+
})).min(1).max(exports.MAX_BATCH),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Сборщик опыта на сервере автора пакета. Установка регистрируется сама и получает токен;
|
|
39
|
+
* карточки приходят обезличенными — без чата, участников и ключей запусков. Удалять опыт
|
|
40
|
+
* может только владелец сборщика (служебные пути этой машины).
|
|
41
|
+
*/
|
|
42
|
+
let CollectorService = class CollectorService {
|
|
43
|
+
db;
|
|
44
|
+
constructor(db) {
|
|
45
|
+
this.db = db;
|
|
46
|
+
}
|
|
47
|
+
async register() {
|
|
48
|
+
const token = (0, tokens_1.randomToken)();
|
|
49
|
+
await this.db.insert(schema_1.collectorInstallations).values({ tokenHash: (0, tokens_1.hashToken)(token) });
|
|
50
|
+
return { token };
|
|
51
|
+
}
|
|
52
|
+
async installation(token) {
|
|
53
|
+
const [row] = token
|
|
54
|
+
? await this.db.select().from(schema_1.collectorInstallations).where((0, drizzle_orm_1.eq)(schema_1.collectorInstallations.tokenHash, (0, tokens_1.hashToken)(token)))
|
|
55
|
+
: [];
|
|
56
|
+
if (!row)
|
|
57
|
+
throw (0, errors_1.unauthenticated)('Установка не зарегистрирована у сборщика.');
|
|
58
|
+
return row;
|
|
59
|
+
}
|
|
60
|
+
/** Принять пачку карточек; та же запись запуска (ref) — та же карточка. */
|
|
61
|
+
async receive(installation, input) {
|
|
62
|
+
const { cards } = (0, validation_1.validate)(uploadSchema, input);
|
|
63
|
+
cards.forEach(({ card }, i) => {
|
|
64
|
+
if (JSON.stringify(card).length > knowledge_service_1.MAX_CARD)
|
|
65
|
+
throw errors_1.ValidationFailed.field(`cards.${i}.card`, 'Карточка слишком большая.');
|
|
66
|
+
});
|
|
67
|
+
const [{ stored }] = await this.db.select({ stored: (0, drizzle_orm_1.count)() }).from(schema_1.collectedCards)
|
|
68
|
+
.where((0, drizzle_orm_1.eq)(schema_1.collectedCards.installationId, installation.id));
|
|
69
|
+
if (stored + cards.length > MAX_PER_INSTALLATION) {
|
|
70
|
+
throw new errors_1.ApiError(common_1.HttpStatus.TOO_MANY_REQUESTS, 'too_many', 'Сборщик больше не принимает карточки этой установки.');
|
|
71
|
+
}
|
|
72
|
+
await this.db.transaction(async (tx) => {
|
|
73
|
+
for (const { ref, card } of cards) {
|
|
74
|
+
await tx.insert(schema_1.collectedCards).values({ installationId: installation.id, ref, card, format: card.format })
|
|
75
|
+
.onConflictDoUpdate({ target: [schema_1.collectedCards.installationId, schema_1.collectedCards.ref], set: { card, format: card.format } });
|
|
76
|
+
}
|
|
77
|
+
await tx.update(schema_1.collectorInstallations).set({ lastSeenAt: (0, clock_1.now)() }).where((0, drizzle_orm_1.eq)(schema_1.collectorInstallations.id, installation.id));
|
|
78
|
+
});
|
|
79
|
+
return { accepted: cards.length };
|
|
80
|
+
}
|
|
81
|
+
/** Что собрано: установки и сколько от каждой карточек, с уроком и без. */
|
|
82
|
+
async overview() {
|
|
83
|
+
const withLesson = (0, drizzle_orm_1.sql) `count(*) filter (where ${schema_1.collectedCards.card} -> 'solution' ->> 'rule' is not null)`.mapWith(Number);
|
|
84
|
+
const rows = await this.db.select({
|
|
85
|
+
installation: schema_1.collectorInstallations, cards: (0, drizzle_orm_1.count)(schema_1.collectedCards.id), lessons: withLesson,
|
|
86
|
+
}).from(schema_1.collectorInstallations)
|
|
87
|
+
.leftJoin(schema_1.collectedCards, (0, drizzle_orm_1.eq)(schema_1.collectedCards.installationId, schema_1.collectorInstallations.id))
|
|
88
|
+
.groupBy(schema_1.collectorInstallations.id)
|
|
89
|
+
.orderBy((0, drizzle_orm_1.desc)(schema_1.collectorInstallations.id));
|
|
90
|
+
return {
|
|
91
|
+
installations: rows.map(r => ({
|
|
92
|
+
id: r.installation.id, cards: r.cards, lessons: r.lessons,
|
|
93
|
+
registered_at: (0, format_1.iso)(r.installation.createdAt), last_seen_at: (0, format_1.iso)(r.installation.lastSeenAt),
|
|
94
|
+
})),
|
|
95
|
+
cards: rows.reduce((sum, r) => sum + r.cards, 0),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Удалить установку со всем её опытом: её токен больше не действует. */
|
|
99
|
+
async removeInstallation(id) {
|
|
100
|
+
const [row] = await this.db.delete(schema_1.collectorInstallations).where((0, drizzle_orm_1.eq)(schema_1.collectorInstallations.id, id)).returning();
|
|
101
|
+
if (!row)
|
|
102
|
+
throw (0, errors_1.notFound)();
|
|
103
|
+
return { deleted: row.id };
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
exports.CollectorService = CollectorService;
|
|
107
|
+
exports.CollectorService = CollectorService = __decorate([
|
|
108
|
+
(0, common_1.Injectable)(),
|
|
109
|
+
__param(0, (0, common_1.Inject)(database_module_1.DATABASE)),
|
|
110
|
+
__metadata("design:paramtypes", [Object])
|
|
111
|
+
], CollectorService);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Время сервера — одно на процесс и сдвигаемое. Сроки здесь — часть поведения: сигнал
|
|
3
|
+
// жизни протухает через 90 с, повторный отчёт не двигает время исхода. Тесты проверяют
|
|
4
|
+
// это сдвигом часов, а не ожиданием. Через эти часы идут и сервисы, и отметки времени
|
|
5
|
+
// в схеме (created_at, updated_at), поэтому сдвиг согласован везде.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.now = now;
|
|
8
|
+
exports.travel = travel;
|
|
9
|
+
exports.resetClock = resetClock;
|
|
10
|
+
let offsetMs = 0;
|
|
11
|
+
function now() {
|
|
12
|
+
return new Date(Date.now() + offsetMs);
|
|
13
|
+
}
|
|
14
|
+
/** Только для тестов: перевести часы вперёд (или назад — отрицательным сдвигом). */
|
|
15
|
+
function travel(ms) {
|
|
16
|
+
offsetMs += ms;
|
|
17
|
+
}
|
|
18
|
+
function resetClock() {
|
|
19
|
+
offsetMs = 0;
|
|
20
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Ошибки API — в той форме, что отдавал PHP-сервер: {error, message}, а при неверных
|
|
3
|
+
// данных — 422 {message, errors: {поле: [тексты]}}. Страницы показывают эти тексты.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ValidationFailed = exports.conflict = exports.notFound = exports.forbidden = exports.unauthenticated = exports.ApiError = void 0;
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
class ApiError extends common_1.HttpException {
|
|
8
|
+
constructor(status, error, message) {
|
|
9
|
+
super(message === undefined ? { error } : { error, message }, status);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.ApiError = ApiError;
|
|
13
|
+
const unauthenticated = (message) => new ApiError(common_1.HttpStatus.UNAUTHORIZED, 'unauthenticated', message);
|
|
14
|
+
exports.unauthenticated = unauthenticated;
|
|
15
|
+
const forbidden = (message) => new ApiError(common_1.HttpStatus.FORBIDDEN, 'forbidden', message);
|
|
16
|
+
exports.forbidden = forbidden;
|
|
17
|
+
const notFound = (message) => new ApiError(common_1.HttpStatus.NOT_FOUND, 'not_found', message);
|
|
18
|
+
exports.notFound = notFound;
|
|
19
|
+
const conflict = (error, message) => new ApiError(common_1.HttpStatus.CONFLICT, error, message);
|
|
20
|
+
exports.conflict = conflict;
|
|
21
|
+
/** Неверные данные. message — первая ошибка и сколько их ещё, как у Laravel. */
|
|
22
|
+
class ValidationFailed extends common_1.HttpException {
|
|
23
|
+
errors;
|
|
24
|
+
constructor(errors) {
|
|
25
|
+
const messages = Object.values(errors).flat();
|
|
26
|
+
const more = messages.length - 1;
|
|
27
|
+
super({
|
|
28
|
+
message: messages[0] + (more > 0 ? ` (и ещё ${more})` : ''),
|
|
29
|
+
errors,
|
|
30
|
+
}, common_1.HttpStatus.UNPROCESSABLE_ENTITY);
|
|
31
|
+
this.errors = errors;
|
|
32
|
+
}
|
|
33
|
+
static field(field, message) {
|
|
34
|
+
return new ValidationFailed({ [field]: [message] });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.ValidationFailed = ValidationFailed;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Форматы, которые видят клиенты: время и выдержки текста — как отдавал PHP-сервер.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.iso = iso;
|
|
5
|
+
exports.excerpt = excerpt;
|
|
6
|
+
function iso(date) {
|
|
7
|
+
return date ? date.toISOString().replace(/\.\d{3}Z$/, '+00:00') : null;
|
|
8
|
+
}
|
|
9
|
+
/** Начало текста одной строкой — как Str::limit у Laravel: длиннее предела — обрезка и «...». */
|
|
10
|
+
function excerpt(body, length = 160) {
|
|
11
|
+
const line = body.replace(/\s+/gu, ' ').trim();
|
|
12
|
+
const chars = [...line];
|
|
13
|
+
return chars.length <= length ? line : chars.slice(0, length).join('').trimEnd() + '...';
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Мелочи HTTP-слоя, общие для контроллеров.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.bearerToken = bearerToken;
|
|
5
|
+
exports.limitFrom = limitFrom;
|
|
6
|
+
/** Токен из `Authorization: Bearer <token>` или null. */
|
|
7
|
+
function bearerToken(request) {
|
|
8
|
+
const match = /^Bearer\s+(.+)$/i.exec(request.headers.authorization ?? '');
|
|
9
|
+
return match ? match[1].trim() : null;
|
|
10
|
+
}
|
|
11
|
+
/** limit из запроса: 1–500, по умолчанию — окно ленты. Как у PHP-сервера. */
|
|
12
|
+
function limitFrom(raw, fallback = 50) {
|
|
13
|
+
if (raw === undefined || raw === '')
|
|
14
|
+
return fallback;
|
|
15
|
+
const value = Number.parseInt(String(raw), 10) || 0;
|
|
16
|
+
return Math.max(1, Math.min(500, value));
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Адрес из названия: «Kettu Driver» → kettu-driver, «Мой проект» → moi-proekt.
|
|
3
|
+
//
|
|
4
|
+
// Правила — как у Str::slug в Laravel (латиница, цифры, дефис; «@» → «at»), а таблица
|
|
5
|
+
// кириллицы — его же, кроме букв, которые он терял: ў и ҳ у него пропадали совсем
|
|
6
|
+
// («Ўзбек» → zbek), а ж/ч/ш сливались с з/ц/с. Адрес строится один раз при создании
|
|
7
|
+
// и дальше не меняется, так что прежние адреса это не задевает.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.slugify = slugify;
|
|
10
|
+
const CYRILLIC = {
|
|
11
|
+
а: 'a', б: 'b', в: 'v', г: 'g', д: 'd', е: 'e', ё: 'yo', ж: 'zh', з: 'z', и: 'i', й: 'y',
|
|
12
|
+
к: 'k', л: 'l', м: 'm', н: 'n', о: 'o', п: 'p', р: 'r', с: 's', т: 't', у: 'u', ф: 'f',
|
|
13
|
+
х: 'x', ц: 'ts', ч: 'ch', ш: 'sh', щ: 'shch', ъ: '', ы: 'y', ь: '', э: 'e', ю: 'yu', я: 'ya',
|
|
14
|
+
// узбекская кириллица
|
|
15
|
+
ў: 'o', қ: 'q', ғ: 'g', ҳ: 'h',
|
|
16
|
+
};
|
|
17
|
+
function slugify(title) {
|
|
18
|
+
return transliterate(title.toLowerCase())
|
|
19
|
+
.normalize('NFKD').replace(/\p{M}/gu, '') // é → e
|
|
20
|
+
.replace(/_+/g, '-')
|
|
21
|
+
.replaceAll('@', '-at-')
|
|
22
|
+
.replace(/[^a-z0-9\s-]+/g, '')
|
|
23
|
+
.replace(/[\s-]+/g, '-')
|
|
24
|
+
.replace(/^-+|-+$/g, '');
|
|
25
|
+
}
|
|
26
|
+
function transliterate(text) {
|
|
27
|
+
return [...text].map(c => CYRILLIC[c] ?? c).join('');
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Токены участников. Токен — длинная случайная строка, а не пароль: перебор нереален,
|
|
3
|
+
// поэтому хеш — быстрый sha256, без bcrypt на каждом запросе. Хеш тот же, что считал
|
|
4
|
+
// PHP-сервер: токены из перенесённой базы продолжают работать.
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.hashToken = hashToken;
|
|
10
|
+
exports.randomToken = randomToken;
|
|
11
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
12
|
+
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
13
|
+
function hashToken(plain) {
|
|
14
|
+
return node_crypto_1.default.createHash('sha256').update(plain).digest('hex');
|
|
15
|
+
}
|
|
16
|
+
/** Случайная строка из латиницы и цифр — как Str::random у Laravel. */
|
|
17
|
+
function randomToken(length = 48) {
|
|
18
|
+
return Array.from({ length }, () => ALPHABET[node_crypto_1.default.randomInt(ALPHABET.length)]).join('');
|
|
19
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Проверка входных данных — zod, и в HTTP, и в MCP: одни схемы, одни тексты ошибок.
|
|
3
|
+
//
|
|
4
|
+
// Типы-помощники принимают то же, что принимал Laravel: число — и строкой, флаг — и
|
|
5
|
+
// 0/1/"true". Иначе запрос с файлами (multipart, где всё — строки) не прошёл бы проверку.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.optionalText = exports.flag = exports.int = void 0;
|
|
8
|
+
exports.validate = validate;
|
|
9
|
+
const zod_1 = require("zod");
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
zod_1.z.config(zod_1.z.locales.ru());
|
|
12
|
+
/** Целое число — и строкой из формы. */
|
|
13
|
+
const int = () => zod_1.z.union([
|
|
14
|
+
zod_1.z.number().int(),
|
|
15
|
+
zod_1.z.string().regex(/^-?\d+$/, { error: 'Ожидается целое число' }).transform(Number),
|
|
16
|
+
]);
|
|
17
|
+
exports.int = int;
|
|
18
|
+
/** Да/нет — как boolean у Laravel: true/false, 1/0, "1"/"0", "true"/"false". */
|
|
19
|
+
const flag = () => zod_1.z.union([
|
|
20
|
+
zod_1.z.boolean(),
|
|
21
|
+
zod_1.z.literal(1).transform(() => true),
|
|
22
|
+
zod_1.z.literal(0).transform(() => false),
|
|
23
|
+
zod_1.z.enum(['1', 'true']).transform(() => true),
|
|
24
|
+
zod_1.z.enum(['0', 'false']).transform(() => false),
|
|
25
|
+
]);
|
|
26
|
+
exports.flag = flag;
|
|
27
|
+
/** Строка, которой может не быть или быть null — nullable у Laravel. */
|
|
28
|
+
const optionalText = (max) => zod_1.z.string().max(max).nullish();
|
|
29
|
+
exports.optionalText = optionalText;
|
|
30
|
+
/** Разобрать вход по схеме или ответить 422 с ошибками по полям. */
|
|
31
|
+
function validate(schema, input) {
|
|
32
|
+
const result = schema.safeParse(input ?? {});
|
|
33
|
+
if (result.success)
|
|
34
|
+
return result.data;
|
|
35
|
+
const errors = {};
|
|
36
|
+
for (const issue of result.error.issues) {
|
|
37
|
+
const field = issue.path.join('.') || 'body';
|
|
38
|
+
(errors[field] ??= []).push(issue.message);
|
|
39
|
+
}
|
|
40
|
+
throw new errors_1.ValidationFailed(errors);
|
|
41
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Настройки сервера — из окружения процесса. Окружение задаёт тот, кто сервер запускает:
|
|
3
|
+
// CLI team-chat, юнит systemd или человек в терминале.
|
|
4
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
+
};
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ConfigError = exports.DEFAULT_COLLECTOR_URL = exports.SERVER_CONFIG = void 0;
|
|
9
|
+
exports.serverConfig = serverConfig;
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
exports.SERVER_CONFIG = Symbol('SERVER_CONFIG');
|
|
13
|
+
/**
|
|
14
|
+
* Сборщик опыта автора пакета: сюда, с согласия владельца установки, уходят обезличенные
|
|
15
|
+
* карточки. Пусто — сборщика ещё нет, не уходит ничего (docs/plan-2026-10-24-knowledge.md, K6).
|
|
16
|
+
*/
|
|
17
|
+
exports.DEFAULT_COLLECTOR_URL = null;
|
|
18
|
+
class ConfigError extends Error {
|
|
19
|
+
}
|
|
20
|
+
exports.ConfigError = ConfigError;
|
|
21
|
+
function serverConfig(env = process.env) {
|
|
22
|
+
const port = Number(env.CHAT_PORT || 8600);
|
|
23
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
24
|
+
throw new ConfigError(`CHAT_PORT=${env.CHAT_PORT} — нужен номер порта`);
|
|
25
|
+
// По умолчанию слушаем только эту машину: наружу чат открывают сознательно (CHAT_HOST=0.0.0.0).
|
|
26
|
+
const host = env.CHAT_HOST || '127.0.0.1';
|
|
27
|
+
const dataDir = node_path_1.default.resolve(env.CHAT_DATA_DIR || node_path_1.default.join(node_os_1.default.homedir(), '.team-chat'));
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
dataDir,
|
|
30
|
+
databaseUrl: env.DATABASE_URL || null,
|
|
31
|
+
host,
|
|
32
|
+
port,
|
|
33
|
+
url: (env.CHAT_URL || `http://localhost:${port}`).replace(/\/+$/, ''),
|
|
34
|
+
bridgeRestartFile: env.CHAT_BRIDGE_RESTART_FILE || node_path_1.default.join(dataDir, 'bridge-restart.request'),
|
|
35
|
+
localSecret: env.CHAT_LOCAL_SECRET || null,
|
|
36
|
+
collector: env.CHAT_COLLECTOR === '1',
|
|
37
|
+
collectorUrl: (env.CHAT_COLLECTOR_URL || exports.DEFAULT_COLLECTOR_URL)?.replace(/\/+$/, '') || null,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ConversationModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const attachment_store_1 = require("./attachments/attachment-store");
|
|
12
|
+
const attachments_controller_1 = require("./attachments/attachments.controller");
|
|
13
|
+
const uploads_interceptor_1 = require("./attachments/uploads.interceptor");
|
|
14
|
+
const bridge_restart_service_1 = require("./bridges/bridge-restart.service");
|
|
15
|
+
const bridges_controller_1 = require("./bridges/bridges.controller");
|
|
16
|
+
const chats_controller_1 = require("./chats/chats.controller");
|
|
17
|
+
const chats_service_1 = require("./chats/chats.service");
|
|
18
|
+
const decisions_controller_1 = require("./decisions/decisions.controller");
|
|
19
|
+
const decisions_service_1 = require("./decisions/decisions.service");
|
|
20
|
+
const documents_controller_1 = require("./documents/documents.controller");
|
|
21
|
+
const documents_service_1 = require("./documents/documents.service");
|
|
22
|
+
const mentions_controller_1 = require("./mentions/mentions.controller");
|
|
23
|
+
const mentions_service_1 = require("./mentions/mentions.service");
|
|
24
|
+
const message_presenter_1 = require("./messages/message.presenter");
|
|
25
|
+
const messages_service_1 = require("./messages/messages.service");
|
|
26
|
+
const thread_controller_1 = require("./messages/thread.controller");
|
|
27
|
+
/**
|
|
28
|
+
* Разговор: чаты, тред, вложения, обращения, решения, документы, мосты. Одним модулем — его части читают
|
|
29
|
+
* друг друга постоянно (лента собирает и сообщения, и открытые обращения, и сводку по
|
|
30
|
+
* чатам), и деление на модули дало бы только круг взаимных импортов.
|
|
31
|
+
*/
|
|
32
|
+
let ConversationModule = class ConversationModule {
|
|
33
|
+
};
|
|
34
|
+
exports.ConversationModule = ConversationModule;
|
|
35
|
+
exports.ConversationModule = ConversationModule = __decorate([
|
|
36
|
+
(0, common_1.Module)({
|
|
37
|
+
providers: [attachment_store_1.AttachmentStore, uploads_interceptor_1.UploadsInterceptor, chats_service_1.ChatsService, message_presenter_1.MessagePresenter, mentions_service_1.MentionsService, messages_service_1.MessagesService, decisions_service_1.DecisionsService, documents_service_1.DocumentsService, bridge_restart_service_1.BridgeRestartService],
|
|
38
|
+
controllers: [chats_controller_1.ChatsController, thread_controller_1.ThreadController, decisions_controller_1.DecisionsController, mentions_controller_1.MentionsController, bridges_controller_1.BridgesController, attachments_controller_1.AttachmentsController, documents_controller_1.DocumentsController],
|
|
39
|
+
exports: [attachment_store_1.AttachmentStore, chats_service_1.ChatsService, message_presenter_1.MessagePresenter, mentions_service_1.MentionsService, messages_service_1.MessagesService, decisions_service_1.DecisionsService, documents_service_1.DocumentsService],
|
|
40
|
+
})
|
|
41
|
+
], ConversationModule);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Подключение к базе. Диалект везде один — Postgres: локально PGlite (Postgres в
|
|
3
|
+
// WebAssembly внутри процесса, ставить ничего не надо), на общем сервере — настоящий
|
|
4
|
+
// Postgres по DATABASE_URL. Схема и запросы одни и те же.
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.openDatabase = openDatabase;
|
|
43
|
+
const pglite_1 = require("@electric-sql/pglite");
|
|
44
|
+
const pglite_2 = require("drizzle-orm/pglite");
|
|
45
|
+
const migrator_1 = require("drizzle-orm/pglite/migrator");
|
|
46
|
+
const node_postgres_1 = require("drizzle-orm/node-postgres");
|
|
47
|
+
const migrator_2 = require("drizzle-orm/node-postgres/migrator");
|
|
48
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
49
|
+
const pg_1 = require("pg");
|
|
50
|
+
const schema = __importStar(require("./schema"));
|
|
51
|
+
/** Миграции лежат рядом со сборкой: server/drizzle. */
|
|
52
|
+
const MIGRATIONS = node_path_1.default.join(__dirname, '..', '..', '..', 'drizzle');
|
|
53
|
+
/**
|
|
54
|
+
* Где база:
|
|
55
|
+
* url — настоящий Postgres (общий сервер);
|
|
56
|
+
* dataDir — каталог PGlite (локальная установка);
|
|
57
|
+
* ни того, ни другого — PGlite в памяти (тесты).
|
|
58
|
+
*
|
|
59
|
+
* Миграции применяются здесь же, до первого запроса: сервер на старой схеме отвечал бы
|
|
60
|
+
* ошибками на всё — так 23.10 и случилось с PHP-сервером, когда забыли migrate.
|
|
61
|
+
*/
|
|
62
|
+
async function openDatabase({ url, dataDir } = {}) {
|
|
63
|
+
if (url) {
|
|
64
|
+
const pool = new pg_1.Pool({ connectionString: url });
|
|
65
|
+
const db = (0, node_postgres_1.drizzle)({ client: pool, schema });
|
|
66
|
+
await (0, migrator_2.migrate)(db, { migrationsFolder: MIGRATIONS });
|
|
67
|
+
return { db: db, close: () => pool.end() };
|
|
68
|
+
}
|
|
69
|
+
const client = new pglite_1.PGlite(dataDir);
|
|
70
|
+
const db = (0, pglite_2.drizzle)({ client, schema });
|
|
71
|
+
await (0, migrator_1.migrate)(db, { migrationsFolder: MIGRATIONS });
|
|
72
|
+
return { db: db, close: () => client.close() };
|
|
73
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
var DatabaseModule_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.DatabaseModule = exports.DATABASE = void 0;
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
/** Токен базы для внедрения: @Inject(DATABASE) db: Database. */
|
|
19
|
+
exports.DATABASE = Symbol('DATABASE');
|
|
20
|
+
const HANDLE = Symbol('DATABASE_HANDLE');
|
|
21
|
+
/**
|
|
22
|
+
* База открывается до создания приложения (миграции — до первого запроса) и приходит
|
|
23
|
+
* готовой, а закрывается вместе с приложением — и по SIGTERM: PGlite пишет на диск,
|
|
24
|
+
* и обрывать его на полуслове не стоит.
|
|
25
|
+
*/
|
|
26
|
+
let DatabaseModule = DatabaseModule_1 = class DatabaseModule {
|
|
27
|
+
handle;
|
|
28
|
+
constructor(handle) {
|
|
29
|
+
this.handle = handle;
|
|
30
|
+
}
|
|
31
|
+
static forRoot(handle) {
|
|
32
|
+
return {
|
|
33
|
+
module: DatabaseModule_1,
|
|
34
|
+
providers: [
|
|
35
|
+
{ provide: HANDLE, useValue: handle },
|
|
36
|
+
{ provide: exports.DATABASE, useValue: handle.db },
|
|
37
|
+
],
|
|
38
|
+
exports: [exports.DATABASE],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async onApplicationShutdown() {
|
|
42
|
+
await this.handle.close();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
exports.DatabaseModule = DatabaseModule;
|
|
46
|
+
exports.DatabaseModule = DatabaseModule = DatabaseModule_1 = __decorate([
|
|
47
|
+
(0, common_1.Global)(),
|
|
48
|
+
(0, common_1.Module)({}),
|
|
49
|
+
__param(0, (0, common_1.Inject)(HANDLE)),
|
|
50
|
+
__metadata("design:paramtypes", [Object])
|
|
51
|
+
], DatabaseModule);
|