Socket io implementation

This commit is contained in:
DomySh
2022-07-08 15:13:46 +02:00
parent a7f23ee194
commit 8cd1b69752
11 changed files with 189 additions and 26 deletions

View File

@@ -8,6 +8,7 @@ from utils import *
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt from jose import JWTError, jwt
from passlib.context import CryptContext from passlib.context import CryptContext
from fastapi_socketio import SocketManager
ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER" ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER"
DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG" DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG"
@@ -30,14 +31,22 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False)
crypto = CryptContext(schemes=["bcrypt"], deprecated="auto") crypto = CryptContext(schemes=["bcrypt"], deprecated="auto")
app = FastAPI(debug=DEBUG, redoc_url=None) app = FastAPI(debug=DEBUG, redoc_url=None)
sio = SocketManager(app, "/sock", socketio_path="")
def APP_STATUS(): return "init" if conf.get("password") is None else "run" def APP_STATUS(): return "init" if conf.get("password") is None else "run"
def JWT_SECRET(): return conf.get("secret") def JWT_SECRET(): return conf.get("secret")
async def refresh_frontend():
await sio.emit("update","Refresh")
@sio.on("update")
async def updater(): pass
@app.on_event("startup") @app.on_event("startup")
async def startup_event(): async def startup_event():
db.init() db.init()
await firewall.init() await firewall.init(refresh_frontend)
await refresh_frontend()
if not JWT_SECRET(): conf.put("secret", secrets.token_hex(32)) if not JWT_SECRET(): conf.put("secret", secrets.token_hex(32))
@app.on_event("shutdown") @app.on_event("shutdown")
@@ -117,6 +126,7 @@ async def change_password(form: PasswordChangeForm, auth: bool = Depends(is_logg
hash_psw = crypto.hash(form.password) hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw) conf.put("password",hash_psw)
await refresh_frontend()
return {"status":"ok", "access_token": create_access_token({"logged_in": True})} return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
@@ -128,6 +138,7 @@ async def set_password(form: PasswordForm):
return {"status":"Cannot insert an empty password!"} return {"status":"Cannot insert an empty password!"}
hash_psw = crypto.hash(form.password) hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw) conf.put("password",hash_psw)
await refresh_frontend()
return {"status":"ok", "access_token": create_access_token({"logged_in": True})} return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
class GeneralStatModel(BaseModel): class GeneralStatModel(BaseModel):
@@ -189,12 +200,14 @@ class StatusMessageModel(BaseModel):
async def service_stop(service_port: int, auth: bool = Depends(is_loggined)): async def service_stop(service_port: int, auth: bool = Depends(is_loggined)):
"""Request the stop of a specific service""" """Request the stop of a specific service"""
await firewall.get(service_port).next(STATUS.STOP) await firewall.get(service_port).next(STATUS.STOP)
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/service/{service_port}/start', response_model=StatusMessageModel) @app.get('/api/service/{service_port}/start', response_model=StatusMessageModel)
async def service_start(service_port: int, auth: bool = Depends(is_loggined)): async def service_start(service_port: int, auth: bool = Depends(is_loggined)):
"""Request the start of a specific service""" """Request the start of a specific service"""
await firewall.get(service_port).next(STATUS.ACTIVE) await firewall.get(service_port).next(STATUS.ACTIVE)
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/service/{service_port}/delete', response_model=StatusMessageModel) @app.get('/api/service/{service_port}/delete', response_model=StatusMessageModel)
@@ -203,6 +216,7 @@ async def service_delete(service_port: int, auth: bool = Depends(is_loggined)):
db.query('DELETE FROM services WHERE port = ?;', service_port) db.query('DELETE FROM services WHERE port = ?;', service_port)
db.query('DELETE FROM regexes WHERE service_port = ?;', service_port) db.query('DELETE FROM regexes WHERE service_port = ?;', service_port)
await firewall.remove(service_port) await firewall.remove(service_port)
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
class RenameForm(BaseModel): class RenameForm(BaseModel):
@@ -213,6 +227,7 @@ async def service_rename(service_port: int, form: RenameForm, auth: bool = Depen
"""Request to change the name of a specific service""" """Request to change the name of a specific service"""
if not form.name: return {'status': 'The name cannot be empty!'} if not form.name: return {'status': 'The name cannot be empty!'}
db.query('UPDATE services SET name=? WHERE port = ?;', form.name, service_port) db.query('UPDATE services SET name=? WHERE port = ?;', form.name, service_port)
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
class RegexModel(BaseModel): class RegexModel(BaseModel):
@@ -254,6 +269,7 @@ async def regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
if len(res) != 0: if len(res) != 0:
db.query('DELETE FROM regexes WHERE regex_id = ?;', regex_id) db.query('DELETE FROM regexes WHERE regex_id = ?;', regex_id)
await firewall.get(res[0]["service_port"]).update_filters() await firewall.get(res[0]["service_port"]).update_filters()
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
@@ -264,6 +280,7 @@ async def regex_enable(regex_id: int, auth: bool = Depends(is_loggined)):
if len(res) != 0: if len(res) != 0:
db.query('UPDATE regexes SET active=1 WHERE regex_id = ?;', regex_id) db.query('UPDATE regexes SET active=1 WHERE regex_id = ?;', regex_id)
await firewall.get(res[0]["service_port"]).update_filters() await firewall.get(res[0]["service_port"]).update_filters()
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/regex/{regex_id}/disable', response_model=StatusMessageModel) @app.get('/api/regex/{regex_id}/disable', response_model=StatusMessageModel)
@@ -273,6 +290,7 @@ async def regex_disable(regex_id: int, auth: bool = Depends(is_loggined)):
if len(res) != 0: if len(res) != 0:
db.query('UPDATE regexes SET active=0 WHERE regex_id = ?;', regex_id) db.query('UPDATE regexes SET active=0 WHERE regex_id = ?;', regex_id)
await firewall.get(res[0]["service_port"]).update_filters() await firewall.get(res[0]["service_port"]).update_filters()
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
class RegexAddForm(BaseModel): class RegexAddForm(BaseModel):
@@ -297,6 +315,7 @@ async def add_new_regex(form: RegexAddForm, auth: bool = Depends(is_loggined)):
return {'status': 'An identical regex already exists'} return {'status': 'An identical regex already exists'}
await firewall.get(form.service_port).update_filters() await firewall.get(form.service_port).update_filters()
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
class ServiceAddForm(BaseModel): class ServiceAddForm(BaseModel):
@@ -312,6 +331,7 @@ async def add_new_service(form: ServiceAddForm, auth: bool = Depends(is_loggined
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
return {'status': 'Name or/and ports of the service has been already assigned to another service'} return {'status': 'Name or/and ports of the service has been already assigned to another service'}
await firewall.reload() await firewall.reload()
await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}

View File

@@ -6,3 +6,4 @@ python-jose[cryptography]
NetfilterQueue NetfilterQueue
scapy scapy
python-pcre python-pcre
fastapi-socketio

View File

@@ -177,9 +177,9 @@ class ProxyManager:
self.lock = asyncio.Lock() self.lock = asyncio.Lock()
self.updater_task = None self.updater_task = None
def init_updater(self): def init_updater(self, callback = None):
if not self.updater_task: if not self.updater_task:
self.updater_task = asyncio.create_task(self._stats_updater()) self.updater_task = asyncio.create_task(self._stats_updater(callback))
def close_updater(self): def close_updater(self):
if self.updater_task: self.updater_task.cancel() if self.updater_task: self.updater_task.cancel()
@@ -196,11 +196,11 @@ class ProxyManager:
await self.proxy_table[port].next(STATUS.STOP) await self.proxy_table[port].next(STATUS.STOP)
del self.proxy_table[port] del self.proxy_table[port]
async def init(self): async def init(self, callback = None):
self.init_updater(callback)
await self.reload() await self.reload()
async def reload(self): async def reload(self):
self.init_updater()
async with self.lock: async with self.lock:
for srv in self.db.query('SELECT port, status FROM services;'): for srv in self.db.query('SELECT port, status FROM services;'):
srv_port, req_status = srv["port"], srv["status"] srv_port, req_status = srv["port"], srv["status"]
@@ -210,7 +210,7 @@ class ProxyManager:
self.proxy_table[srv_port] = ServiceManager(srv_port,self.db) self.proxy_table[srv_port] = ServiceManager(srv_port,self.db)
await self.proxy_table[srv_port].next(req_status) await self.proxy_table[srv_port].next(req_status)
async def _stats_updater(self): async def _stats_updater(self, callback):
try: try:
while True: while True:
try: try:
@@ -218,7 +218,10 @@ class ProxyManager:
self.proxy_table[key].update_stats() self.proxy_table[key].update_stats()
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
await asyncio.sleep(1) if callback:
if asyncio.iscoroutinefunction(callback): await callback()
else: callback()
await asyncio.sleep(5)
except asyncio.CancelledError: except asyncio.CancelledError:
self.updater_task = None self.updater_task = None
return return

View File

@@ -29,6 +29,7 @@
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sass": "^1.52.3", "sass": "^1.52.3",
"socket.io-client": "^4.5.1",
"typescript": "^4.7.3", "typescript": "^4.7.3",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
} }
@@ -3164,6 +3165,11 @@
"@sinonjs/commons": "^1.7.0" "@sinonjs/commons": "^1.7.0"
} }
}, },
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"node_modules/@surma/rollup-plugin-off-main-thread": { "node_modules/@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -6403,6 +6409,46 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/engine.io-client": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.9.3", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
@@ -13217,6 +13263,32 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/socket.io-client": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
"integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.2.1",
"socket.io-parser": "~4.2.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sockjs": { "node_modules/sockjs": {
"version": "0.3.24", "version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -15144,6 +15216,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
}, },
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/xtend": { "node_modules/xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -17365,6 +17445,11 @@
"@sinonjs/commons": "^1.7.0" "@sinonjs/commons": "^1.7.0"
} }
}, },
"@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"@surma/rollup-plugin-off-main-thread": { "@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@@ -19740,6 +19825,31 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
}, },
"engine.io-client": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0"
},
"dependencies": {
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
},
"enhanced-resolve": { "enhanced-resolve": {
"version": "5.9.3", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
@@ -24538,6 +24648,26 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
}, },
"socket.io-client": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.1.tgz",
"integrity": "sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.2.1",
"socket.io-parser": "~4.2.0"
}
},
"socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
}
},
"sockjs": { "sockjs": {
"version": "0.3.24", "version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@@ -26008,6 +26138,11 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
}, },
"xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
},
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@@ -24,6 +24,7 @@
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sass": "^1.52.3", "sass": "^1.52.3",
"socket.io-client": "^4.5.1",
"typescript": "^4.7.3", "typescript": "^4.7.3",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

View File

@@ -5,9 +5,12 @@ import { ImCross } from 'react-icons/im';
import { Navigate, Outlet, Route, Routes } from 'react-router-dom'; import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
import MainLayout from './components/MainLayout'; import MainLayout from './components/MainLayout';
import { PasswordSend, ServerStatusResponse } from './js/models'; import { PasswordSend, ServerStatusResponse } from './js/models';
import { fireUpdateRequest, getstatus, login, setpassword } from './js/utils'; import { errorNotify, fireUpdateRequest, getstatus, login, setpassword } from './js/utils';
import HomePage from './pages/HomePage'; import HomePage from './pages/HomePage';
import ServiceDetails from './pages/ServiceDetails'; import ServiceDetails from './pages/ServiceDetails';
import io from 'socket.io-client';
const socket = io({transports: ["websocket", "polling"], path:"/sock" });
function App() { function App() {
@@ -29,11 +32,19 @@ function App() {
}) })
} }
useEffect(getStatus,[])
useEffect(()=>{ useEffect(()=>{
const updater = setInterval(fireUpdateRequest,2000) getStatus()
return () => clearInterval(updater) socket.on("update", () => {
fireUpdateRequest()
})
socket.on("connect_error", (err) => {
errorNotify("Socket.Io connection failed! ",`Error message: [${err.message}]`)
getStatus()
});
return () => {
socket.off("update")
socket.off("connect_error")
}
},[]) },[])
const form = useForm({ const form = useForm({

View File

@@ -2,7 +2,7 @@ import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Mo
import { useForm } from '@mantine/hooks'; import { useForm } from '@mantine/hooks';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { RegexAddForm } from '../js/models'; import { RegexAddForm } from '../js/models';
import { addregex, b64decode, b64encode, fireUpdateRequest, okNotify } from '../js/utils'; import { addregex, b64decode, b64encode, okNotify } from '../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
import FilterTypeSelector from './FilterTypeSelector'; import FilterTypeSelector from './FilterTypeSelector';
@@ -58,7 +58,6 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
if (!res){ if (!res){
setSubmitLoading(false) setSubmitLoading(false)
close(); close();
fireUpdateRequest();
okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_port} service`) okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_port} service`)
}else if (res.toLowerCase() === "invalid regex"){ }else if (res.toLowerCase() === "invalid regex"){
setSubmitLoading(false) setSubmitLoading(false)

View File

@@ -1,7 +1,7 @@
import { Button, Group, NumberInput, Space, TextInput, Notification, Modal, Switch } from '@mantine/core'; import { Button, Group, NumberInput, Space, TextInput, Notification, Modal, Switch } from '@mantine/core';
import { useForm } from '@mantine/hooks'; import { useForm } from '@mantine/hooks';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { addservice, fireUpdateRequest, okNotify, startservice } from '../js/utils'; import { addservice, okNotify, startservice } from '../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
type ServiceAddForm = { type ServiceAddForm = {
@@ -39,7 +39,6 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
if (res.status === "ok"){ if (res.status === "ok"){
setSubmitLoading(false) setSubmitLoading(false)
close(); close();
fireUpdateRequest();
if (autostart) startservice(port) if (autostart) startservice(port)
okNotify(`Service ${name} has been added`, `Successfully added service with port ${port}`) okNotify(`Service ${name} has been added`, `Successfully added service with port ${port}`)
}else{ }else{

View File

@@ -1,7 +1,7 @@
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core'; import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { RegexFilter } from '../../js/models'; import { RegexFilter } from '../../js/models';
import { activateregex, b64decode, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, okNotify } from '../../js/utils'; import { activateregex, b64decode, deactivateregex, deleteregex, errorNotify, okNotify } from '../../js/utils';
import style from "./index.module.scss"; import style from "./index.module.scss";
import { BsTrashFill } from "react-icons/bs" import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal'; import YesNoModal from '../YesNoModal';
@@ -25,7 +25,6 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
deleteregex(regexInfo.id).then(res => { deleteregex(regexInfo.id).then(res => {
if(!res){ if(!res){
okNotify(`Regex ${regex_expr} deleted successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been deleted!`) okNotify(`Regex ${regex_expr} deleted successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been deleted!`)
fireUpdateRequest()
}else{ }else{
errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${res}`) errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${res}`)
} }
@@ -37,7 +36,6 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
(regexInfo.active?deactivateregex:activateregex)(regexInfo.id).then(res => { (regexInfo.active?deactivateregex:activateregex)(regexInfo.id).then(res => {
if(!res){ if(!res){
okNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivated":"activated"} successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been ${regexInfo.active?"deactivated":"activated"}!`) okNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivated":"activated"} successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been ${regexInfo.active?"deactivated":"activated"}!`)
fireUpdateRequest()
}else{ }else{
errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${res}`) errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${res}`)
} }

View File

@@ -1,7 +1,7 @@
import { Button, Group, Space, TextInput, Notification, Modal } from '@mantine/core'; import { Button, Group, Space, TextInput, Notification, Modal } from '@mantine/core';
import { useForm } from '@mantine/hooks'; import { useForm } from '@mantine/hooks';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { fireUpdateRequest, okNotify, renameservice } from '../../js/utils'; import { okNotify, renameservice } from '../../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
import { Service } from '../../js/models'; import { Service } from '../../js/models';
@@ -29,7 +29,6 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
if (!res){ if (!res){
setSubmitLoading(false) setSubmitLoading(false)
close(); close();
fireUpdateRequest();
okNotify(`Service ${service.name} has been renamed in ${ name }`, `Successfully renamed service on port ${service.port}`) okNotify(`Service ${service.name} has been renamed in ${ name }`, `Successfully renamed service on port ${service.port}`)
}else{ }else{
setSubmitLoading(false) setSubmitLoading(false)

View File

@@ -5,7 +5,7 @@ import { Service } from '../../js/models';
import { MdOutlineArrowForwardIos } from "react-icons/md" import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./index.module.scss"; import style from "./index.module.scss";
import YesNoModal from '../YesNoModal'; import YesNoModal from '../YesNoModal';
import { deleteservice, errorNotify, fireUpdateRequest, okNotify, startservice, stopservice } from '../../js/utils'; import { deleteservice, errorNotify, okNotify, startservice, stopservice } from '../../js/utils';
import { BsTrashFill } from 'react-icons/bs'; import { BsTrashFill } from 'react-icons/bs';
import { BiRename } from 'react-icons/bi' import { BiRename } from 'react-icons/bi'
import RenameForm from './RenameForm'; import RenameForm from './RenameForm';
@@ -28,7 +28,6 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
await stopservice(service.port).then(res => { await stopservice(service.port).then(res => {
if(!res){ if(!res){
okNotify(`Service ${service.name} stopped successfully!`,`The service on ${service.port} has been stopped!`) okNotify(`Service ${service.name} stopped successfully!`,`The service on ${service.port} has been stopped!`)
fireUpdateRequest();
}else{ }else{
errorNotify(`An error as occurred during the stopping of the service ${service.port}`,`Error: ${res}`) errorNotify(`An error as occurred during the stopping of the service ${service.port}`,`Error: ${res}`)
} }
@@ -43,7 +42,6 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
await startservice(service.port).then(res => { await startservice(service.port).then(res => {
if(!res){ if(!res){
okNotify(`Service ${service.name} started successfully!`,`The service on ${service.port} has been started!`) okNotify(`Service ${service.name} started successfully!`,`The service on ${service.port} has been started!`)
fireUpdateRequest();
}else{ }else{
errorNotify(`An error as occurred during the starting of the service ${service.port}`,`Error: ${res}`) errorNotify(`An error as occurred during the starting of the service ${service.port}`,`Error: ${res}`)
} }
@@ -57,7 +55,6 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
deleteservice(service.port).then(res => { deleteservice(service.port).then(res => {
if (!res){ if (!res){
okNotify("Service delete complete!",`The service ${service.name} has been deleted!`) okNotify("Service delete complete!",`The service ${service.name} has been deleted!`)
fireUpdateRequest();
}else }else
errorNotify("An error occurred while deleting a service",`Error: ${res}`) errorNotify("An error occurred while deleting a service",`Error: ${res}`)
}).catch(err => { }).catch(err => {