mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2025-01-18 17:15:13 +01:00
Merge branch 'tweak/json-audit-log' into 'dev'
Set audit logging to logfile instead of DB See merge request crafty-controller/crafty-4!751
This commit is contained in:
commit
28ac3d9915
@ -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))
|
- Reset query arguments on login if `?next` is not available ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/750))
|
||||||
### Tweaks
|
### Tweaks
|
||||||
- Add link to go back to dashboard on error page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/743))
|
- 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
|
### Lang
|
||||||
- Changes of phrase in `cs_CS` translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/749))
|
- Changes of phrase in `cs_CS` translation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/749))
|
||||||
<br><br>
|
<br><br>
|
||||||
|
@ -95,9 +95,6 @@ class ManagementController:
|
|||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# 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):
|
def add_to_audit_log(self, user_id, log_msg, server_id=None, source_ip=None):
|
||||||
return self.management_helper.add_to_audit_log(
|
return self.management_helper.add_to_audit_log(
|
||||||
|
53
app/classes/logging/log_formatter.py
Normal file
53
app/classes/logging/log_formatter.py
Normal file
@ -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)
|
@ -16,28 +16,10 @@ from app.classes.models.base_model import BaseModel
|
|||||||
from app.classes.models.users import HelperUsers
|
from app.classes.models.users import HelperUsers
|
||||||
from app.classes.models.servers import Servers
|
from app.classes.models.servers import Servers
|
||||||
from app.classes.models.server_permissions import PermissionsServers
|
from app.classes.models.server_permissions import PermissionsServers
|
||||||
from app.classes.shared.main_models import DatabaseShortcuts
|
|
||||||
from app.classes.shared.websocket_manager import WebSocketManager
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
auth_logger = logging.getLogger("audit_log")
|
||||||
|
|
||||||
# **********************************************************************************
|
|
||||||
# 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"
|
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -149,10 +131,6 @@ class HelpersManagement:
|
|||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
# Audit_Log Methods
|
# 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):
|
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} ")
|
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)
|
WebSocketManager().broadcast_user(user, "notification", audit_msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error broadcasting to user {user} - {e}")
|
logger.error(f"Error broadcasting to user {user} - {e}")
|
||||||
|
auth_logger.info(
|
||||||
AuditLog.insert(
|
str(log_msg),
|
||||||
{
|
extra={
|
||||||
AuditLog.user_name: user_data["username"],
|
"user_name": user_data["username"],
|
||||||
AuditLog.user_id: user_id,
|
"user_id": user_id,
|
||||||
AuditLog.server_id: server_id,
|
"server_id": server_id,
|
||||||
AuditLog.log_msg: audit_msg,
|
"source_ip": source_ip,
|
||||||
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
|
|
||||||
|
|
||||||
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
||||||
AuditLog.insert(
|
if isinstance(server_id, Servers) and server_id is not None:
|
||||||
{
|
server_id = server_id.server_id
|
||||||
AuditLog.user_name: user_name,
|
auth_logger.info(
|
||||||
AuditLog.user_id: user_id,
|
str(log_msg),
|
||||||
AuditLog.server_id: server_id,
|
extra={
|
||||||
AuditLog.log_msg: log_msg,
|
"user_name": user_name,
|
||||||
AuditLog.source_ip: source_ip,
|
"user_id": user_id,
|
||||||
}
|
"server_id": server_id,
|
||||||
).execute()
|
"source_ip": source_ip,
|
||||||
# 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
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_crafty_row():
|
def create_crafty_row():
|
||||||
|
@ -1506,8 +1506,6 @@ class PanelHandler(BaseHandler):
|
|||||||
template = "panel/panel_edit_role.html"
|
template = "panel/panel_edit_role.html"
|
||||||
|
|
||||||
elif page == "activity_logs":
|
elif page == "activity_logs":
|
||||||
page_data["audit_logs"] = self.controller.management.get_activity_log()
|
|
||||||
|
|
||||||
template = "panel/activity_logs.html"
|
template = "panel/activity_logs.html"
|
||||||
|
|
||||||
elif page == "download_file":
|
elif page == "download_file":
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
from app.classes.web.base_api_handler import BaseApiHandler
|
from app.classes.web.base_api_handler import BaseApiHandler
|
||||||
|
|
||||||
|
|
||||||
@ -22,9 +24,17 @@ class ApiCraftyLogIndexHandler(BaseApiHandler):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
if log_type == "audit":
|
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(
|
return self.finish_json(
|
||||||
200,
|
200,
|
||||||
{"status": "ok", "data": self.controller.management.get_activity_log()},
|
{"status": "ok", "data": rev_log_lines},
|
||||||
)
|
)
|
||||||
|
|
||||||
if log_type == "session":
|
if log_type == "session":
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"auth": {
|
"auth": {
|
||||||
"format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s"
|
"format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s"
|
||||||
},
|
},
|
||||||
|
"audit": {
|
||||||
|
"()": "app.classes.logging.log_formatter.JsonFormatter"
|
||||||
|
},
|
||||||
"cmd_queue": {
|
"cmd_queue": {
|
||||||
"format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s"
|
"format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s"
|
||||||
}
|
}
|
||||||
@ -70,6 +73,14 @@
|
|||||||
"maxBytes": 10485760,
|
"maxBytes": 10485760,
|
||||||
"backupCount": 20,
|
"backupCount": 20,
|
||||||
"encoding": "utf8"
|
"encoding": "utf8"
|
||||||
|
},
|
||||||
|
"audit_log_handler": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"formatter": "audit",
|
||||||
|
"filename": "logs/audit.log",
|
||||||
|
"maxBytes": 10485760,
|
||||||
|
"backupCount": 20,
|
||||||
|
"encoding": "utf8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
@ -108,6 +119,12 @@
|
|||||||
"cmd_queue_file_handler"
|
"cmd_queue_file_handler"
|
||||||
],
|
],
|
||||||
"propagate": false
|
"propagate": false
|
||||||
|
},
|
||||||
|
"audit_log": {
|
||||||
|
"level": "INFO",
|
||||||
|
"handlers": [
|
||||||
|
"audit_log_handler"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,25 +36,21 @@
|
|||||||
<table class="table table-hover" id="audit_table" style="overflow: scroll;" width="100%">
|
<table class="table table-hover" id="audit_table" style="overflow: scroll;" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="rounded">
|
<tr class="rounded">
|
||||||
<td>Username</td>
|
<th>Time</th>
|
||||||
<td>Time</td>
|
<th>Username</th>
|
||||||
<td>Action</td>
|
<th>Action</th>
|
||||||
<td>Server ID</td>
|
<th>Server ID</th>
|
||||||
<td>IP</td>
|
<th>IP</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for row in data['audit_logs'] %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ row['user_name'] }}</td>
|
<td colspan="5" id="image-div" class="text-center"> <!-- Center image within table -->
|
||||||
<td>
|
<img class="img-center" id="logo-animate" src="../static/assets/images/crafty-logo-square-1024.png"
|
||||||
{{ row['created'].strftime('%Y-%m-%d %H:%M:%S') }}
|
alt="Crafty Logo, Crafty is loading" width="20%"><br><br>{{ translate('datatables',
|
||||||
|
'loadingRecords', data['lang'])}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ row['log_msg'] }}</td>
|
|
||||||
<td>{{ row['server_id'] }}</td>
|
|
||||||
<td>{{ row['source_ip'] }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% end %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -79,17 +75,6 @@
|
|||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
console.log('ready for JS!')
|
|
||||||
$('#audit_table').DataTable({
|
|
||||||
'order': [1, 'desc']
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('[data-toggle="popover"]').popover();
|
$('[data-toggle="popover"]').popover();
|
||||||
@ -112,6 +97,74 @@
|
|||||||
$('.too_small').popover("hide");
|
$('.too_small').popover("hide");
|
||||||
} // New width
|
} // New width
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
console.log('ready for JS!')
|
||||||
|
// Initialize DataTables
|
||||||
|
// Load initial data
|
||||||
|
getActivity();
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateActivity(data) {
|
||||||
|
let tbody = $('#audit_table tbody');
|
||||||
|
tbody.empty(); // Clear existing rows
|
||||||
|
$.each(data, function (index, value) {
|
||||||
|
let row = $('<tr>');
|
||||||
|
row.append(`<td>${value.time}</td>`);
|
||||||
|
if (value.user_name != "system" && value.user_id != "-1") {
|
||||||
|
row.append(`<td><a href="/panel/edit_user?id=${value.user_id}">${value.user_name}</a></td>`);
|
||||||
|
} else {
|
||||||
|
row.append(`<td>${value.user_name}</td>`);
|
||||||
|
}
|
||||||
|
row.append(`<td>${value.log_msg}</td>`);
|
||||||
|
row.append(`<td>${value.server_id}</td>`);
|
||||||
|
row.append(`<td>${value.source_ip}</td>`);
|
||||||
|
tbody.append(row);
|
||||||
|
});
|
||||||
|
$('#audit_table').DataTable({
|
||||||
|
'order': [[0, 'desc']], // Sort by the first column in descending order
|
||||||
|
filter: true,
|
||||||
|
"searching": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getActivity() {
|
||||||
|
var token = getCookie("_xsrf");
|
||||||
|
let res = await fetch(`/api/v2/crafty/logs/audit`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-XSRFToken': token
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let responseData = await res.json();
|
||||||
|
console.log(responseData);
|
||||||
|
if (responseData.status === "ok") {
|
||||||
|
updateActivity(responseData.data);
|
||||||
|
console.log("activity update")
|
||||||
|
} else {
|
||||||
|
bootbox.alert(responseData.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rotateImage(degree) {
|
||||||
|
$('#logo-animate').animate({ transform: degree }, {
|
||||||
|
step: function (now, fx) {
|
||||||
|
$(this).css({
|
||||||
|
'-webkit-transform': 'rotate(' + now + 'deg)',
|
||||||
|
'-moz-transform': 'rotate(' + now + 'deg)',
|
||||||
|
'transform': 'rotate(' + now + 'deg)'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
rotateImage(360);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
rotateImage(360);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
@ -6,7 +6,6 @@ import logging
|
|||||||
from app.classes.shared.console import Console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.migration import Migrator, MigrateHistory
|
from app.classes.shared.migration import Migrator, MigrateHistory
|
||||||
from app.classes.models.management import (
|
from app.classes.models.management import (
|
||||||
AuditLog,
|
|
||||||
Webhooks,
|
Webhooks,
|
||||||
Schedules,
|
Schedules,
|
||||||
Backups,
|
Backups,
|
||||||
@ -61,17 +60,6 @@ def migrate(migrator: Migrator, database, **kwargs):
|
|||||||
peewee.CharField(primary_key=True, default=str(uuid.uuid4())),
|
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
|
# Changes on Webhook Table
|
||||||
migrator.alter_column_type(
|
migrator.alter_column_type(
|
||||||
Webhooks,
|
Webhooks,
|
||||||
@ -109,13 +97,6 @@ def rollback(migrator: Migrator, database, **kwargs):
|
|||||||
peewee.AutoField(),
|
peewee.AutoField(),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Changes on Audit Log Table
|
|
||||||
migrator.alter_column_type(
|
|
||||||
AuditLog,
|
|
||||||
"server_id",
|
|
||||||
peewee.IntegerField(default=None, index=True),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Changes on Webhook Table
|
# Changes on Webhook Table
|
||||||
migrator.alter_column_type(
|
migrator.alter_column_type(
|
||||||
Webhooks,
|
Webhooks,
|
||||||
|
@ -6,7 +6,6 @@ import logging
|
|||||||
from app.classes.shared.console import Console
|
from app.classes.shared.console import Console
|
||||||
from app.classes.shared.migration import Migrator, MigrateHistory
|
from app.classes.shared.migration import Migrator, MigrateHistory
|
||||||
from app.classes.models.management import (
|
from app.classes.models.management import (
|
||||||
AuditLog,
|
|
||||||
Webhooks,
|
Webhooks,
|
||||||
Schedules,
|
Schedules,
|
||||||
Backups,
|
Backups,
|
||||||
@ -73,20 +72,6 @@ def migrate(migrator: Migrator, database, **kwargs):
|
|||||||
try:
|
try:
|
||||||
logger.info("Migrating Data from Int to UUID (Foreign Keys)")
|
logger.info("Migrating Data from Int to UUID (Foreign Keys)")
|
||||||
Console.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
|
# Changes on Webhooks Log Table
|
||||||
for webhook in Webhooks.select():
|
for webhook in Webhooks.select():
|
||||||
@ -247,21 +232,6 @@ def rollback(migrator: Migrator, database, **kwargs):
|
|||||||
try:
|
try:
|
||||||
logger.info("Migrating Data from UUID to Int (Foreign Keys)")
|
logger.info("Migrating Data from UUID to Int (Foreign Keys)")
|
||||||
Console.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
|
# Changes on Webhooks Log Table
|
||||||
for webhook in Webhooks.select():
|
for webhook in Webhooks.select():
|
||||||
old_server_id = webhook.server_id_id
|
old_server_id = webhook.server_id_id
|
||||||
|
34
app/migrations/20240420_audit_log_drop.py
Normal file
34
app/migrations/20240420_audit_log_drop.py
Normal file
@ -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)
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Vítejte v Crafty Controlleru"
|
"welcome": "Vítejte v Crafty Controlleru"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Načítání...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktivace řazení sloupce vzestupně",
|
"sortAscending": ": aktivace řazení sloupce vzestupně",
|
||||||
@ -693,4 +694,4 @@
|
|||||||
"webhook_body": "Webhook Body",
|
"webhook_body": "Webhook Body",
|
||||||
"webhooks": "Webhooky"
|
"webhooks": "Webhooky"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Willkommen bei Crafty Controller"
|
"welcome": "Willkommen bei Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Laden...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": Aktivieren, um die Spalte aufsteigend zu sortieren",
|
"sortAscending": ": Aktivieren, um die Spalte aufsteigend zu sortieren",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Webhook-Inhalt",
|
"webhook_body": "Webhook-Inhalt",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -116,6 +116,7 @@
|
|||||||
"welcome": "Welcome to Crafty Controller"
|
"welcome": "Welcome to Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Loading...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activate to sort column ascending",
|
"sortAscending": ": activate to sort column ascending",
|
||||||
@ -672,4 +673,4 @@
|
|||||||
"webhook_body": "Webhook Body",
|
"webhook_body": "Webhook Body",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Bienvenido a Crafty Controller"
|
"welcome": "Bienvenido a Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Cargando...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activar para ordenar las columnas de manera ascendente",
|
"sortAscending": ": activar para ordenar las columnas de manera ascendente",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Cuerpo del Webhook",
|
"webhook_body": "Cuerpo del Webhook",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -100,6 +100,7 @@
|
|||||||
"welcome": "Tervetuloa Crafty Controller"
|
"welcome": "Tervetuloa Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Ladataan...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": lajittele sarake nousevasti",
|
"sortAscending": ": lajittele sarake nousevasti",
|
||||||
@ -560,4 +561,4 @@
|
|||||||
"userSettings": "Käyttäjäasetukset",
|
"userSettings": "Käyttäjäasetukset",
|
||||||
"uses": "Sallittujen käyttäkertojen määtä (-1 == Ei rajaa)"
|
"uses": "Sallittujen käyttäkertojen määtä (-1 == Ei rajaa)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Bienvenue sur Crafty Controller"
|
"welcome": "Bienvenue sur Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Chargement ...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activer pour trier les colonnes dans l'ordre croissant",
|
"sortAscending": ": activer pour trier les colonnes dans l'ordre croissant",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Corps du Webhook",
|
"webhook_body": "Corps du Webhook",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -99,6 +99,7 @@
|
|||||||
"welcome": "Wolkom by Crafty Controller"
|
"welcome": "Wolkom by Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Laden...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktivearje om kolom oprinnend te sortearjen",
|
"sortAscending": ": aktivearje om kolom oprinnend te sortearjen",
|
||||||
@ -529,4 +530,4 @@
|
|||||||
"userSettings": "Brûkersynstellingen",
|
"userSettings": "Brûkersynstellingen",
|
||||||
"uses": "Oantal gebrûk tastien (-1==Gjin limyt)"
|
"uses": "Oantal gebrûk tastien (-1==Gjin limyt)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "ברוכים הבאים ל-פאנל קראפטי"
|
"welcome": "ברוכים הבאים ל-פאנל קראפטי"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "...טוען",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": הפעילו כדי למיין עמודות בסדר עולה",
|
"sortAscending": ": הפעילו כדי למיין עמודות בסדר עולה",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "גוף ה-Webhook",
|
"webhook_body": "גוף ה-Webhook",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -99,6 +99,7 @@
|
|||||||
"welcome": "Dobrodošli u Crafty Controller"
|
"welcome": "Dobrodošli u Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Učitavanje...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktiviraj za sortiranje stupca uzlazno",
|
"sortAscending": ": aktiviraj za sortiranje stupca uzlazno",
|
||||||
@ -529,4 +530,4 @@
|
|||||||
"userSettings": "Korisničke postavke",
|
"userSettings": "Korisničke postavke",
|
||||||
"uses": "Broj dopuštenih upotreba (-1==Bez ograničenja)"
|
"uses": "Broj dopuštenih upotreba (-1==Bez ograničenja)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -100,6 +100,7 @@
|
|||||||
"welcome": "Selamat Datang Di Crafty Controller"
|
"welcome": "Selamat Datang Di Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Loading...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktifkan untuk mengurutkan kolom menaik",
|
"sortAscending": ": aktifkan untuk mengurutkan kolom menaik",
|
||||||
@ -536,4 +537,4 @@
|
|||||||
"userSettings": "Pengaturan Pengguna",
|
"userSettings": "Pengaturan Pengguna",
|
||||||
"uses": "Jumlah penggunaan yang diizinkan (-1==No Limit)"
|
"uses": "Jumlah penggunaan yang diizinkan (-1==No Limit)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Benvenuto su Crafty Controller"
|
"welcome": "Benvenuto su Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Carico...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": attiva per ordinare le colonne in modo ascendente",
|
"sortAscending": ": attiva per ordinare le colonne in modo ascendente",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Corpo del Webhook",
|
"webhook_body": "Corpo del Webhook",
|
||||||
"webhooks": "Webhook"
|
"webhooks": "Webhook"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "WELCOM 2 CWAFTY CONTROLLR"
|
"welcome": "WELCOM 2 CWAFTY CONTROLLR"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Loading...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activate to sort column ascending",
|
"sortAscending": ": activate to sort column ascending",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "WEBHOOK FISH",
|
"webhook_body": "WEBHOOK FISH",
|
||||||
"webhooks": "WEBHOOKZ"
|
"webhooks": "WEBHOOKZ"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -118,6 +118,7 @@
|
|||||||
"welcome": "Esiet sveicināts Crafty Controller"
|
"welcome": "Esiet sveicināts Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Ielādē...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktivizēt lai kārotu kolonnu augoši",
|
"sortAscending": ": aktivizēt lai kārotu kolonnu augoši",
|
||||||
@ -675,4 +676,4 @@
|
|||||||
"webhook_body": "Webhook Saturs",
|
"webhook_body": "Webhook Saturs",
|
||||||
"webhooks": "Webhooki"
|
"webhooks": "Webhooki"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Welkom bij Crafty Controller "
|
"welcome": "Welkom bij Crafty Controller "
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Bezig met laden...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activeren om kolom oplopend te sorteren",
|
"sortAscending": ": activeren om kolom oplopend te sorteren",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Webhook-body",
|
"webhook_body": "Webhook-body",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -99,6 +99,7 @@
|
|||||||
"welcome": "Welkom bij Crafty Controller"
|
"welcome": "Welkom bij Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Laden...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": activeer om kolom oplopend te sorteren",
|
"sortAscending": ": activeer om kolom oplopend te sorteren",
|
||||||
@ -529,4 +530,4 @@
|
|||||||
"userSettings": "Gebruikersinstellingen",
|
"userSettings": "Gebruikersinstellingen",
|
||||||
"uses": "Aantal keer toegestaan (-1==Geen limiet)"
|
"uses": "Aantal keer toegestaan (-1==Geen limiet)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Witamy w Crafty Controller"
|
"welcome": "Witamy w Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Wczytywanie...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": aktywuj, aby sortować kolumny w góre",
|
"sortAscending": ": aktywuj, aby sortować kolumny w góre",
|
||||||
@ -673,4 +674,4 @@
|
|||||||
"webhook_body": "Treść Webhooka",
|
"webhook_body": "Treść Webhooka",
|
||||||
"webhooks": "Webhooki"
|
"webhooks": "Webhooki"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -100,6 +100,7 @@
|
|||||||
"welcome": "Bem-vindo ao Crafty Controller"
|
"welcome": "Bem-vindo ao Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Carregando...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": ative para ordenar a coluna de forma ascendente",
|
"sortAscending": ": ative para ordenar a coluna de forma ascendente",
|
||||||
@ -537,4 +538,4 @@
|
|||||||
"userSettings": "Configurações do Usuário",
|
"userSettings": "Configurações do Usuário",
|
||||||
"uses": "Número de Usos Permitidos (-1==Sem Limite)"
|
"uses": "Número de Usos Permitidos (-1==Sem Limite)"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "ยินดีต้อนรับสู่ Crafty Controller"
|
"welcome": "ยินดีต้อนรับสู่ Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "กำลังโหลด...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": เปิดใช้งานเพื่อเรียงลำดับคอลัมน์จากน้อยไปมาก",
|
"sortAscending": ": เปิดใช้งานเพื่อเรียงลำดับคอลัมน์จากน้อยไปมาก",
|
||||||
@ -673,4 +674,4 @@
|
|||||||
"webhook_body": "ภายใน Webhook",
|
"webhook_body": "ภายใน Webhook",
|
||||||
"webhooks": "Webhooks"
|
"webhooks": "Webhooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Crafty Controller'a Hoşgeldiniz!"
|
"welcome": "Crafty Controller'a Hoşgeldiniz!"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Yükleniyor...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": artan sütun sıralamasını aktifleştir",
|
"sortAscending": ": artan sütun sıralamasını aktifleştir",
|
||||||
@ -673,4 +674,4 @@
|
|||||||
"webhook_body": "Webhook Gövdesi",
|
"webhook_body": "Webhook Gövdesi",
|
||||||
"webhooks": "Webhooklar"
|
"webhooks": "Webhooklar"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "Ласкаво просимо у Crafty Controller"
|
"welcome": "Ласкаво просимо у Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "Завантаження...",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ": активуйте, щоб сортувати стовпці за зростанням",
|
"sortAscending": ": активуйте, щоб сортувати стовпці за зростанням",
|
||||||
@ -673,4 +674,4 @@
|
|||||||
"webhook_body": "Код Вебхука",
|
"webhook_body": "Код Вебхука",
|
||||||
"webhooks": "Вебхуки"
|
"webhooks": "Вебхуки"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -117,6 +117,7 @@
|
|||||||
"welcome": "欢迎来到 Crafty Controller"
|
"welcome": "欢迎来到 Crafty Controller"
|
||||||
},
|
},
|
||||||
"datatables": {
|
"datatables": {
|
||||||
|
"loadingRecords": "正在加载……",
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"aria": {
|
"aria": {
|
||||||
"sortAscending": ":激活对队列的升序排列",
|
"sortAscending": ":激活对队列的升序排列",
|
||||||
@ -674,4 +675,4 @@
|
|||||||
"webhook_body": "Webhook 消息体(Body)",
|
"webhook_body": "Webhook 消息体(Body)",
|
||||||
"webhooks": "Webhook"
|
"webhooks": "Webhook"
|
||||||
}
|
}
|
||||||
}
|
}
|
6
main.py
6
main.py
@ -17,6 +17,7 @@ from app.classes.models.users import HelperUsers
|
|||||||
from app.classes.models.management import HelpersManagement
|
from app.classes.models.management import HelpersManagement
|
||||||
from app.classes.shared.import_helper import ImportHelpers
|
from app.classes.shared.import_helper import ImportHelpers
|
||||||
from app.classes.shared.websocket_manager import WebSocketManager
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
from app.classes.logging.log_formatter import JsonFormatter
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
helper = Helpers()
|
helper = Helpers()
|
||||||
@ -284,6 +285,11 @@ def setup_logging(debug=True):
|
|||||||
|
|
||||||
logging.config.dictConfig(logging_config)
|
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:
|
else:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logging.warning(f"Unable to read logging config from {logging_config_file}")
|
logging.warning(f"Unable to read logging config from {logging_config_file}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user