parent 80a38f0d50
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:
100
backend/app.py
100
backend/app.py
@@ -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
|
||||
)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -443,7 +443,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 5)
|
||||
|
||||
@@ -47,6 +47,33 @@ class SQLite():
|
||||
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):
|
||||
self.db = 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
|
||||
|
||||
@@ -98,10 +125,6 @@ class ServiceManager:
|
||||
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
12
frontend/build/asset-manifest.json
Normal file → Executable 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
2
frontend/build/index.html
Normal file → Executable 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>
|
||||
@@ -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*/
|
||||
2
frontend/build/static/css/main.c375ae17.css
Executable file
2
frontend/build/static/css/main.c375ae17.css
Executable 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*/
|
||||
2
frontend/build/static/css/main.0efd334b.css.map → frontend/build/static/css/main.c375ae17.css.map
Normal file → Executable file
2
frontend/build/static/css/main.0efd334b.css.map → frontend/build/static/css/main.c375ae17.css.map
Normal file → Executable 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":""}
|
||||
6
frontend/build/static/js/main.dbb30aee.js → frontend/build/static/js/main.e6c85636.js
Normal file → Executable file
6
frontend/build/static/js/main.dbb30aee.js → frontend/build/static/js/main.e6c85636.js
Normal file → Executable file
File diff suppressed because one or more lines are too long
0
frontend/build/static/js/main.dbb30aee.js.LICENSE.txt → frontend/build/static/js/main.e6c85636.js.LICENSE.txt
Normal file → Executable file
0
frontend/build/static/js/main.dbb30aee.js.LICENSE.txt → frontend/build/static/js/main.e6c85636.js.LICENSE.txt
Normal file → Executable file
2
frontend/build/static/js/main.dbb30aee.js.map → frontend/build/static/js/main.e6c85636.js.map
Normal file → Executable file
2
frontend/build/static/js/main.dbb30aee.js.map → frontend/build/static/js/main.e6c85636.js.map
Normal file → Executable file
File diff suppressed because one or more lines are too long
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user