opentakserver 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentakserver/EmailValidator.py +39 -0
- opentakserver/SocketServer.py +99 -0
- opentakserver/__init__.py +3 -0
- opentakserver/app.py +281 -0
- opentakserver/blueprints/__init__.py +0 -0
- opentakserver/blueprints/api.py +1044 -0
- opentakserver/blueprints/config.py +116 -0
- opentakserver/blueprints/marti.py +555 -0
- opentakserver/blueprints/ots_socketio.py +33 -0
- opentakserver/blueprints/scheduled_jobs.py +107 -0
- opentakserver/blueprints/scheduler_api.py +90 -0
- opentakserver/ca_config.py +90 -0
- opentakserver/certificate_authority.py +395 -0
- opentakserver/controllers/__init__.py +0 -0
- opentakserver/controllers/client_controller.py +234 -0
- opentakserver/controllers/cot_controller.py +731 -0
- opentakserver/defaultconfig.py +106 -0
- opentakserver/extensions.py +16 -0
- opentakserver/forms/MediaMTXGlobalConfig.py +77 -0
- opentakserver/forms/MediaMTXPathConfig.py +90 -0
- opentakserver/forms/__init__.py +0 -0
- opentakserver/functions.py +15 -0
- opentakserver/models/Alert.py +48 -0
- opentakserver/models/Base.py +5 -0
- opentakserver/models/CasEvac.py +161 -0
- opentakserver/models/Certificate.py +53 -0
- opentakserver/models/Chatrooms.py +30 -0
- opentakserver/models/ChatroomsUids.py +12 -0
- opentakserver/models/CoT.py +63 -0
- opentakserver/models/Config.py +11 -0
- opentakserver/models/DataPackage.py +57 -0
- opentakserver/models/EUD.py +78 -0
- opentakserver/models/GeoChat.py +38 -0
- opentakserver/models/Icon.py +35 -0
- opentakserver/models/Marker.py +89 -0
- opentakserver/models/Point.py +77 -0
- opentakserver/models/RBLine.py +104 -0
- opentakserver/models/Team.py +36 -0
- opentakserver/models/VideoRecording.py +59 -0
- opentakserver/models/VideoStream.py +115 -0
- opentakserver/models/WebAuthn.py +6 -0
- opentakserver/models/ZMIST.py +30 -0
- opentakserver/models/__init__.py +0 -0
- opentakserver/models/role.py +16 -0
- opentakserver/models/user.py +32 -0
- opentakserver/mumble/Murmur.ice +885 -0
- opentakserver/mumble/__init__.py +0 -0
- opentakserver/mumble/mumble_authenticator.py +105 -0
- opentakserver/mumble/mumble_ice_app.py +180 -0
- opentakserver-1.0.0.dist-info/LICENSE +674 -0
- opentakserver-1.0.0.dist-info/METADATA +113 -0
- opentakserver-1.0.0.dist-info/RECORD +53 -0
- opentakserver-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import tldextract
|
|
2
|
+
from flask_security import MailUtil
|
|
3
|
+
from extensions import logger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EmailValidator(MailUtil):
|
|
7
|
+
|
|
8
|
+
def __init__(self, app):
|
|
9
|
+
super().__init__(app)
|
|
10
|
+
self.app = app
|
|
11
|
+
|
|
12
|
+
def validate(self, email: str) -> str:
|
|
13
|
+
domain_whitelist = self.app.config.get("OTS_EMAIL_DOMAIN_WHITELIST")
|
|
14
|
+
domain_blacklist = self.app.config.get("OTS_EMAIL_DOMAIN_BLACKLIST")
|
|
15
|
+
tld_whitelist = self.app.config.get("OTS_EMAIL_TLD_WHITELIST")
|
|
16
|
+
tld_blacklist = self.app.config.get("OTS_EMAIL_TLD_BLACKLIST")
|
|
17
|
+
|
|
18
|
+
domain = email.split("@")[-1]
|
|
19
|
+
parsed_domain = tldextract.extract(domain)
|
|
20
|
+
logger.debug("Got domain: {}".format(domain))
|
|
21
|
+
|
|
22
|
+
if domain_whitelist and domain not in domain_whitelist:
|
|
23
|
+
logger.error("Domain {} is not whitelisted".format(domain))
|
|
24
|
+
raise ValueError("Domain {} is not whitelisted".format(domain))
|
|
25
|
+
|
|
26
|
+
if domain_blacklist and domain in domain_blacklist:
|
|
27
|
+
logger.error("Domain {} is blacklisted".format(domain))
|
|
28
|
+
raise ValueError("Domain {} is blacklisted".format(domain))
|
|
29
|
+
|
|
30
|
+
if tld_whitelist and parsed_domain.suffix not in tld_whitelist:
|
|
31
|
+
logger.error("TLD {} not whitelisted".format(parsed_domain.suffix))
|
|
32
|
+
raise ValueError("TLD {} not whitelisted".format(parsed_domain.suffix))
|
|
33
|
+
|
|
34
|
+
if tld_blacklist and parsed_domain.suffix in tld_blacklist:
|
|
35
|
+
logger.error("TLD {} is blacklisted".format(parsed_domain.suffix))
|
|
36
|
+
raise ValueError("TLD {} is blacklisted".format(parsed_domain.suffix))
|
|
37
|
+
|
|
38
|
+
logger.info("Looks like {} is good".format(email))
|
|
39
|
+
return super().validate(email)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import socket
|
|
3
|
+
import ssl
|
|
4
|
+
from threading import Thread
|
|
5
|
+
|
|
6
|
+
from opentakserver.controllers.client_controller import ClientController
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SocketServer(Thread):
|
|
10
|
+
def __init__(self, logger, app_context=None, port=8088, ssl_server=False):
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self.logger = logger
|
|
14
|
+
self.port = port
|
|
15
|
+
self.ssl = ssl_server
|
|
16
|
+
self.shutdown = False
|
|
17
|
+
self.daemon = True
|
|
18
|
+
self.socket = None
|
|
19
|
+
self.clients = []
|
|
20
|
+
self.app_context = app_context
|
|
21
|
+
|
|
22
|
+
def run(self):
|
|
23
|
+
if self.ssl:
|
|
24
|
+
self.socket = self.launch_ssl_server()
|
|
25
|
+
elif self.app_context.app.config.get("OTS_ENABLE_TCP_STREAMING_PORT"):
|
|
26
|
+
self.socket = self.launch_tcp_server()
|
|
27
|
+
else:
|
|
28
|
+
self.logger.info("TCP connections are disabled")
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
self.socket.settimeout(1.0)
|
|
32
|
+
|
|
33
|
+
while not self.shutdown:
|
|
34
|
+
try:
|
|
35
|
+
sock, addr = self.socket.accept()
|
|
36
|
+
if self.ssl:
|
|
37
|
+
self.logger.info("New SSL connection from {}".format(addr[0]))
|
|
38
|
+
else:
|
|
39
|
+
self.logger.info("New TCP connection from {}".format(addr[0]))
|
|
40
|
+
|
|
41
|
+
new_thread = ClientController(addr[0], addr[1], sock, self.logger, self.app_context.app, self.ssl)
|
|
42
|
+
new_thread.daemon = True
|
|
43
|
+
new_thread.start()
|
|
44
|
+
self.clients.append(new_thread)
|
|
45
|
+
except KeyboardInterrupt:
|
|
46
|
+
break
|
|
47
|
+
except TimeoutError:
|
|
48
|
+
if self.shutdown:
|
|
49
|
+
self.socket.shutdown(socket.SHUT_RDWR)
|
|
50
|
+
self.socket.close()
|
|
51
|
+
|
|
52
|
+
if self.ssl:
|
|
53
|
+
self.logger.info("SSL server has shut down")
|
|
54
|
+
else:
|
|
55
|
+
self.logger.info("TCP server has shut down")
|
|
56
|
+
|
|
57
|
+
def launch_tcp_server(self):
|
|
58
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
59
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
60
|
+
s.bind(('0.0.0.0', self.port))
|
|
61
|
+
s.listen(1)
|
|
62
|
+
|
|
63
|
+
return s
|
|
64
|
+
|
|
65
|
+
def launch_ssl_server(self):
|
|
66
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
|
|
67
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
68
|
+
|
|
69
|
+
context = self.get_ssl_context()
|
|
70
|
+
|
|
71
|
+
sconn = context.wrap_socket(sock, server_side=True)
|
|
72
|
+
sconn.bind(('0.0.0.0', self.port))
|
|
73
|
+
sconn.listen(0)
|
|
74
|
+
|
|
75
|
+
return sconn
|
|
76
|
+
|
|
77
|
+
def stop(self):
|
|
78
|
+
if self.ssl:
|
|
79
|
+
self.logger.warning("Shutting down SSL server")
|
|
80
|
+
else:
|
|
81
|
+
self.logger.warning("Shutting down TCP server")
|
|
82
|
+
|
|
83
|
+
self.shutdown = True
|
|
84
|
+
for client in self.clients:
|
|
85
|
+
self.logger.debug('Attempting to stop client {}'.format(client.address))
|
|
86
|
+
client.stop()
|
|
87
|
+
|
|
88
|
+
def get_ssl_context(self):
|
|
89
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
90
|
+
|
|
91
|
+
with self.app_context:
|
|
92
|
+
context.load_cert_chain(
|
|
93
|
+
os.path.join(self.app_context.app.config.get("OTS_CA_FOLDER"), "certs", "opentakserver", "opentakserver.pem"),
|
|
94
|
+
os.path.join(self.app_context.app.config.get("OTS_CA_FOLDER"), "certs", "opentakserver", "opentakserver.nopass.key"))
|
|
95
|
+
|
|
96
|
+
context.verify_mode = self.app_context.app.config.get("OTS_SSL_VERIFICATION_MODE")
|
|
97
|
+
context.load_verify_locations(cafile=os.path.join(self.app_context.app.config.get("OTS_CA_FOLDER"), 'ca.pem'))
|
|
98
|
+
|
|
99
|
+
return context
|
opentakserver/app.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import eventlet
|
|
4
|
+
try:
|
|
5
|
+
eventlet.monkey_patch()
|
|
6
|
+
except BaseException as e:
|
|
7
|
+
print("Failed to monkey_patch(): {}".format(e))
|
|
8
|
+
|
|
9
|
+
import platform
|
|
10
|
+
import requests
|
|
11
|
+
from sqlalchemy import insert
|
|
12
|
+
import sqlite3
|
|
13
|
+
from opentakserver.models.Icon import Icon
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
|
17
|
+
|
|
18
|
+
from opentakserver.EmailValidator import EmailValidator
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
|
|
23
|
+
import colorlog
|
|
24
|
+
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
import sqlalchemy
|
|
27
|
+
|
|
28
|
+
import flask_wtf
|
|
29
|
+
|
|
30
|
+
import pika
|
|
31
|
+
from flask import Flask, jsonify
|
|
32
|
+
from flask_cors import CORS
|
|
33
|
+
|
|
34
|
+
from flask_security import Security, SQLAlchemyUserDatastore, hash_password, uia_username_mapper, uia_email_mapper
|
|
35
|
+
from flask_security.models import fsqla_v3 as fsqla
|
|
36
|
+
from flask_security.signals import user_registered
|
|
37
|
+
|
|
38
|
+
from opentakserver.extensions import logger, db, socketio, mail, apscheduler
|
|
39
|
+
from opentakserver.defaultconfig import DefaultConfig
|
|
40
|
+
from opentakserver.models.WebAuthn import WebAuthn
|
|
41
|
+
|
|
42
|
+
from opentakserver.controllers.cot_controller import CoTController
|
|
43
|
+
from opentakserver.certificate_authority import CertificateAuthority
|
|
44
|
+
from opentakserver.SocketServer import SocketServer
|
|
45
|
+
try:
|
|
46
|
+
from opentakserver.mumble.mumble_ice_app import MumbleIceDaemon
|
|
47
|
+
except ModuleNotFoundError:
|
|
48
|
+
print("Mumble auth not supported on this platform")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def init_extensions(app):
|
|
52
|
+
db.init_app(app)
|
|
53
|
+
|
|
54
|
+
# Handle config options that can't be serialized to yaml
|
|
55
|
+
app.config.update({"SCHEDULER_JOBSTORES": {'default': SQLAlchemyJobStore(url=app.config.get("SQLALCHEMY_DATABASE_URI"))}})
|
|
56
|
+
identity_attributes = [{"username": {"mapper": uia_username_mapper, "case_insensitive": False}}]
|
|
57
|
+
|
|
58
|
+
# Don't allow registration unless email is enabled
|
|
59
|
+
if app.config.get("OTS_ENABLE_EMAIL"):
|
|
60
|
+
identity_attributes.append({"email": {"mapper": uia_email_mapper, "case_insensitive": True}})
|
|
61
|
+
app.config.update({
|
|
62
|
+
"SECURITY_REGISTERABLE": True,
|
|
63
|
+
"SECURITY_CONFIRMABLE": True,
|
|
64
|
+
"SECURITY_RECOVERABLE": True,
|
|
65
|
+
"SECURITY_TWO_FACTOR_ENABLED_METHODS": ["authenticator", "email"]
|
|
66
|
+
})
|
|
67
|
+
else:
|
|
68
|
+
app.config.update({
|
|
69
|
+
"SECURITY_REGISTERABLE": False,
|
|
70
|
+
"SECURITY_CONFIRMABLE": False,
|
|
71
|
+
"SECURITY_RECOVERABLE": False,
|
|
72
|
+
"SECURITY_TWO_FACTOR_ENABLED_METHODS": ["authenticator"]
|
|
73
|
+
})
|
|
74
|
+
app.config.update({"SECURITY_USER_IDENTITY_ATTRIBUTES": identity_attributes})
|
|
75
|
+
|
|
76
|
+
ca = CertificateAuthority(logger, app)
|
|
77
|
+
ca.create_ca()
|
|
78
|
+
|
|
79
|
+
cors = CORS(app, resources={r"/api/*": {"origins": "*"}, r"/Marti/*": {"origins": "*"}, r"/*": {"origins": "*"}},
|
|
80
|
+
supports_credentials=True)
|
|
81
|
+
flask_wtf.CSRFProtect(app)
|
|
82
|
+
|
|
83
|
+
socketio.init_app(app, logger=logger)
|
|
84
|
+
|
|
85
|
+
rabbit_connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
|
|
86
|
+
channel = rabbit_connection.channel()
|
|
87
|
+
channel.exchange_declare('cot', durable=True, exchange_type='fanout')
|
|
88
|
+
channel.exchange_declare('dms', durable=True, exchange_type='direct')
|
|
89
|
+
channel.exchange_declare('chatrooms', durable=True, exchange_type='direct')
|
|
90
|
+
channel.queue_declare(queue='cot_controller')
|
|
91
|
+
channel.exchange_declare(exchange='cot_controller', exchange_type='fanout')
|
|
92
|
+
|
|
93
|
+
cot_thread = CoTController(app.app_context(), logger, db, socketio)
|
|
94
|
+
app.cot_thread = cot_thread
|
|
95
|
+
|
|
96
|
+
if not apscheduler.running:
|
|
97
|
+
apscheduler.init_app(app)
|
|
98
|
+
apscheduler.start(paused=True)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
fsqla.FsModels.set_db_info(db)
|
|
102
|
+
except sqlalchemy.exc.InvalidRequestError:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
from opentakserver.models.user import User
|
|
106
|
+
from opentakserver.models.role import Role
|
|
107
|
+
|
|
108
|
+
user_datastore = SQLAlchemyUserDatastore(db, User, Role, WebAuthn)
|
|
109
|
+
app.security = Security(app, user_datastore, mail_util_cls=EmailValidator)
|
|
110
|
+
|
|
111
|
+
mail.init_app(app)
|
|
112
|
+
with app.app_context():
|
|
113
|
+
db.create_all()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def setup_logging(app):
|
|
117
|
+
level = logging.INFO
|
|
118
|
+
if app.config.get("DEBUG"):
|
|
119
|
+
level = logging.DEBUG
|
|
120
|
+
|
|
121
|
+
if sys.stdout.isatty():
|
|
122
|
+
color_log_handler = colorlog.StreamHandler()
|
|
123
|
+
color_log_formatter = colorlog.ColoredFormatter(
|
|
124
|
+
'%(log_color)s[%(asctime)s] - OpenTAKServer[%(process)d] - %(module)s - %(levelname)s - %(message)s', datefmt="%Y-%m-%d %H:%M:%S")
|
|
125
|
+
color_log_handler.setFormatter(color_log_formatter)
|
|
126
|
+
logger.setLevel(level)
|
|
127
|
+
logger.addHandler(color_log_handler)
|
|
128
|
+
logger.info("Added color logger")
|
|
129
|
+
|
|
130
|
+
fh = logging.FileHandler(os.path.join(app.config.get("OTS_DATA_FOLDER"), 'opentakserver.log'))
|
|
131
|
+
fh.setLevel(level)
|
|
132
|
+
fh.setFormatter(logging.Formatter("[%(asctime)s] - OpenTAKServer[%(process)d] - %(module)s - %(levelname)s - %(message)s"))
|
|
133
|
+
logger.addHandler(fh)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def create_app():
|
|
137
|
+
app = Flask(__name__)
|
|
138
|
+
app.config.from_object(DefaultConfig)
|
|
139
|
+
setup_logging(app)
|
|
140
|
+
|
|
141
|
+
# Load config.yml if it exists
|
|
142
|
+
if os.path.exists(os.path.join(app.config.get("OTS_DATA_FOLDER"), "config.yml")):
|
|
143
|
+
app.config.from_file(os.path.join(app.config.get("OTS_DATA_FOLDER"), "config.yml"), load=yaml.safe_load)
|
|
144
|
+
else:
|
|
145
|
+
# First run, created config.yml based on default settings
|
|
146
|
+
logger.info("Creating config.yml")
|
|
147
|
+
with open(os.path.join(app.config.get("OTS_DATA_FOLDER"), "config.yml"), "w") as config:
|
|
148
|
+
conf = {}
|
|
149
|
+
for option in DefaultConfig.__dict__:
|
|
150
|
+
# Fix the sqlite DB path on Windows
|
|
151
|
+
if option == "SQLALCHEMY_DATABASE_URI" and platform.system() == "Windows" and DefaultConfig.__dict__[option].startswith("sqlite"):
|
|
152
|
+
conf[option] = DefaultConfig.__dict__[option].replace("////", "///").replace("\\", "/")
|
|
153
|
+
elif option.isupper():
|
|
154
|
+
conf[option] = DefaultConfig.__dict__[option]
|
|
155
|
+
config.write(yaml.safe_dump(conf))
|
|
156
|
+
|
|
157
|
+
# Try to set the MediaMTX token
|
|
158
|
+
try:
|
|
159
|
+
with open(os.path.join(app.config.get("OTS_DATA_FOLDER"), "mediamtx", "mediamtx.yml"), "r") as mediamtx_config:
|
|
160
|
+
conf = mediamtx_config.read()
|
|
161
|
+
conf = conf.replace("MTX_TOKEN", app.config.get("OTS_MEDIAMTX_TOKEN"))
|
|
162
|
+
with open(os.path.join(app.config.get("OTS_DATA_FOLDER"), "mediamtx", "mediamtx.yml"), "w") as mediamtx_config:
|
|
163
|
+
mediamtx_config.write(conf)
|
|
164
|
+
except BaseException as e:
|
|
165
|
+
logger.error("Failed to set MediaMTX token: {}".format(e))
|
|
166
|
+
|
|
167
|
+
init_extensions(app)
|
|
168
|
+
|
|
169
|
+
from opentakserver.blueprints.marti import marti_blueprint
|
|
170
|
+
app.register_blueprint(marti_blueprint)
|
|
171
|
+
|
|
172
|
+
from opentakserver.blueprints.api import api_blueprint
|
|
173
|
+
app.register_blueprint(api_blueprint)
|
|
174
|
+
|
|
175
|
+
from opentakserver.blueprints.ots_socketio import ots_socketio_blueprint
|
|
176
|
+
app.register_blueprint(ots_socketio_blueprint)
|
|
177
|
+
|
|
178
|
+
from opentakserver.blueprints.scheduled_jobs import scheduler_blueprint
|
|
179
|
+
app.register_blueprint(scheduler_blueprint)
|
|
180
|
+
|
|
181
|
+
from opentakserver.blueprints.scheduler_api import scheduler_api_blueprint
|
|
182
|
+
app.register_blueprint(scheduler_api_blueprint)
|
|
183
|
+
|
|
184
|
+
from opentakserver.blueprints.config import config_blueprint
|
|
185
|
+
app.register_blueprint(config_blueprint)
|
|
186
|
+
|
|
187
|
+
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
|
|
188
|
+
|
|
189
|
+
return app
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
app = create_app()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@app.route("/")
|
|
196
|
+
def home():
|
|
197
|
+
return jsonify([])
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@app.after_request
|
|
201
|
+
def after_request_func(response):
|
|
202
|
+
response.direct_passthrough = False
|
|
203
|
+
return response
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@user_registered.connect_via(app)
|
|
207
|
+
def user_registered_sighandler(app, user, confirmation_token, **kwargs):
|
|
208
|
+
default_role = app.security.datastore.find_or_create_role(
|
|
209
|
+
name="user", permissions={"user-read", "user-write"}
|
|
210
|
+
)
|
|
211
|
+
app.security.datastore.add_role_to_user(user, default_role)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
if __name__ == '__main__':
|
|
215
|
+
with app.app_context():
|
|
216
|
+
logger.debug("Loading DB..")
|
|
217
|
+
db.create_all()
|
|
218
|
+
|
|
219
|
+
# Download the icon sets if they aren't already in the DB
|
|
220
|
+
icons = db.session.query(Icon).count()
|
|
221
|
+
if icons == 0:
|
|
222
|
+
logger.info("Downloading icons...")
|
|
223
|
+
try:
|
|
224
|
+
r = requests.get("https://github.com/brian7704/OpenTAKServer-Installer/raw/master/iconsets.sqlite", stream=True)
|
|
225
|
+
with open(os.path.join(app.config.get("OTS_DATA_FOLDER"), "icons.sqlite"), "wb") as f:
|
|
226
|
+
f.write(r.content)
|
|
227
|
+
|
|
228
|
+
def dict_factory(cursor, row):
|
|
229
|
+
d = {}
|
|
230
|
+
for idx, col in enumerate(cursor.description):
|
|
231
|
+
d[col[0]] = row[idx]
|
|
232
|
+
return d
|
|
233
|
+
|
|
234
|
+
con = sqlite3.connect(os.path.join(app.config.get("OTS_DATA_FOLDER"), "icons.sqlite"))
|
|
235
|
+
con.row_factory = dict_factory
|
|
236
|
+
cur = con.cursor()
|
|
237
|
+
rows = cur.execute("SELECT * FROM icons")
|
|
238
|
+
for row in rows:
|
|
239
|
+
db.session.execute(insert(Icon).values(**row))
|
|
240
|
+
db.session.commit()
|
|
241
|
+
except BaseException as e:
|
|
242
|
+
logger.error("Failed to download icons: {}".format(e))
|
|
243
|
+
|
|
244
|
+
if app.config.get("DEBUG"):
|
|
245
|
+
logger.debug("Starting in debug mode")
|
|
246
|
+
else:
|
|
247
|
+
logger.info("Starting in production mode")
|
|
248
|
+
|
|
249
|
+
app.security.datastore.find_or_create_role(
|
|
250
|
+
name="user", permissions={"user-read", "user-write"}
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
app.security.datastore.find_or_create_role(
|
|
254
|
+
name="administrator", permissions={"administrator"}
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not app.security.datastore.find_user(username="administrator"):
|
|
258
|
+
logger.info("Creating administrator account. The password is 'password'")
|
|
259
|
+
app.security.datastore.create_user(username="administrator",
|
|
260
|
+
password=hash_password("password"), roles=["administrator"])
|
|
261
|
+
db.session.commit()
|
|
262
|
+
|
|
263
|
+
tcp_thread = SocketServer(logger, app.app_context(), app.config.get("OTS_TCP_STREAMING_PORT"))
|
|
264
|
+
tcp_thread.start()
|
|
265
|
+
app.tcp_thread = tcp_thread
|
|
266
|
+
|
|
267
|
+
ssl_thread = SocketServer(logger, app.app_context(), app.config.get("OTS_SSL_STREAMING_PORT"), True)
|
|
268
|
+
ssl_thread.start()
|
|
269
|
+
app.ssl_thread = ssl_thread
|
|
270
|
+
|
|
271
|
+
if app.config.get("OTS_ENABLE_MUMBLE_AUTHENTICATION"):
|
|
272
|
+
logger.info("Starting mumble authentication handler")
|
|
273
|
+
mumble_daemon = MumbleIceDaemon(app, logger)
|
|
274
|
+
mumble_daemon.daemon = True
|
|
275
|
+
mumble_daemon.start()
|
|
276
|
+
else:
|
|
277
|
+
logger.info("Mumble authentication handler disabled")
|
|
278
|
+
|
|
279
|
+
app.start_time = datetime.now()
|
|
280
|
+
|
|
281
|
+
socketio.run(app, host="127.0.0.1", port=app.config.get("OTS_LISTENER_PORT"), debug=False, log_output=False)
|
|
File without changes
|