forked from Minecraft/minecraft_manager
Merge branch 'master' of https://git.etztech.xyz/Etzelia/MinecraftManagerDjango
commit
938ec65422
2
admin.py
2
admin.py
|
@ -5,6 +5,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from minecraft_manager.models import Application, Warning, Ticket, Player, IP, UserSettings, Alert, Note
|
from minecraft_manager.models import Application, Warning, Ticket, Player, IP, UserSettings, Alert, Note
|
||||||
|
from minecraft_manager.api.admin import register as api_register
|
||||||
|
|
||||||
|
|
||||||
class PlayerInline(admin.StackedInline):
|
class PlayerInline(admin.StackedInline):
|
||||||
|
@ -100,6 +101,7 @@ try:
|
||||||
admin.site.register(IP, IPAdmin)
|
admin.site.register(IP, IPAdmin)
|
||||||
admin.site.register(Alert)
|
admin.site.register(Alert)
|
||||||
admin.site.register(Note)
|
admin.site.register(Note)
|
||||||
|
api_register()
|
||||||
except admin.sites.AlreadyRegistered:
|
except admin.sites.AlreadyRegistered:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from minecraft_manager.api.models import Token
|
||||||
|
|
||||||
|
|
||||||
|
class TokenActiveFilter(admin.SimpleListFilter):
|
||||||
|
title = _('Active')
|
||||||
|
parameter_name = 'active'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
return (
|
||||||
|
('0', _('Active')),
|
||||||
|
('1', _('Inactive')),
|
||||||
|
)
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
if self.value() == '0':
|
||||||
|
return queryset.filter(active=True)
|
||||||
|
if self.value() == '1':
|
||||||
|
return queryset.filter(active=False)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenAdmin(admin.ModelAdmin):
|
||||||
|
list_filter = (TokenActiveFilter,)
|
||||||
|
list_display = ('display', 'active', 'web_get_permission', 'web_post_permission', 'plugin_get_permission',
|
||||||
|
'plugin_post_permission', 'form_get_permission', 'form_post_permission', 'model_get_permission',
|
||||||
|
'model_post_permission', 'stats_get_permission', 'stats_post_permission')
|
||||||
|
fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('key', 'active', 'description')
|
||||||
|
}),
|
||||||
|
('Permissions', {
|
||||||
|
'fields': ('web_get_permission', 'web_post_permission', 'plugin_get_permission', 'plugin_post_permission',
|
||||||
|
'form_get_permission', 'form_post_permission', 'model_get_permission', 'model_post_permission',
|
||||||
|
'stats_get_permission', 'stats_post_permission')
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
try:
|
||||||
|
admin.site.register(Token, TokenAdmin)
|
||||||
|
except admin.sites.AlreadyRegistered:
|
||||||
|
pass
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import socket, requests, logging, os, datetime, pytz, mcstatus, discord
|
import socket, requests, logging, os, datetime, pytz, mcstatus, random, string
|
||||||
from minecraft_manager.models import Alert
|
from minecraft_manager.models import Alert
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -18,6 +18,7 @@ PLUGIN_ACCEPT = 'accept'
|
||||||
PLUGIN_DENY = 'deny'
|
PLUGIN_DENY = 'deny'
|
||||||
PLUGIN_GLOBAL_CHAT = 'global'
|
PLUGIN_GLOBAL_CHAT = 'global'
|
||||||
PLUGIN_STAFF_CHAT = 'staff'
|
PLUGIN_STAFF_CHAT = 'staff'
|
||||||
|
PLUGIN_DEMOTE = 'demote'
|
||||||
|
|
||||||
|
|
||||||
def plugin(key, command):
|
def plugin(key, command):
|
||||||
|
@ -140,3 +141,7 @@ def get_query():
|
||||||
except:
|
except:
|
||||||
return {'max': 0, 'online': 0,
|
return {'max': 0, 'online': 0,
|
||||||
'players': []}
|
'players': []}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(size=20):
|
||||||
|
return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, size)])
|
19
api/bot.py
19
api/bot.py
|
@ -1,6 +1,7 @@
|
||||||
import discord, logging, re, sys, traceback, asyncio, datetime, os, time
|
import discord, logging, re, sys, traceback, asyncio, datetime, os, time
|
||||||
from minecraft_manager.models import Application, Player
|
from minecraft_manager.models import Application, Player
|
||||||
from minecraft_manager.api import api
|
from minecraft_manager.api import api
|
||||||
|
from django.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
@ -67,7 +68,8 @@ class Discord(discord.Client):
|
||||||
embed.add_field(name="{}[app ]search <username>".format(self.prefix), value="Search for applications by partial or exact username.")
|
embed.add_field(name="{}[app ]search <username>".format(self.prefix), value="Search for applications by partial or exact username.")
|
||||||
embed.add_field(name="{}[app ]info <app ID>".format(self.prefix), value="Get detailed information about a specific application.")
|
embed.add_field(name="{}[app ]info <app ID>".format(self.prefix), value="Get detailed information about a specific application.")
|
||||||
embed.add_field(name="{}[app ]accept|deny <app ID>".format(self.prefix), value="Take action on an application.")
|
embed.add_field(name="{}[app ]accept|deny <app ID>".format(self.prefix), value="Take action on an application.")
|
||||||
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist")
|
embed.add_field(name="{}demote <username>".format(self.prefix), value="Demote a player to the role given to accepted applications.")
|
||||||
|
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist.")
|
||||||
yield from self.discord_message(message.channel, embed)
|
yield from self.discord_message(message.channel, embed)
|
||||||
# APP COMMANDS WITH APP ID
|
# APP COMMANDS WITH APP ID
|
||||||
match = re.match("[{0}](?:app )?(i|info|a|accept|d|deny) (\d+)$".format(self.prefix), message.content)
|
match = re.match("[{0}](?:app )?(i|info|a|accept|d|deny) (\d+)$".format(self.prefix), message.content)
|
||||||
|
@ -103,7 +105,7 @@ class Discord(discord.Client):
|
||||||
match = re.match("[{0}](?:app )?(?:search|info) (\S+)?$".format(self.prefix), message.content)
|
match = re.match("[{0}](?:app )?(?:search|info) (\S+)?$".format(self.prefix), message.content)
|
||||||
if match:
|
if match:
|
||||||
search = match.group(1)
|
search = match.group(1)
|
||||||
yield from self.discord_message(message.channel,"Searching for applications whose username contains '{0}'".format(search))
|
yield from self.discord_message(message.channel, "Searching for applications whose username contains '{0}'".format(search))
|
||||||
applications = Application.objects.filter(username__icontains=search)[:10]
|
applications = Application.objects.filter(username__icontains=search)[:10]
|
||||||
count = Application.objects.filter(username__icontains=search).count()
|
count = Application.objects.filter(username__icontains=search).count()
|
||||||
if count > 0:
|
if count > 0:
|
||||||
|
@ -118,6 +120,19 @@ class Discord(discord.Client):
|
||||||
else:
|
else:
|
||||||
info = "No applications matched that search."
|
info = "No applications matched that search."
|
||||||
yield from self.discord_message(message.channel, info)
|
yield from self.discord_message(message.channel, info)
|
||||||
|
# DEMOTE A PLAYER TO MEMBER
|
||||||
|
match = re.match("[{0}]demote (\w+)$".format(self.prefix), message.content)
|
||||||
|
if match:
|
||||||
|
yield from self.delete_message(message)
|
||||||
|
username = match.group(1)
|
||||||
|
api.plugin(api.PLUGIN_DEMOTE, username)
|
||||||
|
deactivated = ""
|
||||||
|
if User.objects.filter(username__iexact=username).exists():
|
||||||
|
user = User.objects.get(username__iexact=username)
|
||||||
|
user.is_active = False
|
||||||
|
user.save()
|
||||||
|
deactivated = " and de-activated"
|
||||||
|
yield from self.discord_message(message.channel, "{} has been demoted{}.".format(username, deactivated))
|
||||||
# COMPARE DISCORD USERS TO WHITELIST
|
# COMPARE DISCORD USERS TO WHITELIST
|
||||||
match = re.match("[{0}]compare".format(self.prefix), message.content)
|
match = re.match("[{0}]compare".format(self.prefix), message.content)
|
||||||
if match:
|
if match:
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
from django.db import models
|
||||||
|
from minecraft_manager.api.api import generate_password
|
||||||
|
|
||||||
|
|
||||||
|
class Token(models.Model):
|
||||||
|
key = models.CharField("Key", default=generate_password, max_length=50, unique=True)
|
||||||
|
description = models.CharField("Description", max_length=200, blank=True)
|
||||||
|
active = models.BooleanField("Active", default=True)
|
||||||
|
web_get_permission = models.BooleanField("Web GET", default=False)
|
||||||
|
web_post_permission = models.BooleanField("Web POST", default=False)
|
||||||
|
plugin_get_permission = models.BooleanField("Plugin GET", default=False)
|
||||||
|
plugin_post_permission = models.BooleanField("Plugin POST", default=False)
|
||||||
|
form_get_permission = models.BooleanField("Form GET", default=False)
|
||||||
|
form_post_permission = models.BooleanField("Form POST", default=False)
|
||||||
|
model_get_permission = models.BooleanField("Model GET", default=False)
|
||||||
|
model_post_permission = models.BooleanField("Model POST", default=False)
|
||||||
|
stats_get_permission = models.BooleanField("Stats GET", default=False)
|
||||||
|
stats_post_permission = models.BooleanField("Stats POST", default=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display(self):
|
||||||
|
return self.description if self.description else self.key
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.key
|
55
api/views.py
55
api/views.py
|
@ -1,43 +1,43 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import logging, random, string, datetime
|
import logging, datetime
|
||||||
from django.contrib.auth.forms import PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from django.contrib.auth import update_session_auth_hash
|
from django.contrib.auth import update_session_auth_hash
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.http import JsonResponse, HttpResponse
|
from django.http import JsonResponse, HttpResponse
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.forms import modelform_factory
|
from django.forms import modelform_factory
|
||||||
|
|
||||||
import minecraft_manager.forms as MCMForms
|
import minecraft_manager.forms as mcm_forms
|
||||||
from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Warning
|
from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Warning
|
||||||
import minecraft_manager.api.api as mcm_api
|
import minecraft_manager.api.api as mcm_api
|
||||||
|
from minecraft_manager.api.models import Token
|
||||||
import minecraft_manager.utils as mcm_utils
|
import minecraft_manager.utils as mcm_utils
|
||||||
import minecraft_manager.external.stats as mcm_stats
|
import minecraft_manager.external.stats as mcm_stats
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def request_allowed(request):
|
def request_allowed(request, permission):
|
||||||
is_authenticated = False
|
is_authenticated = False
|
||||||
if hasattr(request, 'user'):
|
if hasattr(request, 'user'):
|
||||||
if hasattr(request.user, 'is_authenticated'):
|
if hasattr(request.user, 'is_authenticated'):
|
||||||
is_authenticated = request.user.is_authenticated
|
is_authenticated = request.user.is_authenticated
|
||||||
password = getattr(settings, 'API_PASSWORD', None)
|
|
||||||
get = request.GET
|
get = request.GET
|
||||||
post= request.POST
|
post = request.POST
|
||||||
request_password = None
|
request_password = None
|
||||||
if 'api' in get:
|
if 'api' in get:
|
||||||
request_password = get['api']
|
request_password = get['api']
|
||||||
elif 'api' in post:
|
elif 'api' in post:
|
||||||
request_password = post['api']
|
request_password = post['api']
|
||||||
correct_password = False
|
token_permission = False
|
||||||
if password and request_password:
|
if Token.objects.filter(active=True, key=request_password).exists():
|
||||||
correct_password = request_password == password
|
token = Token.objects.get(active=True, key=request_password)
|
||||||
return is_authenticated or correct_password
|
token_permission = getattr(token, permission, False)
|
||||||
|
return is_authenticated or token_permission
|
||||||
|
|
||||||
|
|
||||||
def clean(model, data):
|
def clean(model, data):
|
||||||
|
@ -51,16 +51,12 @@ def clean(model, data):
|
||||||
return cleaned
|
return cleaned
|
||||||
|
|
||||||
|
|
||||||
def generate_password():
|
|
||||||
return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, 20)])
|
|
||||||
|
|
||||||
|
|
||||||
class WebAPI(View):
|
class WebAPI(View):
|
||||||
|
|
||||||
def get(self, request, keyword):
|
def get(self, request, keyword):
|
||||||
get = request.GET
|
get = request.GET
|
||||||
data = {'success': False, 'message': 'API failed'}
|
data = {'success': False, 'message': 'API failed'}
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'web_get_permission'):
|
||||||
keyword = keyword.lower()
|
keyword = keyword.lower()
|
||||||
if keyword == 'log':
|
if keyword == 'log':
|
||||||
html_global = ""
|
html_global = ""
|
||||||
|
@ -102,7 +98,7 @@ class WebAPI(View):
|
||||||
def post(self, request, keyword):
|
def post(self, request, keyword):
|
||||||
post = request.POST
|
post = request.POST
|
||||||
data = {}
|
data = {}
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'web_post_permission'):
|
||||||
keyword = keyword.lower()
|
keyword = keyword.lower()
|
||||||
if keyword == 'settings' and request.user.usersettings:
|
if keyword == 'settings' and request.user.usersettings:
|
||||||
for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]:
|
for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]:
|
||||||
|
@ -119,7 +115,7 @@ class WebAPI(View):
|
||||||
else:
|
else:
|
||||||
return HttpResponse(form.as_p())
|
return HttpResponse(form.as_p())
|
||||||
elif keyword == 'alert':
|
elif keyword == 'alert':
|
||||||
form = MCMForms.AlertForm(request.POST)
|
form = mcm_forms.AlertForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
if mcm_api.create_alert(form.cleaned_data['message']):
|
if mcm_api.create_alert(form.cleaned_data['message']):
|
||||||
data = {'success': True}
|
data = {'success': True}
|
||||||
|
@ -155,7 +151,7 @@ class PluginAPI(View):
|
||||||
|
|
||||||
def get(self, request, keyword):
|
def get(self, request, keyword):
|
||||||
json = {'status': True, 'message': '', 'extra': ''}
|
json = {'status': True, 'message': '', 'extra': ''}
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'plugin_get_permission'):
|
||||||
get = request.GET
|
get = request.GET
|
||||||
keyword = keyword.lower()
|
keyword = keyword.lower()
|
||||||
|
|
||||||
|
@ -163,7 +159,7 @@ class PluginAPI(View):
|
||||||
|
|
||||||
def post(self, request, keyword):
|
def post(self, request, keyword):
|
||||||
json = {'status': True, 'message': '', 'extra': ''}
|
json = {'status': True, 'message': '', 'extra': ''}
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'plugin_post_permission'):
|
||||||
post = request.POST
|
post = request.POST
|
||||||
keyword = keyword.lower()
|
keyword = keyword.lower()
|
||||||
if "application" == keyword:
|
if "application" == keyword:
|
||||||
|
@ -272,10 +268,13 @@ class PluginAPI(View):
|
||||||
player.last_seen = timezone.now().strftime("%Y-%m-%d")
|
player.last_seen = timezone.now().strftime("%Y-%m-%d")
|
||||||
player.save()
|
player.save()
|
||||||
if new_player and ip.associated:
|
if new_player and ip.associated:
|
||||||
|
associated = []
|
||||||
for assoc in ip.associated:
|
for assoc in ip.associated:
|
||||||
if assoc.uuid is not player.uuid and assoc.is_banned:
|
if assoc.uuid is not player.uuid and assoc.is_banned:
|
||||||
mcm_api.plugin("staff", "Server {0}'s IP matches the banned player {1}".format(player.username, assoc.username))
|
associated.append(assoc)
|
||||||
mcm_api.discord_notification("{0}'s IP matches the banned player {1}".format(player.username, assoc.username), ping=True)
|
if associated:
|
||||||
|
mcm_api.plugin("staff", "Server {0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])))
|
||||||
|
mcm_api.discord_notification("{0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])), ping=True)
|
||||||
json['status'] = True
|
json['status'] = True
|
||||||
json['message'] = "Updated {0}".format(post['username'])
|
json['message'] = "Updated {0}".format(post['username'])
|
||||||
elif "register" == keyword:
|
elif "register" == keyword:
|
||||||
|
@ -284,7 +283,7 @@ class PluginAPI(View):
|
||||||
json['status'] = False
|
json['status'] = False
|
||||||
json['message'] = "You are already registered. To change your password, contact an Admin."
|
json['message'] = "You are already registered. To change your password, contact an Admin."
|
||||||
else:
|
else:
|
||||||
password = generate_password()
|
password = mcm_api.generate_password()
|
||||||
user = User.objects.create_user(username=player.username.lower(), password=password)
|
user = User.objects.create_user(username=player.username.lower(), password=password)
|
||||||
user.save()
|
user.save()
|
||||||
player.auth_user = user
|
player.auth_user = user
|
||||||
|
@ -323,7 +322,7 @@ class FormAPI(View):
|
||||||
|
|
||||||
def get(self, request, request_model):
|
def get(self, request, request_model):
|
||||||
html = ""
|
html = ""
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'form_get_permission'):
|
||||||
get = request.GET
|
get = request.GET
|
||||||
model = None
|
model = None
|
||||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||||
|
@ -332,7 +331,7 @@ class FormAPI(View):
|
||||||
break
|
break
|
||||||
if model:
|
if model:
|
||||||
form = None
|
form = None
|
||||||
for modelform in MCMForms.__all__():
|
for modelform in mcm_forms.__all__():
|
||||||
if modelform.Meta.model == model:
|
if modelform.Meta.model == model:
|
||||||
form = modelform()
|
form = modelform()
|
||||||
break
|
break
|
||||||
|
@ -346,7 +345,7 @@ class FormAPI(View):
|
||||||
|
|
||||||
def post(self, request, request_model):
|
def post(self, request, request_model):
|
||||||
html = ""
|
html = ""
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'form_post_permission'):
|
||||||
post = request.POST
|
post = request.POST
|
||||||
model = None
|
model = None
|
||||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||||
|
@ -355,7 +354,7 @@ class FormAPI(View):
|
||||||
break
|
break
|
||||||
if model:
|
if model:
|
||||||
form = None
|
form = None
|
||||||
for modelform in MCMForms.__all__():
|
for modelform in mcm_forms.__all__():
|
||||||
if modelform.Meta.model == model:
|
if modelform.Meta.model == model:
|
||||||
form = modelform(post)
|
form = modelform(post)
|
||||||
break
|
break
|
||||||
|
@ -376,7 +375,7 @@ class ModelAPI(View):
|
||||||
|
|
||||||
def get(self, request, request_model):
|
def get(self, request, request_model):
|
||||||
json = []
|
json = []
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'model_get_permission'):
|
||||||
get = request.GET
|
get = request.GET
|
||||||
model = None
|
model = None
|
||||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||||
|
@ -404,7 +403,7 @@ class StatsAPI(View):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
json = []
|
json = []
|
||||||
if request_allowed(request):
|
if request_allowed(request, 'stats_get_permission'):
|
||||||
get = request.GET
|
get = request.GET
|
||||||
if 'stat' in get:
|
if 'stat' in get:
|
||||||
if 'uuid' in get:
|
if 'uuid' in get:
|
||||||
|
|
|
@ -47,8 +47,6 @@ Optional
|
||||||
|
|
||||||
``SERVER_QUERY_IP`` - The full IP (and port) used to query your server. (This is used to get a player list)
|
``SERVER_QUERY_IP`` - The full IP (and port) used to query your server. (This is used to get a player list)
|
||||||
|
|
||||||
``API_PASSWORD`` - The password used to validate API requests from unauthenticated sources.
|
|
||||||
|
|
||||||
``COREPROTECT_WEB_URL`` - The URL to your CoreProtect Web UI, if it exists.
|
``COREPROTECT_WEB_URL`` - The URL to your CoreProtect Web UI, if it exists.
|
||||||
|
|
||||||
``COREPROTECT_ACTIVITY_URL`` - The URL to your CoreProtect Activity Web UI, if it exists.
|
``COREPROTECT_ACTIVITY_URL`` - The URL to your CoreProtect Activity Web UI, if it exists.
|
||||||
|
|
|
@ -47,6 +47,7 @@ Add MCM urls to your ``urls.py``
|
||||||
Django doesn't provide login/logout templates by default, so MCM has some generic ones if needed.
|
Django doesn't provide login/logout templates by default, so MCM has some generic ones if needed.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
path('accounts/login/', auth_views.LoginView.as_view(template_name='minecraft_manager/login.html'), name='login'),
|
path('accounts/login/', auth_views.LoginView.as_view(template_name='minecraft_manager/login.html'), name='login'),
|
||||||
path('accounts/logout/', auth_views.LogoutView.as_view(template_name='minecraft_manager/logged_out.html'), name='logout'),
|
path('accounts/logout/', auth_views.LogoutView.as_view(template_name='minecraft_manager/logged_out.html'), name='logout'),
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import os, json
|
import os, json, copy
|
||||||
from minecraft_manager.models import Player
|
from minecraft_manager.models import Player
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
@ -16,9 +16,13 @@ def get_stats():
|
||||||
with open(stats_dir + "/" + filename) as json_file:
|
with open(stats_dir + "/" + filename) as json_file:
|
||||||
raw = json.load(json_file)['stats']
|
raw = json.load(json_file)['stats']
|
||||||
clean = {}
|
clean = {}
|
||||||
for r in raw:
|
raw_copy = copy.deepcopy(raw)
|
||||||
if not any(sf.lower() in r.lower() for sf in stats_filter):
|
for ra in raw_copy:
|
||||||
clean[r] = raw[r]
|
if not any(sf.lower() in ra.lower() for sf in stats_filter):
|
||||||
|
for r in raw_copy[ra]:
|
||||||
|
if any(sf.lower() in r.lower() for sf in stats_filter):
|
||||||
|
del raw[ra][r]
|
||||||
|
clean[ra] = raw[ra]
|
||||||
uuid = filename.replace(".json", "")
|
uuid = filename.replace(".json", "")
|
||||||
stats[uuid] = clean
|
stats[uuid] = clean
|
||||||
return stats
|
return stats
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from django.conf import settings
|
||||||
|
from minecraft_manager.models import Application, Player, Ticket, Warning, IP
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
|
def overview_data():
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
with open(os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json'), encoding='utf-8') as f:
|
||||||
|
bans = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
# Totals
|
||||||
|
data['total'] = {
|
||||||
|
'application': {
|
||||||
|
'accepted': Application.objects.filter(accepted=True).count(),
|
||||||
|
'denied': Application.objects.filter(accepted=False).count(),
|
||||||
|
'all': Application.objects.count()
|
||||||
|
},
|
||||||
|
'player': {
|
||||||
|
'banned': len(bans),
|
||||||
|
'unbanned': Player.objects.count() - len(bans),
|
||||||
|
'all': Player.objects.count()
|
||||||
|
},
|
||||||
|
'ticket': {
|
||||||
|
'claimed': Ticket.objects.filter(staff__isnull=False).count(),
|
||||||
|
'unclaimed': Ticket.objects.filter(staff__isnull=True).count(),
|
||||||
|
'resolved': Ticket.objects.filter(resolved=True).count(),
|
||||||
|
'unresolved': Ticket.objects.filter(resolved=False).count(),
|
||||||
|
'all': Ticket.objects.count()
|
||||||
|
},
|
||||||
|
'warning': Warning.objects.count(),
|
||||||
|
'ip': IP.objects.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Averages
|
||||||
|
data['average'] = {
|
||||||
|
'age': round(sum([application.age for application in Application.objects.filter(accepted=True)]) /
|
||||||
|
data['total']['application']['accepted'] if data['total']['application']['accepted'] != 0 else 1, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Percentage
|
||||||
|
data['percentage'] = {
|
||||||
|
'accepted': round((data['total']['application']['accepted'] /
|
||||||
|
data['total']['application']['all'] if data['total']['application']['all'] != 0 else 1) * 100, 2),
|
||||||
|
'banned': round((data['total']['player']['banned'] /
|
||||||
|
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2),
|
||||||
|
'applied': round((data['total']['application']['all'] / data['total']['player']['all']) * 100, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Unique logins
|
||||||
|
now = datetime.now()
|
||||||
|
day = now - timedelta(days=1)
|
||||||
|
week = now - timedelta(weeks=1)
|
||||||
|
month = now - timedelta(weeks=4)
|
||||||
|
data['unique'] = {
|
||||||
|
'day': Player.objects.filter(last_seen__range=[day, now]).count(),
|
||||||
|
'week': Player.objects.filter(last_seen__range=[week, now]).count(),
|
||||||
|
'month': Player.objects.filter(last_seen__range=[month, now]).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
data['resolved'] = []
|
||||||
|
for user in User.objects.all().order_by('username'):
|
||||||
|
data['resolved'].append({
|
||||||
|
'active': user.is_active,
|
||||||
|
'username': user.username,
|
||||||
|
'tickets': Ticket.objects.filter(staff=user).count()
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
|
@ -6,141 +6,72 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block section %}
|
{% block section %}
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
{% if request.user.is_staff %}
|
||||||
|
<div class="panel panel-danger">
|
||||||
|
<div class="panel-body">
|
||||||
|
<h4><span class="label label-danger">Admin Area</span></h4>
|
||||||
|
<h3>Resolved Tickets</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-6">
|
{% for staff in data.resolved %}
|
||||||
<h3 class="center">Applications{% if form.apps.unanswered > 0 %} <a href="{% url "application" %}?accepted=">({{ form.apps.unanswered }} Unanswered)</a>{% endif %}</h3>
|
<div class="col-xs-6 col-md-4">
|
||||||
<canvas id="appChart" style="width:30em;height:15em" ></canvas>
|
<p><span class="label label-{% if staff.active %}success{% else %}danger{% endif %}">{% if staff.active %}Active{% else %}Inactive{% endif %}</span> {{ staff.username }}: {{ staff.tickets }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-9 col-md-6">
|
{% endfor %}
|
||||||
<h3 class="center">Tickets{% if form.tickets.unclaimed > 0 %} <a href="{% url "ticket" %}?claimed=false">({{ form.tickets.unclaimed }} Unclaimed)</a>{% endif %}</h3>
|
|
||||||
<h4></h4>
|
|
||||||
<canvas id="ticketChart" style="width:30em;height:15em" ></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-xs-18 col-md-12">
|
</div>
|
||||||
<h3 class="center">Totals</h3>
|
<hr/>
|
||||||
<canvas id="totalChart" style="width:30em;height:15em" ></canvas>
|
{% endif %}
|
||||||
</div>
|
<div class="row">
|
||||||
|
<div class="col-xs-9 col-md-6">
|
||||||
|
<h3>Applications: {{ data.total.application.all }}</h3>
|
||||||
|
<p>Accepted: {{ data.total.application.accepted }}</p>
|
||||||
|
<p>Denied: {{ data.total.application.denied }}</p>
|
||||||
|
<br/>
|
||||||
|
<h3>Tickets: {{ data.total.ticket.all }}</h3>
|
||||||
|
<p>Claimed: {{ data.total.ticket.claimed }}</p>
|
||||||
|
<p>Unclaimed: {{ data.total.ticket.unclaimed }}</p>
|
||||||
|
<p>Resolved: {{ data.total.ticket.resolved }}</p>
|
||||||
|
<p>Unresolved: {{ data.total.ticket.unresolved }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-9 col-md-6">
|
||||||
|
<h3>Players: {{ data.total.player.all }}</h3>
|
||||||
|
<p>Not Banned: {{ data.total.player.unbanned }}</p>
|
||||||
|
<p>Banned: {{ data.total.player.banned }}</p>
|
||||||
|
<br/>
|
||||||
|
<h3>Warnings: {{ data.total.warning }}</h3>
|
||||||
|
<br/>
|
||||||
|
<h3>IPs: {{ data.total.ip }}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<hr/>
|
||||||
$(document).ready(function() {
|
<div class="row">
|
||||||
var app_data = {
|
<div class="col-xs-9 col-md-6">
|
||||||
labels: [
|
<h3>Average Age: {{ data.average.age }}</h3>
|
||||||
"Unanswered",
|
</div>
|
||||||
"Denied",
|
<div class="col-xs-9 col-md-6">
|
||||||
"Accepted"
|
|
||||||
],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: [{{ form.apps.unanswered }}, {{ form.apps.denied }}, {{ form.apps.accepted }}],
|
|
||||||
backgroundColor: [
|
|
||||||
"#8080ff",
|
|
||||||
"#ff3333",
|
|
||||||
"#33cc33"
|
|
||||||
],
|
|
||||||
hoverBackgroundColor: [
|
|
||||||
"#8080ff",
|
|
||||||
"#ff3333",
|
|
||||||
"#33cc33"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
var app_options = {
|
|
||||||
animation: {
|
|
||||||
animateRotate: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var app_ctx = $("#appChart");
|
|
||||||
var app_chart = new Chart(app_ctx,
|
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
data: app_data,
|
|
||||||
options: app_options
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
var ticket_data = {
|
</div>
|
||||||
labels: [
|
</div>
|
||||||
"Unclaimed",
|
<hr/>
|
||||||
"Claimed",
|
<div class="row">
|
||||||
"Resolved"
|
<div class="col-xs-9 col-md-6">
|
||||||
],
|
<h3>Acceptance Rate: {{ data.percentage.accepted }}%</h3>
|
||||||
datasets: [
|
<h3>Application/Player Rate: {{ data.percentage.applied }}%</h3>
|
||||||
{
|
</div>
|
||||||
data: [{{ form.tickets.unclaimed }}, {{ form.tickets.claimed }}, {{ form.tickets.resolved }}],
|
<div class="col-xs-9 col-md-6">
|
||||||
backgroundColor: [
|
<h3>Ban Rate: {{ data.percentage.banned }}%</h3>
|
||||||
"#8080ff",
|
</div>
|
||||||
"#ff3333",
|
</div>
|
||||||
"#33cc33"
|
<hr/>
|
||||||
],
|
<div class="row">
|
||||||
hoverBackgroundColor: [
|
<div class="col-xs-9 col-md-6">
|
||||||
"#8080ff",
|
<h3>Logins Today: {{ data.unique.day }}</h3>
|
||||||
"#ff3333",
|
<h3>Logins Last Month: {{ data.unique.month }}</h3>
|
||||||
"#33cc33"
|
</div>
|
||||||
]
|
<div class="col-xs-9 col-md-6">
|
||||||
}
|
<h3>Logins Last Week: {{ data.unique.week }}</h3>
|
||||||
]
|
</div>
|
||||||
};
|
</div>
|
||||||
var ticket_options = {
|
</div>
|
||||||
animation: {
|
|
||||||
animateRotate: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ticket_ctx = $("#ticketChart");
|
|
||||||
var ticket_chart = new Chart(ticket_ctx,
|
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
data: ticket_data,
|
|
||||||
options: ticket_options
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
var total_data = {
|
|
||||||
labels: [
|
|
||||||
"Applications",
|
|
||||||
"Players",
|
|
||||||
"IPs",
|
|
||||||
"Tickets",
|
|
||||||
"Warnings"
|
|
||||||
],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Totals",
|
|
||||||
data: [
|
|
||||||
{{ form.counts.applications }},
|
|
||||||
{{ form.counts.players }},
|
|
||||||
{{ form.counts.ips }},
|
|
||||||
{{ form.counts.tickets }},
|
|
||||||
{{ form.counts.warnings }}
|
|
||||||
],
|
|
||||||
backgroundColor: [
|
|
||||||
'rgba(255, 99, 132, 0.2)',
|
|
||||||
'rgba(54, 162, 235, 0.2)',
|
|
||||||
'rgba(255, 206, 86, 0.2)',
|
|
||||||
'rgba(75, 192, 192, 0.2)',
|
|
||||||
'rgba(153, 102, 255, 0.2)'
|
|
||||||
],
|
|
||||||
borderColor: [
|
|
||||||
'rgba(255,99,132,1)',
|
|
||||||
'rgba(54, 162, 235, 1)',
|
|
||||||
'rgba(255, 206, 86, 1)',
|
|
||||||
'rgba(75, 192, 192, 1)',
|
|
||||||
'rgba(153, 102, 255, 1)'
|
|
||||||
],
|
|
||||||
borderWidth: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
var total_ctx = $("#totalChart");
|
|
||||||
var total_chart = new Chart(total_ctx,
|
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
data: total_data
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock section %}
|
{% endblock section %}
|
|
@ -17,7 +17,7 @@ def get_sidebar(current_app, request):
|
||||||
|
|
||||||
ret = '<li {}><a href="{}"><span class="glyphicon glyphicon-home"></span> Overview</a></li>'.format('class="active"' if current_app == 'overview' else "", reverse('overview'))
|
ret = '<li {}><a href="{}"><span class="glyphicon glyphicon-home"></span> Overview</a></li>'.format('class="active"' if current_app == 'overview' else "", reverse('overview'))
|
||||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-ban-circle"></span> Bans</a></li>'.format('class="active"' if current_app == 'ban' else '', reverse('ban'))
|
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-ban-circle"></span> Bans</a></li>'.format('class="active"' if current_app == 'ban' else '', reverse('ban'))
|
||||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-inbox"></span> Alerts{}</a></li>'.format('class="active"' if current_app == 'alert' else '', reverse('alert'), unseen_html)
|
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-bell"></span> Alerts{}</a></li>'.format('class="active"' if current_app == 'alert' else '', reverse('alert'), unseen_html)
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-file"></span> Applications</a></li>'.format('class="active"' if current_app == 'application' else '', reverse('application'))
|
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-file"></span> Applications</a></li>'.format('class="active"' if current_app == 'application' else '', reverse('application'))
|
||||||
|
|
17
views.py
17
views.py
|
@ -14,6 +14,7 @@ from django.views.generic import View
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, Warning as WarningModel, IP as IPModel, Alert as AlertModel, Note as NoteModel, UserSettings as UserSettingsModel
|
from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, Warning as WarningModel, IP as IPModel, Alert as AlertModel, Note as NoteModel, UserSettings as UserSettingsModel
|
||||||
from minecraft_manager.forms import WarningForm, NoteForm
|
from minecraft_manager.forms import WarningForm, NoteForm
|
||||||
|
from minecraft_manager.overview import overview_data
|
||||||
import minecraft_manager.api.api as API
|
import minecraft_manager.api.api as API
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -33,19 +34,9 @@ class Overview(View):
|
||||||
request.user.usersettings = UserSettingsModel(auth_user=request.user)
|
request.user.usersettings = UserSettingsModel(auth_user=request.user)
|
||||||
request.user.usersettings.last_ip = user_ip
|
request.user.usersettings.last_ip = user_ip
|
||||||
request.user.usersettings.save()
|
request.user.usersettings.save()
|
||||||
unanswered_apps = AppModel.objects.filter(accepted=None).count()
|
|
||||||
accepted_apps = AppModel.objects.filter(accepted=True).count()
|
|
||||||
denied_apps = AppModel.objects.filter(accepted=False).count()
|
return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'data': overview_data()})
|
||||||
unclaimed_tickets = TicketModel.objects.filter(staff=None, resolved=False).count()
|
|
||||||
claimed_tickets = TicketModel.objects.filter(staff__isnull=False, resolved=False).count()
|
|
||||||
resolved_tickets = TicketModel.objects.filter(resolved=True).count()
|
|
||||||
counts = {'applications': AppModel.objects.count(), 'players': PlayerModel.objects.count(),
|
|
||||||
"ips": IPModel.objects.count(), "tickets": TicketModel.objects.count(),
|
|
||||||
"warnings": WarningModel.objects.count()}
|
|
||||||
form = {'apps': {'unanswered': unanswered_apps, 'accepted': accepted_apps, 'denied': denied_apps},
|
|
||||||
'tickets': {'unclaimed': unclaimed_tickets, 'claimed': claimed_tickets, 'resolved': resolved_tickets},
|
|
||||||
'counts': counts}
|
|
||||||
return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'form': form})
|
|
||||||
|
|
||||||
|
|
||||||
class CoreProtect(View):
|
class CoreProtect(View):
|
||||||
|
|
Loading…
Reference in New Issue