2024-03-18 22:53:43 +02:00
import threading
import time
2023-09-25 01:19:50 +03:00
import yaml
import logging
from datetime import datetime
2023-09-25 03:53:08 +03:00
import random
import string
2023-12-17 17:06:30 +02:00
import argparse
import uuid
2024-04-19 20:10:31 +03:00
import bcrypt
2023-09-25 01:19:50 +03:00
2024-03-18 16:07:48 +02:00
import k8s
2023-09-25 01:19:50 +03:00
from flask import Flask , render_template , request , url_for , redirect
2023-09-25 23:48:25 +03:00
from flask_cors import CORS
2024-04-19 20:10:31 +03:00
from werkzeug . routing import BaseConverter
2024-03-19 01:11:08 +02:00
from lib import Server , write_config , get_config , args , lock
2023-09-25 01:19:50 +03:00
2024-06-12 12:08:41 +03:00
class DuplicateFilter ( logging . Filter ) :
def filter ( self , record ) :
2024-06-12 12:20:09 +03:00
# add other fields if you need more granular comparison, depends on your app
current_log = ( record . module , record . levelno , record . msg )
if current_log != getattr ( self , " last_log " , None ) :
self . last_log = current_log
2024-06-12 12:08:41 +03:00
return True
return False
2024-03-18 16:07:48 +02:00
2023-12-19 12:35:00 +02:00
logging . getLogger ( " werkzeug " ) . setLevel ( logging . ERROR )
2024-03-16 04:29:49 +02:00
logging . basicConfig (
level = logging . INFO ,
format = " %(asctime)s - %(name)s - %(levelname)s - %(message)s " ,
datefmt = " %d - % m- % Y % H: % M: % S " ,
)
log = logging . getLogger ( " OutFleet " )
file_handler = logging . FileHandler ( " sync.log " )
formatter = logging . Formatter (
" %(asctime)s - %(name)s - %(levelname)s - %(message)s "
)
file_handler . setFormatter ( formatter )
log . addHandler ( file_handler )
2024-06-12 12:20:09 +03:00
duplicate_filter = DuplicateFilter ( )
2024-06-12 12:08:41 +03:00
log . addFilter ( duplicate_filter )
2024-03-16 04:29:49 +02:00
2024-03-18 18:53:38 +02:00
CFG_PATH = args . config
NAMESPACE = k8s . NAMESPACE
2023-09-25 01:19:50 +03:00
SERVERS = list ( )
2024-03-18 16:07:48 +02:00
BROKEN_SERVERS = list ( )
2023-09-25 23:48:25 +03:00
CLIENTS = dict ( )
2024-06-12 12:21:03 +03:00
VERSION = ' 8.1 '
2024-04-19 20:10:31 +03:00
SECRET_LINK_LENGTH = 8
2024-04-23 19:30:14 +03:00
SECRET_LINK_PREFIX = ' $2b$12$ '
SS_PREFIX = " \u0005 \u00DC \u005F \u00E0 \u0001 \u0020 "
2023-12-19 12:35:00 +02:00
HOSTNAME = " "
2024-04-19 20:10:31 +03:00
WRONG_DOOR = " Hey buddy, i think you got the wrong door the leather-club is two blocks down "
2023-09-25 01:19:50 +03:00
app = Flask ( __name__ )
2023-09-25 23:48:25 +03:00
CORS ( app )
2023-09-25 01:19:50 +03:00
2023-12-19 12:35:00 +02:00
2023-09-25 01:19:50 +03:00
def format_timestamp ( ts ) :
2023-12-19 12:35:00 +02:00
return datetime . fromtimestamp ( ts / / 1000 ) . strftime ( " % Y- % m- %d % H: % M: % S " )
2023-09-25 03:53:08 +03:00
2023-09-26 00:55:02 +03:00
def random_string ( length = 64 ) :
2023-09-25 03:53:08 +03:00
letters = string . ascii_letters + string . digits
2023-12-19 12:35:00 +02:00
return " " . join ( random . choice ( letters ) for i in range ( length ) )
2023-09-25 01:19:50 +03:00
2024-03-18 16:07:48 +02:00
2024-03-19 02:47:29 +02:00
def update_state ( timer = 40 ) :
2024-03-18 22:53:43 +02:00
while True :
2024-03-19 01:11:08 +02:00
with lock :
global SERVERS
global CLIENTS
global BROKEN_SERVERS
global HOSTNAME
config = get_config ( )
2023-09-25 01:19:50 +03:00
2024-03-19 01:11:08 +02:00
if config :
HOSTNAME = config . get ( " ui_hostname " , " my-own-SSL-ENABLED-domain.com " )
servers = config . get ( " servers " , dict ( ) )
2024-03-19 02:47:29 +02:00
_SERVERS = list ( )
2024-03-19 01:11:08 +02:00
for local_server_id , server_config in servers . items ( ) :
try :
server = Server (
url = server_config [ " url " ] ,
cert = server_config [ " cert " ] ,
comment = server_config . get ( " comment " , ' ' ) ,
local_server_id = local_server_id ,
)
2024-03-19 02:47:29 +02:00
_SERVERS . append ( server )
log . debug (
2024-03-19 01:11:08 +02:00
" Server state updated: %s , [ %s ] " ,
server . info ( ) [ " name " ] ,
local_server_id ,
)
except Exception as e :
BROKEN_SERVERS . append ( {
" config " : server_config ,
" error " : e ,
" id " : local_server_id
} )
log . warning ( " Can ' t access server: %s - %s " , server_config [ " url " ] , e )
2024-03-19 02:47:29 +02:00
SERVERS = _SERVERS
2024-03-19 01:11:08 +02:00
CLIENTS = config . get ( " clients " , dict ( ) )
2024-03-19 02:47:29 +02:00
if timer == 0 :
break
2024-03-18 22:53:43 +02:00
time . sleep ( 40 )
2023-09-25 03:53:08 +03:00
2023-09-25 01:19:50 +03:00
2023-12-19 12:35:00 +02:00
@app.route ( " / " , methods = [ " GET " , " POST " ] )
2023-09-25 01:19:50 +03:00
def index ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " GET " :
2024-03-18 16:07:48 +02:00
#if request.args.get("broken") == True:
2023-09-25 01:19:50 +03:00
return render_template (
2023-12-19 12:35:00 +02:00
" index.html " ,
2023-09-25 01:19:50 +03:00
SERVERS = SERVERS ,
2024-02-27 20:41:11 +02:00
VERSION = VERSION ,
2024-03-18 22:08:43 +02:00
K8S_NAMESPACE = k8s . NAMESPACE ,
2024-03-18 16:07:48 +02:00
BROKEN_SERVERS = BROKEN_SERVERS ,
2023-12-19 12:35:00 +02:00
nt = request . args . get ( " nt " ) ,
nl = request . args . get ( " nl " ) ,
selected_server = request . args . get ( " selected_server " ) ,
2024-03-18 16:07:48 +02:00
broken = request . args . get ( " broken " , False ) ,
2023-12-19 12:35:00 +02:00
add_server = request . args . get ( " add_server " , None ) ,
2023-09-25 03:53:08 +03:00
format_timestamp = format_timestamp ,
)
2023-12-19 12:35:00 +02:00
elif request . method == " POST " :
server = request . form [ " server_id " ]
server = next (
2024-02-18 15:52:26 +02:00
( item for item in SERVERS if item . info ( ) [ " local_server_id " ] == server ) , None
)
2024-03-19 02:47:29 +02:00
server . apply_config ( request . form )
update_state ( timer = 0 )
2023-09-25 03:53:08 +03:00
return redirect (
2023-12-19 12:35:00 +02:00
url_for (
" index " ,
nt = " Updated Outline VPN Server " ,
selected_server = request . args . get ( " selected_server " ) ,
)
)
2023-11-05 19:32:50 +02:00
else :
2023-12-19 12:35:00 +02:00
return redirect ( url_for ( " index " ) )
2023-09-25 01:19:50 +03:00
2023-12-19 12:35:00 +02:00
@app.route ( " /clients " , methods = [ " GET " , " POST " ] )
2023-09-25 03:53:08 +03:00
def clients ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " GET " :
2023-09-25 03:53:08 +03:00
return render_template (
2023-12-19 12:35:00 +02:00
" clients.html " ,
2023-09-25 03:53:08 +03:00
SERVERS = SERVERS ,
2024-04-23 19:30:14 +03:00
bcrypt = bcrypt ,
2023-09-25 03:53:08 +03:00
CLIENTS = CLIENTS ,
2024-03-16 00:22:05 +02:00
VERSION = VERSION ,
2024-04-23 19:30:14 +03:00
SECRET_LINK_LENGTH = SECRET_LINK_LENGTH ,
SECRET_LINK_PREFIX = SECRET_LINK_PREFIX ,
2024-03-18 22:08:43 +02:00
K8S_NAMESPACE = k8s . NAMESPACE ,
2023-12-19 12:35:00 +02:00
nt = request . args . get ( " nt " ) ,
nl = request . args . get ( " nl " ) ,
selected_client = request . args . get ( " selected_client " ) ,
add_client = request . args . get ( " add_client " , None ) ,
2023-09-25 23:48:25 +03:00
format_timestamp = format_timestamp ,
dynamic_hostname = HOSTNAME ,
)
2023-09-25 03:53:08 +03:00
2023-12-19 12:35:00 +02:00
@app.route ( " /add_server " , methods = [ " POST " ] )
2023-09-25 03:53:08 +03:00
def add_server ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " POST " :
2023-09-26 01:20:30 +03:00
try :
2024-03-18 18:53:38 +02:00
config = get_config ( )
2023-12-19 12:35:00 +02:00
servers = config . get ( " servers " , dict ( ) )
2024-02-17 17:40:27 +02:00
local_server_id = str ( uuid . uuid4 ( ) )
2023-09-25 01:19:50 +03:00
2023-12-19 12:35:00 +02:00
new_server = Server (
url = request . form [ " url " ] ,
cert = request . form [ " cert " ] ,
comment = request . form [ " comment " ] ,
local_server_id = local_server_id ,
)
2023-09-26 01:20:30 +03:00
2023-12-17 17:06:30 +02:00
servers [ new_server . data [ " local_server_id " ] ] = {
2023-12-19 12:35:00 +02:00
" name " : new_server . data [ " name " ] ,
" url " : new_server . data [ " url " ] ,
" comment " : new_server . data [ " comment " ] ,
" cert " : request . form [ " cert " ] ,
2023-09-26 01:20:30 +03:00
}
config [ " servers " ] = servers
2024-03-18 18:53:38 +02:00
write_config ( config )
2023-09-27 22:42:32 +03:00
log . info ( " Added server: %s " , new_server . data [ " name " ] )
2024-03-19 02:47:29 +02:00
update_state ( timer = 0 )
2023-12-19 12:35:00 +02:00
return redirect ( url_for ( " index " , nt = " Added Outline VPN Server " ) )
2023-09-26 01:20:30 +03:00
except Exception as e :
2023-12-19 12:35:00 +02:00
return redirect (
url_for (
" index " , nt = f " Couldn ' t access Outline VPN Server: { e } " , nl = " error "
)
)
2023-09-25 01:19:50 +03:00
2024-04-19 20:10:31 +03:00
2024-03-15 20:15:43 +02:00
@app.route ( " /del_server " , methods = [ " POST " ] )
def del_server ( ) :
if request . method == " POST " :
2024-03-18 18:53:38 +02:00
config = get_config ( )
2024-03-15 20:15:43 +02:00
local_server_id = request . form . get ( " local_server_id " )
2024-03-16 00:22:05 +02:00
server_name = None
2024-03-15 20:15:43 +02:00
try :
2024-03-16 00:22:05 +02:00
server_name = config [ " servers " ] . pop ( local_server_id ) [ " name " ]
2024-03-15 20:15:43 +02:00
except KeyError as e :
pass
for client_id , client_config in config [ " clients " ] . items ( ) :
try :
client_config [ " servers " ] . remove ( local_server_id )
except ValueError as e :
pass
2024-03-18 18:53:38 +02:00
write_config ( config )
2024-03-16 00:22:05 +02:00
log . info ( " Deleting server %s [ %s ] " , server_name , request . form . get ( " local_server_id " ) )
2024-03-19 02:47:29 +02:00
update_state ( timer = 0 )
2024-03-16 00:22:05 +02:00
return redirect ( url_for ( " index " , nt = f " Server { server_name } has been deleted " ) )
2024-03-15 20:15:43 +02:00
2023-09-25 01:19:50 +03:00
2023-12-19 12:35:00 +02:00
@app.route ( " /add_client " , methods = [ " POST " ] )
2023-09-25 03:53:08 +03:00
def add_client ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " POST " :
2024-03-18 18:53:38 +02:00
config = get_config ( )
2023-09-25 03:53:08 +03:00
2023-12-19 12:35:00 +02:00
clients = config . get ( " clients " , dict ( ) )
user_id = request . form . get ( " user_id " , random_string ( ) )
2023-09-25 03:53:08 +03:00
clients [ user_id ] = {
2023-12-19 12:35:00 +02:00
" name " : request . form . get ( " name " ) ,
" comment " : request . form . get ( " comment " ) ,
" servers " : request . form . getlist ( " servers " ) ,
2023-09-25 03:53:08 +03:00
}
config [ " clients " ] = clients
2024-03-18 18:53:38 +02:00
write_config ( config )
2023-12-19 12:35:00 +02:00
log . info ( " Client %s updated " , request . form . get ( " name " ) )
2023-09-25 03:53:08 +03:00
for server in SERVERS :
2023-12-19 12:35:00 +02:00
if server . data [ " local_server_id " ] in request . form . getlist ( " servers " ) :
client = next (
(
item
for item in server . data [ " keys " ]
if item . name == request . form . get ( " old_name " )
) ,
None ,
)
2023-09-25 20:22:44 +03:00
if client :
2023-12-19 12:35:00 +02:00
if client . name == request . form . get ( " name " ) :
2023-09-25 20:22:44 +03:00
pass
else :
2023-12-19 12:35:00 +02:00
server . rename_key ( client . key_id , request . form . get ( " name " ) )
log . info (
" Renaming key %s to %s on server %s " ,
request . form . get ( " old_name " ) ,
request . form . get ( " name " ) ,
server . data [ " name " ] ,
)
2023-09-25 20:22:44 +03:00
else :
2023-12-19 12:35:00 +02:00
server . create_key ( request . form . get ( " name " ) )
log . info (
" Creating key %s on server %s " ,
request . form . get ( " name " ) ,
server . data [ " name " ] ,
)
2023-09-25 20:22:44 +03:00
else :
2023-12-19 12:35:00 +02:00
client = next (
(
item
for item in server . data [ " keys " ]
if item . name == request . form . get ( " old_name " )
) ,
None ,
)
2023-09-25 20:22:44 +03:00
if client :
server . delete_key ( client . key_id )
2023-12-19 12:35:00 +02:00
log . info (
" Deleting key %s on server %s " ,
request . form . get ( " name " ) ,
server . data [ " name " ] ,
)
2024-03-19 02:47:29 +02:00
update_state ( timer = 0 )
2023-12-19 12:35:00 +02:00
return redirect (
url_for (
" clients " ,
nt = " Clients updated " ,
selected_client = request . form . get ( " user_id " ) ,
)
)
2023-09-27 22:42:32 +03:00
else :
2023-12-19 12:35:00 +02:00
return redirect ( url_for ( " clients " ) )
2023-09-25 20:22:44 +03:00
2023-12-19 12:35:00 +02:00
@app.route ( " /del_client " , methods = [ " POST " ] )
2023-09-25 20:22:44 +03:00
def del_client ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " POST " :
2024-03-18 18:53:38 +02:00
config = get_config ( )
2023-12-19 12:35:00 +02:00
clients = config . get ( " clients " , dict ( ) )
user_id = request . form . get ( " user_id " )
2023-09-25 20:22:44 +03:00
if user_id in clients :
for server in SERVERS :
2023-12-19 12:35:00 +02:00
client = next (
(
item
for item in server . data [ " keys " ]
if item . name == request . form . get ( " name " )
) ,
None ,
)
2023-09-25 20:22:44 +03:00
if client :
server . delete_key ( client . key_id )
config [ " clients " ] . pop ( user_id )
2024-03-18 18:53:38 +02:00
write_config ( config )
2023-12-19 12:35:00 +02:00
log . info ( " Deleting client %s " , request . form . get ( " name " ) )
2024-03-19 02:47:29 +02:00
update_state ( timer = 0 )
2023-12-19 12:35:00 +02:00
return redirect ( url_for ( " clients " , nt = " User has been deleted " ) )
2023-09-25 03:53:08 +03:00
2024-10-26 10:45:33 +03:00
def append_to_log ( log_entry ) :
with open ( " access_log.log " , " a " ) as log_file :
log_file . write ( log_entry + " \n " )
2024-04-23 19:30:14 +03:00
@app.route ( " /dynamic/<path:hash_secret> " , methods = [ " GET " ] , strict_slashes = False )
2024-04-19 20:10:31 +03:00
def dynamic ( hash_secret ) :
2024-04-23 19:30:14 +03:00
# Depricated scheme.
for server in SERVERS :
if hash_secret . startswith ( server . data [ " name " ] ) :
log . warning ( " Deprecated key request " )
server_name = hash_secret . split ( ' / ' ) [ 0 ]
client_id = hash_secret . split ( ' / ' ) [ 1 ]
return dynamic_depticated ( server_name , client_id )
2024-04-19 20:10:31 +03:00
try :
short_hash_server = hash_secret [ 0 : SECRET_LINK_LENGTH ]
short_hash_client = hash_secret [ SECRET_LINK_LENGTH : SECRET_LINK_LENGTH * 2 ]
client_provided_secret = hash_secret [ SECRET_LINK_LENGTH * 2 : ]
hash_server = None
hash_client = None
server = None
client = None
for _server in SERVERS :
if _server . data [ " local_server_id " ] [ : SECRET_LINK_LENGTH ] == short_hash_server :
hash_server = _server . data [ " local_server_id " ]
server = _server
for client_id , values in CLIENTS . items ( ) :
if client_id [ : SECRET_LINK_LENGTH ] == short_hash_client :
hash_client = client_id
client = CLIENTS [ client_id ]
if server and client :
2024-10-26 10:45:33 +03:00
append_to_log ( f " User: { client [ " name " ] } . Server: { server . data [ ' name ' ] } client secret string: { hash_secret } " )
2024-04-19 20:10:31 +03:00
client_shadowsocks_key = next (
( item for item in server . data [ " keys " ] if item . key_id == client [ " name " ] ) , None
)
secret_string = hash_server + hash_client
check_secret_hash = bcrypt . checkpw (
password = secret_string . encode ( ' utf-8 ' ) ,
2024-04-23 19:30:14 +03:00
hashed_password = f " { SECRET_LINK_PREFIX } { client_provided_secret } " . encode ( ' utf-8 ' )
2024-04-19 20:10:31 +03:00
)
if check_secret_hash :
2024-04-23 19:30:14 +03:00
log . info ( f " Client { client [ ' name ' ] } has been requested ssconf for { server . data [ ' name ' ] } . Bcrypt client hash { client_provided_secret [ 0 : 16 ] } ...[FULL HASH SECURED] " )
2024-04-19 20:10:31 +03:00
return {
" server " : server . data [ " hostname_for_access_keys " ] ,
" server_port " : client_shadowsocks_key . port ,
" password " : client_shadowsocks_key . password ,
" method " : client_shadowsocks_key . method ,
2024-04-23 19:30:14 +03:00
" prefix " : SS_PREFIX ,
2024-04-19 20:10:31 +03:00
" info " : " Managed by OutFleet [github.com/house-of-vanity/OutFleet/] " ,
}
else :
2024-04-23 19:30:14 +03:00
log . warning ( f " Hack attempt! Client secret does not match: { client_provided_secret } " )
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
else :
2024-04-23 19:30:14 +03:00
log . warning ( f " Hack attempt! Client or server doesn ' t exist. payload: { hash_secret [ 0 : 200 ] } " )
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
except Exception as e :
2024-04-23 19:30:14 +03:00
log . error ( f " Dynamic V2 parse error: { e } " )
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
def dynamic_depticated ( server_name , client_id ) :
2023-09-27 17:31:34 +03:00
try :
2023-12-19 12:35:00 +02:00
client = next (
( keys for client , keys in CLIENTS . items ( ) if client == client_id ) , None
)
server = next (
( item for item in SERVERS if item . info ( ) [ " name " ] == server_name ) , None
)
key = next (
2024-02-18 16:57:41 +02:00
( item for item in server . data [ " keys " ] if item . key_id == client [ " name " ] ) , None
2023-12-19 12:35:00 +02:00
)
2023-09-27 17:31:34 +03:00
if server and client and key :
2023-12-17 17:06:30 +02:00
if server . data [ " local_server_id " ] in client [ " servers " ] :
2023-12-19 12:35:00 +02:00
log . info (
2024-04-23 19:30:14 +03:00
" Client %s has been requested ssconf for %s " , client [ " name " ] , server . data [ " name " ]
2023-12-19 12:35:00 +02:00
)
2023-09-27 17:31:34 +03:00
return {
2023-12-19 12:35:00 +02:00
" server " : server . data [ " hostname_for_access_keys " ] ,
" server_port " : key . port ,
" password " : key . password ,
" method " : key . method ,
2024-04-23 19:30:14 +03:00
" prefix " : SS_PREFIX ,
2023-12-19 12:35:00 +02:00
" info " : " Managed by OutFleet [github.com/house-of-vanity/OutFleet/] " ,
2023-09-27 17:31:34 +03:00
}
2023-09-27 22:42:32 +03:00
else :
2023-12-19 12:35:00 +02:00
log . warning (
" Hack attempt! Client %s denied by ACL on %s " ,
client [ " name " ] ,
server . data [ " name " ] ,
)
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
2023-09-27 17:31:34 +03:00
except :
2023-09-27 22:42:32 +03:00
log . warning ( " Hack attempt! Client or server doesn ' t exist. SCAM " )
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
2023-09-25 23:48:25 +03:00
2023-12-19 12:35:00 +02:00
2024-04-19 20:10:31 +03:00
@app.route ( " /dynamic " , methods = [ " GET " ] , strict_slashes = False )
2023-12-17 17:06:30 +02:00
def _dynamic ( ) :
log . warning ( " Hack attempt! Client or server doesn ' t exist. SCAM " )
2024-04-19 20:10:31 +03:00
return WRONG_DOOR
2023-12-17 12:32:18 +00:00
2023-12-19 12:35:00 +02:00
@app.route ( " /sync " , methods = [ " GET " , " POST " ] )
2023-12-17 12:32:18 +00:00
def sync ( ) :
2023-12-19 12:35:00 +02:00
if request . method == " GET " :
2023-12-17 17:15:54 +02:00
try :
2023-12-19 12:35:00 +02:00
with open ( " sync.log " , " r " ) as file :
2023-12-17 17:15:54 +02:00
lines = file . readlines ( )
except :
lines = [ ]
2023-12-17 17:06:30 +02:00
return render_template (
2023-12-19 12:35:00 +02:00
" sync.html " ,
2023-12-17 17:06:30 +02:00
SERVERS = SERVERS ,
CLIENTS = CLIENTS ,
lines = lines ,
)
2023-12-19 12:35:00 +02:00
if request . method == " POST " :
2024-03-19 02:47:29 +02:00
with lock :
if request . form . get ( " wipe " ) == ' all ' :
for server in SERVERS :
log . info ( " Wiping all keys on [ %s ] " , server . data [ " name " ] )
for client in server . data [ ' keys ' ] :
server . delete_key ( client . key_id )
2023-12-17 17:06:30 +02:00
server_hash = { }
2024-03-19 02:47:29 +02:00
with lock :
for server in SERVERS :
server_hash [ server . data [ " local_server_id " ] ] = server
with lock :
for key , client in CLIENTS . items ( ) :
for u_server_id in client [ " servers " ] :
if u_server_id in server_hash :
if not server_hash [ u_server_id ] . check_client ( client [ " name " ] ) :
log . warning (
f " Client { client [ ' name ' ] } absent on { server_hash [ u_server_id ] . data [ ' name ' ] } "
)
server_hash [ u_server_id ] . create_key ( client [ " name " ] )
else :
log . info (
f " Client { client [ ' name ' ] } already present on { server_hash [ u_server_id ] . data [ ' name ' ] } "
)
2023-12-17 17:06:30 +02:00
else :
2023-12-19 12:35:00 +02:00
log . info (
2024-03-19 02:47:29 +02:00
f " Client { client [ ' name ' ] } incorrect server_id { u_server_id } "
2023-12-19 12:35:00 +02:00
)
2024-03-19 02:47:29 +02:00
update_state ( timer = 0 )
2023-12-19 12:35:00 +02:00
return redirect ( url_for ( " sync " ) )
2023-12-17 12:32:18 +00:00
2024-04-19 20:10:31 +03:00
2023-12-19 12:35:00 +02:00
if __name__ == " __main__ " :
2024-03-19 01:11:08 +02:00
update_state_thread = threading . Thread ( target = update_state )
update_state_thread . start ( )
2024-03-19 02:47:29 +02:00
2024-03-19 01:11:08 +02:00
discovery_servers_thread = threading . Thread ( target = k8s . discovery_servers )
discovery_servers_thread . start ( )
2023-12-19 12:35:00 +02:00
app . run ( host = " 0.0.0.0 " )