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 datetime import datetime, timedelta
import sqlite3, uvicorn, sys, secrets, re, os, asyncio, httpx, urllib, websockets
from tabnanny import check
from typing import Union
from fastapi import FastAPI, Request, HTTPException, WebSocket, Depends
from pydantic import BaseModel
from fastapi import FastAPI, HTTPException, WebSocket, Depends
from pydantic import BaseModel, BaseSettings
from fastapi.responses import FileResponse, StreamingResponse
from utils import SQLite, KeyValueStorage, gen_internal_port, ProxyManager, from_name_get_id, STATUS
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
@@ -20,16 +17,22 @@ db = SQLite('db/firegex.db')
conf = KeyValueStorage(db)
firewall = ProxyManager(db)
JWT_ALGORITHM="HS256"
JWT_SECRET = secrets.token_hex(32)
APP_STATUS = "init"
REACT_BUILD_DIR = "../frontend/build/" if not ON_DOCKER else "frontend/"
REACT_HTML_PATH = os.path.join(REACT_BUILD_DIR,"index.html")
class Settings(BaseSettings):
JWT_ALGORITHM: str = "HS256"
REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
settings = Settings()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False)
crypto = CryptContext(schemes=["bcrypt"], deprecated="auto")
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")
async def shutdown_event():
await firewall.close()
@@ -37,50 +40,21 @@ async def shutdown_event():
@app.on_event("startup")
async def startup_event():
global APP_STATUS
db.connect()
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"
db.init()
if not JWT_SECRET(): conf.put("secret", secrets.token_hex(32))
await firewall.reload()
def create_access_token(data: dict):
global JWT_SECRET
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
async def check_login(token: str = Depends(oauth2_scheme)):
global JWT_SECRET
if not token:
return False
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")
except JWTError:
return False
@@ -97,9 +71,8 @@ async def is_loggined(auth: bool = Depends(check_login)):
@app.get("/api/status")
async def get_status(auth: bool = Depends(check_login)):
global APP_STATUS
return {
"status":APP_STATUS,
"status": APP_STATUS(),
"loggined": auth
}
@@ -112,29 +85,23 @@ class PasswordChangeForm(BaseModel):
@app.post("/api/login")
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 == "":
return {"status":"Cannot insert an empty password!"}
await asyncio.sleep(0.3) # No bruteforce :)
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"}
raise HTTPException(406,"Wrong password!")
@app.post('/api/change-password')
async def change_password(form: PasswordChangeForm, auth: bool = Depends(is_loggined)):
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 == "":
return {"status":"Cannot insert an empty password!"}
if form.expire:
JWT_SECRET = secrets.token_hex(32)
conf.put("secret", secrets.token_hex(32))
hash_psw = crypto.hash(form.password)
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')
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 == "":
return {"status":"Cannot insert an empty password!"}
hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw)
APP_STATUS = "run"
return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
@app.get('/api/general-stats')
@@ -165,7 +129,6 @@ async def get_general_stats(auth: bool = Depends(is_loggined)):
@app.get('/api/services')
async def get_services(auth: bool = Depends(is_loggined)):
return db.query("""
SELECT
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')
async def get_service_stop(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.STOP)
return {'status': 'ok'}
@app.get('/api/service/{service_id}/pause')
async def get_service_pause(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.PAUSE)
return {'status': 'ok'}
@app.get('/api/service/{service_id}/start')
async def get_service_start(service_id: str, auth: bool = Depends(is_loggined)):
await firewall.get(service_id).next(STATUS.ACTIVE)
return {'status': 'ok'}
@app.get('/api/service/{service_id}/delete')
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 regexes WHERE service_id = ?;', 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')
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)
await firewall.get(service_id).update_port()
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')
async def get_service_regexes(service_id: str, auth: bool = Depends(is_loggined)):
return db.query("""
SELECT
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}')
async def get_regex_id(regex_id: int, auth: bool = Depends(is_loggined)):
res = db.query("""
SELECT
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')
async def get_regex_delete(regex_id: int, auth: bool = Depends(is_loggined)):
res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id)
if len(res) != 0:
db.query('DELETE FROM regexes WHERE regex_id = ?;', regex_id)
await firewall.get(res[0]["service_id"]).update_filters()
@@ -274,7 +228,6 @@ class RegexAddForm(BaseModel):
@app.post('/api/regexes/add')
async def post_regexes_add(form: RegexAddForm, auth: bool = Depends(is_loggined)):
try:
re.compile(b64decode(form.regex))
except Exception:
@@ -294,7 +247,6 @@ class ServiceAddForm(BaseModel):
@app.post('/api/services/add')
async def post_services_add(form: ServiceAddForm, auth: bool = Depends(is_loggined)):
serv_id = from_name_get_id(form.name)
try:
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)
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):
return FileResponse(REACT_HTML_PATH, media_type='text/html')
return FileResponse(settings.REACT_HTML_PATH, media_type='text/html')
else:
return FileResponse(file_request)
@@ -356,5 +308,5 @@ if __name__ == '__main__':
port=int(os.getenv("PORT","4444")),
reload=DEBUG,
access_log=DEBUG,
workers=2
workers=1
)

View File

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

View File

@@ -443,7 +443,6 @@ private:
};
int main(int argc, char* argv[])
{
if (argc < 5)
@@ -500,4 +499,4 @@ int main(int argc, char* argv[])
#endif
return 0;
}
}

View File

@@ -46,6 +46,33 @@ class SQLite():
cur.close()
try: self.conn.commit()
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:
def __init__(self, db):
@@ -70,8 +97,7 @@ class STATUS:
PAUSE = "pause"
ACTIVE = "active"
class ServiceNotFoundException(Exception):
pass
class ServiceNotFoundException(Exception): pass
class ServiceManager:
def __init__(self, id, db):
@@ -83,7 +109,8 @@ class ServiceManager:
)
self.status = STATUS.STOP
self.filters = {}
self._proxy_update()
self._update_port_from_db()
self._update_filters_from_db()
self.lock = asyncio.Lock()
self.starter = None
@@ -97,11 +124,7 @@ class ServiceManager:
if len(res) == 0: raise ServiceNotFoundException()
self.proxy.internal_port = res[0]["internal_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):
res = self.db.query("""
SELECT
@@ -133,8 +156,8 @@ class ServiceManager:
)
self.proxy.filters = list(self.filters.values())
def __update_status_db(self, id, status):
self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, id)
def __update_status_db(self, status):
self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.id)
async def next(self,to):
async with self.lock:
@@ -171,7 +194,7 @@ class ServiceManager:
def _set_status(self,status):
self.status = status
self.__update_status_db(self.id,status)
self.__update_status_db(status)
async def update_filters(self):
@@ -222,7 +245,10 @@ class ProxyManager:
await self.proxy_table[srv_id].next(req_status)
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):
try:

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

@@ -1,13 +1,13 @@
{
"files": {
"main.css": "/static/css/main.0efd334b.css",
"main.js": "/static/js/main.dbb30aee.js",
"main.css": "/static/css/main.c375ae17.css",
"main.js": "/static/js/main.e6c85636.js",
"index.html": "/index.html",
"main.0efd334b.css.map": "/static/css/main.0efd334b.css.map",
"main.dbb30aee.js.map": "/static/js/main.dbb30aee.js.map"
"main.c375ae17.css.map": "/static/css/main.c375ae17.css.map",
"main.e6c85636.js.map": "/static/js/main.e6c85636.js.map"
},
"entrypoints": [
"static/css/main.0efd334b.css",
"static/js/main.dbb30aee.js"
"static/css/main.c375ae17.css",
"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(){
window.dispatchEvent(new Event(eventUpdateName))
}