diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6197feba..5515ec9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ TBD
- Reset query arguments on login if `?next` is not available ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/750))
### Tweaks
- Add link to go back to dashboard on error page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/743))
+- Set audit logging to logfile instead of DB ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/751))
### Lang
- Changes of phrase in `cs_CS` translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/749))
diff --git a/app/classes/controllers/management_controller.py b/app/classes/controllers/management_controller.py
index 7085b503..cab755b6 100644
--- a/app/classes/controllers/management_controller.py
+++ b/app/classes/controllers/management_controller.py
@@ -95,9 +95,6 @@ class ManagementController:
# **********************************************************************************
# Audit_Log Methods
# **********************************************************************************
- @staticmethod
- def get_activity_log():
- return HelpersManagement.get_activity_log()
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
return self.management_helper.add_to_audit_log(
diff --git a/app/classes/logging/log_formatter.py b/app/classes/logging/log_formatter.py
new file mode 100644
index 00000000..e3f2b4f7
--- /dev/null
+++ b/app/classes/logging/log_formatter.py
@@ -0,0 +1,53 @@
+import logging
+import logging.config
+import json
+from datetime import datetime
+
+
+class JsonEncoderStrFallback(json.JSONEncoder):
+ def default(self, o):
+ try:
+ return super().default(o)
+ except TypeError as exc:
+ if "not JSON serializable" in str(exc):
+ return str(o)
+ raise
+
+
+class JsonEncoderDatetime(JsonEncoderStrFallback):
+ def default(self, o):
+ if isinstance(o, datetime):
+ return o.strftime("%Y-%m-%dT%H:%M:%S%z")
+
+ return super().default(o)
+
+
+class JsonFormatter(logging.Formatter):
+ def formatTime(self, record, datefmt=None):
+ """
+ Override formatTime to customize the time format.
+ """
+ timestamp = datetime.fromtimestamp(record.created)
+ if datefmt:
+ # Use the specified date format
+ return timestamp.strftime(datefmt)
+ # Default date format: YYYY-MM-DD HH:MM:SS,mmm
+ secs = int(record.msecs)
+ return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')},{secs:03d}"
+
+ def format(self, record):
+ log_data = {
+ "level": record.levelname,
+ "time": self.formatTime(record),
+ "log_msg": record.getMessage(),
+ }
+
+ # Filter out standard log record attributes and include only custom ones
+ custom_attrs = ["user_name", "user_id", "server_id", "source_ip"]
+ extra_attrs = {
+ key: value for key, value in record.__dict__.items() if key in custom_attrs
+ }
+
+ # Merge extra attributes with log data
+ log_data.update(extra_attrs)
+ return json.dumps(log_data)
diff --git a/app/classes/models/management.py b/app/classes/models/management.py
index ffe207c2..e59dd39c 100644
--- a/app/classes/models/management.py
+++ b/app/classes/models/management.py
@@ -16,28 +16,10 @@ from app.classes.models.base_model import BaseModel
from app.classes.models.users import HelperUsers
from app.classes.models.servers import Servers
from app.classes.models.server_permissions import PermissionsServers
-from app.classes.shared.main_models import DatabaseShortcuts
from app.classes.shared.websocket_manager import WebSocketManager
logger = logging.getLogger(__name__)
-
-
-# **********************************************************************************
-# Audit_Log Class
-# **********************************************************************************
-class AuditLog(BaseModel):
- audit_id = AutoField()
- created = DateTimeField(default=datetime.datetime.now)
- user_name = CharField(default="")
- user_id = IntegerField(default=0, index=True)
- source_ip = CharField(default="127.0.0.1")
- server_id = ForeignKeyField(
- Servers, backref="audit_server", null=True
- ) # When auditing global events, use server ID null
- log_msg = TextField(default="")
-
- class Meta:
- table_name = "audit_log"
+auth_logger = logging.getLogger("audit_log")
# **********************************************************************************
@@ -149,10 +131,6 @@ class HelpersManagement:
# **********************************************************************************
# Audit_Log Methods
# **********************************************************************************
- @staticmethod
- def get_activity_log():
- query = AuditLog.select()
- return DatabaseShortcuts.return_db_rows(query)
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
logger.debug(f"Adding to audit log User:{user_id} - Message: {log_msg} ")
@@ -166,50 +144,28 @@ class HelpersManagement:
WebSocketManager().broadcast_user(user, "notification", audit_msg)
except Exception as e:
logger.error(f"Error broadcasting to user {user} - {e}")
-
- AuditLog.insert(
- {
- AuditLog.user_name: user_data["username"],
- AuditLog.user_id: user_id,
- AuditLog.server_id: server_id,
- AuditLog.log_msg: audit_msg,
- AuditLog.source_ip: source_ip,
- }
- ).execute()
- # deletes records when there's more than 300
- ordered = AuditLog.select().order_by(+AuditLog.created)
- for item in ordered:
- if not self.helper.get_setting("max_audit_entries"):
- max_entries = 300
- else:
- max_entries = self.helper.get_setting("max_audit_entries")
- if AuditLog.select().count() > max_entries:
- AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
- else:
- return
+ auth_logger.info(
+ str(log_msg),
+ extra={
+ "user_name": user_data["username"],
+ "user_id": user_id,
+ "server_id": server_id,
+ "source_ip": source_ip,
+ },
+ )
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
- AuditLog.insert(
- {
- AuditLog.user_name: user_name,
- AuditLog.user_id: user_id,
- AuditLog.server_id: server_id,
- AuditLog.log_msg: log_msg,
- AuditLog.source_ip: source_ip,
- }
- ).execute()
- # deletes records when there's more than 300
- ordered = AuditLog.select().order_by(+AuditLog.created)
- for item in ordered:
- # configurable through app/config/config.json
- if not self.helper.get_setting("max_audit_entries"):
- max_entries = 300
- else:
- max_entries = self.helper.get_setting("max_audit_entries")
- if AuditLog.select().count() > max_entries:
- AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
- else:
- return
+ if isinstance(server_id, Servers) and server_id is not None:
+ server_id = server_id.server_id
+ auth_logger.info(
+ str(log_msg),
+ extra={
+ "user_name": user_name,
+ "user_id": user_id,
+ "server_id": server_id,
+ "source_ip": source_ip,
+ },
+ )
@staticmethod
def create_crafty_row():
diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py
index e1beb547..0ceffb7c 100644
--- a/app/classes/web/panel_handler.py
+++ b/app/classes/web/panel_handler.py
@@ -1506,8 +1506,6 @@ class PanelHandler(BaseHandler):
template = "panel/panel_edit_role.html"
elif page == "activity_logs":
- page_data["audit_logs"] = self.controller.management.get_activity_log()
-
template = "panel/activity_logs.html"
elif page == "download_file":
diff --git a/app/classes/web/routes/api/crafty/clogs/index.py b/app/classes/web/routes/api/crafty/clogs/index.py
index 97a24a34..f42d7e2c 100644
--- a/app/classes/web/routes/api/crafty/clogs/index.py
+++ b/app/classes/web/routes/api/crafty/clogs/index.py
@@ -1,3 +1,5 @@
+import os
+import json
from app.classes.web.base_api_handler import BaseApiHandler
@@ -22,9 +24,17 @@ class ApiCraftyLogIndexHandler(BaseApiHandler):
raise NotImplementedError
if log_type == "audit":
+ with open(
+ os.path.join(self.controller.project_root, "logs", "audit.log"),
+ "r",
+ encoding="utf-8",
+ ) as f:
+ log_lines = [json.loads(line) for line in f]
+ rev_log_lines = log_lines[::-1]
+
return self.finish_json(
200,
- {"status": "ok", "data": self.controller.management.get_activity_log()},
+ {"status": "ok", "data": rev_log_lines},
)
if log_type == "session":
diff --git a/app/config/logging.json b/app/config/logging.json
index fd1173eb..d0a20cdf 100644
--- a/app/config/logging.json
+++ b/app/config/logging.json
@@ -14,6 +14,9 @@
"auth": {
"format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s"
},
+ "audit": {
+ "()": "app.classes.logging.log_formatter.JsonFormatter"
+ },
"cmd_queue": {
"format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s"
}
@@ -70,6 +73,14 @@
"maxBytes": 10485760,
"backupCount": 20,
"encoding": "utf8"
+ },
+ "audit_log_handler": {
+ "class": "logging.handlers.RotatingFileHandler",
+ "formatter": "audit",
+ "filename": "logs/audit.log",
+ "maxBytes": 10485760,
+ "backupCount": 20,
+ "encoding": "utf8"
}
},
"loggers": {
@@ -108,6 +119,12 @@
"cmd_queue_file_handler"
],
"propagate": false
+ },
+ "audit_log": {
+ "level": "INFO",
+ "handlers": [
+ "audit_log_handler"
+ ]
}
}
}
\ No newline at end of file
diff --git a/app/frontend/templates/panel/activity_logs.html b/app/frontend/templates/panel/activity_logs.html
index 389edaf5..e053e50e 100644
--- a/app/frontend/templates/panel/activity_logs.html
+++ b/app/frontend/templates/panel/activity_logs.html
@@ -36,25 +36,21 @@
- Username |
- Time |
- Action |
- Server ID |
- IP |
+ Time |
+ Username |
+ Action |
+ Server ID |
+ IP |
- {% for row in data['audit_logs'] %}
- {{ row['user_name'] }} |
-
- {{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
+ |
+
{{ translate('datatables',
+ 'loadingRecords', data['lang'])}}
|
- {{ row['log_msg'] }} |
- {{ row['server_id'] }} |
- {{ row['source_ip'] }} |
- {% end %}
@@ -79,17 +75,6 @@
{% end %}
{% block js %}
-
{% end %}
\ No newline at end of file
diff --git a/app/migrations/20240216_rework_servers_uuid.py b/app/migrations/20240216_rework_servers_uuid.py
index facd0e42..7850346d 100644
--- a/app/migrations/20240216_rework_servers_uuid.py
+++ b/app/migrations/20240216_rework_servers_uuid.py
@@ -6,7 +6,6 @@ import logging
from app.classes.shared.console import Console
from app.classes.shared.migration import Migrator, MigrateHistory
from app.classes.models.management import (
- AuditLog,
Webhooks,
Schedules,
Backups,
@@ -61,17 +60,6 @@ def migrate(migrator: Migrator, database, **kwargs):
peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
)
- # Changes on Audit Log Table
- migrator.alter_column_type(
- AuditLog,
- "server_id",
- peewee.ForeignKeyField(
- Servers,
- backref="audit_server",
- null=True,
- field=peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
- ),
- )
# Changes on Webhook Table
migrator.alter_column_type(
Webhooks,
@@ -109,13 +97,6 @@ def rollback(migrator: Migrator, database, **kwargs):
peewee.AutoField(),
)
- # Changes on Audit Log Table
- migrator.alter_column_type(
- AuditLog,
- "server_id",
- peewee.IntegerField(default=None, index=True),
- )
-
# Changes on Webhook Table
migrator.alter_column_type(
Webhooks,
diff --git a/app/migrations/20240217_rework_servers_uuid_part2.py b/app/migrations/20240217_rework_servers_uuid_part2.py
index eab04c28..a4b01c45 100644
--- a/app/migrations/20240217_rework_servers_uuid_part2.py
+++ b/app/migrations/20240217_rework_servers_uuid_part2.py
@@ -6,7 +6,6 @@ import logging
from app.classes.shared.console import Console
from app.classes.shared.migration import Migrator, MigrateHistory
from app.classes.models.management import (
- AuditLog,
Webhooks,
Schedules,
Backups,
@@ -73,20 +72,6 @@ def migrate(migrator: Migrator, database, **kwargs):
try:
logger.info("Migrating Data from Int to UUID (Foreign Keys)")
Console.info("Migrating Data from Int to UUID (Foreign Keys)")
- # Changes on Audit Log Table
- for audit_log in AuditLog.select():
- old_server_id = audit_log.server_id_id
- if old_server_id == "0" or old_server_id is None:
- server_uuid = None
- else:
- try:
- server = Servers.get_by_id(old_server_id)
- server_uuid = server.server_uuid
- except:
- server_uuid = old_server_id
- AuditLog.update(server_id=server_uuid).where(
- AuditLog.audit_id == audit_log.audit_id
- ).execute()
# Changes on Webhooks Log Table
for webhook in Webhooks.select():
@@ -247,21 +232,6 @@ def rollback(migrator: Migrator, database, **kwargs):
try:
logger.info("Migrating Data from UUID to Int (Foreign Keys)")
Console.info("Migrating Data from UUID to Int (Foreign Keys)")
- # Changes on Audit Log Table
- for audit_log in AuditLog.select():
- old_server_id = audit_log.server_id_id
- if old_server_id is None:
- new_server_id = 0
- else:
- try:
- server = Servers.get_or_none(Servers.server_uuid == old_server_id)
- new_server_id = server.server_id
- except:
- new_server_id = old_server_id
- AuditLog.update(server_id=new_server_id).where(
- AuditLog.audit_id == audit_log.audit_id
- ).execute()
-
# Changes on Webhooks Log Table
for webhook in Webhooks.select():
old_server_id = webhook.server_id_id
diff --git a/app/migrations/20240420_audit_log_drop.py b/app/migrations/20240420_audit_log_drop.py
new file mode 100644
index 00000000..be153f11
--- /dev/null
+++ b/app/migrations/20240420_audit_log_drop.py
@@ -0,0 +1,34 @@
+import peewee
+import datetime
+from peewee import (
+ AutoField,
+ DateTimeField,
+ CharField,
+ IntegerField,
+ ForeignKeyField,
+ TextField,
+)
+
+from app.classes.shared.server import Servers
+
+
+def migrate(migrator, db):
+ migrator.drop_table("audit_log")
+
+
+def rollback(migrator, db):
+ class AuditLog(peewee.Model):
+ audit_id = AutoField()
+ created = DateTimeField(default=datetime.datetime.now)
+ user_name = CharField(default="")
+ user_id = IntegerField(default=0, index=True)
+ source_ip = CharField(default="127.0.0.1")
+ server_id = ForeignKeyField(
+ Servers, backref="audit_server", null=True
+ ) # When auditing global events, use server ID null
+ log_msg = TextField(default="")
+
+ class Meta:
+ table_name = "audit_log"
+
+ migrator.create_table(AuditLog)
diff --git a/app/translations/cs_CS.json b/app/translations/cs_CS.json
index 81f5ef21..79026d51 100644
--- a/app/translations/cs_CS.json
+++ b/app/translations/cs_CS.json
@@ -117,6 +117,7 @@
"welcome": "Vítejte v Crafty Controlleru"
},
"datatables": {
+ "loadingRecords": "Načítání...",
"i18n": {
"aria": {
"sortAscending": ": aktivace řazení sloupce vzestupně",
@@ -693,4 +694,4 @@
"webhook_body": "Webhook Body",
"webhooks": "Webhooky"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json
index f1594576..c8358427 100644
--- a/app/translations/de_DE.json
+++ b/app/translations/de_DE.json
@@ -117,6 +117,7 @@
"welcome": "Willkommen bei Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Laden...",
"i18n": {
"aria": {
"sortAscending": ": Aktivieren, um die Spalte aufsteigend zu sortieren",
@@ -674,4 +675,4 @@
"webhook_body": "Webhook-Inhalt",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json
index a74250e1..a0723002 100644
--- a/app/translations/en_EN.json
+++ b/app/translations/en_EN.json
@@ -116,6 +116,7 @@
"welcome": "Welcome to Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Loading...",
"i18n": {
"aria": {
"sortAscending": ": activate to sort column ascending",
@@ -672,4 +673,4 @@
"webhook_body": "Webhook Body",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/es_ES.json b/app/translations/es_ES.json
index 361b896b..7531ed1c 100644
--- a/app/translations/es_ES.json
+++ b/app/translations/es_ES.json
@@ -117,6 +117,7 @@
"welcome": "Bienvenido a Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Cargando...",
"i18n": {
"aria": {
"sortAscending": ": activar para ordenar las columnas de manera ascendente",
@@ -674,4 +675,4 @@
"webhook_body": "Cuerpo del Webhook",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/fi_FI_incomplete.json b/app/translations/fi_FI_incomplete.json
index 55cd990c..8d6168a6 100644
--- a/app/translations/fi_FI_incomplete.json
+++ b/app/translations/fi_FI_incomplete.json
@@ -100,6 +100,7 @@
"welcome": "Tervetuloa Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Ladataan...",
"i18n": {
"aria": {
"sortAscending": ": lajittele sarake nousevasti",
@@ -560,4 +561,4 @@
"userSettings": "Käyttäjäasetukset",
"uses": "Sallittujen käyttäkertojen määtä (-1 == Ei rajaa)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json
index 24a4ee7e..739ed9d9 100644
--- a/app/translations/fr_FR.json
+++ b/app/translations/fr_FR.json
@@ -117,6 +117,7 @@
"welcome": "Bienvenue sur Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Chargement ...",
"i18n": {
"aria": {
"sortAscending": ": activer pour trier les colonnes dans l'ordre croissant",
@@ -674,4 +675,4 @@
"webhook_body": "Corps du Webhook",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/fy_NL_incomplete.json b/app/translations/fy_NL_incomplete.json
index 98785a2b..6c5d76e8 100644
--- a/app/translations/fy_NL_incomplete.json
+++ b/app/translations/fy_NL_incomplete.json
@@ -99,6 +99,7 @@
"welcome": "Wolkom by Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Laden...",
"i18n": {
"aria": {
"sortAscending": ": aktivearje om kolom oprinnend te sortearjen",
@@ -529,4 +530,4 @@
"userSettings": "Brûkersynstellingen",
"uses": "Oantal gebrûk tastien (-1==Gjin limyt)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json
index 5d472597..6c552ab0 100644
--- a/app/translations/he_IL.json
+++ b/app/translations/he_IL.json
@@ -117,6 +117,7 @@
"welcome": "ברוכים הבאים ל-פאנל קראפטי"
},
"datatables": {
+ "loadingRecords": "...טוען",
"i18n": {
"aria": {
"sortAscending": ": הפעילו כדי למיין עמודות בסדר עולה",
@@ -674,4 +675,4 @@
"webhook_body": "גוף ה-Webhook",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/hr_HR_incomplete.json b/app/translations/hr_HR_incomplete.json
index 3f63770f..392ee098 100644
--- a/app/translations/hr_HR_incomplete.json
+++ b/app/translations/hr_HR_incomplete.json
@@ -99,6 +99,7 @@
"welcome": "Dobrodošli u Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Učitavanje...",
"i18n": {
"aria": {
"sortAscending": ": aktiviraj za sortiranje stupca uzlazno",
@@ -529,4 +530,4 @@
"userSettings": "Korisničke postavke",
"uses": "Broj dopuštenih upotreba (-1==Bez ograničenja)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/id_ID_incomplete.json b/app/translations/id_ID_incomplete.json
index cfec5557..c2e8813d 100644
--- a/app/translations/id_ID_incomplete.json
+++ b/app/translations/id_ID_incomplete.json
@@ -100,6 +100,7 @@
"welcome": "Selamat Datang Di Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Loading...",
"i18n": {
"aria": {
"sortAscending": ": aktifkan untuk mengurutkan kolom menaik",
@@ -536,4 +537,4 @@
"userSettings": "Pengaturan Pengguna",
"uses": "Jumlah penggunaan yang diizinkan (-1==No Limit)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/it_IT.json b/app/translations/it_IT.json
index 16a863f3..e93b4828 100644
--- a/app/translations/it_IT.json
+++ b/app/translations/it_IT.json
@@ -117,6 +117,7 @@
"welcome": "Benvenuto su Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Carico...",
"i18n": {
"aria": {
"sortAscending": ": attiva per ordinare le colonne in modo ascendente",
@@ -674,4 +675,4 @@
"webhook_body": "Corpo del Webhook",
"webhooks": "Webhook"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json
index 22d1c268..2a0450ca 100644
--- a/app/translations/lol_EN.json
+++ b/app/translations/lol_EN.json
@@ -117,6 +117,7 @@
"welcome": "WELCOM 2 CWAFTY CONTROLLR"
},
"datatables": {
+ "loadingRecords": "Loading...",
"i18n": {
"aria": {
"sortAscending": ": activate to sort column ascending",
@@ -674,4 +675,4 @@
"webhook_body": "WEBHOOK FISH",
"webhooks": "WEBHOOKZ"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json
index b66d9dbc..43501f47 100644
--- a/app/translations/lv_LV.json
+++ b/app/translations/lv_LV.json
@@ -118,6 +118,7 @@
"welcome": "Esiet sveicināts Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Ielādē...",
"i18n": {
"aria": {
"sortAscending": ": aktivizēt lai kārotu kolonnu augoši",
@@ -675,4 +676,4 @@
"webhook_body": "Webhook Saturs",
"webhooks": "Webhooki"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json
index 9ccf4f2b..ba2d792b 100644
--- a/app/translations/nl_BE.json
+++ b/app/translations/nl_BE.json
@@ -117,6 +117,7 @@
"welcome": "Welkom bij Crafty Controller "
},
"datatables": {
+ "loadingRecords": "Bezig met laden...",
"i18n": {
"aria": {
"sortAscending": ": activeren om kolom oplopend te sorteren",
@@ -674,4 +675,4 @@
"webhook_body": "Webhook-body",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/nl_NL_incomplete.json b/app/translations/nl_NL_incomplete.json
index 8fd97240..1d158594 100644
--- a/app/translations/nl_NL_incomplete.json
+++ b/app/translations/nl_NL_incomplete.json
@@ -99,6 +99,7 @@
"welcome": "Welkom bij Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Laden...",
"i18n": {
"aria": {
"sortAscending": ": activeer om kolom oplopend te sorteren",
@@ -529,4 +530,4 @@
"userSettings": "Gebruikersinstellingen",
"uses": "Aantal keer toegestaan (-1==Geen limiet)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json
index 9229b474..c0b5c0f4 100644
--- a/app/translations/pl_PL.json
+++ b/app/translations/pl_PL.json
@@ -117,6 +117,7 @@
"welcome": "Witamy w Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Wczytywanie...",
"i18n": {
"aria": {
"sortAscending": ": aktywuj, aby sortować kolumny w góre",
@@ -673,4 +674,4 @@
"webhook_body": "Treść Webhooka",
"webhooks": "Webhooki"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/pt_BR_incomplete.json b/app/translations/pt_BR_incomplete.json
index c8844f40..dcd375f6 100644
--- a/app/translations/pt_BR_incomplete.json
+++ b/app/translations/pt_BR_incomplete.json
@@ -100,6 +100,7 @@
"welcome": "Bem-vindo ao Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Carregando...",
"i18n": {
"aria": {
"sortAscending": ": ative para ordenar a coluna de forma ascendente",
@@ -537,4 +538,4 @@
"userSettings": "Configurações do Usuário",
"uses": "Número de Usos Permitidos (-1==Sem Limite)"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json
index e6a8be73..96a1a0e5 100644
--- a/app/translations/th_TH.json
+++ b/app/translations/th_TH.json
@@ -117,6 +117,7 @@
"welcome": "ยินดีต้อนรับสู่ Crafty Controller"
},
"datatables": {
+ "loadingRecords": "กำลังโหลด...",
"i18n": {
"aria": {
"sortAscending": ": เปิดใช้งานเพื่อเรียงลำดับคอลัมน์จากน้อยไปมาก",
@@ -673,4 +674,4 @@
"webhook_body": "ภายใน Webhook",
"webhooks": "Webhooks"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json
index 5da06afc..789c7dc9 100644
--- a/app/translations/tr_TR.json
+++ b/app/translations/tr_TR.json
@@ -117,6 +117,7 @@
"welcome": "Crafty Controller'a Hoşgeldiniz!"
},
"datatables": {
+ "loadingRecords": "Yükleniyor...",
"i18n": {
"aria": {
"sortAscending": ": artan sütun sıralamasını aktifleştir",
@@ -673,4 +674,4 @@
"webhook_body": "Webhook Gövdesi",
"webhooks": "Webhooklar"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json
index eff48185..56f02b6c 100644
--- a/app/translations/uk_UA.json
+++ b/app/translations/uk_UA.json
@@ -117,6 +117,7 @@
"welcome": "Ласкаво просимо у Crafty Controller"
},
"datatables": {
+ "loadingRecords": "Завантаження...",
"i18n": {
"aria": {
"sortAscending": ": активуйте, щоб сортувати стовпці за зростанням",
@@ -673,4 +674,4 @@
"webhook_body": "Код Вебхука",
"webhooks": "Вебхуки"
}
-}
+}
\ No newline at end of file
diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json
index cf9f4b1c..c9283d8d 100644
--- a/app/translations/zh_CN.json
+++ b/app/translations/zh_CN.json
@@ -117,6 +117,7 @@
"welcome": "欢迎来到 Crafty Controller"
},
"datatables": {
+ "loadingRecords": "正在加载……",
"i18n": {
"aria": {
"sortAscending": ":激活对队列的升序排列",
@@ -674,4 +675,4 @@
"webhook_body": "Webhook 消息体(Body)",
"webhooks": "Webhook"
}
-}
+}
\ No newline at end of file
diff --git a/main.py b/main.py
index ebaf7806..f8a4aab1 100644
--- a/main.py
+++ b/main.py
@@ -17,6 +17,7 @@ from app.classes.models.users import HelperUsers
from app.classes.models.management import HelpersManagement
from app.classes.shared.import_helper import ImportHelpers
from app.classes.shared.websocket_manager import WebSocketManager
+from app.classes.logging.log_formatter import JsonFormatter
console = Console()
helper = Helpers()
@@ -284,6 +285,11 @@ def setup_logging(debug=True):
logging.config.dictConfig(logging_config)
+ # Apply JSON formatting to the "audit" handler
+ for handler in logging.getLogger().handlers:
+ if handler.name == "audit_log_handler":
+ handler.setFormatter(JsonFormatter())
+
else:
logging.basicConfig(level=logging.DEBUG)
logging.warning(f"Unable to read logging config from {logging_config_file}")