jskelet 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -6
- package/docs/07-yapilandirma.md +10 -8
- package/docs/en/07-configuration.md +9 -7
- package/package.json +1 -1
- package/src/config/defaults.js +1 -1
- package/src/config/index.js +64 -13
- package/src/server/logs/pipeline.js +12 -22
package/CHANGELOG.md
CHANGED
|
@@ -30,12 +30,11 @@ one is listed under a **Breaking** heading.
|
|
|
30
30
|
- Top-level `logs` config for persistent sinks: daily NDJSON files
|
|
31
31
|
(`logs.file`) and batched S3 PutObject (`logs.s3`) with embedded SigV4 — no
|
|
32
32
|
`@aws-sdk` dependency. `kinds` selects `http` / `event` / `error`; `console`
|
|
33
|
-
toggles runtime stdout lines. `JSKELET_LOG_BUCKET`
|
|
34
|
-
be a plain bucket or a
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`
|
|
38
|
-
`JSKELET_S3_SESSION_TOKEN`, `JSKELET_S3_REGION`, `JSKELET_S3_API_URL`.
|
|
33
|
+
toggles runtime stdout lines. `JSKELET_LOG_BUCKET` / `JSKELET_S3_BUCKET`
|
|
34
|
+
(+ optional `JSKELET_S3_KEY_PREFIX`) may be a plain bucket or a
|
|
35
|
+
`bucket/prefix/…` path; with credentials present the sink turns on without
|
|
36
|
+
`enabled: true`. `JSKELET_S3_API_URL` sets the S3-compatible endpoint
|
|
37
|
+
(region defaults to `auto`). Env: `JSKELET_LOG_BUCKET`, `JSKELET_S3_*`.
|
|
39
38
|
- Admin panel pages under `/_jskelet/admin`: Overview, Cache, Routes, Views,
|
|
40
39
|
Logs and System. Configurable `allowIps` (exact or CIDR), `blockBots` (default
|
|
41
40
|
on — crawler UAs get 404 before login), and `logSize`. Live Logs use an
|
package/docs/07-yapilandirma.md
CHANGED
|
@@ -768,8 +768,8 @@ mevcut davranışını korur. Açıldığında HTTP access log ile framework ola
|
|
|
768
768
|
| `s3.enabled` | `boolean` | `false` | S3 batch PutObject sink'i |
|
|
769
769
|
| `s3.bucket` | `string \| null` | `null` | Bucket ya da `bucket/prefix/…` yolu; `JSKELET_LOG_BUCKET` ezer |
|
|
770
770
|
| `s3.prefix` | `string` | `"jskelet/logs/"` | Nesne anahtarı öneki (yolda verilmediyse) |
|
|
771
|
-
| `s3.region` | `string \| null` | `
|
|
772
|
-
| `s3.endpoint` | `string \| null` | `null` |
|
|
771
|
+
| `s3.region` | `string \| null` | `"auto"` | Bölge; verilmezse `JSKELET_S3_REGION`, yoksa `auto` |
|
|
772
|
+
| `s3.endpoint` | `string \| null` | `null` | S3-uyumlu API adresi; `JSKELET_S3_API_URL` ezer |
|
|
773
773
|
| `s3.flushIntervalMs` | `number` | `5000` | Batch flush aralığı |
|
|
774
774
|
| `s3.maxBatch` | `number` | `100` | Bu kadar satırda erken flush |
|
|
775
775
|
|
|
@@ -975,12 +975,14 @@ basılmaz.
|
|
|
975
975
|
| `JSKELET_SECRET` | `jskelet/cookies` | — | İmzalı cookie sırrı. `security.cookieSecret` verilmediğinde buradan okunur; ikisi de yoksa imzalı cookie API'si hata verir. [12](./12-panel-ve-oturum.md) |
|
|
976
976
|
| `DEV_TOKEN` | `devGate`, `prewarm` | — | Ayarlıysa token taşımayan her isteğe 404 döner. Isıtma token'ı çerez olarak taşır. [09](./09-dev-araclari.md) |
|
|
977
977
|
| `JSKELET_ADMIN` | `createApp` | — | Ayarlıysa yönetim panelini açar; `0` config'te açık olan paneli kapatır. Env config'i ezer, çünkü panel genelde bir arıza sırasında tek seferlik açılır. [06](./06-cache.md) |
|
|
978
|
-
| `JSKELET_LOG_BUCKET` | `logs.s3` | — |
|
|
979
|
-
| `
|
|
980
|
-
| `
|
|
981
|
-
| `
|
|
982
|
-
| `
|
|
983
|
-
| `
|
|
978
|
+
| `JSKELET_LOG_BUCKET` | `logs.s3` | — | Log hedefi: bucket ya da `bucket/prefix` yolu. Credential ile birlikte varsa sink otomatik açılır |
|
|
979
|
+
| `JSKELET_S3_BUCKET` | `logs.s3` | — | `JSKELET_LOG_BUCKET` yoksa bucket; `JSKELET_S3_KEY_PREFIX` ile birleşir |
|
|
980
|
+
| `JSKELET_S3_KEY_PREFIX` | `logs.s3` | — | `JSKELET_S3_BUCKET` ile kullanılır (`bucket/prefix`) |
|
|
981
|
+
| `JSKELET_S3_ACCESS_KEY_ID` | `logs.s3` | — | PutObject imzası |
|
|
982
|
+
| `JSKELET_S3_SECRET_ACCESS_KEY` | `logs.s3` | — | İmza sırrı (`JSKELET_S3_ACCESS_SECRET` yedek ad) |
|
|
983
|
+
| `JSKELET_S3_SESSION_TOKEN` | `logs.s3` | — | Geçici credential için isteğe bağlı |
|
|
984
|
+
| `JSKELET_S3_REGION` | `logs.s3` | `auto` | Verilmezse `auto` |
|
|
985
|
+
| `JSKELET_S3_API_URL` | `logs.s3` | — | S3-uyumlu endpoint; `logs.s3.endpoint`'i ezer |
|
|
984
986
|
| `JSKELET_CLOUDFLARE_KEY` | Cloudflare cache yüzeyi | — | API token. Verilene kadar CDN purge'ü ve edge analitiği kapalıdır; config'teki `apiToken`'ı ezer. Token hiçbir cevapta dönmez. [06](./06-cache.md) |
|
|
985
987
|
| `JSKELET_CLOUDFLARE_ZONE_ID` | Cloudflare cache yüzeyi | — | Zone kimliği. Token'la birlikte verilmedikçe hiçbir Cloudflare ucu çağrılmaz |
|
|
986
988
|
| `JSKELET_CLOUDFLARE_HOSTNAME` | Cloudflare cache yüzeyi | — | Purge URL'lerinin kökü. Panel iç bir adresten açılıyorsa gerekir |
|
|
@@ -784,8 +784,8 @@ events (`event` / `error`) are written as NDJSON lines to a file and/or S3.
|
|
|
784
784
|
| `s3.enabled` | `boolean` | `false` | S3 batch PutObject sink |
|
|
785
785
|
| `s3.bucket` | `string \| null` | `null` | Bucket or a `bucket/prefix/…` path; `JSKELET_LOG_BUCKET` overrides |
|
|
786
786
|
| `s3.prefix` | `string` | `"jskelet/logs/"` | Object key prefix (when not given in the path) |
|
|
787
|
-
| `s3.region` | `string \| null` | `
|
|
788
|
-
| `s3.endpoint` | `string \| null` | `null` |
|
|
787
|
+
| `s3.region` | `string \| null` | `"auto"` | Region; falls back to `JSKELET_S3_REGION`, otherwise `auto` |
|
|
788
|
+
| `s3.endpoint` | `string \| null` | `null` | S3-compatible API URL; `JSKELET_S3_API_URL` overrides |
|
|
789
789
|
| `s3.flushIntervalMs` | `number` | `5000` | Batch flush interval |
|
|
790
790
|
| `s3.maxBatch` | `number` | `100` | Flush early after this many lines |
|
|
791
791
|
|
|
@@ -995,12 +995,14 @@ and no warning is printed.
|
|
|
995
995
|
| `JSKELET_SECRET` | `jskelet/cookies` | — | The signed cookie secret. Read when `security.cookieSecret` is not set; if neither exists, the signed cookie API throws. [12](./12-dashboards-and-sessions.md) |
|
|
996
996
|
| `DEV_TOKEN` | `devGate`, `prewarm` | — | If set, every request without a token gets a 404. Prewarming carries the token as a cookie. [09](./09-dev-tools.md) |
|
|
997
997
|
| `JSKELET_ADMIN` | `createApp` | — | When set, turns the admin panel on; `0` turns off a panel enabled in the config. The env wins because the panel is usually opened once during an incident. [06](./06-caching.md) |
|
|
998
|
-
| `JSKELET_LOG_BUCKET` | `logs.s3` | — |
|
|
999
|
-
| `
|
|
1000
|
-
| `
|
|
998
|
+
| `JSKELET_LOG_BUCKET` | `logs.s3` | — | Log target: bucket or `bucket/prefix` path. With credentials, the sink turns on automatically |
|
|
999
|
+
| `JSKELET_S3_BUCKET` | `logs.s3` | — | Bucket when `JSKELET_LOG_BUCKET` is unset; joins with `JSKELET_S3_KEY_PREFIX` |
|
|
1000
|
+
| `JSKELET_S3_KEY_PREFIX` | `logs.s3` | — | Used with `JSKELET_S3_BUCKET` (`bucket/prefix`) |
|
|
1001
|
+
| `JSKELET_S3_ACCESS_KEY_ID` | `logs.s3` | — | Signs PutObject |
|
|
1002
|
+
| `JSKELET_S3_SECRET_ACCESS_KEY` | `logs.s3` | — | Signing secret (`JSKELET_S3_ACCESS_SECRET` is an alias) |
|
|
1001
1003
|
| `JSKELET_S3_SESSION_TOKEN` | `logs.s3` | — | Optional, for temporary credentials |
|
|
1002
|
-
| `JSKELET_S3_REGION` | `logs.s3` |
|
|
1003
|
-
| `JSKELET_S3_API_URL` | `logs.s3` | — |
|
|
1004
|
+
| `JSKELET_S3_REGION` | `logs.s3` | `auto` | Defaults to `auto` when unset |
|
|
1005
|
+
| `JSKELET_S3_API_URL` | `logs.s3` | — | S3-compatible endpoint; overrides `logs.s3.endpoint` |
|
|
1004
1006
|
| `JSKELET_CLOUDFLARE_KEY` | Cloudflare cache surface | — | API token. Until it is set, CDN purging and edge analytics stay off; it overrides `apiToken` in the config. The token is never returned in a response. [06](./06-caching.md) |
|
|
1005
1007
|
| `JSKELET_CLOUDFLARE_ZONE_ID` | Cloudflare cache surface | — | Zone identifier. No Cloudflare endpoint is called unless it is set alongside the token |
|
|
1006
1008
|
| `JSKELET_CLOUDFLARE_HOSTNAME` | Cloudflare cache surface | — | The root for purge URLs. Required when the panel is opened over an internal address |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jskelet",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "A framework that feels like no framework: Express 5 + EJS server rendering, vanilla JS islands, Tailwind v4 and an in-process HTML TTL cache.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/config/defaults.js
CHANGED
|
@@ -203,7 +203,7 @@ export const DEFAULT_REDIS = {
|
|
|
203
203
|
* `JSKELET_S3_SECRET_ACCESS_KEY` (ve isteğe bağlı `JSKELET_S3_SESSION_TOKEN`).
|
|
204
204
|
* Bucket için `JSKELET_LOG_BUCKET` (veya `JSKELET_S3_BUCKET`) env'i config'i
|
|
205
205
|
* ezer ve `ayberkenis/jskelet/logs` gibi `bucket/prefix` yolunu kabul eder.
|
|
206
|
-
* Uyumlu API adresi `JSKELET_S3_API_URL` (
|
|
206
|
+
* Uyumlu API adresi `JSKELET_S3_API_URL` (S3-compatible endpoint).
|
|
207
207
|
*/
|
|
208
208
|
export const DEFAULT_LOGS = {
|
|
209
209
|
console: true,
|
package/src/config/index.js
CHANGED
|
@@ -430,6 +430,48 @@ export function splitS3BucketPath(value) {
|
|
|
430
430
|
};
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
/**
|
|
434
|
+
* @param {unknown} value
|
|
435
|
+
* @returns {string | null}
|
|
436
|
+
*/
|
|
437
|
+
function envText(value) {
|
|
438
|
+
return typeof value === "string" && value ? value : null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* @returns {{ accessKeyId: string, secretAccessKey: string,
|
|
443
|
+
* sessionToken: string | null } | null}
|
|
444
|
+
*/
|
|
445
|
+
export function readS3CredentialsFromEnv() {
|
|
446
|
+
const accessKeyId = envText(process.env.JSKELET_S3_ACCESS_KEY_ID);
|
|
447
|
+
const secretAccessKey =
|
|
448
|
+
envText(process.env.JSKELET_S3_SECRET_ACCESS_KEY) ??
|
|
449
|
+
envText(process.env.JSKELET_S3_ACCESS_SECRET);
|
|
450
|
+
if (!accessKeyId || !secretAccessKey) return null;
|
|
451
|
+
return {
|
|
452
|
+
accessKeyId,
|
|
453
|
+
secretAccessKey,
|
|
454
|
+
sessionToken: envText(process.env.JSKELET_S3_SESSION_TOKEN),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Log hedefi. Öncelik: `JSKELET_LOG_BUCKET` → `JSKELET_S3_BUCKET`
|
|
460
|
+
* (+ isteğe bağlı `JSKELET_S3_KEY_PREFIX`).
|
|
461
|
+
*
|
|
462
|
+
* @returns {string | null}
|
|
463
|
+
*/
|
|
464
|
+
function resolveLogBucketEnv() {
|
|
465
|
+
const logPath = envText(process.env.JSKELET_LOG_BUCKET);
|
|
466
|
+
if (logPath) return logPath;
|
|
467
|
+
|
|
468
|
+
const bucket = envText(process.env.JSKELET_S3_BUCKET);
|
|
469
|
+
if (!bucket) return null;
|
|
470
|
+
|
|
471
|
+
const prefix = envText(process.env.JSKELET_S3_KEY_PREFIX);
|
|
472
|
+
return prefix ? `${bucket}/${prefix}` : bucket;
|
|
473
|
+
}
|
|
474
|
+
|
|
433
475
|
/**
|
|
434
476
|
* Kalıcı log sink'leri. Bozuk bir `kinds` listesi siteyi düşürmemeli —
|
|
435
477
|
* bilinmeyen girdiler atılır; hiç geçerli tür kalmazsa varsayılana dönülür.
|
|
@@ -461,13 +503,8 @@ export function normalizeLogs(raw) {
|
|
|
461
503
|
const flush = Number(s3Raw.flushIntervalMs);
|
|
462
504
|
const batch = Number(s3Raw.maxBatch);
|
|
463
505
|
|
|
464
|
-
/** @param {unknown} value */
|
|
465
|
-
const text = (value) => (typeof value === "string" && value ? value : null);
|
|
466
|
-
|
|
467
506
|
const bucketPath = splitS3BucketPath(
|
|
468
|
-
|
|
469
|
-
text(process.env.JSKELET_S3_BUCKET) ??
|
|
470
|
-
text(s3Raw.bucket),
|
|
507
|
+
resolveLogBucketEnv() ?? envText(s3Raw.bucket),
|
|
471
508
|
);
|
|
472
509
|
|
|
473
510
|
const configPrefix =
|
|
@@ -478,14 +515,28 @@ export function normalizeLogs(raw) {
|
|
|
478
515
|
: DEFAULT_LOGS.s3.prefix;
|
|
479
516
|
|
|
480
517
|
const endpoint =
|
|
481
|
-
|
|
518
|
+
envText(process.env.JSKELET_S3_API_URL) ?? envText(s3Raw.endpoint);
|
|
482
519
|
|
|
483
|
-
//
|
|
484
|
-
//
|
|
520
|
+
// Uyumlu API'lerde (Cloudflare R2 vb.) imza bölgesi çoğu zaman `auto`.
|
|
521
|
+
// Region hiçbir kurulumda zorunlu değil.
|
|
485
522
|
const region =
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
523
|
+
envText(s3Raw.region) ??
|
|
524
|
+
envText(process.env.JSKELET_S3_REGION) ??
|
|
525
|
+
"auto";
|
|
526
|
+
|
|
527
|
+
const credentials = readS3CredentialsFromEnv();
|
|
528
|
+
// `JSKELET_LOG_BUCKET` (veya S3 bucket) + credential varsa config'te
|
|
529
|
+
// `enabled: true` unutulmuş olsa bile aç. Açık `enabled: false` ezer.
|
|
530
|
+
const envWantsLogs = Boolean(
|
|
531
|
+
envText(process.env.JSKELET_LOG_BUCKET) ||
|
|
532
|
+
envText(process.env.JSKELET_S3_BUCKET),
|
|
533
|
+
);
|
|
534
|
+
const enabled =
|
|
535
|
+
s3Raw.enabled === true ||
|
|
536
|
+
(s3Raw.enabled !== false &&
|
|
537
|
+
envWantsLogs &&
|
|
538
|
+
Boolean(credentials) &&
|
|
539
|
+
Boolean(bucketPath.bucket));
|
|
489
540
|
|
|
490
541
|
return {
|
|
491
542
|
console: source.console !== false,
|
|
@@ -499,7 +550,7 @@ export function normalizeLogs(raw) {
|
|
|
499
550
|
rotate: "daily",
|
|
500
551
|
},
|
|
501
552
|
s3: {
|
|
502
|
-
enabled
|
|
553
|
+
enabled,
|
|
503
554
|
bucket: bucketPath.bucket,
|
|
504
555
|
// Yoldaki önek tek env ile klasör vermeyi mümkün kılar; yoksa config.
|
|
505
556
|
prefix: bucketPath.prefix ?? configPrefix,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import * as log from "../../log.mjs";
|
|
9
|
+
import { readS3CredentialsFromEnv } from "../../config/index.js";
|
|
9
10
|
import { createFileSink } from "./file-sink.js";
|
|
10
11
|
import { createS3Sink } from "./s3-sink.js";
|
|
11
12
|
|
|
@@ -23,20 +24,6 @@ let sinks = [];
|
|
|
23
24
|
let kindSet = new Set();
|
|
24
25
|
let accessMounted = false;
|
|
25
26
|
|
|
26
|
-
/**
|
|
27
|
-
* @returns {import('./s3-put.js').AwsCredentials | null}
|
|
28
|
-
*/
|
|
29
|
-
function readCredentials() {
|
|
30
|
-
const accessKeyId = process.env.JSKELET_S3_ACCESS_KEY_ID;
|
|
31
|
-
const secretAccessKey = process.env.JSKELET_S3_SECRET_ACCESS_KEY;
|
|
32
|
-
if (!accessKeyId || !secretAccessKey) return null;
|
|
33
|
-
return {
|
|
34
|
-
accessKeyId,
|
|
35
|
-
secretAccessKey,
|
|
36
|
-
sessionToken: process.env.JSKELET_S3_SESSION_TOKEN || null,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
27
|
/**
|
|
41
28
|
* Access middleware mount edilmeli mi?
|
|
42
29
|
*
|
|
@@ -79,31 +66,34 @@ export async function configureLogs(config) {
|
|
|
79
66
|
}
|
|
80
67
|
|
|
81
68
|
if (logs.s3.enabled) {
|
|
82
|
-
const credentials =
|
|
69
|
+
const credentials = readS3CredentialsFromEnv();
|
|
83
70
|
if (!logs.s3.bucket) {
|
|
84
71
|
console.warn(
|
|
85
|
-
"[logs] s3.enabled but no bucket (set JSKELET_LOG_BUCKET or logs.s3.bucket)",
|
|
86
|
-
);
|
|
87
|
-
} else if (!logs.s3.region) {
|
|
88
|
-
console.warn(
|
|
89
|
-
"[logs] s3.enabled but no region (set JSKELET_S3_REGION or logs.s3.region)",
|
|
72
|
+
"[logs] s3.enabled but no bucket (set JSKELET_LOG_BUCKET, JSKELET_S3_BUCKET or logs.s3.bucket)",
|
|
90
73
|
);
|
|
91
74
|
} else if (!credentials) {
|
|
92
75
|
console.warn(
|
|
93
|
-
"[logs] s3.enabled but
|
|
76
|
+
"[logs] s3.enabled but credentials missing — set JSKELET_S3_ACCESS_KEY_ID and JSKELET_S3_SECRET_ACCESS_KEY",
|
|
94
77
|
);
|
|
95
78
|
} else {
|
|
79
|
+
const region = logs.s3.region || "auto";
|
|
96
80
|
next.push(
|
|
97
81
|
createS3Sink({
|
|
98
82
|
bucket: logs.s3.bucket,
|
|
99
83
|
prefix: logs.s3.prefix,
|
|
100
|
-
region
|
|
84
|
+
region,
|
|
101
85
|
endpoint: logs.s3.endpoint,
|
|
102
86
|
credentials,
|
|
103
87
|
flushIntervalMs: logs.s3.flushIntervalMs,
|
|
104
88
|
maxBatch: logs.s3.maxBatch,
|
|
105
89
|
}),
|
|
106
90
|
);
|
|
91
|
+
|
|
92
|
+
// Redis satırıyla aynı kanal: console → jskelet dev `server` scope'una
|
|
93
|
+
// düşer. Credential basılmaz; yalnızca hedef konum.
|
|
94
|
+
const target = `${logs.s3.bucket}/${logs.s3.prefix}`.replace(/\/+/g, "/");
|
|
95
|
+
const via = logs.s3.endpoint ? ` @ ${logs.s3.endpoint}` : "";
|
|
96
|
+
console.log(`[logs] s3 connected — objects under \`${target}\`${via}`);
|
|
107
97
|
}
|
|
108
98
|
}
|
|
109
99
|
|