Merge branch 'dev' into dev_websocket_server_status

This commit is contained in:
Silversthorn 2022-01-21 23:54:59 +01:00
commit bae08d05d9
21 changed files with 357 additions and 172 deletions

View File

@ -40,13 +40,11 @@ class Management_Controller:
@staticmethod
def send_command(user_id, server_id, remote_ip, command):
server_name = servers_helper.get_server_friendly_name(server_id)
# Example: Admin issued command start_server for server Survival
management_helper.add_to_audit_log(user_id, "issued command {} for server {}".format(command, server_name),
server_id, remote_ip)
management_helper.add_command(server_id, user_id, remote_ip, command)
@staticmethod

View File

@ -100,9 +100,7 @@ class Servers_Controller:
@staticmethod
def get_authorized_servers_stats(user_id):
server_data = []
print('test 1')
authorized_servers = Servers_Controller.get_authorized_servers(user_id)
print('test 2')
for s in authorized_servers:
latest = servers_helper.get_latest_server_stats(s.get('server_id'))
@ -137,11 +135,10 @@ class Servers_Controller:
@staticmethod
def server_id_authorized(server_id_a, user_id):
print("Server id authorized: ")
user_roles = users_helper.user_role_query(user_id)
for role in user_roles:
for server_id_b in server_permissions.get_role_servers_from_role_id(role.role_id):
if server_id_a == server_id_b:
if str(server_id_a) == str(server_id_b.server_id):
return True
return False

View File

@ -12,6 +12,7 @@ from app.classes.shared.console import console
from app.classes.models.servers import Servers
from app.classes.minecraft.server_props import ServerProps
from app.classes.web.websocket_helper import websocket_helper
from app.classes.models.server_permissions import server_permissions
logger = logging.getLogger(__name__)
@ -190,7 +191,6 @@ class ServerJars:
except Exception as e:
logger.error("Unable to save jar to {path} due to error:{error}".format(path=path, error=e))
pass
websocket_helper.broadcast('notification', "Executable download finished for server named: " + name)
return False

View File

@ -9,6 +9,8 @@ from app.classes.shared.main_models import db_helper
from app.classes.models.users import Users, users_helper
from app.classes.models.servers import Servers, servers_helper
from app.classes.web.websocket_helper import websocket_helper
from app.classes.models.server_permissions import server_permissions
import time
logger = logging.getLogger(__name__)
@ -156,7 +158,7 @@ class helpers_management:
@staticmethod
def get_unactioned_commands():
query = Commands.select().where(Commands.executed == 0)
return db_helper.return_rows(query)
return query
@staticmethod
def mark_command_complete(command_id=None):
@ -181,7 +183,9 @@ class helpers_management:
audit_msg = "{} {}".format(str(user_data['username']).capitalize(), log_msg)
websocket_helper.broadcast('notification', audit_msg)
server_users = server_permissions.get_server_user_list(server_id)
for user in server_users:
websocket_helper.broadcast_user(user,'notification', audit_msg)
Audit_Log.insert({
Audit_Log.user_name: user_data['username'],

View File

@ -7,7 +7,7 @@ from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.models.servers import Servers
from app.classes.models.roles import Roles
from app.classes.models.users import users_helper, ApiKeys, Users
from app.classes.models.users import User_Roles, users_helper, ApiKeys, Users
from app.classes.shared.permission_helper import permission_helper
logger = logging.getLogger(__name__)
@ -172,11 +172,26 @@ class Permissions_Servers:
if user.superuser:
permissions_mask = '1' * len(server_permissions.get_permissions_list())
else:
roles_list = users_helper.get_user_roles_id(user['user_id'])
roles_list = users_helper.get_user_roles_id(user.user_id)
role_server = Role_Servers.select().where(Role_Servers.role_id.in_(roles_list)).where(Role_Servers.server_id == server_id).execute()
permissions_mask = role_server[0].permissions
return permissions_mask
@staticmethod
def get_server_user_list(server_id):
final_users = []
server_roles = Role_Servers.select().where(Role_Servers.server_id == server_id)
super_users = Users.select().where(Users.superuser == True)
for role in server_roles:
users = User_Roles.select().where(User_Roles.role_id == role.role_id)
for user in users:
if user.user_id.user_id not in final_users:
final_users.append(user.user_id.user_id)
for suser in super_users:
if suser.user_id not in final_users:
final_users.append(suser.user_id)
return final_users
@staticmethod
def get_user_id_permissions_list(user_id, server_id: str):
user = users_helper.get_user_model(user_id)

View File

@ -174,6 +174,15 @@ class helper_users:
if up_data:
Users.update(up_data).where(Users.user_id == user_id).execute()
@staticmethod
def get_super_user_list():
final_users = []
super_users = Users.select().where(Users.superuser == True)
for suser in super_users:
if suser.user_id not in final_users:
final_users.append(suser.user_id)
return final_users
@staticmethod
def remove_user(user_id):
with database.atomic():

View File

@ -137,6 +137,8 @@ class Controller:
def package_support_logs(self, exec_user):
#pausing so on screen notifications can run for user
time.sleep(7)
websocket_helper.broadcast_user(exec_user['user_id'], 'notification', 'Preparing your support logs')
tempDir = tempfile.mkdtemp()
tempZipStorage = tempfile.mkdtemp()
@ -161,7 +163,10 @@ class Controller:
for server in auth_servers:
final_path = os.path.join(server_path, str(server['server_name']))
os.mkdir(final_path)
shutil.copy(server['log_path'], final_path)
try:
shutil.copy(server['log_path'], final_path)
except Exception as e:
logger.warning("Failed to copy file with error: {}".format(e))
#Copy crafty logs to archive dir
full_log_name = os.path.join(crafty_path, 'logs')
shutil.copytree(os.path.join(self.project_root, 'logs'), full_log_name)
@ -195,7 +200,7 @@ class Controller:
def get_server_data(self, server_id: str):
for s in self.servers_list:
if s['server_id'] == server_id:
if str(s['server_id']) == str(server_id):
return s['server_data_obj']
logger.warning("Unable to find server object for server id {}".format(server_id))
@ -281,7 +286,12 @@ class Controller:
logger.error("Unable to create required server files due to :{}".format(e))
return False
server_command = 'java -Xms{}M -Xmx{}M -jar "{}" nogui'.format(helper.float_to_string(min_mem),
if helper.is_os_windows():
server_command = 'java -Xms{}M -Xmx{}M -jar "{}" nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
full_jar_path)
else:
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
full_jar_path)
server_log_file = "{}/logs/latest.log".format(server_dir)
@ -315,7 +325,7 @@ class Controller:
new_server_dir = os.path.join(helper.servers_dir, server_id)
backup_path = os.path.join(helper.backup_path, server_id)
if helper.is_os_windows():
server_dir = helper.wtol_path(new_server_dir)
new_server_dir = helper.wtol_path(new_server_dir)
backup_path = helper.wtol_path(backup_path)
new_server_dir.replace(' ', '^ ')
backup_path.replace(' ', '^ ')
@ -337,9 +347,14 @@ class Controller:
full_jar_path = os.path.join(new_server_dir, server_jar)
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
if helper.is_os_windows():
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
+'"'+full_jar_path+'"')
'"'+full_jar_path+'"')
else:
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
full_jar_path)
server_log_file = "{}/logs/latest.log".format(new_server_dir)
server_stop = "stop"
@ -377,9 +392,14 @@ class Controller:
full_jar_path = os.path.join(new_server_dir, server_jar)
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
if helper.is_os_windows():
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
+'"'+full_jar_path+'"')
'"'+full_jar_path+'"')
else:
server_command = 'java -Xms{}M -Xmx{}M -jar {} nogui'.format(helper.float_to_string(min_mem),
helper.float_to_string(max_mem),
full_jar_path)
logger.debug('command: ' + server_command)
server_log_file = "{}/logs/latest.log".format(new_server_dir)
server_stop = "stop"
@ -430,7 +450,7 @@ class Controller:
for s in self.servers_list:
# if this is the droid... im mean server we are looking for...
if s['server_id'] == server_id:
if str(s['server_id']) == str(server_id):
server_data = self.get_server_data(server_id)
server_name = server_data['server_name']
backup_dir = self.servers.get_server_data_by_id(server_id)['backup_path']

View File

@ -25,6 +25,7 @@ from app.classes.models.management import management_helper
from app.classes.web.websocket_helper import websocket_helper
from app.classes.shared.translation import translation
from app.classes.models.users import users_helper
from app.classes.models.server_permissions import server_permissions
logger = logging.getLogger(__name__)
@ -383,11 +384,10 @@ class Server:
return False
def send_command(self, command):
console.info("COMMAND TIME: {}".format(command))
if not self.check_running() and command.lower() != 'start':
logger.warning("Server not running, unable to send command \"{}\"".format(command))
return False
console.info("COMMAND TIME: {}".format(command))
logger.debug("Sending command {} to server".format(command))
# send it
@ -477,7 +477,7 @@ class Server:
def remove_watcher_thread(self):
logger.info("Removing old crash detection watcher thread")
console.info("Removing old crash detection watcher thread")
schedule.clear(self.name)
self.crash_watcher_schedule.remove(self.server_name)
def agree_eula(self, user_id):
file = os.path.join(self.server_path, 'eula.txt')
@ -610,7 +610,9 @@ class Server:
if len(websocket_helper.clients) > 0:
# There are clients
self.check_update()
websocket_helper.broadcast('notification', "Executable update finished for " + self.name)
server_users = server_permissions.get_server_user_list(self.server_id)
for user in server_users:
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for " + self.name)
time.sleep(3)
websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', {
'isUpdating': self.check_update(),
@ -619,15 +621,18 @@ class Server:
})
websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', {
})
websocket_helper.broadcast('notification', "Executable update finished for "+self.name)
server_users = server_permissions.get_server_user_list(self.server_id)
for user in server_users:
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for "+self.name)
management_helper.add_to_audit_log_raw('Alert', '-1', self.server_id, "Executable update finished for "+self.name, self.settings['server_ip'])
if wasStarted:
self.start_server()
elif not downloaded and not self.is_backingup:
time.sleep(5)
servers_helper.set_update(self.server_id, False)
websocket_helper.broadcast('notification',
server_users = server_permissions.get_server_user_list(self.server_id)
for user in server_users:
websocket_helper.broadcast_user(user,'notification',
"Executable update failed for " + self.name + ". Check log file for details.")
logger.error("Executable download failed.")
pass

View File

@ -26,6 +26,7 @@ logger = logging.getLogger('apscheduler')
try:
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e.name), exc_info=True)
@ -52,8 +53,8 @@ class TasksManager:
self.controller = controller
self.tornado = Webserver(controller, self)
tz = get_localzone()
self.scheduler = BackgroundScheduler(timezone=str(tz))
self.tz = get_localzone()
self.scheduler = BackgroundScheduler(timezone=str(self.tz))
self.users_controller = Users_Controller()
@ -86,10 +87,14 @@ class TasksManager:
# select any commands waiting to be processed
commands = management_helper.get_unactioned_commands()
for c in commands:
svr = self.controller.get_server_obj(c['server_id']['server_id'])
user_id = c.get('user')['user_id']
command = c.get('command', None)
try:
svr = self.controller.get_server_obj(c.server_id)
except:
logger.error("Server value requested does note exist purging item from waiting commands.")
management_helper.mark_command_complete(c.command_id)
user_id = c.user_id
command = c.command
if command == 'start_server':
svr.run_threaded_server(user_id)
@ -107,7 +112,7 @@ class TasksManager:
svr.jar_update()
else:
svr.send_command(command)
management_helper.mark_command_complete(c.get('command_id', None))
management_helper.mark_command_complete(c.command_id)
time.sleep(1)
@ -154,11 +159,19 @@ class TasksManager:
schedules = management_helper.get_schedules_enabled()
self.scheduler.add_listener(self.schedule_watcher, mask=EVENT_JOB_EXECUTED)
#self.scheduler.add_job(self.scheduler.print_jobs, 'interval', seconds=10, id='-1')
#load schedules from DB
for schedule in schedules:
if schedule.cron_string != "":
cron = schedule.cron_string.split(' ')
self.scheduler.add_job(management_helper.add_command, 'cron', minute = cron[0], hour = cron[1], day = cron[2], month = cron[3], day_of_week = cron[4], id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
try:
self.scheduler.add_job(management_helper.add_command, CronTrigger.from_crontab(schedule.cron_string, timezone=str(self.tz)), id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
except Exception as e:
console.error("Failed to schedule task with error: {}.".format(e))
console.warning("Removing failed task from DB.")
logger.error("Failed to schedule task with error: {}.".format(e))
logger.warning("Removing failed task from DB.")
#remove items from DB if task fails to add to apscheduler
management_helper.delete_scheduled_task(schedule.schedule_id)
else:
if schedule.interval_type == 'hours':
self.scheduler.add_job(management_helper.add_command, 'cron', minute = 0, hour = '*/'+str(schedule.interval), id=str(schedule.schedule_id), args=[schedule.server_id, self.users_controller.get_id_by_name('system'), '127.0.0.1', schedule.command])
@ -178,12 +191,14 @@ class TasksManager:
sch_id = management_helper.create_scheduled_task(job_data['server_id'], job_data['action'], job_data['interval'], job_data['interval_type'], job_data['start_time'], job_data['command'], "None", job_data['enabled'], job_data['one_time'], job_data['cron_string'])
if job_data['enabled']:
if job_data['cron_string'] != "":
cron = job_data['cron_string'].split(' ')
try:
self.scheduler.add_job(management_helper.add_command, 'cron', minute = cron[0], hour = cron[1], day = cron[2], month = cron[3], day_of_week = cron[4], id=str(sch_id), args=[job_data['server_id'], self.users_controller.get_id_by_name('system'), '127.0.0.1', job_data['command']])
self.scheduler.add_job(management_helper.add_command, CronTrigger.from_crontab(job_data['cron_string'], timezone=str(self.tz)), id=str(sch_id), args=[job_data['server_id'], self.users_controller.get_id_by_name('system'), '127.0.0.1', job_data['command']])
except Exception as e:
console.error("Failed to schedule task with error: {}.".format(e))
console.info("Removing failed task from DB.")
console.warning("Removing failed task from DB.")
logger.error("Failed to schedule task with error: {}.".format(e))
logger.warning("Removing failed task from DB.")
#remove items from DB if task fails to add to apscheduler
management_helper.delete_scheduled_task(sch_id)
else:
if job_data['interval_type'] == 'hours':

View File

@ -199,6 +199,14 @@ class AjaxHandler(BaseHandler):
srv_obj = self.controller.get_server_obj(server_id)
if command == srv_obj.settings['stop_command']:
logger.info("Stop command detected as terminal input - intercepting. Starting Crafty's stop process for server with id: {}.".format(server_id))
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'stop_server')
command = None
elif command == 'restart':
logger.info("Restart command detected as terminal input - intercepting. Starting Crafty's stop process for server with id: {}.".format(server_id))
self.controller.management.send_command(exec_user['user_id'], server_id, self.get_remote_ip(), 'restart_server')
command = None
if command:
if srv_obj.check_running():
srv_obj.send_command(command)
@ -419,7 +427,7 @@ class AjaxHandler(BaseHandler):
server_data = self.controller.get_server_data(server_id)
server_name = server_data['server_name']
self.controller.management.add_to_audit_log(exec_user_id,
self.controller.management.add_to_audit_log(exec_user['user_id'],
"Deleted server {} named {}".format(server_id, server_name),
server_id,
self.get_remote_ip())
@ -439,7 +447,7 @@ class AjaxHandler(BaseHandler):
server_data = self.controller.get_server_data(server_id)
server_name = server_data['server_name']
self.controller.management.add_to_audit_log(exec_user_id,
self.controller.management.add_to_audit_log(exec_user['user_id'],
"Deleted server {} named {}".format(server_id, server_name),
server_id,
self.get_remote_ip())

View File

@ -17,6 +17,8 @@ import threading
from cron_validator import CronValidator
#TZLocal is set as a hidden import on win pipeline
from tzlocal import get_localzone
import libgravatar
import requests
from tornado import locale, iostream
from tornado.ioloop import IOLoop
@ -128,7 +130,7 @@ class PanelHandler(BaseHandler):
# increasing and will eat up the RAM
del chunk
def check_server_id(self) -> Optional[str]:
def check_server_id(self):
server_id = self.get_argument('id', None)
api_key, _, exec_user = self.current_user
@ -244,6 +246,24 @@ class PanelHandler(BaseHandler):
} if api_key is not None else None,
'superuser': superuser
}
if helper.get_setting("allow_nsfw_profile_pictures"):
rating = "x"
else:
rating = "g"
#Get grvatar hash for profile pictures
if exec_user['email'] != 'default@example.com' or "":
g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user['email']))
url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404"
if requests.head(url).status_code != 404:
profile_url = url
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
page_data['user_image'] = profile_url
if page == 'unauthorized':
template = "panel/denied.html"
@ -539,7 +559,6 @@ class PanelHandler(BaseHandler):
'Players': Enum_Permissions_Server.Players,
}
page_data['user_permissions'] = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id)
exec_user_server_permissions = self.controller.server_perms.get_user_permissions_list(exec_user["user_id"], server_id)
page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id)
page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id)
page_data['new_schedule'] = True
@ -557,7 +576,7 @@ class PanelHandler(BaseHandler):
page_data['schedule']['difficulty'] = "basic"
page_data['schedule']['interval_type'] = 'days'
if not Enum_Permissions_Server.Schedule in exec_user_server_permissions:
if not Enum_Permissions_Server.Schedule in page_data['user_permissions']:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access To Scheduled Tasks")
return
@ -581,7 +600,6 @@ class PanelHandler(BaseHandler):
'Players': Enum_Permissions_Server.Players,
}
page_data['user_permissions'] = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id)
exec_user_server_permissions = self.controller.server_perms.get_user_permissions_list(exec_user["user_id"], server_id)
page_data['server_data'] = self.controller.servers.get_server_data_by_id(server_id)
page_data['server_stats'] = self.controller.servers.get_server_stats_by_id(server_id)
page_data['new_schedule'] = False
@ -609,7 +627,7 @@ class PanelHandler(BaseHandler):
if sch_id == None or server_id == None:
self.redirect("/panel/error?error=Invalid server ID or Schedule ID")
if not Enum_Permissions_Server.Schedule in exec_user_server_permissions:
if not Enum_Permissions_Server.Schedule in page_data['user_permissions']:
if not superuser:
self.redirect("/panel/error?error=Unauthorized access To Scheduled Tasks")
return
@ -864,7 +882,6 @@ class PanelHandler(BaseHandler):
'Config': Enum_Permissions_Server.Config,
'Players': Enum_Permissions_Server.Players,
}
user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id)
exec_user_role = set()
if superuser:
# defined_servers = self.controller.list_defined_servers()
@ -878,7 +895,7 @@ class PanelHandler(BaseHandler):
exec_user_role.add(role['role_name'])
if page == 'server_detail':
if not permissions['Config'] in user_perms:
if not permissions['Config'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access to Config")
return
@ -886,7 +903,13 @@ class PanelHandler(BaseHandler):
server_obj = self.controller.servers.get_server_obj(server_id)
if superuser:
server_path = self.get_argument('server_path', None)
if helper.is_os_windows():
server_path.replace(' ', '^ ')
server_path = helper.wtol_path(server_path)
log_path = self.get_argument('log_path', None)
if helper.is_os_windows():
log_path.replace(' ', '^ ')
log_path = helper.wtol_path(log_path)
executable = self.get_argument('executable', None)
execution_command = self.get_argument('execution_command', None)
server_ip = self.get_argument('server_ip', None)
@ -955,11 +978,14 @@ class PanelHandler(BaseHandler):
server_obj = self.controller.servers.get_server_obj(server_id)
if superuser:
backup_path = bleach.clean(self.get_argument('backup_path', None))
if helper.is_os_windows():
backup_path.replace(' ', '^ ')
backup_path = helper.wtol_path(backup_path)
else:
backup_path = server_obj.backup_path
max_backups = bleach.clean(self.get_argument('max_backups', None))
if not permissions['Backup'] in user_perms:
if not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id):
if not superuser:
self.redirect("/panel/error?error=Unauthorized access: User not authorized")
return
@ -1035,7 +1061,7 @@ class PanelHandler(BaseHandler):
else:
one_time = False
if not superuser and not permissions['Backup'] in user_perms:
if not superuser and not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id):
self.redirect("/panel/error?error=Unauthorized access: User not authorized")
return
elif server_id is None:
@ -1155,7 +1181,7 @@ class PanelHandler(BaseHandler):
else:
one_time = False
if not superuser and not permissions['Backup'] in user_perms:
if not superuser and not permissions['Backup'] in self.controller.server_perms.get_user_id_permissions_list(exec_user["user_id"], server_id):
self.redirect("/panel/error?error=Unauthorized access: User not authorized")
return
elif server_id is None:

View File

@ -120,23 +120,6 @@ class PublicHandler(BaseHandler):
# log this login
self.controller.management.add_to_audit_log(user_data.user_id, "Logged in", 0, self.get_remote_ip())
if helper.get_setting("allow_nsfw_profile_pictures"):
rating = "x"
else:
rating = "g"
#Get grvatar hash for profile pictures
if user_data.email != 'default@example.com' or "":
g = libgravatar.Gravatar(libgravatar.sanitize_email(user_data.email))
url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404"
if requests.head(url).status_code != 404:
profile_url = url
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
next_page = "/panel/dashboard"
self.redirect(next_page)
else:

View File

@ -9,6 +9,8 @@ from app.classes.web.base_handler import BaseHandler
from app.classes.models.crafty_permissions import Enum_Permissions_Crafty
from app.classes.minecraft.serverjars import server_jar_obj
from app.classes.shared.helpers import helper
import libgravatar
import requests
logger = logging.getLogger(__name__)
@ -81,6 +83,23 @@ class ServerHandler(BaseHandler):
} if api_key is not None else None,
'superuser': superuser
}
if helper.get_setting("allow_nsfw_profile_pictures"):
rating = "x"
else:
rating = "g"
if exec_user['email'] != 'default@example.com' or "":
g = libgravatar.Gravatar(libgravatar.sanitize_email(exec_user['email']))
url = g.get_image(size=80, default="404", force_default=False, rating=rating, filetype_extension=False, use_ssl=True) # + "?d=404"
if requests.head(url).status_code != 404:
profile_url = url
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
else:
profile_url = "/static/assets/images/faces-clipart/pic-3.png"
page_data['user_image'] = profile_url
if superuser:
page_data['roles'] = list_roles

View File

@ -18,10 +18,10 @@
<li class="nav-item dropdown user-dropdown">
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_data'].get('profile_url') }}" alt="Profile image"> </a>
<img class="img-xs rounded-circle profile-picture" src="{{ data['user_image'] }}" alt="Profile image"> </a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
<div class="dropdown-header text-center">
<img class="img-md rounded-circle profile-picture" src="{{ data['user_data'].get('profile_url') }}" alt="Profile image">
<img class="img-md rounded-circle profile-picture" src="{{ data['user_image'] }}" alt="Profile image">
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
<p class="font-weight-light text-muted mb-0">Roles: </p>
{% for r in data['user_role'] %}

View File

@ -3,7 +3,7 @@
{% block meta %}
{% end %}
{% block title %}Crafty Controller - Panel Config{% end %}
{% block title %}Crafty Controller - {{ translate('panelConfig', 'pageTitle', data['lang']) }}{% end %}
{% block content %}
@ -14,7 +14,7 @@
<div class="col-12">
<div class="page-header">
<!-- TODO: Translate the following -->
<h4 class="page-title">Panel Config</h4>
<h4 class="page-title">{{ translate('panelConfig', 'pageTitle', data['lang']) }}</h4>
</div>
</div>
@ -30,11 +30,11 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-users"></i> Users</h4>
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('panelConfig', 'users', data['lang']) }}</h4>
<span class="too_small" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
<!-- TODO: Translate the following -->
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> &nbsp; Add New User</a></div>
<div><a class="nav-link" href="/panel/add_user"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('panelConfig', 'newUser', data['lang']) }}</a></div>
</div>
<div class="card-body">
<div class="table-responsive">
@ -42,11 +42,11 @@
<thead>
<!-- TODO: Translate the following -->
<tr class="rounded">
<th>User</th>
<th>Enabled</th>
<th>Allowed Servers</th>
<th>Assigned Roles</th>
<th>Edit</th>
<th>{{ translate('panelConfig', 'user', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'enabled', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'allowedServers', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'assignedRoles', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'edit', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -93,9 +93,9 @@
<div class="col-md-12 col-lg-12 grid-margin stretch-card">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> Roles</h4>
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('panelConfig', 'roles', data['lang']) }}</h4>
<span class="too_small2" title="{{ translate('dashboard', 'cannotSee', data['lang']) }}", data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}", data-placement="top"></span>
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> &nbsp; Add New Role</a></div>
<div><a class="nav-link" href="/panel/add_role"><i class="fas fa-plus-circle"></i> &nbsp; {{ translate('panelConfig', 'newRole', data['lang']) }}</a></div>
</div>
<div class="card-body">
<div class="table-responsive">
@ -103,10 +103,10 @@
<thead>
<!-- TODO: Translate the following -->
<tr class="rounded">
<th>Role</th>
<th>Allowed Servers</th>
<th>Role Users</th>
<th>Edit</th>
<th>{{ translate('panelConfig', 'role', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'allowedServers', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'roleUsers', data['lang']) }}</th>
<th>{{ translate('panelConfig', 'edit', data['lang']) }}</th>
</tr>
</thead>
<tbody>

View File

@ -3,7 +3,7 @@
{% block meta %}
{% end %}
{% block title %}Crafty Controller - Edit Role{% end %}
{% block title %}Crafty Controller - {{ translate('rolesConfig', 'pageTitle', data['lang']) }}{% end %}
{% block content %}
@ -15,13 +15,13 @@
<div class="page-header">
{% if data['new_role'] %}
<h4 class="page-title">
New Role
{{ translate('rolesConfig', 'pageTitleNew', data['lang']) }}
<br />
<small>RID: N/A</small>
</h4>
{% else %}
<h4 class="page-title">
Edit Role - {{ data['role']['role_name'] }}
{{ translate('rolesConfig', 'pageTitle', data['lang']) }} - {{ data['role']['role_name'] }}
<br />
<small>RID: {{ data['role']['role_id'] }}</small>
</h4>
@ -40,7 +40,7 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link active" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('rolesConfig', 'config', data['lang']) }}</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" href="/panel/edit_role?id={{ data['role']['role_id'] }}&subpage=other" role="tab" aria-selected="false">
@ -60,11 +60,11 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> Role Settings</h4>
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('rolesConfig', 'roleTitle', data['lang']) }}</h4>
</div>
<div class="card-body">
<div class="form-group">
<label for="role_name">Role Name <small class="text-muted ml-1"> - What you wish to call this role</small> </label>
<label for="role_name">{{ translate('rolesConfig', 'roleName', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('rolesConfig', 'roleDesc', data['lang']) }}</small> </label>
<input type="text" class="form-control" name="role_name" id="role_name" value="{{ data['role']['role_name'] }}" placeholder="Role Name" >
</div>
</div>
@ -72,7 +72,7 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-server"></i> Allowed Servers <small class="text-muted ml-1"> - servers this role is allowed to access </small> </h4>
<h4 class="card-title"><i class="fas fa-server"></i> {{ translate('rolesConfig', 'roleServers', data['lang']) }} <small class="text-muted ml-1"> {{ translate('rolesConfig', 'serversDesc', data['lang']) }}</small> </h4>
</div>
<div class="card-body">
<div class="form-group">
@ -80,8 +80,8 @@
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>Server Name</th>
<th>Access?</th>
<th>{{ translate('rolesConfig', 'serverName', data['lang']) }}</th>
<th>{{ translate('rolesConfig', 'serverAccess', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -107,7 +107,7 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> Roles Permissions <small class="text-muted ml-1"> - permissions this role has on this/these servers </small></h4>
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('rolesConfig', 'rolePerms', data['lang']) }}<small class="text-muted ml-1"> - {{ translate('rolesConfig', 'permsServer', data['lang']) }} </small></h4>
</div>
<div class="card-body">
<div class="form-group">
@ -115,8 +115,8 @@
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>Permission Name</th>
<th>Authorized ?</th>
<th>{{ translate('rolesConfig', 'permName', data['lang']) }}</th>
<th>{{ translate('rolesConfig', 'permAccess', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -148,14 +148,14 @@
<div class="col-md-3 col-sm-12">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-users"></i> Users Assigned to Role:</h4>
<h4 class="card-title"><i class="fas fa-users"></i> {{ translate('rolesConfig', 'roleUsers', data['lang']) }}</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>User Name</th>
<th>{{ translate('rolesConfig', 'roleUserName', data['lang']) }}</th>
<th></th>
</tr>
</thead>
@ -182,22 +182,22 @@
<div class="col-md-3 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">Role Config Area</h4>
<p class="card-description"> Here is where you can change the configuration of your role</p>
<h4 class="card-title">{{ translate('rolesConfig', 'roleConfigArea', data['lang']) }}</h4>
<p class="card-description"> {{ translate('rolesConfig', 'configDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
Created: {{ str(data['role']['created']) }}
{{ translate('rolesConfig', 'created', data['lang']) }} {{ str(data['role']['created']) }}
<br />
Last updated: {{ str(data['role']['last_update']) }}
{{ translate('rolesConfig', 'configUpdate', data['lang']) }} {{ str(data['role']['last_update']) }}
<br />
</p>
</blockquote>
<div class="text-center">
{% if data['new_role'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> Delete Role</a><br />
<small>You cannot delete something that does not yet exist</small>
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a><br />
<small>{{ translate('rolesConfig', 'doesNotExist', data['lang']) }}</small>
{% else %}
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i> Delete Role</a>
<a href="/panel/remove_role?id={{ data['role']['role_id'] }}" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i>{{ translate('rolesConfig', 'delRole', data['lang']) }}</a>
{% end %}
</div>
</div>

View File

@ -15,13 +15,13 @@
<div class="page-header">
{% if data['new_user'] %}
<h4 class="page-title">
New User
{{ translate('userConfig', 'pageTitleNew', data['lang']) }}
<br />
<small>UID: N/A</small>
</h4>
{% else %}
<h4 class="page-title">
Edit User - {{ data['user']['user_id'] }}
{{ translate('userConfig', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
<br />
<small>UID: {{ data['user']['user_id'] }}</small>
</h4>
@ -40,16 +40,12 @@
<ul class="nav nav-tabs col-md-12 tab-simple-styled " role="tablist">
<li class="nav-item">
<a class="nav-link active" href="/panel/{{ 'add_user' if data['new_user'] else 'edit_user' }}?id={{ data['user']['user_id'] }}&subpage=config" role="tab" aria-selected="true">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i> {{ translate('userConfig', 'config', data['lang']) }} - {{ data['user']['user_id'] }}</a>
</li>
{% if not data['new_user'] %}
<li class="nav-item">
<a class="nav-link" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}" role="tab" aria-selected="false">
<i class="fas fa-key"></i>API Keys</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel/add_user?id={{ data['user']['user_id'] }}&subpage=other" role="tab" aria-selected="false">
<i class="fas fa-folder-tree"></i>Other</a>
<i class="fas fa-key"></i>{{ translate('userConfig', 'apiKey', data['lang']) }} - {{ data['user']['user_id'] }}</a>
</li>
{% end %}
</ul>
@ -68,27 +64,27 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user"></i> User Settings</h4>
<h4 class="card-title"><i class="fas fa-user"></i> {{ translate('userConfig', 'userSettings', data['lang']) }} - {{ data['user']['user_id'] }}</h4>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="username">User Name <small class="text-muted ml-1"> - What you wish to call this user</small> </label>
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="text" class="form-control" name="username" id="username" value="{{ data['user']['username'] }}" placeholder="User Name" >
</div>
<div class="form-group">
<label class="form-label" for="password0">Password <small class="text-muted ml-1"> - leave blank to don't change</small> </label>
<label class="form-label" for="password0">{{ translate('userConfig', 'password', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="password" class="form-control" name="password0" id="password0" value="" placeholder="Password" >
</div>
<div class="form-group">
<label class="form-label" for="password1">Repeat Password <small class="text-muted ml-1"> - leave blank to don't change</small> </label>
<label class="form-label" for="password1">{{ translate('userConfig', 'repeat', data['lang']) }} - {{ data['user']['user_id'] }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'leaveBlank', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="password" class="form-control" name="password1" id="password1" value="" placeholder="Repeat Password" >
</div>
<div class="form-group">
<label class="form-label" for="email">Gravatar Email <small class="text-muted ml-1"> - for the profile picture. this is not required. crafty will never make use of user emails. User emails are strictly for Gravatar</small> </label>
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang']) }} - {{ data['user']['user_id'] }}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang']) }} - {{ data['user']['user_id'] }}</small> </label>
<input type="email" class="form-control" name="email" id="email" value="{{ data['user']['email'] }}" placeholder="Gravatar Email" >
</div>
<div class="form-group">
<label class="form-label" for="language">User Language:</label>
<label class="form-label" for="language">{{ translate('userConfig', 'userLang', data['lang']) }}</label>
<select class="form-select form-control form-control-lg select-css" id="language" name="language" form="user_form">
{% for lang in data['languages'] %}
<option value="{{lang}}">{{lang}}</option>
@ -100,7 +96,7 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-tag"></i> Roles <small class="text-muted ml-1"> - the roles this user is a member of</small></h4>
<h4 class="card-title"><i class="fas fa-user-tag"></i> {{ translate('userConfig', 'userRoles', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'userRolesDesc', data['lang']) }}</small></h4>
</div>
<div class="card-body">
<div class="form-group">
@ -108,8 +104,8 @@
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>Role Name</th>
<th>Member?</th>
<th>{{ translate('userConfig', 'roleName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'member', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -137,7 +133,7 @@
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-user-lock"></i> Crafty Permissions <small class="text-muted ml-1"> - permissions this user has on Crafty Controller </small></h4>
<h4 class="card-title"><i class="fas fa-user-lock"></i> {{ translate('userConfig', 'craftyPerms', data['lang']) }} <small class="text-muted ml-1"> - {{ translate('userConfig', 'craftyPermDesc', data['lang']) }}permissions this user has on Crafty Controller </small></h4>
</div>
<div class="card-body">
<div class="form-group">
@ -145,9 +141,9 @@
<table class="table table-hover">
<thead>
<tr class="rounded">
<th>Permission Name</th>
<th>Authorized ?</th>
<th>Number of Uses Allowed (-1=No Limit)</th>
<th>{{ translate('userConfig', 'permName', data['lang']) }}</th>
<th>{{ translate('userConfig', 'auth', data['lang']) }}</th>
<th>{{ translate('userConfig', 'uses', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -174,17 +170,17 @@
<div class="form-check-flat">
<label for="enabled" class="form-check-label ml-4 mb-4">
{% if data['user']['enabled'] %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked="" value="1">Enabled
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" checked="" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
{% else %}
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">Enabled
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" value="1">{{ translate('userConfig', 'enabled', data['lang']) }}
{% end %}
</label>
<label for="superuser" class="form-check-label ml-4 mb-4">
{% if data['user']['superuser'] %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" checked="" value="1" {{ data['super-disabled'] }} >Super User
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" checked="" value="1" {{ data['super-disabled'] }} >{{ translate('userConfig', 'super', data['lang']) }}
{% else %}
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" {{ data['super-disabled'] }} value="1" >Super User
<input type="checkbox" onclick="superConfirm()" class="form-check-input" id="superuser" name="superuser" {{ data['super-disabled'] }} value="1" >{{ translate('userConfig', 'super', data['lang']) }}
{% end %}
</label>
@ -198,19 +194,17 @@
<div class="col-md-6 col-sm-12">
<div class="card">
<div class="card-body">
<h4 class="card-title"><i class="fas fa-user-cog"></i> User Config Area</h4>
<p class="card-description"> Here is where you can change the configuration of your user</p>
<h4 class="card-title"><i class="fas fa-user-cog"></i> {{ translate('userConfig', 'configArea', data['lang']) }}</h4>
<p class="card-description"> {{ translate('userConfig', 'configAreaDesc', data['lang']) }}</p>
<blockquote class="blockquote">
<p class="mb-0">
Created: {{ str(data['user']['created']) }}
{{ translate('userConfig', 'created', data['lang']) }} {{ str(data['user']['created']) }}
<br />
Last login: {{ str(data['user']['last_login']) }}
{{ translate('userConfig', 'lastLogin', data['lang']) }} {{ str(data['user']['last_login']) }}
<br />
Last update: {{ str(data['user']['last_update']) }}
{{ translate('userConfig', 'lastUpdate', data['lang']) }} {{ str(data['user']['last_update']) }}
<br />
Last IP: {{ data['user']['last_ip'] }}
<br />
API Key: TODO! <!-- TODO -->
{{ translate('userConfig', 'lastIP', data['lang']) }} {{ data['user']['last_ip'] }}
<br />
</p>
</blockquote>
@ -218,13 +212,13 @@
</div>
<div class="text-center">
{% if data['new_user'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> Delete User</a><br />
<small>You cannot delete something that does not yet exist</small>
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i>{{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'notExist', data['lang']) }}</small>
{% elif data['user']['superuser'] %}
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> Delete User</a><br />
<small>You cannot delete a superuser</small>
<a class="btn btn-sm btn-danger disabled"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a><br />
<small>{{ translate('userConfig', 'delSuper', data['lang']) }}</small>
{% else %}
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> Delete User</a>
<button class="btn btn-sm btn-danger delete-user"><i class="fas fa-trash"></i> {{ translate('userConfig', 'deleteUserB', data['lang']) }}</a>
{% end %}
</div>
@ -251,8 +245,8 @@
console.log("User to delete is {{ data['user']['username'] }}");
bootbox.confirm({
title: "{% raw translate('usersConfig', 'deleteUser', data['lang']) %}"+"{{ data['user']['username'] }}",
message: "{{ translate('usersConfig', 'confirmDelete', data['lang']) }}",
title: "{% raw translate('userConfig', 'deleteUser', data['lang']) %}"+"{{ data['user']['username'] }}",
message: "{{ translate('userConfig', 'confirmDelete', data['lang']) }}",
buttons: {
cancel: {
label: '<i class="fas fa-times"></i> {{ translate("serverBackups", "cancel", data['lang']) }}'

View File

@ -14,7 +14,7 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
Edit User API Keys - {{ data['user']['user_id'] }}
{{ translate('apiKeys', 'pageTitle', data['lang']) }} - {{ data['user']['user_id'] }}
<br/>
<small>UID: {{ data['user']['user_id'] }}</small>
</h4>
@ -34,13 +34,13 @@
<a class="nav-link" href="/panel/edit_user?id={{ data['user']['user_id'] }}&subpage=config"
role="tab"
aria-selected="false">
<i class="fas fa-cogs"></i>Config</a>
<i class="fas fa-cogs"></i>{{ translate('apiKeys', 'config', data['lang']) }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/panel/edit_user_apikeys?id={{ data['user']['user_id'] }}"
role="tab"
aria-selected="true">
<i class="fas fa-key"></i>API Keys</a>
<i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</a>
</li>
</ul>
@ -48,7 +48,7 @@
<div class="col-md-7 col-sm-12">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-key"></i> API Keys</h4>
<h4 class="card-title"><i class="fas fa-key"></i>{{ translate('apiKeys', 'apiKeys', data['lang']) }}</h4>
</div>
<div class="card-body">
<div class="form-group">
@ -57,11 +57,11 @@
<thead>
<tr class="rounded">
<!--<th>ID</th>-->
<th>Name</th>
<th>Created</th>
<th>Superuser</th>
<th>Permissions</th>
<th>Buttons</th>
<th>{{ translate('apiKeys', 'name', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'created', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'superUser', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'perms', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'buttons', data['lang']) }}</th>
</tr>
</thead>
<tbody>
@ -73,16 +73,16 @@
<td>
{% if apikey.superuser %}
<span class="text-success">
<i class="fas fa-check-square"></i> Yes
<i class="fas fa-check-square"></i> {{ translate('apiKeys', 'yes', data['lang']) }}
</span>
{% else %}
<span class="text-danger">
<i class="far fa-times-square"></i> No
<i class="far fa-times-square"></i> {{ translate('apiKeys', 'no', data['lang']) }}
</span>
{% end %}
</td>
<td>Server: {{ apikey.server_permissions }}
Crafty: {{ apikey.crafty_permissions }}</td>
<td>{{ translate('apiKeys', 'server', data['lang']) }} {{ apikey.server_permissions }}
{{ translate('apiKeys', 'crafty', data['lang']) }} {{ apikey.crafty_permissions }}</td>
<td>
<button
class="btn btn-danger delete-api-key"
@ -93,7 +93,7 @@
class="btn btn-outline-primary get-a-token"
data-key-id="{{ apikey.token_id }}"
data-key-name="{{ apikey.name }}"
>Get a token
>{{ translate('apiKeys', 'getToken', data['lang']) }}
</button>
</td>
</tr>
@ -109,7 +109,7 @@
<div class="col-md-5 col-sm-12">
<div class="card">
<div class="card-header header-sm d-flex justify-content-between align-items-center">
<h4 class="card-title"><i class="fas fa-plus"></i> Create new API key</h4>
<h4 class="card-title"><i class="fas fa-plus"></i> {{ translate('apiKeys', 'createNew', data['lang']) }}</h4>
</div>
<div class="card-body">
<form id="user_form" class="forms-sample" method="post"
@ -118,9 +118,8 @@
<input type="hidden" name="id" value="{{ data['user']['user_id'] }}">
<div class="form-group">
<label class="form-label" for="username">Name <small
class="text-muted ml-1"> - What you wish to
call this API key</small> </label>
<label class="form-label" for="username">{{ translate('apiKeys', 'name', data['lang']) }}<small
class="text-muted ml-1"> - {{ translate('apiKeys', 'nameDesc', data['lang']) }}</small> </label>
<input type="text" class="form-control" name="name" id="name"
placeholder="API Key">
</div>
@ -128,8 +127,8 @@
<table class="table table-hover mb-3">
<thead>
<tr class="rounded">
<th>Permission Name</th>
<th>Authorized ?</th>
<th>{{ translate('apiKeys', 'permName', data['lang']) }}</th>
<th>{{ translate('apiKeys', 'auth', data['lang']) }}</th>
</tr>
</thead>
<tbody>

View File

@ -202,9 +202,9 @@ try{
}catch{
console.log("no element named {{ data['schedule']['action'] }}")
}
ifDays();
yesnoCheck();
basicAdvanced();
ifDays();
if("{{ data['schedule']['enabled'] }}" == 'True'){
document.getElementById('enabled').checked = true;
}else{

View File

@ -76,10 +76,6 @@
"clickRoot": "Click here to select Root Dir",
"explainRoot": "Please click the button below to select your server's root dir inside of the archive"
},
"usersConfig":{
"deleteUser": "Delete user: ",
"confirmDelete": "Are you sure you want to delete this user? This action is irreversible."
},
"dashboard": {
"dashboard": "Dashboard",
"memUsage": "Memory Usage",
@ -305,12 +301,85 @@
]
},
"panelConfig": {
"pageTitle": "Panel Config",
"users": "Users",
"roles": "Roles",
"newUser": "Add New User",
"newRole": "Add New Role",
"user": "User",
"enabled": "Enabled",
"allowedServers": "Allowed Servers",
"assignedRoles": "Assigned Roles",
"edit": "Edit",
"role": "Role",
"roleUsers": "Role Users",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"superConfirmTitle": "Enable superuser? Are you sure?",
"superConfirm": "Proceed only if you want this user to have access to EVERYTHING (all user accounts, servers, panel settings, etc.). They can even revoke your superuser rights."
},
"userConfig": {
"pageTitle": "Edit User",
"pageTitleNew": "Create User",
"config": "Config",
"apiKey": "API Keys",
"userSettings": "User Settings",
"userName": "User Name",
"userNameDesc": "What do you want to call this user?",
"password": "Password",
"repeat": "Repeat Password",
"leaveBlank": "To edit user without changing password leave it blank.",
"craftyPermDesc": "Crafty permissions this user has ",
"gravEmail": "Gravatar™ Email",
"gravDesc": "This email is strictly for use with Gravatar™. Crafty will not, under any circumstance make use of this email for anything other than looking up your Gravatar™",
"userLang": "User Language",
"userRoles": "User Roles",
"userRolesDesc": "Roles this user is a member of.",
"roleName": "Role Name",
"member": "Member?",
"craftyPerms": "Crafty Permissons: ",
"permName":"Permission Name",
"auth": "Authorized? ",
"uses": "Number of uses allowed (-1==No Limit)",
"super": "Super User",
"enabled": "Enabled",
"configArea": "User Config Area",
"configAreaDesc": "Here is where you change all of your user settings",
"created": "Created: ",
"lastLogin": "Last Login: ",
"lastUpdate": "Last Update: ",
"lastIP": "Last IP: ",
"deleteUserB": "Delete User",
"notExist": "You cannot delete something that doesn't exist!",
"delSuper": "You cannot delete a super user",
"deleteUser": "Delete user: ",
"confirmDelete": "Are you sure you want to delete this user? This action is irreversible."
},
"rolesConfig": {
"pageTitle": "Edit Role",
"pageTitleNew": "New Role",
"config": "Role Config",
"roleTitle": "Roles Settings",
"roleName": "Role Name: ",
"roleDesc": "What would you like to call this role?",
"roleServers": "Allowed Servers",
"serversDesc": "servers this role is allowed to access",
"serverName": "Server Name",
"serverAccess": "Access?",
"rolePerms": "Role Permissions",
"permsServer": "Permissions this role has for these specified servers",
"permName": "Permission Name",
"permAccess": "Access?",
"roleUsers": "Role Users: ",
"roleUserName": "User Name",
"roleConfigArea": "Role Config Area",
"configDesc": "Here is where you can change the configuration of your role",
"created": "Created: ",
"configUpdate": "Last Updated: ",
"delRole": "Delete Role",
"doesNotExist": "You cannot delete something that does not exist yet"
},
"datatables": {
"i18n": {
"decimal": "",
@ -379,6 +448,23 @@
"doesNotWorkWithoutJavascript": "<strong>Warning: </strong>Crafty doesn't work properly when JavaScript isn't enabled!"
},
"apiKeys": {
"pageTitle": "Edit User API Keys",
"config": "Config",
"apiKeys": "API Keys",
"name": "Name",
"created": "Created",
"perms": "Permissions",
"buttons": "Buttons",
"yes": "Yes",
"no": "No",
"server": "Server: ",
"crafty": "Crafty: ",
"getToken": "Get A Token",
"createNew": "Create new API Token",
"nameDesc": "What would you like to call this API token? ",
"permName": "Permission Name",
"auth":"Authorized? ",
"superUser": "Super User",
"deleteKeyConfirmation": "Do you want to delete this API key? This cannot be undone.",
"deleteKeyConfirmationTitle": "Remove API key ${keyId}?"
}

View File

@ -6,13 +6,20 @@ upstream crafty {
}
server {
listen 80 default_server;
listen 80;
server_name <DOMAIN>;
if ($host !~* ^<SUBDOMAIN>\.<EXAMPLE>\.com$ ) {
return 444;
}
rewrite ^(.*) https://$host$1 permanent;
}
server {
listen 443 ssl;
server_name <DOMAIN>;
if ($host !~* ^<SUBDOMAIN>\.<EXAMPLE>\.com$ ) {
return 444;
}
ssl_certificate <CERIFICATE_LOCATION>;
ssl_certificate_key <KEYFILE_LOCATION>;
location / {