remove master project
This commit is contained in:
parent
6e95a7b015
commit
4c583e1c53
@ -30,8 +30,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProfanityDeterment", "Plugi
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Login", "Plugins\Login\Login.csproj", "{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Login", "Plugins\Login\Login.csproj", "{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Master", "Master\Master.pyproj", "{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Plugins\Tests\Tests.csproj", "{B72DEBFB-9D48-4076-8FF5-1FD72A830845}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Plugins\Tests\Tests.csproj", "{B72DEBFB-9D48-4076-8FF5-1FD72A830845}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IW4ScriptCommands", "Plugins\IW4ScriptCommands\IW4ScriptCommands.csproj", "{6C706CE5-A206-4E46-8712-F8C48D526091}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IW4ScriptCommands", "Plugins\IW4ScriptCommands\IW4ScriptCommands.csproj", "{6C706CE5-A206-4E46-8712-F8C48D526091}"
|
||||||
@ -248,30 +246,6 @@ Global
|
|||||||
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x64.Build.0 = Release|Any CPU
|
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.ActiveCfg = Release|Any CPU
|
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.Build.0 = Release|Any CPU
|
{D9F2ED28-6FA5-40CA-9912-E7A849147AB1}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|Mixed Platforms.ActiveCfg = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|Mixed Platforms.Build.0 = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|x64.ActiveCfg = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|x64.Build.0 = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|x86.ActiveCfg = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Prerelease|x86.Build.0 = Prerelease|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{F5051A32-6BD0-4128-ABBA-C202EE15FC5C}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{B72DEBFB-9D48-4076-8FF5-1FD72A830845}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
@ -1,173 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
|
||||||
<PropertyGroup>
|
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<ProjectGuid>f5051a32-6bd0-4128-abba-c202ee15fc5c</ProjectGuid>
|
|
||||||
<ProjectHome>.</ProjectHome>
|
|
||||||
<ProjectTypeGuids>{789894c7-04a9-4a11-a6b5-3f4435165112};{1b580a1a-fdb3-4b32-83e1-6407eb2722e6};{349c5851-65df-11da-9384-00065b846f21};{888888a0-9f3d-457c-b088-3a5042f75d52}</ProjectTypeGuids>
|
|
||||||
<StartupFile>master\runserver.py</StartupFile>
|
|
||||||
<SearchPath>
|
|
||||||
</SearchPath>
|
|
||||||
<WorkingDirectory>.</WorkingDirectory>
|
|
||||||
<LaunchProvider>Standard Python launcher</LaunchProvider>
|
|
||||||
<WebBrowserUrl>http://localhost</WebBrowserUrl>
|
|
||||||
<OutputPath>.</OutputPath>
|
|
||||||
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
|
||||||
<Name>Master</Name>
|
|
||||||
<RootNamespace>Master</RootNamespace>
|
|
||||||
<InterpreterId>MSBuild|env_master|$(MSBuildProjectFullPath)</InterpreterId>
|
|
||||||
<IsWindowsApplication>False</IsWindowsApplication>
|
|
||||||
<PythonRunWebServerCommand>
|
|
||||||
</PythonRunWebServerCommand>
|
|
||||||
<PythonDebugWebServerCommand>
|
|
||||||
</PythonDebugWebServerCommand>
|
|
||||||
<PythonRunWebServerCommandType>script</PythonRunWebServerCommandType>
|
|
||||||
<PythonDebugWebServerCommandType>script</PythonDebugWebServerCommandType>
|
|
||||||
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
|
||||||
<OutputPath>bin\Prerelease\</OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="master\context\base.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\context\history.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\context\__init__.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\models\instancemodel.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\models\servermodel.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\models\__init__.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\authenticate.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\history_graph.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\instance.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\localization.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\null.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Master\resources\server.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\version.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\resources\__init__.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\routes.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\runserver.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\schema\instanceschema.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\schema\serverschema.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\schema\__init__.py">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="master\__init__.py" />
|
|
||||||
<Compile Include="master\views.py" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="master\" />
|
|
||||||
<Folder Include="master\context\" />
|
|
||||||
<Folder Include="master\models\" />
|
|
||||||
<Folder Include="master\config\" />
|
|
||||||
<Folder Include="master\schema\" />
|
|
||||||
<Folder Include="Master\resources\" />
|
|
||||||
<Folder Include="master\static\" />
|
|
||||||
<Folder Include="master\templates\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="master\config\master.json" />
|
|
||||||
<Content Include="master\templates\serverlist.html" />
|
|
||||||
<None Include="Release.pubxml" />
|
|
||||||
<Content Include="requirements.txt" />
|
|
||||||
<Content Include="master\templates\index.html" />
|
|
||||||
<Content Include="master\templates\layout.html" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Interpreter Include="env_master\">
|
|
||||||
<Id>env_master</Id>
|
|
||||||
<Version>3.6</Version>
|
|
||||||
<Description>env_master (Python 3.6 (64-bit))</Description>
|
|
||||||
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
|
||||||
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
|
||||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
|
||||||
<Architecture>X64</Architecture>
|
|
||||||
</Interpreter>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
|
||||||
<!-- Specify pre- and post-build commands in the BeforeBuild and
|
|
||||||
AfterBuild targets below. -->
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
<ProjectExtensions>
|
|
||||||
<VisualStudio>
|
|
||||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
|
||||||
<WebProjectProperties>
|
|
||||||
<AutoAssignPort>True</AutoAssignPort>
|
|
||||||
<UseCustomServer>True</UseCustomServer>
|
|
||||||
<CustomServerUrl>http://localhost</CustomServerUrl>
|
|
||||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
|
||||||
</WebProjectProperties>
|
|
||||||
</FlavorProperties>
|
|
||||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
|
|
||||||
<WebProjectProperties>
|
|
||||||
<StartPageUrl>
|
|
||||||
</StartPageUrl>
|
|
||||||
<StartAction>CurrentPage</StartAction>
|
|
||||||
<AspNetDebugging>True</AspNetDebugging>
|
|
||||||
<SilverlightDebugging>False</SilverlightDebugging>
|
|
||||||
<NativeDebugging>False</NativeDebugging>
|
|
||||||
<SQLDebugging>False</SQLDebugging>
|
|
||||||
<ExternalProgram>
|
|
||||||
</ExternalProgram>
|
|
||||||
<StartExternalURL>
|
|
||||||
</StartExternalURL>
|
|
||||||
<StartCmdLineArguments>
|
|
||||||
</StartCmdLineArguments>
|
|
||||||
<StartWorkingDirectory>
|
|
||||||
</StartWorkingDirectory>
|
|
||||||
<EnableENC>False</EnableENC>
|
|
||||||
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
|
|
||||||
</WebProjectProperties>
|
|
||||||
</FlavorProperties>
|
|
||||||
</VisualStudio>
|
|
||||||
</ProjectExtensions>
|
|
||||||
</Project>
|
|
@ -1,19 +0,0 @@
|
|||||||
"""
|
|
||||||
The flask application package.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from flask import Flask
|
|
||||||
from flask_restful import Resource, Api
|
|
||||||
from flask_jwt_extended import JWTManager
|
|
||||||
from master.context.base import Base
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['JWT_SECRET_KEY'] = 'my key!'
|
|
||||||
app.config['PROPAGATE_EXCEPTIONS'] = True
|
|
||||||
jwt = JWTManager(app)
|
|
||||||
api = Api(app)
|
|
||||||
ctx = Base()
|
|
||||||
#config = json.load(open('./master/config/master.json'))
|
|
||||||
|
|
||||||
import master.routes
|
|
||||||
import master.views
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"current-version-stable": 2.1,
|
|
||||||
"current-version-prerelease": 2.1
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,89 +0,0 @@
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
|
||||||
from apscheduler.triggers.interval import IntervalTrigger
|
|
||||||
from master.context.history import History
|
|
||||||
|
|
||||||
from master.schema.instanceschema import InstanceSchema
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
class Base():
|
|
||||||
def __init__(self):
|
|
||||||
self.history = History()
|
|
||||||
self.instance_list = {}
|
|
||||||
self.token_list = {}
|
|
||||||
self.scheduler = BackgroundScheduler()
|
|
||||||
self.scheduler.start()
|
|
||||||
self.scheduler.add_job(
|
|
||||||
func=self._remove_staleinstances,
|
|
||||||
trigger=IntervalTrigger(seconds=60),
|
|
||||||
id='stale_instance_remover',
|
|
||||||
name='Remove stale instances if no heartbeat in 60 seconds',
|
|
||||||
replace_existing=True
|
|
||||||
)
|
|
||||||
self.scheduler.add_job(
|
|
||||||
func=self._update_history_count,
|
|
||||||
trigger=IntervalTrigger(seconds=30),
|
|
||||||
id='update history',
|
|
||||||
name='update client and instance count every 30 seconds',
|
|
||||||
replace_existing=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def _update_history_count(self):
|
|
||||||
servers = [instance.servers for instance in self.instance_list.values()]
|
|
||||||
servers = [inner for outer in servers for inner in outer]
|
|
||||||
client_num = 0
|
|
||||||
# force it being a number
|
|
||||||
for server in servers:
|
|
||||||
client_num += server.clientnum
|
|
||||||
self.history.add_client_history(client_num)
|
|
||||||
self.history.add_instance_history(len(self.instance_list))
|
|
||||||
self.history.add_server_history(len(servers))
|
|
||||||
|
|
||||||
def _remove_staleinstances(self):
|
|
||||||
for key, value in list(self.instance_list.items()):
|
|
||||||
if int(time.time()) - value.last_heartbeat > 60:
|
|
||||||
print('[_remove_staleinstances] removing stale instance {id}'.format(id=key))
|
|
||||||
del self.instance_list[key]
|
|
||||||
del self.token_list[key]
|
|
||||||
print('[_remove_staleinstances] {count} active instances'.format(count=len(self.instance_list.items())))
|
|
||||||
|
|
||||||
def get_instances(self):
|
|
||||||
return self.instance_list.values()
|
|
||||||
|
|
||||||
def get_instance_count(self):
|
|
||||||
return self.instance_list.count
|
|
||||||
|
|
||||||
def get_instance(self, id):
|
|
||||||
return self.instance_list[id]
|
|
||||||
|
|
||||||
def instance_exists(self, instance_id):
|
|
||||||
if instance_id in self.instance_list.keys():
|
|
||||||
return instance_id
|
|
||||||
else:
|
|
||||||
False
|
|
||||||
|
|
||||||
def add_instance(self, instance):
|
|
||||||
if instance.id in self.instance_list:
|
|
||||||
print('[add_instance] instance {id} already added, updating instead'.format(id=instance.id))
|
|
||||||
return self.update_instance(instance)
|
|
||||||
else:
|
|
||||||
print('[add_instance] adding instance {id}'.format(id=instance.id))
|
|
||||||
self.instance_list[instance.id] = instance
|
|
||||||
|
|
||||||
def update_instance(self, instance):
|
|
||||||
if instance.id not in self.instance_list:
|
|
||||||
print('[update_instance] instance {id} not added, adding instead'.format(id=instance.id))
|
|
||||||
return self.add_instance(instance)
|
|
||||||
else:
|
|
||||||
print('[update_instance] updating instance {id}'.format(id=instance.id))
|
|
||||||
self.instance_list[instance.id] = instance
|
|
||||||
|
|
||||||
def add_token(self, instance_id, token):
|
|
||||||
print('[add_token] adding {token} for id {id}'.format(token=token, id=instance_id))
|
|
||||||
self.token_list[instance_id] = token
|
|
||||||
|
|
||||||
def get_token(self, instance_id):
|
|
||||||
try:
|
|
||||||
return self.token_list[instance_id]
|
|
||||||
except KeyError:
|
|
||||||
return False
|
|
@ -1,32 +0,0 @@
|
|||||||
import time
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
class History():
|
|
||||||
def __init__(self):
|
|
||||||
self.client_history = list()
|
|
||||||
self.instance_history = list()
|
|
||||||
self.server_history = list()
|
|
||||||
|
|
||||||
def add_client_history(self, client_num):
|
|
||||||
if len(self.client_history) > 20160:
|
|
||||||
self.client_history = self.client_history[1:]
|
|
||||||
self.client_history.append({
|
|
||||||
'count' : client_num,
|
|
||||||
'time' : int(time.time())
|
|
||||||
})
|
|
||||||
|
|
||||||
def add_server_history(self, server_num):
|
|
||||||
if len(self.server_history) > 20160:
|
|
||||||
self.server_history = self.server_history[1:]
|
|
||||||
self.server_history.append({
|
|
||||||
'count' : server_num,
|
|
||||||
'time' : int(time.time())
|
|
||||||
})
|
|
||||||
|
|
||||||
def add_instance_history(self, instance_num):
|
|
||||||
if len(self.instance_history) > 20160:
|
|
||||||
self.instance_history = self.instance_history[1:]
|
|
||||||
self.instance_history.append({
|
|
||||||
'count' : instance_num,
|
|
||||||
'time' : int(time.time())
|
|
||||||
})
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
class InstanceModel(object):
|
|
||||||
def __init__(self, id, version, uptime, servers):
|
|
||||||
self.id = id
|
|
||||||
self.version = version
|
|
||||||
self.uptime = uptime
|
|
||||||
self.servers = servers
|
|
||||||
self.last_heartbeat = int(time.time())
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<InstanceModel(id={id})>'.format(id=self.id)
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
class ServerModel(object):
|
|
||||||
def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype, ip, version):
|
|
||||||
self.id = id
|
|
||||||
self.port = port
|
|
||||||
self.version = version
|
|
||||||
self.game = game
|
|
||||||
self.hostname = hostname
|
|
||||||
self.clientnum = clientnum
|
|
||||||
self.maxclientnum = maxclientnum
|
|
||||||
self.map = map
|
|
||||||
self.gametype = gametype
|
|
||||||
self.ip = ip
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<ServerModel(id={id})>'.format(id=self.id)
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
from flask import request, jsonify
|
|
||||||
from flask_jwt_extended import create_access_token
|
|
||||||
from master import app, ctx
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
class Authenticate(Resource):
|
|
||||||
def post(self):
|
|
||||||
instance_id = request.json['id']
|
|
||||||
#todo: see why this is failing
|
|
||||||
#if ctx.get_token(instance_id) is not False:
|
|
||||||
# return { 'message' : 'that id already has a token'}, 401
|
|
||||||
#else:
|
|
||||||
expires = datetime.timedelta(days=30)
|
|
||||||
token = create_access_token(instance_id, expires_delta=expires)
|
|
||||||
ctx.add_token(instance_id, token)
|
|
||||||
return { 'access_token' : token }, 200
|
|
@ -1,49 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
from pygal.style import Style
|
|
||||||
from master import ctx
|
|
||||||
import pygal
|
|
||||||
import timeago
|
|
||||||
from math import ceil
|
|
||||||
|
|
||||||
class HistoryGraph(Resource):
|
|
||||||
def get(self, history_count):
|
|
||||||
try:
|
|
||||||
custom_style = Style(
|
|
||||||
background='transparent',
|
|
||||||
plot_background='transparent',
|
|
||||||
foreground='#6c757d',
|
|
||||||
foreground_strong='#6c757d',
|
|
||||||
foreground_subtle='#6c757d',
|
|
||||||
opacity='0.1',
|
|
||||||
opacity_hover='0.2',
|
|
||||||
transition='0ms',
|
|
||||||
colors=('#749363','#007acc'),
|
|
||||||
)
|
|
||||||
|
|
||||||
graph = pygal.Line(
|
|
||||||
stroke_style={'width': 0.4},
|
|
||||||
#show_dots=False,
|
|
||||||
show_legend=False,
|
|
||||||
fill=True,
|
|
||||||
style=custom_style,
|
|
||||||
disable_xml_declaration=True)
|
|
||||||
|
|
||||||
instance_count = [history['time'] for history in ctx.history.instance_history][-history_count:]
|
|
||||||
|
|
||||||
if len(instance_count) > 0:
|
|
||||||
graph.x_labels = [ timeago.format(instance_count[0])]
|
|
||||||
|
|
||||||
instance_counts = [history['count'] for history in ctx.history.instance_history][-history_count:]
|
|
||||||
client_counts = [history['count'] for history in ctx.history.client_history][-history_count:]
|
|
||||||
server_counts = [history['count'] for history in ctx.history.server_history][-history_count:]
|
|
||||||
|
|
||||||
graph.add('Client Count', client_counts)
|
|
||||||
graph.add('Instance Count', instance_counts)
|
|
||||||
return { 'message' : graph.render().replace("<title>Pygal</title>", ""),
|
|
||||||
'data_points' : len(instance_count),
|
|
||||||
'instance_count' : 0 if len(instance_counts) is 0 else instance_counts[-1],
|
|
||||||
'client_count' : 0 if len(client_counts) is 0 else client_counts[-1],
|
|
||||||
'server_count' : 0 if len(server_counts) is 0 else server_counts[-1]
|
|
||||||
}, 200
|
|
||||||
except Exception as e:
|
|
||||||
return { 'message' : str(e) }, 500
|
|
@ -1,49 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
from flask import request
|
|
||||||
from flask_jwt_extended import jwt_required
|
|
||||||
from marshmallow import ValidationError
|
|
||||||
from master.schema.instanceschema import InstanceSchema
|
|
||||||
from master import ctx
|
|
||||||
import json
|
|
||||||
from netaddr import IPAddress
|
|
||||||
|
|
||||||
class Instance(Resource):
|
|
||||||
def get(self, id=None):
|
|
||||||
if id is None:
|
|
||||||
schema = InstanceSchema(many=True)
|
|
||||||
instances = schema.dump(ctx.get_instances())
|
|
||||||
return instances
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
instance = ctx.get_instance(id)
|
|
||||||
return InstanceSchema().dump(instance)
|
|
||||||
except KeyError:
|
|
||||||
return {'message' : 'instance not found'}, 404
|
|
||||||
|
|
||||||
@jwt_required
|
|
||||||
def put(self, id):
|
|
||||||
try:
|
|
||||||
for server in request.json['servers']:
|
|
||||||
if 'ip' not in server or IPAddress(server['ip']).is_private() or IPAddress(server['ip']).is_loopback():
|
|
||||||
server['ip'] = request.remote_addr
|
|
||||||
if 'version' not in server:
|
|
||||||
server['version'] = 'Unknown'
|
|
||||||
instance = InstanceSchema().load(request.json)
|
|
||||||
except ValidationError as err:
|
|
||||||
return {'message' : err.messages }, 400
|
|
||||||
ctx.update_instance(instance)
|
|
||||||
return { 'message' : 'instance updated successfully' }, 200
|
|
||||||
|
|
||||||
@jwt_required
|
|
||||||
def post(self):
|
|
||||||
try:
|
|
||||||
for server in request.json['servers']:
|
|
||||||
if 'ip' not in server or server['ip'] == 'localhost':
|
|
||||||
server['ip'] = request.remote_addr
|
|
||||||
if 'version' not in server:
|
|
||||||
server['version'] = 'Unknown'
|
|
||||||
instance = InstanceSchema().load(request.json)
|
|
||||||
except ValidationError as err:
|
|
||||||
return {'message' : err.messages }, 400
|
|
||||||
ctx.add_instance(instance)
|
|
||||||
return { 'message' : 'instance added successfully' }, 200
|
|
@ -1,59 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
from flask import request, jsonify
|
|
||||||
from flask_jwt_extended import create_access_token
|
|
||||||
from master import app, ctx
|
|
||||||
import datetime
|
|
||||||
import urllib.request
|
|
||||||
import csv
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
class Localization(Resource):
|
|
||||||
def list(self):
|
|
||||||
response = urllib.request.urlopen('https://docs.google.com/spreadsheets/d/e/2PACX-1vRQjCqPvd0Xqcn86WqpFqp_lx4KKpel9O4OV13NycmV8rmqycorgJQm-8qXMfw37QJHun3pqVZFUKG-/pub?gid=0&single=true&output=csv')
|
|
||||||
data = response.read().decode('utf-8')
|
|
||||||
|
|
||||||
localization = []
|
|
||||||
csv_data = csv.DictReader(StringIO(data))
|
|
||||||
|
|
||||||
for language in csv_data.fieldnames[1:]:
|
|
||||||
localization.append({
|
|
||||||
'LocalizationName' : language,
|
|
||||||
'LocalizationIndex' : {
|
|
||||||
'Set' : {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
for row in csv_data:
|
|
||||||
localization_string = row['STRING']
|
|
||||||
count = 0
|
|
||||||
for language in csv_data.fieldnames[1:]:
|
|
||||||
localization[count]['LocalizationIndex']['Set'][localization_string] = row[language]
|
|
||||||
count += 1
|
|
||||||
|
|
||||||
return localization, 200
|
|
||||||
|
|
||||||
def get(self, language_tag=None):
|
|
||||||
response = urllib.request.urlopen('https://docs.google.com/spreadsheets/d/e/2PACX-1vRQjCqPvd0Xqcn86WqpFqp_lx4KKpel9O4OV13NycmV8rmqycorgJQm-8qXMfw37QJHun3pqVZFUKG-/pub?gid=0&single=true&output=csv')
|
|
||||||
data = response.read().decode('utf-8')
|
|
||||||
|
|
||||||
csv_data = csv.DictReader(StringIO(data))
|
|
||||||
|
|
||||||
|
|
||||||
if language_tag != None:
|
|
||||||
valid_language_tag = next((l for l in csv_data.fieldnames[1:] if l == language_tag), None)
|
|
||||||
if valid_language_tag is None:
|
|
||||||
valid_language_tag = next((l for l in csv_data.fieldnames[1:] if l.startswith(language_tag[:2])), None)
|
|
||||||
if valid_language_tag is None:
|
|
||||||
valid_language_tag = 'en-US'
|
|
||||||
localization = {
|
|
||||||
'LocalizationName' : valid_language_tag,
|
|
||||||
'LocalizationIndex' : {
|
|
||||||
'Set' : {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for row in csv_data:
|
|
||||||
localization_string = row['STRING']
|
|
||||||
localization['LocalizationIndex']['Set'][localization_string] = row[valid_language_tag]
|
|
||||||
return localization, 200
|
|
||||||
else:
|
|
||||||
return self.list()[0][0], 200
|
|
@ -1,11 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
from master.models.servermodel import ServerModel
|
|
||||||
from master.schema.serverschema import ServerSchema
|
|
||||||
from master.models.instancemodel import InstanceModel
|
|
||||||
from master.schema.instanceschema import InstanceSchema
|
|
||||||
|
|
||||||
class Null(Resource):
|
|
||||||
def get(self):
|
|
||||||
server = ServerModel(1, 'T6M', 'test', 0, 18, 'mp_test', 'tdm')
|
|
||||||
instance = InstanceModel(1, 1.5, 132, [server])
|
|
||||||
return InstanceSchema().dump(instance)
|
|
@ -1,6 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
|
|
||||||
class Server(Resource):
|
|
||||||
"""description of class"""
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
from flask_restful import Resource
|
|
||||||
import json
|
|
||||||
|
|
||||||
class Version(Resource):
|
|
||||||
def get(self):
|
|
||||||
config = json.load(open('./master/config/master.json'))
|
|
||||||
return {
|
|
||||||
'current-version-stable' : config['current-version-stable'],
|
|
||||||
'current-version-prerelease' : config['current-version-prerelease']
|
|
||||||
}, 200
|
|
@ -1,17 +0,0 @@
|
|||||||
from master import api
|
|
||||||
|
|
||||||
from master.resources.null import Null
|
|
||||||
from master.resources.instance import Instance
|
|
||||||
from master.resources.authenticate import Authenticate
|
|
||||||
from master.resources.version import Version
|
|
||||||
from master.resources.history_graph import HistoryGraph
|
|
||||||
from master.resources.localization import Localization
|
|
||||||
from master.resources.server import Server
|
|
||||||
|
|
||||||
api.add_resource(Null, '/null')
|
|
||||||
api.add_resource(Instance, '/instance/', '/instance/<string:id>')
|
|
||||||
api.add_resource(Version, '/version')
|
|
||||||
api.add_resource(Authenticate, '/authenticate')
|
|
||||||
api.add_resource(HistoryGraph, '/history/', '/history/<int:history_count>')
|
|
||||||
api.add_resource(Localization, '/localization/', '/localization/<string:language_tag>')
|
|
||||||
api.add_resource(Server, '/server')
|
|
@ -1,9 +0,0 @@
|
|||||||
"""
|
|
||||||
This script runs the Master application using a development server.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from os import environ
|
|
||||||
from master import app
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0', port=80, debug=True)
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,29 +0,0 @@
|
|||||||
from marshmallow import Schema, fields, post_load, validate
|
|
||||||
from master.models.instancemodel import InstanceModel
|
|
||||||
from master.schema.serverschema import ServerSchema
|
|
||||||
|
|
||||||
class InstanceSchema(Schema):
|
|
||||||
id = fields.String(
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
version = fields.Float(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(1.0, 10.0, 'invalid version number')
|
|
||||||
)
|
|
||||||
servers = fields.Nested(
|
|
||||||
ServerSchema,
|
|
||||||
many=True,
|
|
||||||
validate=validate.Length(0, 32, 'invalid server count')
|
|
||||||
)
|
|
||||||
uptime = fields.Int(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(0, 2147483647, 'invalid uptime')
|
|
||||||
)
|
|
||||||
last_heartbeat = fields.Int(
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
@post_load
|
|
||||||
def make_instance(self, data):
|
|
||||||
return InstanceModel(**data)
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
from marshmallow import Schema, fields, post_load, validate
|
|
||||||
from master.models.servermodel import ServerModel
|
|
||||||
|
|
||||||
class ServerSchema(Schema):
|
|
||||||
id = fields.Int(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(0, 25525525525565535, 'invalid id')
|
|
||||||
)
|
|
||||||
ip = fields.Str(
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
port = fields.Int(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(1, 65535, 'invalid port')
|
|
||||||
)
|
|
||||||
version = fields.String(
|
|
||||||
required=False,
|
|
||||||
validate=validate.Length(0, 128, 'invalid server version')
|
|
||||||
)
|
|
||||||
game = fields.String(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Length(1, 5, 'invalid game name')
|
|
||||||
)
|
|
||||||
hostname = fields.String(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Length(1, 128, 'invalid hostname')
|
|
||||||
)
|
|
||||||
clientnum = fields.Int(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(0, 128, 'invalid clientnum')
|
|
||||||
)
|
|
||||||
maxclientnum = fields.Int(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Range(1, 128, 'invalid maxclientnum')
|
|
||||||
)
|
|
||||||
map = fields.String(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Length(0, 64, 'invalid map name')
|
|
||||||
)
|
|
||||||
gametype = fields.String(
|
|
||||||
required=True,
|
|
||||||
validate=validate.Length(1, 16, 'invalid gametype')
|
|
||||||
)
|
|
||||||
|
|
||||||
@post_load
|
|
||||||
def make_instance(self, data):
|
|
||||||
return ServerModel(**data)
|
|
@ -1,60 +0,0 @@
|
|||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-sm-8 ml-auto mr-auto">
|
|
||||||
<figure>
|
|
||||||
<div id="history_graph">{{history_graph|safe}}</div>
|
|
||||||
<figcaption class="float-right">
|
|
||||||
<span id="history_graph_zoom_out" class="h4 oi oi-zoom-out text-muted" style="cursor:pointer;"></span>
|
|
||||||
<span id="history_graph_zoom_in" class="h4 oi oi-zoom-in text-muted" style="cursor:pointer;"></span>
|
|
||||||
</figcaption>
|
|
||||||
<figcaption class="float-left">
|
|
||||||
<span class="h4 text-muted">{{instance_count}} instances</span>
|
|
||||||
<span class="h4 text-muted">— {{client_count}} clients</span>
|
|
||||||
<span class="h4 text-muted">— {{server_count}} servers</span>
|
|
||||||
</figcaption>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script type="text/javascript" src="http://kozea.github.com/pygal.js/latest/pygal-tooltips.min.js"></script>
|
|
||||||
<script>
|
|
||||||
let dataPoints = {{data_points}};
|
|
||||||
let maxPoints = 2880;
|
|
||||||
maxPoints = Math.min(maxPoints, dataPoints);
|
|
||||||
let zoomLevel = Math.floor(maxPoints);
|
|
||||||
let performingZoom = false;
|
|
||||||
|
|
||||||
function updateHistoryGraph() {
|
|
||||||
perfomingZoom = true;
|
|
||||||
$.get('/history/' + zoomLevel)
|
|
||||||
.done(function (content) {
|
|
||||||
$('#history_graph').html(content.message);
|
|
||||||
//maxPoints = Math.min(maxPoints, dataPoints);
|
|
||||||
perfomingZoom = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//setInterval(updateHistoryGraph, 30000);
|
|
||||||
|
|
||||||
$('#history_graph_zoom_out').click(function () {
|
|
||||||
if (performingZoom === true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
zoomLevel = Math.floor(zoomLevel * 2) <= maxPoints ? Math.floor(zoomLevel * 2) : maxPoints;
|
|
||||||
updateHistoryGraph();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#history_graph_zoom_in').click(function () {
|
|
||||||
if (performingZoom === true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
zoomLevel = zoomLevel / 2 > 2 ? Math.ceil(zoomLevel / 2) : 2;
|
|
||||||
updateHistoryGraph();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
@ -1,45 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html class="bg-dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>IW4MAdmin Master | {{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" integrity="sha256-BJ/G+e+y7bQdrYkS2RBTyNfBHpA9IuGaPmf9htub5MQ=" crossorigin="anonymous" />
|
|
||||||
<style type="text/css">
|
|
||||||
.active {
|
|
||||||
stroke-width: initial !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.oi:hover {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
.dot {
|
|
||||||
opacity: 0;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dot:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-box {
|
|
||||||
fill: #343a40 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="bg-dark">
|
|
||||||
|
|
||||||
<div class="container body-content bg-dark">
|
|
||||||
{% block content %}{% endblock %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
|
|
||||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
|
||||||
{% block scripts %}{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,113 +0,0 @@
|
|||||||
{% extends "layout.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<!-- todo: move this! -->
|
|
||||||
<style>
|
|
||||||
.server-row {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content, .nav-item {
|
|
||||||
background-color: #212529;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header, .modal-footer {
|
|
||||||
border-color: #32383e !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-dark button.close, a.nav-link {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="modal modal-dark" id="serverModalCenter" tabindex="-1" role="dialog" aria-labelledby="serverModalCenterTitle" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="serverModalTitle">Modal title</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="h5" id="server_socket"></div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button id="connect_button" type="button" class="btn btn-dark">Connect</button>
|
|
||||||
<button type="button" class="btn btn-dark" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<nav>
|
|
||||||
<div class="nav nav-tabs" id="server_game_tabs" role="tablist">
|
|
||||||
{% for game in games %}
|
|
||||||
<a class="nav-item nav-link {{'active' if loop.first else ''}}" id="{{game}}_servers_tab" data-toggle="tab" href="#{{game}}_servers" role="tab" aria-controls="{{game}}_servers" aria-selected="{{'true' if loop.first else 'false' }}">{{game}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div class="tab-content" id="server_game_tabs_content">
|
|
||||||
{% for game, servers in games.items() %}
|
|
||||||
|
|
||||||
<div class="tab-pane {{'show active' if loop.first else ''}}" id="{{game}}_servers" role="tabpanel" aria-labelledby="{{game}}_servers_tab">
|
|
||||||
|
|
||||||
<table class="table table-dark table-striped table-hover table-responsive-lg">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Server Name</th>
|
|
||||||
<th>Map Name</th>
|
|
||||||
<th>Players</th>
|
|
||||||
<th>Mode</th>
|
|
||||||
<th class="text-center">Connect</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
{% for server in servers %}
|
|
||||||
|
|
||||||
<tr class="server-row" data-toggle="modal" data-target="#serverModalCenter"
|
|
||||||
data-ip="{{server.ip}}" data-port="{{server.port}}">
|
|
||||||
<td data-hostname="{{server.hostname}}" class="server-hostname">{{server.hostname}}</td>
|
|
||||||
<td data-map="{{server.map}} " class="server-map">{{server.map}}</td>
|
|
||||||
<td data-clientnum="{{server.clientnum}}" data-maxclientnum="{{server.maxclientnum}}"
|
|
||||||
class="server-clientnum">
|
|
||||||
{{server.clientnum}}/{{server.maxclientnum}}
|
|
||||||
</td>
|
|
||||||
<td data-gametype="{{server.gametype}}" class="server-gametype">{{server.gametype}}</td>
|
|
||||||
<td class="text-center"><span class="oi oi-play-circle"></span></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="w-100 small text-right text-muted">
|
|
||||||
<span>Developed by RaidMax</span><br />
|
|
||||||
<span>PRERELEASE</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script>
|
|
||||||
$(document).ready(() => {
|
|
||||||
$('.server-row').off('click');
|
|
||||||
$('.server-row').on('click', function (e) {
|
|
||||||
$('#serverModalTitle').text($(this).find('.server-hostname').text());
|
|
||||||
$('#server_socket').text(`/connect ${$(this).data('ip')}:${$(this).data('port')}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#connect_button').off('click');
|
|
||||||
$('#connect_button').on('click', (e) => alert('soon...'));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
@ -1,35 +0,0 @@
|
|||||||
"""
|
|
||||||
Routes and views for the flask application.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from flask import render_template
|
|
||||||
from master import app, ctx
|
|
||||||
from master.resources.history_graph import HistoryGraph
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def home():
|
|
||||||
_history_graph = HistoryGraph().get(2880)
|
|
||||||
return render_template(
|
|
||||||
'index.html',
|
|
||||||
title='API Overview',
|
|
||||||
history_graph = _history_graph[0]['message'],
|
|
||||||
data_points = _history_graph[0]['data_points'],
|
|
||||||
instance_count = _history_graph[0]['instance_count'],
|
|
||||||
client_count = _history_graph[0]['client_count'],
|
|
||||||
server_count = _history_graph[0]['server_count']
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.route('/servers')
|
|
||||||
def servers():
|
|
||||||
servers = defaultdict(list)
|
|
||||||
if len(ctx.instance_list.values()) > 0:
|
|
||||||
ungrouped_servers = [server for instance in ctx.instance_list.values() for server in instance.servers]
|
|
||||||
for server in ungrouped_servers:
|
|
||||||
servers[server.game].append(server)
|
|
||||||
return render_template(
|
|
||||||
'serverlist.html',
|
|
||||||
title = 'Server List',
|
|
||||||
games = servers
|
|
||||||
)
|
|
@ -1,26 +0,0 @@
|
|||||||
aniso8601==3.0.2
|
|
||||||
APScheduler==3.5.3
|
|
||||||
certifi==2018.10.15
|
|
||||||
chardet==3.0.4
|
|
||||||
click==6.7
|
|
||||||
Flask==1.0.2
|
|
||||||
Flask-JWT==0.3.2
|
|
||||||
Flask-JWT-Extended==3.8.1
|
|
||||||
Flask-RESTful==0.3.6
|
|
||||||
idna==2.7
|
|
||||||
itsdangerous==0.24
|
|
||||||
Jinja2==2.10
|
|
||||||
MarkupSafe==1.0
|
|
||||||
marshmallow==3.0.0b8
|
|
||||||
pip==9.0.3
|
|
||||||
psutil==5.4.8
|
|
||||||
pygal==2.4.0
|
|
||||||
PyJWT==1.4.2
|
|
||||||
pytz==2018.7
|
|
||||||
requests==2.20.0
|
|
||||||
setuptools==40.5.0
|
|
||||||
six==1.11.0
|
|
||||||
timeago==1.0.8
|
|
||||||
tzlocal==1.5.1
|
|
||||||
urllib3==1.24
|
|
||||||
Werkzeug==0.15.3
|
|
Loading…
x
Reference in New Issue
Block a user