wacrawl-dashboard 0.1.2 → 0.2.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.
Files changed (44) hide show
  1. package/README.md +220 -105
  2. package/apps/api/dist/demo/demoArchive.js +371 -0
  3. package/apps/api/dist/demo/demoCast.js +138 -0
  4. package/apps/api/dist/demo/demoMedia.js +108 -0
  5. package/apps/api/dist/index.js +109 -29
  6. package/apps/api/dist/lib/archiveSchema.js +57 -0
  7. package/apps/api/dist/lib/archiveSync.js +151 -0
  8. package/apps/api/dist/lib/chatKind.js +41 -0
  9. package/apps/api/dist/lib/displayName.js +20 -0
  10. package/apps/api/dist/lib/mediaFsPath.js +27 -0
  11. package/apps/api/dist/lib/percentile.js +23 -0
  12. package/apps/api/dist/lib/query.js +33 -7
  13. package/apps/api/dist/lib/rollupDb.js +253 -0
  14. package/apps/api/dist/lib/searchFilters.js +141 -0
  15. package/apps/api/dist/lib/selfIdentity.js +42 -0
  16. package/apps/api/dist/lib/statsCache.js +83 -0
  17. package/apps/api/dist/lib/timezone.js +102 -0
  18. package/apps/api/dist/lib/wordCloudUsefulStopWords.js +36 -3
  19. package/apps/api/dist/pathsResolve.js +17 -3
  20. package/apps/api/dist/routes/data.js +141 -77
  21. package/apps/api/dist/routes/settings.js +3 -0
  22. package/apps/api/dist/routes/stats/profiles.js +522 -0
  23. package/apps/api/dist/routes/stats/reports.js +555 -0
  24. package/apps/api/dist/routes/stats/shared.js +75 -0
  25. package/apps/api/dist/routes/stats.js +273 -323
  26. package/apps/api/dist/routes/sync.js +60 -0
  27. package/apps/api/dist/scripts/generateDemoArchive.js +24 -0
  28. package/apps/api/package.json +31 -0
  29. package/apps/web/dist/apple-touch-icon.png +0 -0
  30. package/apps/web/dist/assets/charts-BMGh5n-d.js +36 -0
  31. package/apps/web/dist/assets/icons-BdElrSaU.js +1 -0
  32. package/apps/web/dist/assets/index-Bb8Zj42D.css +1 -0
  33. package/apps/web/dist/assets/index-wrxENU1e.js +9 -0
  34. package/apps/web/dist/assets/react-D3RL6ob4.js +3 -0
  35. package/apps/web/dist/favicon-16x16.png +0 -0
  36. package/apps/web/dist/favicon-32x32.png +0 -0
  37. package/apps/web/dist/favicon-512x512.png +0 -0
  38. package/apps/web/dist/favicon.ico +0 -0
  39. package/apps/web/dist/favicon.svg +59 -0
  40. package/apps/web/dist/index.html +11 -2
  41. package/bin/wacrawl-dashboard.js +64 -15
  42. package/package.json +20 -4
  43. package/apps/web/dist/assets/index-CFh_Wz98.js +0 -43
  44. package/apps/web/dist/assets/index-DJiLhgJZ.css +0 -1
@@ -0,0 +1,60 @@
1
+ import { Router } from 'express';
2
+ import { readArchiveSyncInfo, startSync, syncEnabled, syncJobStatus, } from '../lib/archiveSync.js';
3
+ import { getResolvedPaths } from '../runtimePaths.js';
4
+ export const syncRouter = Router();
5
+ /**
6
+ * Every other route here is a readonly GET, so CORS hiding the response body is
7
+ * protection enough. This one spawns a process, and CORS does not stop the
8
+ * request from arriving — a page on any origin can fire a "simple" cross-origin
9
+ * POST at 127.0.0.1 and never read the reply.
10
+ *
11
+ * Requiring a header the browser does not consider simple forces a preflight,
12
+ * which the origin check in `createApp` rejects for anything but loopback.
13
+ */
14
+ export const SYNC_REQUEST_HEADER = 'x-wacrawl-request';
15
+ const requireDashboardOrigin = (req, res, next) => {
16
+ if (req.headers[SYNC_REQUEST_HEADER] !== '1') {
17
+ res.status(403).json({
18
+ error: {
19
+ code: 'MISSING_REQUEST_HEADER',
20
+ message: `Sync requests must send ${SYNC_REQUEST_HEADER}: 1.`,
21
+ },
22
+ });
23
+ return;
24
+ }
25
+ next();
26
+ };
27
+ syncRouter.get('/status', (_req, res) => {
28
+ // Job state changes second to second while an import runs; a cached poll
29
+ // response would report a finished sync as still running.
30
+ res.setHeader('Cache-Control', 'no-store');
31
+ res.json({
32
+ enabled: syncEnabled(),
33
+ job: syncJobStatus(),
34
+ archive: readArchiveSyncInfo(),
35
+ });
36
+ });
37
+ syncRouter.post('/', requireDashboardOrigin, (_req, res) => {
38
+ if (!syncEnabled()) {
39
+ res.status(403).json({
40
+ error: {
41
+ code: 'SYNC_DISABLED',
42
+ message: 'Archive sync is disabled by WACRAWL_DISABLE_SYNC.',
43
+ },
44
+ });
45
+ return;
46
+ }
47
+ const { primaryDb } = getResolvedPaths();
48
+ const { started, job } = startSync(primaryDb);
49
+ if (!started) {
50
+ res.status(409).json({
51
+ error: {
52
+ code: 'SYNC_ALREADY_RUNNING',
53
+ message: 'An archive sync is already running.',
54
+ },
55
+ job,
56
+ });
57
+ return;
58
+ }
59
+ res.status(202).json({ job });
60
+ });
@@ -0,0 +1,24 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { parseArgs } from 'node:util';
5
+ import { DEFAULT_DEMO_SEED, generateDemoArchive } from '../demo/demoArchive.js';
6
+ const { values } = parseArgs({
7
+ options: {
8
+ out: { type: 'string' },
9
+ seed: { type: 'string', default: String(DEFAULT_DEMO_SEED) },
10
+ now: { type: 'string' },
11
+ },
12
+ });
13
+ const seed = Number(values.seed);
14
+ const now = values.now ? new Date(values.now) : new Date();
15
+ if (!Number.isInteger(seed) || Number.isNaN(now.getTime())) {
16
+ console.error('Usage: generateDemoArchive [--out <dir>] [--seed <integer>] [--now <ISO date>]');
17
+ process.exit(1);
18
+ }
19
+ const dir = values.out ? path.resolve(values.out) : fs.mkdtempSync(path.join(os.tmpdir(), 'wacrawl-demo-'));
20
+ const startedAt = Date.now();
21
+ const archive = generateDemoArchive({ dir, seed, now });
22
+ console.log(`Wrote ${archive.messages} messages across ${archive.chats} chats in ${Date.now() - startedAt}ms.`);
23
+ console.log(`WACRAWL_DB=${archive.dbPath}`);
24
+ console.log(`WACRAWL_MEDIA_ROOT=${archive.mediaRoot}`);
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@wacrawl/api",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "tsx watch src/index.ts",
8
+ "build": "tsc -p tsconfig.json",
9
+ "start": "node dist/index.js",
10
+ "optimize-db": "tsx src/scripts/optimizeDb.ts",
11
+ "demo:generate": "tsx src/scripts/generateDemoArchive.ts",
12
+ "test": "vitest run",
13
+ "typecheck": "tsc -p tsconfig.json --noEmit"
14
+ },
15
+ "dependencies": {
16
+ "better-sqlite3": "^12.4.6",
17
+ "cors": "^2.8.5",
18
+ "date-fns": "^4.1.0",
19
+ "dotenv": "^17.2.3",
20
+ "express": "^5.2.1",
21
+ "stopword": "^3.1.5"
22
+ },
23
+ "devDependencies": {
24
+ "@types/better-sqlite3": "^7.6.13",
25
+ "@types/cors": "^2.8.19",
26
+ "@types/express": "^5.0.6",
27
+ "@types/node": "^25.0.3",
28
+ "tsx": "^4.21.0",
29
+ "vitest": "^4.0.15"
30
+ }
31
+ }