add connection and file transfer logs

This commit is contained in:
Bryan Gerlach 2024-04-17 08:39:02 -05:00
parent 53068fd8de
commit c5fe3e125c
9 changed files with 337 additions and 2 deletions

View File

@ -94,6 +94,8 @@ admin.site.register(models.RustDeskTag, models.RustDeskTagAdmin)
admin.site.register(models.RustDeskPeer, models.RustDeskPeerAdmin)
admin.site.register(models.RustDesDevice, models.RustDesDeviceAdmin)
admin.site.register(models.ShareLink, models.ShareLinkAdmin)
admin.site.register(models.ConnLog, models.ConnLogAdmin)
admin.site.register(models.FileLog, models.FileLogAdmin)
admin.site.unregister(Group)
admin.site.site_header = _('RustDesk自建Web')
admin.site.site_title = _('未定义')

View File

@ -0,0 +1,41 @@
# Generated by Django 5.0.3 on 2024-04-17 08:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_alter_rustdesdevice_options_and_more'),
]
operations = [
migrations.CreateModel(
name='ConnLog',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False, verbose_name='ID')),
('action', models.CharField(max_length=20, null=True, verbose_name='Action')),
('conn_id', models.CharField(max_length=10, null=True, verbose_name='Connection ID')),
('from_ip', models.CharField(max_length=30, null=True, verbose_name='From IP')),
('from_id', models.CharField(max_length=20, null=True, verbose_name='From ID')),
('rid', models.CharField(max_length=20, null=True, verbose_name='To ID')),
('conn_start', models.DateTimeField(null=True, verbose_name='Connected')),
('conn_end', models.DateTimeField(null=True, verbose_name='Disconnected')),
('session_id', models.CharField(max_length=60, null=True, verbose_name='Session ID')),
('uuid', models.CharField(max_length=60, null=True, verbose_name='uuid')),
],
),
migrations.CreateModel(
name='FileLog',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False, verbose_name='ID')),
('file', models.CharField(max_length=500, verbose_name='Path')),
('remote_id', models.CharField(default='0', max_length=20, verbose_name='Remote ID')),
('user_id', models.CharField(default='0', max_length=20, verbose_name='User ID')),
('user_ip', models.CharField(default='0', max_length=20, verbose_name='User IP')),
('filesize', models.CharField(default='', max_length=500, verbose_name='Filesize')),
('direction', models.IntegerField(default=0, verbose_name='Direction')),
('logged_at', models.DateTimeField(null=True, verbose_name='Logged At')),
],
),
]

View File

@ -88,7 +88,37 @@ class RustDesDeviceAdmin(admin.ModelAdmin):
search_fields = ('hostname', 'memory')
list_filter = ('rid', )
class ConnLog(models.Model):
id = models.IntegerField(verbose_name='ID',primary_key=True)
action = models.CharField(verbose_name='Action', max_length=20, null=True)
conn_id = models.CharField(verbose_name='Connection ID', max_length=10, null=True)
from_ip = models.CharField(verbose_name='From IP', max_length=30, null=True)
from_id = models.CharField(verbose_name='From ID', max_length=20, null=True)
rid = models.CharField(verbose_name='To ID', max_length=20, null=True)
conn_start = models.DateTimeField(verbose_name='Connected', null=True)
conn_end = models.DateTimeField(verbose_name='Disconnected', null=True)
session_id = models.CharField(verbose_name='Session ID', max_length=60, null=True)
uuid = models.CharField(verbose_name='uuid', max_length=60, null=True)
class ConnLogAdmin(admin.ModelAdmin):
list_display = ('id', 'action', 'conn_id', 'from_ip', 'from_id', 'rid', 'conn_start', 'conn_end', 'session_id', 'uuid')
search_fields = ('from_ip', 'rid')
list_filter = ('id', 'from_ip', 'from_id', 'rid', 'conn_start', 'conn_end')
class FileLog(models.Model):
id = models.IntegerField(verbose_name='ID',primary_key=True)
file = models.CharField(verbose_name='Path', max_length=500)
remote_id = models.CharField(verbose_name='Remote ID', max_length=20, default='0')
user_id = models.CharField(verbose_name='User ID', max_length=20, default='0')
user_ip = models.CharField(verbose_name='User IP', max_length=20, default='0')
filesize = models.CharField(verbose_name='Filesize', max_length=500, default='')
direction = models.IntegerField(verbose_name='Direction', default=0)
logged_at = models.DateTimeField(verbose_name='Logged At', null=True)
class FileLogAdmin(admin.ModelAdmin):
list_display = ('id', 'file', 'remote_id', 'user_id', 'user_ip', 'filesize', 'direction', 'logged_at')
search_fields = ('file', 'remote_id', 'user_id', 'user_ip')
list_filter = ('id', 'file', 'remote_id', 'user_id', 'user_ip', 'filesize', 'direction', 'logged_at')
class ShareLink(models.Model):
''' 分享链接

View File

@ -39,6 +39,8 @@
{% if u.is_admin %}
<li class="layui-nav-item"><a href="/admin" target="_blank">{% trans "管理后台" %}</a>
</li>
<li class="layui-nav-item"><a href="/api/conn_log">Connection Log</a></li>
<li class="layui-nav-item"><a href="/api/file_log">File Transfer Log</a></li>
{% endif %}
<li class="layui-nav-item"><a href="/api/user_action?action=logout" target="_blank">{% trans "退出" %}</a></li>
</ul>

View File

@ -0,0 +1,61 @@
{% extends "base.html" %}
{% block title %}RustDesk WebUI{% endblock %}
{% block content %}
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md15">
<div class="layui-card">
<div class="layui-card-header">{{ "Connection Log" }}:{{u.username}}</div>
<div class="layui-card-body">
<table class="layui-table">
<thead>
<tr>
<th>User IP</th>
<th>User ID</th>
<th>User Alias</th>
<th>Remote ID</th>
<th>Remote Alias</th>
<th>Connection Start Time</th>
<th>Connection End Time</th>
<th>Duration (HH:MM:SS)</th>
</tr>
</thead>
<tbody>
{% for one in page_obj %}
<tr>
<td>{{one.from_ip}} </td>
<td>{{one.from_id}}</td>
<td>{{one.from_alias}}</td>
<td>{{one.rid}}</td>
<td>{{one.alias}}</td>
<td>{{one.conn_start}}</td>
<td>{{one.conn_end}}</td>
<td>{{one.duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-md-offset4">
<span class="step-links">
{% if page_obj.has_previous %}
<button class="layui-btn" ><a href="?page=1">&laquo; {{ "First" }}</a></button>
<button class="layui-btn" ><a href="?page={{ page_obj.previous_page_number }}">{{ "Previous" }}</a></button>
{% endif %}
{% if page_obj.paginator.num_pages > 1 %}
<span class="current">
{{ "Page #" }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% endif %}
{% if page_obj.has_next %}
<button class="layui-btn" > <a href="?page={{ page_obj.next_page_number }}">{{ "First" }}</a></button>
<button class="layui-btn" ><a href="?page={{ page_obj.paginator.num_pages }}">{{ "Next" }} &raquo;</a></button>
{% endif %}
</span>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,67 @@
{% extends "base.html" %}
{% block title %}RustDesk WebUI{% endblock %}
{% block content %}
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md15">
<div class="layui-card">
<div class="layui-card-header">{{ "File Transfer Log" }}:{{u.username}}</div>
<div class="layui-card-body">
<table class="layui-table">
<thead>
<tr>
<th>File</th>
<th>Remote ID</th>
<th>Remote Alias</th>
<th>User ID</th>
<th>User Alias</th>
<th>User IP</th>
<th>Filesize</th>
<th>Sent/Received</th>
<th>Logged At</th>
</tr>
</thead>
<tbody>
{% for one in page_obj %}
<tr>
<td>{{one.file}}</td>
<td>{{one.remote_id}} </td>
<td>{{one.remote_alias}}</td>
<td>{{one.user_id}}</td>
<td>{{one.user_alias}}</td>
<td>{{one.user_ip}}</td>
<td>{{one.filesize}}</td>
{% if one.direction == 0 %}
<td>User Received File</td>
{% else %}
<td>User Sent File</td>
{% endif %}
<td>{{one.logged_at}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="layui-col-md4 layui-col-md-offset4">
<span class="step-links">
{% if page_obj.has_previous %}
<button class="layui-btn" ><a href="?page=1">&laquo; {{ "First" }}</a></button>
<button class="layui-btn" ><a href="?page={{ page_obj.previous_page_number }}">{{ "Previous" }}</a></button>
{% endif %}
{% if page_obj.paginator.num_pages > 1 %}
<span class="current">
{{ "page #" }} {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% endif %}
{% if page_obj.has_next %}
<button class="layui-btn" > <a href="?page={{ page_obj.next_page_number }}">{{ "Next" }}</a></button>
<button class="layui-btn" ><a href="?page={{ page_obj.paginator.num_pages }}">{{ "Last" }} &raquo;</a></button>
{% endif %}
</span>
</div>
</div>
</div>
{% endblock %}

View File

@ -21,4 +21,7 @@ urlpatterns = [
url(r'^work',views.work), # 前端
url(r'^down_peers$',views.down_peers), # 前端
url(r'^share',views.share), # 前端
url(r'^conn_log',views.conn_log),
url(r'^file_log',views.file_log),
url(r'^audit',views.audit),
]

View File

@ -4,9 +4,10 @@ import json
import time
import datetime
import hashlib
import math
from django.contrib import auth
from django.forms.models import model_to_dict
from api.models import RustDeskToken, UserProfile, RustDeskTag, RustDeskPeer, RustDesDevice
from api.models import RustDeskToken, UserProfile, RustDeskTag, RustDeskPeer, RustDesDevice, ConnLog, FileLog
from django.db.models import Q
import copy
from .views_front import *
@ -245,6 +246,61 @@ def heartbeat(request):
result = {}
result['data'] = _('在线')
return JsonResponse(result)
def audit(request):
postdata = json.loads(request.body)
#print(postdata)
audit_type = postdata['action'] if 'action' in postdata else ''
if audit_type == 'new':
new_conn_log = ConnLog(
action=postdata['action'] if 'action' in postdata else '',
conn_id=postdata['conn_id'] if 'conn_id' in postdata else 0,
from_ip=postdata['ip'] if 'ip' in postdata else '',
from_id='',
rid=postdata['id'] if 'id' in postdata else '',
conn_start=datetime.datetime.now(),
session_id=postdata['session_id'] if 'session_id' in postdata else 0,
uuid=postdata['uuid'] if 'uuid' in postdata else '',
)
new_conn_log.save()
elif audit_type =="close":
ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(conn_end=datetime.datetime.now())
elif 'is_file' in postdata:
print(postdata)
files = json.loads(postdata['info'])['files']
filesize = convert_filesize(int(files[0][1]))
new_file_log = FileLog(
file=postdata['path'],
user_id=postdata['peer_id'],
user_ip=json.loads(postdata['info'])['ip'],
remote_id=postdata['id'],
filesize=filesize,
direction=postdata['type'],
logged_at=datetime.datetime.now(),
)
new_file_log.save()
else:
try:
peer = postdata['peer']
ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(session_id=postdata['session_id'])
ConnLog.objects.filter(Q(conn_id=postdata['conn_id'])).update(from_id=peer[0])
except:
print(postdata)
result = {
'code':1,
'data':'ok'
}
return JsonResponse(result)
def convert_filesize(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return "%s %s" % (s, size_name[i])
def users(request):
result = {

View File

@ -6,7 +6,7 @@ from django.http import JsonResponse
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from django.contrib import auth
from api.models import RustDeskPeer, RustDesDevice, UserProfile, ShareLink
from api.models import RustDeskPeer, RustDesDevice, UserProfile, ShareLink, ConnLog, FileLog
from django.forms.models import model_to_dict
from django.core.paginator import Paginator
from django.http import HttpResponse
@ -360,3 +360,76 @@ def share(request):
return JsonResponse({'code':1, 'shash':sharelink.shash})
def get_conn_log():
logs = ConnLog.objects.all()
logs = {x.id:model_to_dict(x) for x in logs}
for k, v in logs.items():
try:
peer = RustDeskPeer.objects.get(rid=v['rid'])
logs[k]['alias'] = peer.alias
except:
logs[k]['alias'] = 'UNKNOWN'
try:
peer = RustDeskPeer.objects.get(rid=v['from_id'])
logs[k]['from_alias'] = peer.alias
except:
logs[k]['from_alias'] = 'UNKNOWN'
#from_zone = tz.tzutc()
#to_zone = tz.tzlocal()
#utc = logs[k]['logged_at']
#utc = utc.replace(tzinfo=from_zone)
#logs[k]['logged_at'] = utc.astimezone(to_zone)
try:
duration = round((logs[k]['conn_end'] - logs[k]['conn_start']).total_seconds())
m, s = divmod(duration, 60)
h, m = divmod(m, 60)
#d, h = divmod(h, 24)
logs[k]['duration'] = f'{h:02d}:{m:02d}:{s:02d}'
except:
logs[k]['duration'] = -1
sorted_logs = sorted(logs.items(), key=lambda x: x[1]['conn_start'], reverse=True)
new_ordered_dict = {}
for key, alog in sorted_logs:
new_ordered_dict[key] = alog
return [v for k, v in new_ordered_dict.items()]
def get_file_log():
logs = FileLog.objects.all()
logs = {x.id:model_to_dict(x) for x in logs}
for k, v in logs.items():
try:
peer_remote = RustDeskPeer.objects.get(rid=v['remote_id'])
logs[k]['remote_alias'] = peer_remote.alias
except:
logs[k]['remote_alias'] = 'UNKNOWN'
try:
peer_user = RustDeskPeer.objects.get(rid=v['user_id'])
logs[k]['user_alias'] = peer_user.alias
except:
logs[k]['user_alias'] = 'UNKNOWN'
sorted_logs = sorted(logs.items(), key=lambda x: x[1]['logged_at'], reverse=True)
new_ordered_dict = {}
for key, alog in sorted_logs:
new_ordered_dict[key] = alog
return [v for k, v in new_ordered_dict.items()]
@login_required(login_url='/api/user_action?action=login')
def conn_log(request):
paginator = Paginator(get_conn_log(), 20)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'show_conn_log.html', {'page_obj':page_obj})
@login_required(login_url='/api/user_action?action=login')
def file_log(request):
paginator = Paginator(get_file_log(), 20)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'show_file_log.html', {'page_obj':page_obj})