nextjs-cms 0.5.90 → 0.5.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +57 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/lib/serverActions.d.ts +2 -1
- package/dist/api/lib/serverActions.d.ts.map +1 -1
- package/dist/api/lib/serverActions.js +49 -15
- package/dist/api/root.d.ts +114 -0
- package/dist/api/root.d.ts.map +1 -1
- package/dist/api/root.js +2 -0
- package/dist/api/routers/accountSettings.d.ts.map +1 -1
- package/dist/api/routers/accountSettings.js +65 -3
- package/dist/api/routers/admins.d.ts.map +1 -1
- package/dist/api/routers/admins.js +54 -2
- package/dist/api/routers/auth.d.ts.map +1 -1
- package/dist/api/routers/auth.js +27 -2
- package/dist/api/routers/categorySection.d.ts.map +1 -1
- package/dist/api/routers/categorySection.js +3 -1
- package/dist/api/routers/hasItemsSection.d.ts.map +1 -1
- package/dist/api/routers/hasItemsSection.js +3 -1
- package/dist/api/routers/logs.d.ts +59 -0
- package/dist/api/routers/logs.d.ts.map +1 -0
- package/dist/api/routers/logs.js +75 -0
- package/dist/core/fields/photo.d.ts +6 -6
- package/dist/core/fields/richText.d.ts +9 -9
- package/dist/core/fields/select.d.ts +1 -1
- package/dist/core/sections/category.d.ts +6 -6
- package/dist/core/sections/hasItems.d.ts +12 -12
- package/dist/core/sections/section.d.ts +3 -3
- package/dist/core/sections/simple.d.ts +4 -4
- package/dist/core/submit/ItemEditSubmit.d.ts +7 -0
- package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -1
- package/dist/core/submit/ItemEditSubmit.js +32 -0
- package/dist/core/submit/NewItemSubmit.d.ts +2 -0
- package/dist/core/submit/NewItemSubmit.d.ts.map +1 -1
- package/dist/core/submit/NewItemSubmit.js +3 -0
- package/dist/core/submit/submit.d.ts +8 -4
- package/dist/core/submit/submit.d.ts.map +1 -1
- package/dist/core/submit/submit.js +58 -8
- package/dist/db/schema.d.ts +231 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/logging/audit.d.ts +20 -0
- package/dist/logging/audit.d.ts.map +1 -0
- package/dist/logging/audit.js +48 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +1 -0
- package/dist/logging/log.d.ts +20 -0
- package/dist/logging/log.d.ts.map +1 -0
- package/dist/logging/log.js +48 -0
- package/dist/translations/dictionaries/ar.json +6 -0
- package/dist/translations/dictionaries/en.json +6 -0
- package/package.json +7 -3
|
@@ -3,11 +3,13 @@ import type { Field } from '../fields';
|
|
|
3
3
|
import type { Section } from '../sections';
|
|
4
4
|
import { entityKind } from '../helpers';
|
|
5
5
|
import type { User } from '../../auth';
|
|
6
|
+
import { type LogEventType, type RequestMetadata } from '../../logging/index.js';
|
|
6
7
|
type ConstructorType = {
|
|
7
8
|
sectionName: string;
|
|
8
9
|
user: User;
|
|
9
10
|
postData: FormData;
|
|
10
11
|
preSubmit?: boolean;
|
|
12
|
+
requestMetadata?: RequestMetadata;
|
|
11
13
|
};
|
|
12
14
|
export declare abstract class Submit {
|
|
13
15
|
static readonly [entityKind]: string;
|
|
@@ -22,10 +24,11 @@ export declare abstract class Submit {
|
|
|
22
24
|
protected preSubmit: boolean;
|
|
23
25
|
protected sqlNamesAndValues: Record<string, any>;
|
|
24
26
|
protected fields: Field[];
|
|
27
|
+
protected requestMetadata?: RequestMetadata;
|
|
25
28
|
/**
|
|
26
29
|
* Constructor
|
|
27
30
|
*/
|
|
28
|
-
constructor({ preSubmit, sectionName, user, postData }: ConstructorType);
|
|
31
|
+
constructor({ preSubmit, sectionName, user, postData, requestMetadata }: ConstructorType);
|
|
29
32
|
/**
|
|
30
33
|
* Run custom hooks before the item is updated
|
|
31
34
|
* @protected
|
|
@@ -41,9 +44,10 @@ export declare abstract class Submit {
|
|
|
41
44
|
* @protected
|
|
42
45
|
*/
|
|
43
46
|
protected runErrorHooks(error?: unknown): Promise<void>;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
protected getLogEventType(): LogEventType | null;
|
|
48
|
+
protected getChangedFields(): string[];
|
|
49
|
+
protected getExistingFieldValue(_fieldName: string): unknown;
|
|
50
|
+
protected getEntityLabel(): string | null;
|
|
47
51
|
private logOperation;
|
|
48
52
|
/**
|
|
49
53
|
* Must be called after the constructor
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/submit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { SectionFactory } from '../factories';
|
|
|
4
4
|
import { entityKind, is } from '../helpers';
|
|
5
5
|
import { NumberField, PhotoField, SelectField, TextField } from '../fields';
|
|
6
6
|
import { MysqlTableChecker } from '../db';
|
|
7
|
+
import { recordLog } from '../../logging/index.js';
|
|
7
8
|
export class Submit {
|
|
8
9
|
static [entityKind] = 'Submit';
|
|
9
10
|
user;
|
|
@@ -17,14 +18,16 @@ export class Submit {
|
|
|
17
18
|
preSubmit = false;
|
|
18
19
|
sqlNamesAndValues = {};
|
|
19
20
|
fields = [];
|
|
21
|
+
requestMetadata;
|
|
20
22
|
/**
|
|
21
23
|
* Constructor
|
|
22
24
|
*/
|
|
23
|
-
constructor({ preSubmit, sectionName, user, postData }) {
|
|
25
|
+
constructor({ preSubmit, sectionName, user, postData, requestMetadata }) {
|
|
24
26
|
this.sectionName = sectionName;
|
|
25
27
|
this.user = user;
|
|
26
28
|
this._postData = postData;
|
|
27
29
|
this.preSubmit = preSubmit ?? false;
|
|
30
|
+
this.requestMetadata = requestMetadata;
|
|
28
31
|
}
|
|
29
32
|
/**
|
|
30
33
|
* Run custom hooks before the item is updated
|
|
@@ -88,13 +91,54 @@ export class Submit {
|
|
|
88
91
|
console.error('onError hook failed:', e);
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
getLogEventType() {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
getChangedFields() {
|
|
98
|
+
return Object.keys(this.sqlNamesAndValues).filter((fieldName) => !['created_by', 'updated_by'].includes(fieldName));
|
|
99
|
+
}
|
|
100
|
+
getExistingFieldValue(_fieldName) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
getEntityLabel() {
|
|
104
|
+
if (!this._sectionInfo || this._sectionInfo.type === 'simple')
|
|
105
|
+
return null;
|
|
106
|
+
const sectionWithHeading = this._sectionInfo;
|
|
107
|
+
if (!sectionWithHeading.headingField?.name)
|
|
108
|
+
return null;
|
|
109
|
+
const headingFieldName = sectionWithHeading.headingField.name;
|
|
110
|
+
const currentValue = this.sqlNamesAndValues[headingFieldName];
|
|
111
|
+
if (currentValue !== undefined && currentValue !== null && String(currentValue).trim() !== '') {
|
|
112
|
+
return String(currentValue);
|
|
113
|
+
}
|
|
114
|
+
const existingValue = this.getExistingFieldValue(headingFieldName);
|
|
115
|
+
if (existingValue !== undefined && existingValue !== null && String(existingValue).trim() !== '') {
|
|
116
|
+
return String(existingValue);
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
async logOperation() {
|
|
121
|
+
const eventType = this.getLogEventType();
|
|
122
|
+
if (!eventType || !this._sectionInfo?.name || !this._itemId)
|
|
123
|
+
return;
|
|
124
|
+
const changedFields = this.getChangedFields();
|
|
125
|
+
const metadata = changedFields.length > 0
|
|
126
|
+
? {
|
|
127
|
+
fields: changedFields,
|
|
128
|
+
sectionType: this._sectionInfo.type,
|
|
129
|
+
}
|
|
130
|
+
: undefined;
|
|
131
|
+
await recordLog({
|
|
132
|
+
eventType,
|
|
133
|
+
actorId: this.user.id,
|
|
134
|
+
actorUsername: this.user.name,
|
|
135
|
+
entityType: 'section_item',
|
|
136
|
+
entityId: this._itemId,
|
|
137
|
+
entityLabel: this.getEntityLabel(),
|
|
138
|
+
sectionName: this._sectionInfo.name,
|
|
139
|
+
metadata,
|
|
140
|
+
requestMetadata: this.requestMetadata,
|
|
141
|
+
});
|
|
98
142
|
}
|
|
99
143
|
/**
|
|
100
144
|
* Must be called after the constructor
|
|
@@ -444,6 +488,12 @@ export class Submit {
|
|
|
444
488
|
* Handle gallery photos
|
|
445
489
|
*/
|
|
446
490
|
await this.handleGallery();
|
|
491
|
+
/**
|
|
492
|
+
* Log the operation
|
|
493
|
+
*/
|
|
494
|
+
if (!this._error && !this.preSubmit) {
|
|
495
|
+
await this.logOperation();
|
|
496
|
+
}
|
|
447
497
|
return true;
|
|
448
498
|
}
|
|
449
499
|
async handleGallery() {
|
package/dist/db/schema.d.ts
CHANGED
|
@@ -558,4 +558,235 @@ export declare const EditorPhotosTable: import("drizzle-orm/mysql-core").MySqlTa
|
|
|
558
558
|
};
|
|
559
559
|
dialect: "mysql";
|
|
560
560
|
}>;
|
|
561
|
+
/**
|
|
562
|
+
* This table is used to store logs for admin actions
|
|
563
|
+
*/
|
|
564
|
+
export declare const LogsTable: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
|
|
565
|
+
name: "logs";
|
|
566
|
+
schema: undefined;
|
|
567
|
+
columns: {
|
|
568
|
+
id: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
569
|
+
name: "id";
|
|
570
|
+
tableName: "logs";
|
|
571
|
+
dataType: "number";
|
|
572
|
+
columnType: "MySqlInt";
|
|
573
|
+
data: number;
|
|
574
|
+
driverParam: string | number;
|
|
575
|
+
notNull: true;
|
|
576
|
+
hasDefault: true;
|
|
577
|
+
isPrimaryKey: true;
|
|
578
|
+
isAutoincrement: true;
|
|
579
|
+
hasRuntimeDefault: false;
|
|
580
|
+
enumValues: undefined;
|
|
581
|
+
baseColumn: never;
|
|
582
|
+
identity: undefined;
|
|
583
|
+
generated: undefined;
|
|
584
|
+
}, {}, {}>;
|
|
585
|
+
eventType: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
586
|
+
name: "event_type";
|
|
587
|
+
tableName: "logs";
|
|
588
|
+
dataType: "string";
|
|
589
|
+
columnType: "MySqlVarChar";
|
|
590
|
+
data: string;
|
|
591
|
+
driverParam: string | number;
|
|
592
|
+
notNull: true;
|
|
593
|
+
hasDefault: false;
|
|
594
|
+
isPrimaryKey: false;
|
|
595
|
+
isAutoincrement: false;
|
|
596
|
+
hasRuntimeDefault: false;
|
|
597
|
+
enumValues: [string, ...string[]];
|
|
598
|
+
baseColumn: never;
|
|
599
|
+
identity: undefined;
|
|
600
|
+
generated: undefined;
|
|
601
|
+
}, {}, {}>;
|
|
602
|
+
actorId: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
603
|
+
name: "actor_id";
|
|
604
|
+
tableName: "logs";
|
|
605
|
+
dataType: "string";
|
|
606
|
+
columnType: "MySqlVarChar";
|
|
607
|
+
data: string;
|
|
608
|
+
driverParam: string | number;
|
|
609
|
+
notNull: false;
|
|
610
|
+
hasDefault: false;
|
|
611
|
+
isPrimaryKey: false;
|
|
612
|
+
isAutoincrement: false;
|
|
613
|
+
hasRuntimeDefault: false;
|
|
614
|
+
enumValues: [string, ...string[]];
|
|
615
|
+
baseColumn: never;
|
|
616
|
+
identity: undefined;
|
|
617
|
+
generated: undefined;
|
|
618
|
+
}, {}, {}>;
|
|
619
|
+
actorUsername: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
620
|
+
name: "actor_username";
|
|
621
|
+
tableName: "logs";
|
|
622
|
+
dataType: "string";
|
|
623
|
+
columnType: "MySqlVarChar";
|
|
624
|
+
data: string;
|
|
625
|
+
driverParam: string | number;
|
|
626
|
+
notNull: false;
|
|
627
|
+
hasDefault: false;
|
|
628
|
+
isPrimaryKey: false;
|
|
629
|
+
isAutoincrement: false;
|
|
630
|
+
hasRuntimeDefault: false;
|
|
631
|
+
enumValues: [string, ...string[]];
|
|
632
|
+
baseColumn: never;
|
|
633
|
+
identity: undefined;
|
|
634
|
+
generated: undefined;
|
|
635
|
+
}, {}, {}>;
|
|
636
|
+
entityType: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
637
|
+
name: "entity_type";
|
|
638
|
+
tableName: "logs";
|
|
639
|
+
dataType: "string";
|
|
640
|
+
columnType: "MySqlVarChar";
|
|
641
|
+
data: string;
|
|
642
|
+
driverParam: string | number;
|
|
643
|
+
notNull: false;
|
|
644
|
+
hasDefault: false;
|
|
645
|
+
isPrimaryKey: false;
|
|
646
|
+
isAutoincrement: false;
|
|
647
|
+
hasRuntimeDefault: false;
|
|
648
|
+
enumValues: [string, ...string[]];
|
|
649
|
+
baseColumn: never;
|
|
650
|
+
identity: undefined;
|
|
651
|
+
generated: undefined;
|
|
652
|
+
}, {}, {}>;
|
|
653
|
+
entityId: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
654
|
+
name: "entity_id";
|
|
655
|
+
tableName: "logs";
|
|
656
|
+
dataType: "string";
|
|
657
|
+
columnType: "MySqlVarChar";
|
|
658
|
+
data: string;
|
|
659
|
+
driverParam: string | number;
|
|
660
|
+
notNull: false;
|
|
661
|
+
hasDefault: false;
|
|
662
|
+
isPrimaryKey: false;
|
|
663
|
+
isAutoincrement: false;
|
|
664
|
+
hasRuntimeDefault: false;
|
|
665
|
+
enumValues: [string, ...string[]];
|
|
666
|
+
baseColumn: never;
|
|
667
|
+
identity: undefined;
|
|
668
|
+
generated: undefined;
|
|
669
|
+
}, {}, {}>;
|
|
670
|
+
entityLabel: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
671
|
+
name: "entity_label";
|
|
672
|
+
tableName: "logs";
|
|
673
|
+
dataType: "string";
|
|
674
|
+
columnType: "MySqlVarChar";
|
|
675
|
+
data: string;
|
|
676
|
+
driverParam: string | number;
|
|
677
|
+
notNull: false;
|
|
678
|
+
hasDefault: false;
|
|
679
|
+
isPrimaryKey: false;
|
|
680
|
+
isAutoincrement: false;
|
|
681
|
+
hasRuntimeDefault: false;
|
|
682
|
+
enumValues: [string, ...string[]];
|
|
683
|
+
baseColumn: never;
|
|
684
|
+
identity: undefined;
|
|
685
|
+
generated: undefined;
|
|
686
|
+
}, {}, {}>;
|
|
687
|
+
sectionName: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
688
|
+
name: "section_name";
|
|
689
|
+
tableName: "logs";
|
|
690
|
+
dataType: "string";
|
|
691
|
+
columnType: "MySqlVarChar";
|
|
692
|
+
data: string;
|
|
693
|
+
driverParam: string | number;
|
|
694
|
+
notNull: false;
|
|
695
|
+
hasDefault: false;
|
|
696
|
+
isPrimaryKey: false;
|
|
697
|
+
isAutoincrement: false;
|
|
698
|
+
hasRuntimeDefault: false;
|
|
699
|
+
enumValues: [string, ...string[]];
|
|
700
|
+
baseColumn: never;
|
|
701
|
+
identity: undefined;
|
|
702
|
+
generated: undefined;
|
|
703
|
+
}, {}, {}>;
|
|
704
|
+
metadata: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
705
|
+
name: "metadata";
|
|
706
|
+
tableName: "logs";
|
|
707
|
+
dataType: "string";
|
|
708
|
+
columnType: "MySqlText";
|
|
709
|
+
data: string;
|
|
710
|
+
driverParam: string;
|
|
711
|
+
notNull: false;
|
|
712
|
+
hasDefault: false;
|
|
713
|
+
isPrimaryKey: false;
|
|
714
|
+
isAutoincrement: false;
|
|
715
|
+
hasRuntimeDefault: false;
|
|
716
|
+
enumValues: [string, ...string[]];
|
|
717
|
+
baseColumn: never;
|
|
718
|
+
identity: undefined;
|
|
719
|
+
generated: undefined;
|
|
720
|
+
}, {}, {}>;
|
|
721
|
+
ipAddress: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
722
|
+
name: "ip_address";
|
|
723
|
+
tableName: "logs";
|
|
724
|
+
dataType: "string";
|
|
725
|
+
columnType: "MySqlVarChar";
|
|
726
|
+
data: string;
|
|
727
|
+
driverParam: string | number;
|
|
728
|
+
notNull: false;
|
|
729
|
+
hasDefault: false;
|
|
730
|
+
isPrimaryKey: false;
|
|
731
|
+
isAutoincrement: false;
|
|
732
|
+
hasRuntimeDefault: false;
|
|
733
|
+
enumValues: [string, ...string[]];
|
|
734
|
+
baseColumn: never;
|
|
735
|
+
identity: undefined;
|
|
736
|
+
generated: undefined;
|
|
737
|
+
}, {}, {}>;
|
|
738
|
+
userAgent: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
739
|
+
name: "user_agent";
|
|
740
|
+
tableName: "logs";
|
|
741
|
+
dataType: "string";
|
|
742
|
+
columnType: "MySqlVarChar";
|
|
743
|
+
data: string;
|
|
744
|
+
driverParam: string | number;
|
|
745
|
+
notNull: false;
|
|
746
|
+
hasDefault: false;
|
|
747
|
+
isPrimaryKey: false;
|
|
748
|
+
isAutoincrement: false;
|
|
749
|
+
hasRuntimeDefault: false;
|
|
750
|
+
enumValues: [string, ...string[]];
|
|
751
|
+
baseColumn: never;
|
|
752
|
+
identity: undefined;
|
|
753
|
+
generated: undefined;
|
|
754
|
+
}, {}, {}>;
|
|
755
|
+
source: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
756
|
+
name: "source";
|
|
757
|
+
tableName: "logs";
|
|
758
|
+
dataType: "string";
|
|
759
|
+
columnType: "MySqlVarChar";
|
|
760
|
+
data: string;
|
|
761
|
+
driverParam: string | number;
|
|
762
|
+
notNull: false;
|
|
763
|
+
hasDefault: false;
|
|
764
|
+
isPrimaryKey: false;
|
|
765
|
+
isAutoincrement: false;
|
|
766
|
+
hasRuntimeDefault: false;
|
|
767
|
+
enumValues: [string, ...string[]];
|
|
768
|
+
baseColumn: never;
|
|
769
|
+
identity: undefined;
|
|
770
|
+
generated: undefined;
|
|
771
|
+
}, {}, {}>;
|
|
772
|
+
createdAt: import("drizzle-orm/mysql-core").MySqlColumn<{
|
|
773
|
+
name: "created_at";
|
|
774
|
+
tableName: "logs";
|
|
775
|
+
dataType: "date";
|
|
776
|
+
columnType: "MySqlTimestamp";
|
|
777
|
+
data: Date;
|
|
778
|
+
driverParam: string | number;
|
|
779
|
+
notNull: true;
|
|
780
|
+
hasDefault: true;
|
|
781
|
+
isPrimaryKey: false;
|
|
782
|
+
isAutoincrement: false;
|
|
783
|
+
hasRuntimeDefault: false;
|
|
784
|
+
enumValues: undefined;
|
|
785
|
+
baseColumn: never;
|
|
786
|
+
identity: undefined;
|
|
787
|
+
generated: undefined;
|
|
788
|
+
}, {}, {}>;
|
|
789
|
+
};
|
|
790
|
+
dialect: "mysql";
|
|
791
|
+
}>;
|
|
561
792
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/db/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAchC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAa7B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAI/B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ5B,CAAA"}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAchC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAa7B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAI/B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ5B,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBrB,CAAA"}
|
package/dist/db/schema.js
CHANGED
|
@@ -62,3 +62,28 @@ export const EditorPhotosTable = mysqlTable('editor_photos', {
|
|
|
62
62
|
linked: boolean('linked').default(false),
|
|
63
63
|
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
64
64
|
});
|
|
65
|
+
/**
|
|
66
|
+
* This table is used to store logs for admin actions
|
|
67
|
+
*/
|
|
68
|
+
export const LogsTable = mysqlTable('logs', {
|
|
69
|
+
id: int('id').primaryKey().autoincrement(),
|
|
70
|
+
eventType: varchar('event_type', { length: 100 }).notNull(),
|
|
71
|
+
actorId: varchar('actor_id', { length: 50 }),
|
|
72
|
+
actorUsername: varchar('actor_username', { length: 100 }),
|
|
73
|
+
entityType: varchar('entity_type', { length: 50 }),
|
|
74
|
+
entityId: varchar('entity_id', { length: 100 }),
|
|
75
|
+
entityLabel: varchar('entity_label', { length: 255 }),
|
|
76
|
+
sectionName: varchar('section_name', { length: 100 }),
|
|
77
|
+
metadata: longtext('metadata'),
|
|
78
|
+
ipAddress: varchar('ip_address', { length: 45 }),
|
|
79
|
+
userAgent: varchar('user_agent', { length: 255 }),
|
|
80
|
+
source: varchar('source', { length: 50 }),
|
|
81
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
82
|
+
}, (table) => [
|
|
83
|
+
index('logsEventTypeIdx').on(table.eventType),
|
|
84
|
+
index('logsActorIdIdx').on(table.actorId),
|
|
85
|
+
index('logsSectionNameIdx').on(table.sectionName),
|
|
86
|
+
index('logsEntityTypeIdx').on(table.entityType),
|
|
87
|
+
index('logsEntityIdIdx').on(table.entityId),
|
|
88
|
+
index('logsCreatedAtIdx').on(table.createdAt),
|
|
89
|
+
]);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './api/index.js';
|
|
|
2
2
|
export * from './auth/index.js';
|
|
3
3
|
export * from './core/index.js';
|
|
4
4
|
export * from './db/index.js';
|
|
5
|
+
export * from './logging/index.js';
|
|
5
6
|
export * from './utils/index.js';
|
|
6
7
|
export * from './validators/index.js';
|
|
7
8
|
export * from './translations/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from './api/index.js';
|
|
|
2
2
|
export * from './auth/index.js';
|
|
3
3
|
export * from './core/index.js';
|
|
4
4
|
export * from './db/index.js';
|
|
5
|
+
export * from './logging/index.js';
|
|
5
6
|
export * from './utils/index.js';
|
|
6
7
|
export * from './validators/index.js';
|
|
7
8
|
export * from './translations/index.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type AuditEventType = 'auth.login' | 'auth.logout' | 'section.item.create' | 'section.item.update' | 'section.item.delete' | 'admin.section.create' | 'admin.section.update' | 'admin.section.delete' | 'admin.username.change' | 'admin.settings.change';
|
|
2
|
+
export type RequestMetadata = {
|
|
3
|
+
ipAddress?: string | null;
|
|
4
|
+
userAgent?: string | null;
|
|
5
|
+
source?: string | null;
|
|
6
|
+
};
|
|
7
|
+
export type AuditLogInput = {
|
|
8
|
+
eventType: AuditEventType;
|
|
9
|
+
actorId?: string | number | null;
|
|
10
|
+
actorUsername?: string | null;
|
|
11
|
+
entityType?: string | null;
|
|
12
|
+
entityId?: string | number | null;
|
|
13
|
+
entityLabel?: string | null;
|
|
14
|
+
sectionName?: string | null;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
requestMetadata?: RequestMetadata;
|
|
17
|
+
};
|
|
18
|
+
export declare const getRequestMetadataFromHeaders: (headers?: Headers | null) => RequestMetadata;
|
|
19
|
+
export declare const recordAuditLog: (input: AuditLogInput) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/logging/audit.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GACpB,YAAY,GACZ,aAAa,GACb,qBAAqB,GACrB,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,sBAAsB,GACtB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,CAAA;AAE7B,MAAM,MAAM,eAAe,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IACxB,SAAS,EAAE,cAAc,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,eAAe,CAAC,EAAE,eAAe,CAAA;CACpC,CAAA;AAcD,eAAO,MAAM,6BAA6B,GAAI,UAAU,OAAO,GAAG,IAAI,KAAG,eAaxE,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,OAAO,aAAa,KAAG,OAAO,CAAC,IAAI,CAqBvE,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { db } from '../db/client.js';
|
|
2
|
+
import { AuditLogsTable } from '../db/schema.js';
|
|
3
|
+
const toNullableString = (value) => value === null || value === undefined ? null : String(value);
|
|
4
|
+
const safeJsonStringify = (value) => {
|
|
5
|
+
if (!value)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return JSON.stringify({ error: 'metadata_serialization_failed' });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export const getRequestMetadataFromHeaders = (headers) => {
|
|
15
|
+
if (!headers)
|
|
16
|
+
return {};
|
|
17
|
+
const forwardedFor = headers.get('x-forwarded-for');
|
|
18
|
+
const realIp = headers.get('x-real-ip');
|
|
19
|
+
const cfIp = headers.get('cf-connecting-ip');
|
|
20
|
+
const ipAddress = (forwardedFor?.split(',')[0] || realIp || cfIp || '').trim() || null;
|
|
21
|
+
return {
|
|
22
|
+
ipAddress,
|
|
23
|
+
userAgent: headers.get('user-agent') || null,
|
|
24
|
+
source: headers.get('x-trpc-source') || headers.get('x-requested-with') || null,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export const recordAuditLog = async (input) => {
|
|
28
|
+
const metadata = safeJsonStringify(input.metadata);
|
|
29
|
+
const request = input.requestMetadata ?? {};
|
|
30
|
+
try {
|
|
31
|
+
await db.insert(AuditLogsTable).values({
|
|
32
|
+
eventType: input.eventType,
|
|
33
|
+
actorId: toNullableString(input.actorId),
|
|
34
|
+
actorUsername: input.actorUsername ?? null,
|
|
35
|
+
entityType: input.entityType ?? null,
|
|
36
|
+
entityId: toNullableString(input.entityId),
|
|
37
|
+
entityLabel: input.entityLabel ?? null,
|
|
38
|
+
sectionName: input.sectionName ?? null,
|
|
39
|
+
metadata,
|
|
40
|
+
ipAddress: request.ipAddress ?? null,
|
|
41
|
+
userAgent: request.userAgent ?? null,
|
|
42
|
+
source: request.source ?? null,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Failed to write audit log entry', error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,6BAA6B,EAC7B,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,eAAe,GACvB,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { recordLog, getRequestMetadataFromHeaders, } from './log.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type LogEventType = 'auth.login' | 'auth.logout' | 'section.item.create' | 'section.item.update' | 'section.item.delete' | 'admin.section.create' | 'admin.section.update' | 'admin.section.delete' | 'admin.username.change' | 'admin.settings.change';
|
|
2
|
+
export type RequestMetadata = {
|
|
3
|
+
ipAddress?: string | null;
|
|
4
|
+
userAgent?: string | null;
|
|
5
|
+
source?: string | null;
|
|
6
|
+
};
|
|
7
|
+
export type LogInput = {
|
|
8
|
+
eventType: LogEventType;
|
|
9
|
+
actorId?: string | number | null;
|
|
10
|
+
actorUsername?: string | null;
|
|
11
|
+
entityType?: string | null;
|
|
12
|
+
entityId?: string | number | null;
|
|
13
|
+
entityLabel?: string | null;
|
|
14
|
+
sectionName?: string | null;
|
|
15
|
+
metadata?: Record<string, unknown>;
|
|
16
|
+
requestMetadata?: RequestMetadata;
|
|
17
|
+
};
|
|
18
|
+
export declare const getRequestMetadataFromHeaders: (headers?: Headers | null) => RequestMetadata;
|
|
19
|
+
export declare const recordLog: (input: LogInput) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/logging/log.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAClB,YAAY,GACZ,aAAa,GACb,qBAAqB,GACrB,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,sBAAsB,GACtB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,CAAA;AAE7B,MAAM,MAAM,eAAe,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACnB,SAAS,EAAE,YAAY,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,eAAe,CAAC,EAAE,eAAe,CAAA;CACpC,CAAA;AAcD,eAAO,MAAM,6BAA6B,GAAI,UAAU,OAAO,GAAG,IAAI,KAAG,eAaxE,CAAA;AAED,eAAO,MAAM,SAAS,GAAU,OAAO,QAAQ,KAAG,OAAO,CAAC,IAAI,CAqB7D,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { db } from '../db/client.js';
|
|
2
|
+
import { LogsTable } from '../db/schema.js';
|
|
3
|
+
const toNullableString = (value) => value === null || value === undefined ? null : String(value);
|
|
4
|
+
const safeJsonStringify = (value) => {
|
|
5
|
+
if (!value)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return JSON.stringify({ error: 'metadata_serialization_failed' });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export const getRequestMetadataFromHeaders = (headers) => {
|
|
15
|
+
if (!headers)
|
|
16
|
+
return {};
|
|
17
|
+
const forwardedFor = headers.get('x-forwarded-for');
|
|
18
|
+
const realIp = headers.get('x-real-ip');
|
|
19
|
+
const cfIp = headers.get('cf-connecting-ip');
|
|
20
|
+
const ipAddress = (forwardedFor?.split(',')[0] || realIp || cfIp || '').trim() || null;
|
|
21
|
+
return {
|
|
22
|
+
ipAddress,
|
|
23
|
+
userAgent: headers.get('user-agent') || null,
|
|
24
|
+
source: headers.get('x-trpc-source') || headers.get('x-requested-with') || null,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export const recordLog = async (input) => {
|
|
28
|
+
const metadata = safeJsonStringify(input.metadata);
|
|
29
|
+
const request = input.requestMetadata ?? {};
|
|
30
|
+
try {
|
|
31
|
+
await db.insert(LogsTable).values({
|
|
32
|
+
eventType: input.eventType,
|
|
33
|
+
actorId: toNullableString(input.actorId),
|
|
34
|
+
actorUsername: input.actorUsername ?? null,
|
|
35
|
+
entityType: input.entityType ?? null,
|
|
36
|
+
entityId: toNullableString(input.entityId),
|
|
37
|
+
entityLabel: input.entityLabel ?? null,
|
|
38
|
+
sectionName: input.sectionName ?? null,
|
|
39
|
+
metadata,
|
|
40
|
+
ipAddress: request.ipAddress ?? null,
|
|
41
|
+
userAgent: request.userAgent ?? null,
|
|
42
|
+
source: request.source ?? null,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Failed to write log entry', error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
"delete_admin_text": "هل أنت متأكد من رغبتك في حذف هذا المدير؟",
|
|
10
10
|
"edit_admin": "تعديل مدير",
|
|
11
11
|
"edit_admin_text": "يمكنك تعديل هذا المدير هنا",
|
|
12
|
+
"admin": "المسؤول",
|
|
13
|
+
"action": "الإجراء",
|
|
14
|
+
"date": "التاريخ",
|
|
15
|
+
"details": "التفاصيل",
|
|
16
|
+
"section": "القسم",
|
|
17
|
+
"no_data": "لا توجد بيانات",
|
|
12
18
|
"adminNotFound": "المدير غير موجود",
|
|
13
19
|
"sectionNotFound": "القسم غير موجود",
|
|
14
20
|
"adminDeletedSuccessfully": "تم حذف المدير بنجاح",
|
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
"delete_admin_text": "Are you sure you want to delete this admin?",
|
|
10
10
|
"edit_admin": "Edit Admin",
|
|
11
11
|
"edit_admin_text": "Edit Admin",
|
|
12
|
+
"admin": "Admin",
|
|
12
13
|
"adminNotFound": "Admin not found",
|
|
13
14
|
"sectionNotFound": "Section not found",
|
|
14
15
|
"adminDeletedSuccessfully": "Admin deleted successfully",
|
|
15
16
|
"masterAdminCannotBeDeleted": "Master admin cannot be deleted",
|
|
16
17
|
"masterAdminCannotBeModified": "Master admin cannot be modified",
|
|
18
|
+
"action": "Action",
|
|
19
|
+
"date": "Date",
|
|
20
|
+
"details": "Details",
|
|
21
|
+
"section": "Section",
|
|
22
|
+
"no_data": "No data available",
|
|
17
23
|
"login_to_your_account": "Login to your account",
|
|
18
24
|
"login": "Login",
|
|
19
25
|
"logout": "Logout",
|