author DomySh <me@domysh.com> 1656456810 +0200
committer DomySh <me@domysh.com> 1656457473 +0200

Small Fixes (Stable only with 1 worker!)
This commit is contained in:
DomySh
2022-06-29 00:53:30 +02:00
parent 80a38f0d50
commit fad6ad4e68
13 changed files with 80 additions and 127 deletions

View File

@@ -1,10 +1,7 @@
from base64 import b64decode from base64 import b64decode
from datetime import datetime, timedelta
import sqlite3, uvicorn, sys, secrets, re, os, asyncio, httpx, urllib, websockets import sqlite3, uvicorn, sys, secrets, re, os, asyncio, httpx, urllib, websockets
from tabnanny import check from fastapi import FastAPI, HTTPException, WebSocket, Depends
from typing import Union from pydantic import BaseModel, BaseSettings
from fastapi import FastAPI, Request, HTTPException, WebSocket, Depends
from pydantic import BaseModel
from fastapi.responses import FileResponse, StreamingResponse from fastapi.responses import FileResponse, StreamingResponse
from utils import SQLite, KeyValueStorage, gen_internal_port, ProxyManager, from_name_get_id, STATUS from utils import SQLite, KeyValueStorage, gen_internal_port, ProxyManager, from_name_get_id, STATUS
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
@@ -20,16 +17,22 @@ db = SQLite('db/firegex.db')
conf = KeyValueStorage(db) conf = KeyValueStorage(db)
firewall = ProxyManager(db) firewall = ProxyManager(db)
JWT_ALGORITHM="HS256"
JWT_SECRET = secrets.token_hex(32) class Settings(BaseSettings):
APP_STATUS = "init" JWT_ALGORITHM: str = "HS256"
REACT_BUILD_DIR = "../frontend/build/" if not ON_DOCKER else "frontend/" REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
REACT_HTML_PATH = os.path.join(REACT_BUILD_DIR,"index.html") REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
settings = Settings()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False) 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) app = FastAPI(debug=DEBUG)
def APP_STATUS(): return "init" if conf.get("password") is None else "run"
def JWT_SECRET(): return conf.get("secret")
@app.on_event("shutdown") @app.on_event("shutdown")
async def shutdown_event(): async def shutdown_event():
await firewall.close() await firewall.close()
@@ -37,50 +40,21 @@ async def shutdown_event():
@app.on_event("startup") @app.on_event("startup")
async def startup_event(): async def startup_event():
global APP_STATUS db.init()
db.connect() if not JWT_SECRET(): conf.put("secret", secrets.token_hex(32))
db.create_schema({
'services': {
'status': 'VARCHAR(100) NOT NULL',
'service_id': 'VARCHAR(100) PRIMARY KEY',
'internal_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536) UNIQUE',
'public_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536) UNIQUE',
'name': 'VARCHAR(100) NOT NULL'
},
'regexes': {
'regex': 'TEXT NOT NULL',
'mode': 'VARCHAR(1) NOT NULL',
'service_id': 'VARCHAR(100) NOT NULL',
'is_blacklist': 'BOOLEAN NOT NULL CHECK (is_blacklist IN (0, 1))',
'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0',
'regex_id': 'INTEGER PRIMARY KEY',
'is_case_sensitive' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1))',
'FOREIGN KEY (service_id)':'REFERENCES services (service_id)',
},
'keys_values': {
'key': 'VARCHAR(100) PRIMARY KEY',
'value': 'VARCHAR(100) NOT NULL',
},
})
db.query("CREATE UNIQUE INDEX IF NOT EXISTS unique_regex_service ON regexes (regex,service_id,is_blacklist,mode,is_case_sensitive);")
if not conf.get("password") is None:
APP_STATUS = "run"
await firewall.reload() await firewall.reload()
def create_access_token(data: dict): def create_access_token(data: dict):
global JWT_SECRET
to_encode = data.copy() to_encode = data.copy()
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM) encoded_jwt = jwt.encode(to_encode, JWT_SECRET(), algorithm=settings.JWT_ALGORITHM)
return encoded_jwt return encoded_jwt
async def check_login(token: str = Depends(oauth2_scheme)): async def check_login(token: str = Depends(oauth2_scheme)):
global JWT_SECRET
if not token: if not token:
return False return False
try: try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) payload = jwt.decode(token, JWT_SECRET(), algorithms=[settings.JWT_ALGORITHM])
logged_in: bool = payload.get("logged_in") logged_in: bool = payload.get("logged_in")
except JWTError: except JWTError:
return False return False
@@ -97,9 +71,8 @@ async def is_loggined(auth: bool = Depends(check_login)):
@app.get("/api/status") @app.get("/api/status")
async def get_status(auth: bool = Depends(check_login)): async def get_status(auth: bool = Depends(check_login)):
global APP_STATUS
return { return {
"status":APP_STATUS, "status": APP_STATUS(),
"loggined": auth "loggined": auth
} }
@@ -112,29 +85,23 @@ class PasswordChangeForm(BaseModel):
@app.post("/api/login") @app.post("/api/login")
async def login_api(form: OAuth2PasswordRequestForm = Depends()): async def login_api(form: OAuth2PasswordRequestForm = Depends()):
global APP_STATUS, JWT_SECRET if APP_STATUS() != "run": raise HTTPException(status_code=400)
if APP_STATUS != "run": raise HTTPException(status_code=400)
if form.password == "": if form.password == "":
return {"status":"Cannot insert an empty password!"} return {"status":"Cannot insert an empty password!"}
await asyncio.sleep(0.3) # No bruteforce :) await asyncio.sleep(0.3) # No bruteforce :)
if crypto.verify(form.password, conf.get("password")): if crypto.verify(form.password, conf.get("password")):
print("access granted, good job")
return {"access_token": create_access_token({"logged_in": True}), "token_type": "bearer"} return {"access_token": create_access_token({"logged_in": True}), "token_type": "bearer"}
raise HTTPException(406,"Wrong password!") raise HTTPException(406,"Wrong password!")
@app.post('/api/change-password') @app.post('/api/change-password')
async def change_password(form: PasswordChangeForm, auth: bool = Depends(is_loggined)): async def change_password(form: PasswordChangeForm, auth: bool = Depends(is_loggined)):
if APP_STATUS() != "run": raise HTTPException(status_code=400)
global APP_STATUS, JWT_SECRET
if APP_STATUS != "run": raise HTTPException(status_code=400)
if form.password == "": if form.password == "":
return {"status":"Cannot insert an empty password!"} return {"status":"Cannot insert an empty password!"}
if form.expire: if form.expire:
JWT_SECRET = secrets.token_hex(32) conf.put("secret", secrets.token_hex(32))
hash_psw = crypto.hash(form.password) hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw) conf.put("password",hash_psw)
@@ -143,14 +110,11 @@ async def change_password(form: PasswordChangeForm, auth: bool = Depends(is_logg
@app.post('/api/set-password') @app.post('/api/set-password')
async def set_password(form: PasswordForm): async def set_password(form: PasswordForm):
global APP_STATUS, JWT_SECRET if APP_STATUS() != "init": raise HTTPException(status_code=400)
if APP_STATUS != "init": raise HTTPException(status_code=400)
if form.password == "": if form.password == "":
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)
APP_STATUS = "run"
return {"status":"ok", "access_token": create_access_token({"logged_in": True})} return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
@app.get('/api/general-stats') @app.get('/api/general-stats')
@@ -165,7 +129,6 @@ async def get_general_stats(auth: bool = Depends(is_loggined)):
@app.get('/api/services') @app.get('/api/services')
async def get_services(auth: bool = Depends(is_loggined)): async def get_services(auth: bool = Depends(is_loggined)):
return db.query(""" return db.query("""
SELECT SELECT
s.service_id `id`, s.service_id `id`,
@@ -199,25 +162,21 @@ async def get_service(service_id: str, auth: bool = Depends(is_loggined)):
@app.get('/api/service/{service_id}/stop') @app.get('/api/service/{service_id}/stop')
async def get_service_stop(service_id: str, auth: bool = Depends(is_loggined)): async def get_service_stop(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.STOP) await firewall.get(service_id).next(STATUS.STOP)
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/service/{service_id}/pause') @app.get('/api/service/{service_id}/pause')
async def get_service_pause(service_id: str, auth: bool = Depends(is_loggined)): async def get_service_pause(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.PAUSE) await firewall.get(service_id).next(STATUS.PAUSE)
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/service/{service_id}/start') @app.get('/api/service/{service_id}/start')
async def get_service_start(service_id: str, auth: bool = Depends(is_loggined)): async def get_service_start(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.ACTIVE) await firewall.get(service_id).next(STATUS.ACTIVE)
return {'status': 'ok'} return {'status': 'ok'}
@app.get('/api/service/{service_id}/delete') @app.get('/api/service/{service_id}/delete')
async def get_service_delete(service_id: str, auth: bool = Depends(is_loggined)): async def get_service_delete(service_id: str, auth: bool = Depends(is_loggined)):
db.query('DELETE FROM services WHERE service_id = ?;', service_id) db.query('DELETE FROM services WHERE service_id = ?;', service_id)
db.query('DELETE FROM regexes WHERE service_id = ?;', service_id) db.query('DELETE FROM regexes WHERE service_id = ?;', service_id)
await firewall.remove(service_id) await firewall.remove(service_id)
@@ -226,7 +185,6 @@ async def get_service_delete(service_id: str, auth: bool = Depends(is_loggined))
@app.get('/api/service/{service_id}/regen-port') @app.get('/api/service/{service_id}/regen-port')
async def get_regen_port(service_id: str, auth: bool = Depends(is_loggined)): async def get_regen_port(service_id: str, auth: bool = Depends(is_loggined)):
db.query('UPDATE services SET internal_port = ? WHERE service_id = ?;', gen_internal_port(db), service_id) db.query('UPDATE services SET internal_port = ? WHERE service_id = ?;', gen_internal_port(db), service_id)
await firewall.get(service_id).update_port() await firewall.get(service_id).update_port()
return {'status': 'ok'} return {'status': 'ok'}
@@ -234,7 +192,6 @@ async def get_regen_port(service_id: str, auth: bool = Depends(is_loggined)):
@app.get('/api/service/{service_id}/regexes') @app.get('/api/service/{service_id}/regexes')
async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)): async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)):
return db.query(""" return db.query("""
SELECT SELECT
regex, mode, regex_id `id`, service_id, is_blacklist, regex, mode, regex_id `id`, service_id, is_blacklist,
@@ -244,7 +201,6 @@ async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)
@app.get('/api/regex/{regex_id}') @app.get('/api/regex/{regex_id}')
async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)): async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)):
res = db.query(""" res = db.query("""
SELECT SELECT
regex, mode, regex_id `id`, service_id, is_blacklist, regex, mode, regex_id `id`, service_id, is_blacklist,
@@ -256,9 +212,7 @@ async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)):
@app.get('/api/regex/{regex_id}/delete') @app.get('/api/regex/{regex_id}/delete')
async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)): async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id) res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id)
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_id"]).update_filters() await firewall.get(res[0]["service_id"]).update_filters()
@@ -274,7 +228,6 @@ class RegexAddForm(BaseModel):
@app.post('/api/regexes/add') @app.post('/api/regexes/add')
async def post_regexes_add(form: RegexAddForm, auth: bool = Depends(is_loggined)): async def post_regexes_add(form: RegexAddForm, auth: bool = Depends(is_loggined)):
try: try:
re.compile(b64decode(form.regex)) re.compile(b64decode(form.regex))
except Exception: except Exception:
@@ -294,7 +247,6 @@ class ServiceAddForm(BaseModel):
@app.post('/api/services/add') @app.post('/api/services/add')
async def post_services_add(form: ServiceAddForm, auth: bool = Depends(is_loggined)): async def post_services_add(form: ServiceAddForm, auth: bool = Depends(is_loggined)):
serv_id = from_name_get_id(form.name) serv_id = from_name_get_id(form.name)
try: try:
db.query("INSERT INTO services (name, service_id, internal_port, public_port, status) VALUES (?, ?, ?, ?, ?)", db.query("INSERT INTO services (name, service_id, internal_port, public_port, status) VALUES (?, ?, ?, ?, ?)",
@@ -312,9 +264,9 @@ async def frontend_debug_proxy(path):
return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code) return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code)
async def react_deploy(path): async def react_deploy(path):
file_request = os.path.join(REACT_BUILD_DIR, path) file_request = os.path.join(settings.REACT_BUILD_DIR, path)
if not os.path.isfile(file_request): if not os.path.isfile(file_request):
return FileResponse(REACT_HTML_PATH, media_type='text/html') return FileResponse(settings.REACT_HTML_PATH, media_type='text/html')
else: else:
return FileResponse(file_request) return FileResponse(file_request)
@@ -356,5 +308,5 @@ if __name__ == '__main__':
port=int(os.getenv("PORT","4444")), port=int(os.getenv("PORT","4444")),
reload=DEBUG, reload=DEBUG,
access_log=DEBUG, access_log=DEBUG,
workers=2 workers=1
) )

View File

@@ -69,7 +69,6 @@ class Proxy:
async with self.status_change: async with self.status_change:
if self.isactive(): if self.isactive():
self.process.kill() self.process.kill()
self.process = None
return False return False
return True return True
@@ -92,9 +91,7 @@ class Proxy:
await self.update_config(filters_codes) await self.update_config(filters_codes)
def isactive(self): def isactive(self):
if self.process and not self.process.returncode is None: return self.process and self.process.returncode is None
self.process = None
return True if self.process else False
async def pause(self): async def pause(self):
if self.isactive(): if self.isactive():

View File

@@ -443,7 +443,6 @@ private:
}; };
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
if (argc < 5) if (argc < 5)

View File

@@ -47,6 +47,33 @@ class SQLite():
try: self.conn.commit() try: self.conn.commit()
except Exception: pass except Exception: pass
def init(self):
self.connect()
self.create_schema({
'services': {
'status': 'VARCHAR(100) NOT NULL',
'service_id': 'VARCHAR(100) PRIMARY KEY',
'internal_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536) UNIQUE',
'public_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536) UNIQUE',
'name': 'VARCHAR(100) NOT NULL'
},
'regexes': {
'regex': 'TEXT NOT NULL',
'mode': 'VARCHAR(1) NOT NULL',
'service_id': 'VARCHAR(100) NOT NULL',
'is_blacklist': 'BOOLEAN NOT NULL CHECK (is_blacklist IN (0, 1))',
'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0',
'regex_id': 'INTEGER PRIMARY KEY',
'is_case_sensitive' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1))',
'FOREIGN KEY (service_id)':'REFERENCES services (service_id)',
},
'keys_values': {
'key': 'VARCHAR(100) PRIMARY KEY',
'value': 'VARCHAR(100) NOT NULL',
},
})
self.query("CREATE UNIQUE INDEX IF NOT EXISTS unique_regex_service ON regexes (regex,service_id,is_blacklist,mode,is_case_sensitive);")
class KeyValueStorage: class KeyValueStorage:
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
@@ -70,8 +97,7 @@ class STATUS:
PAUSE = "pause" PAUSE = "pause"
ACTIVE = "active" ACTIVE = "active"
class ServiceNotFoundException(Exception): class ServiceNotFoundException(Exception): pass
pass
class ServiceManager: class ServiceManager:
def __init__(self, id, db): def __init__(self, id, db):
@@ -83,7 +109,8 @@ class ServiceManager:
) )
self.status = STATUS.STOP self.status = STATUS.STOP
self.filters = {} self.filters = {}
self._proxy_update() self._update_port_from_db()
self._update_filters_from_db()
self.lock = asyncio.Lock() self.lock = asyncio.Lock()
self.starter = None self.starter = None
@@ -98,10 +125,6 @@ class ServiceManager:
self.proxy.internal_port = res[0]["internal_port"] self.proxy.internal_port = res[0]["internal_port"]
self.proxy.public_port = res[0]["public_port"] self.proxy.public_port = res[0]["public_port"]
def _proxy_update(self):
self._update_port_from_db()
self._update_filters_from_db()
def _update_filters_from_db(self): def _update_filters_from_db(self):
res = self.db.query(""" res = self.db.query("""
SELECT SELECT
@@ -133,8 +156,8 @@ class ServiceManager:
) )
self.proxy.filters = list(self.filters.values()) self.proxy.filters = list(self.filters.values())
def __update_status_db(self, id, status): def __update_status_db(self, status):
self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, id) self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.id)
async def next(self,to): async def next(self,to):
async with self.lock: async with self.lock:
@@ -171,7 +194,7 @@ class ServiceManager:
def _set_status(self,status): def _set_status(self,status):
self.status = status self.status = status
self.__update_status_db(self.id,status) self.__update_status_db(status)
async def update_filters(self): async def update_filters(self):
@@ -222,7 +245,10 @@ class ProxyManager:
await self.proxy_table[srv_id].next(req_status) await self.proxy_table[srv_id].next(req_status)
def get(self,id): def get(self,id):
return self.proxy_table[id] if id in self.proxy_table:
return self.proxy_table[id]
else:
raise ServiceNotFoundException()
def check_port_is_open(port): def check_port_is_open(port):
try: try:

12
frontend/build/asset-manifest.json Normal file → Executable file
View File

@@ -1,13 +1,13 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.0efd334b.css", "main.css": "/static/css/main.c375ae17.css",
"main.js": "/static/js/main.dbb30aee.js", "main.js": "/static/js/main.e6c85636.js",
"index.html": "/index.html", "index.html": "/index.html",
"main.0efd334b.css.map": "/static/css/main.0efd334b.css.map", "main.c375ae17.css.map": "/static/css/main.c375ae17.css.map",
"main.dbb30aee.js.map": "/static/js/main.dbb30aee.js.map" "main.e6c85636.js.map": "/static/js/main.e6c85636.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.0efd334b.css", "static/css/main.c375ae17.css",
"static/js/main.dbb30aee.js" "static/js/main.e6c85636.js"
] ]
} }

2
frontend/build/index.html Normal file → Executable file
View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.dbb30aee.js"></script><link href="/static/css/main.0efd334b.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.e6c85636.js"></script><link href="/static/css/main.c375ae17.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

View File

@@ -1,2 +0,0 @@
@import url(https://fonts.googleapis.com/css2?family=Lato&display=swap);.center-flex,.center-flex-row{align-items:center;display:flex;justify-content:center}.center-flex-row{flex-direction:column}.flex-spacer{flex-grow:1}.Footer_center-flex-row__EeEEN,.Footer_center-flex__vCncJ,.Footer_footer__PxxIj{align-items:center;display:flex;justify-content:center}.Footer_center-flex-row__EeEEN{flex-direction:column}.Footer_flex-spacer__MHTsy{flex-grow:1}.Footer_footer__PxxIj{background-color:#242a33;height:150px;margin-top:50px}.Header_header__OKWO7{align-items:center;background-color:#242a33;display:flex;height:140px;justify-content:center;width:100%}.Header_logo__shVBB{height:70%;margin-left:40px;width:200px}body{font-family:Lato,sans-serif;margin:0}.ServiceRow_center-flex-row__g7ljt,.ServiceRow_center-flex__BYani,.ServiceRow_row__X48wF{align-items:center;display:flex;justify-content:center}.ServiceRow_center-flex-row__g7ljt{flex-direction:column}.ServiceRow_flex-spacer__zolmX{flex-grow:1}::-webkit-scrollbar{background:#333;cursor:pointer;margin:3px;width:12px}::-webkit-scrollbar-thumb{background:#757575;border-radius:8px}.ServiceRow_row__X48wF{border-radius:20px;margin:10px;padding:30px 0;width:95%}.ServiceRow_name__fL\+Lz{color:#fff;font-size:2.3em;font-weight:bolder;margin-bottom:13px;margin-right:10px}.RegexView_box__PVbdO{margin:5px;padding:30px}.RegexView_regex_text__rxij9{background-color:#25262b;border-radius:15px;padding:10px}
/*# sourceMappingURL=main.0efd334b.css.map*/

View File

@@ -0,0 +1,2 @@
@import url(https://fonts.googleapis.com/css2?family=Lato&display=swap);.center-flex,.center-flex-row{align-items:center;display:flex;justify-content:center}.center-flex-row{flex-direction:column}.flex-spacer{flex-grow:1}.Footer_center-flex-row__yo4XA,.Footer_center-flex__uvxL9,.Footer_footer__S9pCA{align-items:center;display:flex;justify-content:center}.Footer_center-flex-row__yo4XA{flex-direction:column}.Footer_flex-spacer__VEUAB{flex-grow:1}.Footer_footer__S9pCA{background-color:#242a33;height:150px;margin-top:50px}.Header_header__Ri4em{align-items:center;background-color:#242a33;display:flex;height:140px;justify-content:center;width:100%}.Header_logo__3Bvhn{height:70%;margin-left:40px;width:200px}body{font-family:Lato,sans-serif;margin:0}.ServiceRow_center-flex-row__E5vBK,.ServiceRow_center-flex__hmJif,.ServiceRow_row__-A-i2{align-items:center;display:flex;justify-content:center}.ServiceRow_center-flex-row__E5vBK{flex-direction:column}.ServiceRow_flex-spacer__vmC0u{flex-grow:1}::-webkit-scrollbar{background:#333;cursor:pointer;margin:3px;width:12px}::-webkit-scrollbar-thumb{background:#757575;border-radius:8px}.ServiceRow_row__-A-i2{border-radius:20px;margin:10px;padding:30px 0;width:95%}.ServiceRow_name__6f1O6{color:#fff;font-size:2.3em;font-weight:bolder;margin-bottom:13px;margin-right:10px}.RegexView_box__g-S5R{margin:5px;padding:30px}.RegexView_regex_text__Fy5MY{background-color:#25262b;border-radius:15px;padding:10px}
/*# sourceMappingURL=main.c375ae17.css.map*/

View File

@@ -1 +1 @@
{"version":3,"file":"static/css/main.0efd334b.css","mappings":"wEAUA,8BAGE,mBAFA,aACA,sBACA,CAGF,iBAEE,sBAGF,aACE,YAZF,gFAGE,mBAFA,aACA,sBACA,CAGF,+BAEE,sBAGF,2BACE,YCnBF,sBAGI,yBAFA,aACA,eCJY,CCEhB,sBAKI,mBAFA,wBDLY,CCMZ,aAFA,aAIA,uBALA,UAKA,CAGJ,oBAGI,WADA,iBADA,WAEA,CHVJ,KAEE,4BADA,QACA,CAGF,yFAGE,mBAFA,aACA,sBACA,CAGF,mCAEE,sBAGF,+BACE,YAGF,oBAGE,gBACA,eAFA,UAAU,CADV,UAGA,CAEF,0BACE,mBACA,kBI9BF,uBAGI,mBACA,YAFA,eADA,SAGA,CAIJ,yBAKI,WAJA,gBACA,mBAEA,mBADA,iBAEA,CCbJ,sBAEI,UAAS,CADT,YACU,CAGd,6BAEI,wBHPS,CGQT,mBAFA,YAEA","sources":["index.scss","components/Footer/Footer.module.scss","_vars.scss","components/Header/Header.module.scss","components/ServiceRow/ServiceRow.module.scss","components/RegexView/RegexView.module.scss"],"sourcesContent":["\n@use \"vars\" as *;\n\n@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');\n\nbody {\n margin: 0;\n font-family: 'Lato', sans-serif;\n}\n\n.center-flex{\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.center-flex-row{\n @extend .center-flex;\n flex-direction: column;\n}\n\n.flex-spacer{\n flex-grow: 1;\n}\n\n::-webkit-scrollbar {\n width: 12px;\n margin:3px;\n background: #333;\n cursor: pointer;\n}\n::-webkit-scrollbar-thumb {\n background: #757575;\n border-radius: 8px;\n}","@use \"../../vars\" as *;\r\n@use \"../../index.scss\" as *;\r\n\r\n.footer{\r\n height: 150px;\r\n margin-top: 50px;\r\n background-color: $primary_color;\r\n @extend .center-flex;\r\n}","\r\n$primary_color: #242a33;\r\n$second_color: #1A1B1E;\r\n$third_color:#25262b;\r\n","\r\n@use \"../../vars\" as *;\r\n\r\n.header{\r\n width: 100%;\r\n height: 140px;\r\n background-color: $primary_color;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.logo{\r\n width: 200px;\r\n margin-left: 40px;\r\n height: 70%;\r\n}","\r\n@use \"../../index.scss\" as *;\r\n\r\n.row{\r\n width: 95%;\r\n padding: 30px 0px;\r\n border-radius: 20px;\r\n margin: 10px;\r\n @extend .center-flex;\r\n}\r\n\r\n.name{\r\n font-size: 2.3em;\r\n font-weight: bolder;\r\n margin-right: 10px;\r\n margin-bottom: 13px;\r\n color:#FFF;\r\n}","\r\n@use \"../../vars\" as *;\r\n\r\n.box{\r\n padding:30px;\r\n margin:5px;\r\n}\r\n\r\n.regex_text{\r\n padding: 10px;\r\n background-color: $third_color;\r\n border-radius: 15px;\r\n}"],"names":[],"sourceRoot":""} {"version":3,"file":"static/css/main.c375ae17.css","mappings":"wEAUA,8BAGE,mBAFA,aACA,sBACA,CAGF,iBAEE,sBAGF,aACE,YAZF,gFAGE,mBAFA,aACA,sBACA,CAGF,+BAEE,sBAGF,2BACE,YCnBF,sBAGI,yBAFA,aACA,eCJY,CCEhB,sBAKI,mBAFA,wBDLY,CCMZ,aAFA,aAIA,uBALA,UAKA,CAGJ,oBAGI,WADA,iBADA,WAEA,CHVJ,KAEE,4BADA,QACA,CAGF,yFAGE,mBAFA,aACA,sBACA,CAGF,mCAEE,sBAGF,+BACE,YAGF,oBAGE,gBACA,eAFA,UAAU,CADV,UAGA,CAEF,0BACE,mBACA,kBI9BF,uBAGI,mBACA,YAFA,eADA,SAGA,CAIJ,wBAKI,WAJA,gBACA,mBAEA,mBADA,iBAEA,CCbJ,sBAEI,UAAS,CADT,YACU,CAGd,6BAEI,wBHPS,CGQT,mBAFA,YAEA","sources":["index.scss","components/Footer/Footer.module.scss","_vars.scss","components/Header/Header.module.scss","components/ServiceRow/ServiceRow.module.scss","components/RegexView/RegexView.module.scss"],"sourcesContent":["\n@use \"vars\" as *;\n\n@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');\n\nbody {\n margin: 0;\n font-family: 'Lato', sans-serif;\n}\n\n.center-flex{\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.center-flex-row{\n @extend .center-flex;\n flex-direction: column;\n}\n\n.flex-spacer{\n flex-grow: 1;\n}\n\n::-webkit-scrollbar {\n width: 12px;\n margin:3px;\n background: #333;\n cursor: pointer;\n}\n::-webkit-scrollbar-thumb {\n background: #757575;\n border-radius: 8px;\n}","@use \"../../vars\" as *;\r\n@use \"../../index.scss\" as *;\r\n\r\n.footer{\r\n height: 150px;\r\n margin-top: 50px;\r\n background-color: $primary_color;\r\n @extend .center-flex;\r\n}","\r\n$primary_color: #242a33;\r\n$second_color: #1A1B1E;\r\n$third_color:#25262b;\r\n","\r\n@use \"../../vars\" as *;\r\n\r\n.header{\r\n width: 100%;\r\n height: 140px;\r\n background-color: $primary_color;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.logo{\r\n width: 200px;\r\n margin-left: 40px;\r\n height: 70%;\r\n}","\r\n@use \"../../index.scss\" as *;\r\n\r\n.row{\r\n width: 95%;\r\n padding: 30px 0px;\r\n border-radius: 20px;\r\n margin: 10px;\r\n @extend .center-flex;\r\n}\r\n\r\n.name{\r\n font-size: 2.3em;\r\n font-weight: bolder;\r\n margin-right: 10px;\r\n margin-bottom: 13px;\r\n color:#FFF;\r\n}","\r\n@use \"../../vars\" as *;\r\n\r\n.box{\r\n padding:30px;\r\n margin:5px;\r\n}\r\n\r\n.regex_text{\r\n padding: 10px;\r\n background-color: $third_color;\r\n border-radius: 15px;\r\n}"],"names":[],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -48,27 +48,6 @@ export async function postapi(path:string,data:any,is_form:boolean=false):Promis
}); });
} }
export async function postform(path:string,data:any):Promise<any>{
return await new Promise((resolve, reject) => {
fetch(`/api/${path}`, {
method: 'POST',
credentials: "same-origin",
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(res => {
if(res.status === 401) window.location.reload()
if(!res.ok) reject(res.statusText)
res.json().then( res => resolve(res) ).catch( err => reject(err))
})
.catch(err => {
reject(err)
})
});
}
export function fireUpdateRequest(){ export function fireUpdateRequest(){
window.dispatchEvent(new Event(eventUpdateName)) window.dispatchEvent(new Event(eventUpdateName))
} }