oauth2 on fastapi

This commit is contained in:
nik012003
2022-06-28 21:49:03 +02:00
parent 4971281f5a
commit e0a881abdb
14 changed files with 159 additions and 189 deletions

View File

@@ -1,10 +1,15 @@
from base64 import b64decode
import sqlite3, uvicorn, sys, bcrypt, secrets, re, os, asyncio, httpx, urllib, websockets
from fastapi import FastAPI, Request, HTTPException, WebSocket
from starlette.middleware.sessions import SessionMiddleware
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.responses import FileResponse, StreamingResponse
from utils import SQLite, KeyValueStorage, gen_internal_port, ProxyManager, from_name_get_id, STATUS
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER"
DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG"
@@ -15,6 +20,14 @@ 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")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False)
crypto = CryptContext(schemes=["bcrypt"], deprecated="auto")
app = FastAPI(debug=DEBUG)
@app.on_event("shutdown")
@@ -56,28 +69,38 @@ async def startup_event():
await firewall.reload()
app.add_middleware(SessionMiddleware, secret_key=os.urandom(32))
SESSION_TOKEN = secrets.token_hex(8)
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")
def create_access_token(data: dict):
global JWT_SECRET
to_encode = data.copy()
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=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])
logged_in: bool = payload.get("logged_in")
except JWTError:
return False
return logged_in
def is_loggined(request: Request):
global SESSION_TOKEN
return request.session.get("token", "") == SESSION_TOKEN
def login_check(request: Request):
if is_loggined(request): return True
raise HTTPException(status_code=401, detail="Invalid login session!")
async def is_loggined(auth: bool = Depends(check_login)):
if not auth:
raise HTTPException(
status_code=401,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return True
@app.get("/api/status")
async def get_status(request: Request):
async def get_status(auth: bool = Depends(check_login)):
global APP_STATUS
return {
"status":APP_STATUS,
"loggined": is_loggined(request)
"loggined": auth
}
class PasswordForm(BaseModel):
@@ -88,58 +111,51 @@ class PasswordChangeForm(BaseModel):
expire: bool
@app.post("/api/login")
async def login_api(request: Request, form: PasswordForm):
global APP_STATUS, SESSION_TOKEN
async def login_api(form: OAuth2PasswordRequestForm = Depends()):
global APP_STATUS, JWT_SECRET
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!")
if bcrypt.checkpw(form.password.encode(), conf.get("password").encode()):
request.session["token"] = SESSION_TOKEN
return { "status":"ok" }
return {"status":"Wrong password!"}
@app.get("/api/logout")
async def logout(request: Request):
request.session["token"] = False
return { "status":"ok" }
@app.post('/api/change-password')
async def change_password(request: Request, form: PasswordChangeForm):
login_check(request)
global APP_STATUS, SESSION_TOKEN
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 form.password == "":
return {"status":"Cannot insert an empty password!"}
if form.expire:
SESSION_TOKEN = secrets.token_hex(8)
request.session["token"] = SESSION_TOKEN
JWT_SECRET = secrets.token_hex(32)
hash_psw = bcrypt.hashpw(form.password.encode(), bcrypt.gensalt())
conf.put("password",hash_psw.decode())
return {"status":"ok"}
hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw)
return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
@app.post('/api/set-password')
async def set_password(request: Request, form: PasswordForm):
global APP_STATUS, SESSION_TOKEN
async def set_password(form: PasswordForm):
global APP_STATUS, JWT_SECRET
if APP_STATUS != "init": raise HTTPException(status_code=400)
if form.password == "":
return {"status":"Cannot insert an empty password!"}
hash_psw = bcrypt.hashpw(form.password.encode(), bcrypt.gensalt())
conf.put("password",hash_psw.decode())
hash_psw = crypto.hash(form.password)
conf.put("password",hash_psw)
APP_STATUS = "run"
request.session["token"] = SESSION_TOKEN
return {"status":"ok"}
return {"status":"ok", "access_token": create_access_token({"logged_in": True})}
@app.get('/api/general-stats')
async def get_general_stats(request: Request):
login_check(request)
async def get_general_stats(auth: bool = Depends(is_loggined)):
return db.query("""
SELECT
(SELECT COALESCE(SUM(blocked_packets),0) FROM regexes) closed,
@@ -148,8 +164,8 @@ async def get_general_stats(request: Request):
""")[0]
@app.get('/api/services')
async def get_services(request: Request):
login_check(request)
async def get_services(auth: bool = Depends(is_loggined)):
return db.query("""
SELECT
s.service_id `id`,
@@ -164,8 +180,8 @@ async def get_services(request: Request):
""")
@app.get('/api/service/{service_id}')
async def get_service(request: Request, service_id: str):
login_check(request)
async def get_service(service_id: str, auth: bool = Depends(is_loggined)):
res = db.query("""
SELECT
s.service_id `id`,
@@ -182,26 +198,26 @@ async def get_service(request: Request, service_id: str):
return res[0]
@app.get('/api/service/{service_id}/stop')
async def get_service_stop(request: Request, service_id: str):
login_check(request)
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(request: Request, service_id: str):
login_check(request)
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(request: Request, service_id: str):
login_check(request)
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(request: Request, service_id: str):
login_check(request)
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)
@@ -209,16 +225,16 @@ async def get_service_delete(request: Request, service_id: str):
@app.get('/api/service/{service_id}/regen-port')
async def get_regen_port(request: Request, service_id: str):
login_check(request)
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'}
@app.get('/api/service/{service_id}/regexes')
async def get_service_regexes(request: Request, service_id: str):
login_check(request)
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,
@@ -227,8 +243,8 @@ async def get_service_regexes(request: Request, service_id: str):
""", service_id)
@app.get('/api/regex/{regex_id}')
async def get_regex_id(request: Request, regex_id: int):
login_check(request)
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,
@@ -239,8 +255,8 @@ async def get_regex_id(request: Request, regex_id: int):
return res[0]
@app.get('/api/regex/{regex_id}/delete')
async def get_regex_delete(request: Request, regex_id: int):
login_check(request)
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:
@@ -257,8 +273,8 @@ class RegexAddForm(BaseModel):
is_case_sensitive: bool
@app.post('/api/regexes/add')
async def post_regexes_add(request: Request, form: RegexAddForm):
login_check(request)
async def post_regexes_add(form: RegexAddForm, auth: bool = Depends(is_loggined)):
try:
re.compile(b64decode(form.regex))
except Exception:
@@ -277,8 +293,8 @@ class ServiceAddForm(BaseModel):
port: int
@app.post('/api/services/add')
async def post_services_add(request: Request, form: ServiceAddForm):
login_check(request)
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 (?, ?, ?, ?, ?)",
@@ -323,7 +339,7 @@ if DEBUG:
await asyncio.gather(fwd_task, rev_task)
@app.get("/{full_path:path}")
async def catch_all(request: Request, full_path:str):
async def catch_all(full_path:str):
if DEBUG:
try:
return await frontend_debug_proxy(full_path)
@@ -339,5 +355,6 @@ if __name__ == '__main__':
host="0.0.0.0",
port=int(os.getenv("PORT","4444")),
reload=DEBUG,
access_log=DEBUG
access_log=DEBUG,
workers=2
)

View File

@@ -1,6 +1,4 @@
import subprocess, re, os, asyncio
#c++ -o proxy proxy.cpp
import re, os, asyncio
class Filter:
def __init__(self, regex, is_case_sensitive=True, is_blacklist=True, c_to_s=False, s_to_c=False, blocked_packets=0, code=None):

View File

@@ -1,5 +1,5 @@
fastapi[all]
httpx
uvicorn[standard]
bcrypt
kthread
passlib[bcrypt]
python-jose[cryptography]

View File

@@ -182,6 +182,7 @@ class ServiceManager:
def __proxy_starter(self,to):
async def func():
try:
while True:
if check_port_is_open(self.proxy.public_port):
self._set_status(to)
@@ -190,6 +191,8 @@ class ServiceManager:
return
else:
await asyncio.sleep(.5)
except Exception:
await self.proxy.stop()
self.starter = asyncio.create_task(func())
class ProxyManager:

View File

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

View File

@@ -1 +0,0 @@
<!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.f153478b.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>

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

@@ -1 +0,0 @@
{"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":""}

File diff suppressed because one or more lines are too long

View File

@@ -1,70 +0,0 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* React Router v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

View File

@@ -98,7 +98,7 @@ function App() {
if(!res){
setSystemStatus({...systemStatus, loggined:true})
}else{
setError(res)
setError("Login failed")
}
}).catch( err => setError(err.toString()))
setLoadingBtn(false)

View File

@@ -23,13 +23,24 @@ export type ServerResponse = {
status:string
}
export type ServerResponseToken = {
status:string,
access_token?:string
}
export type LoginResponse = {
status?:string,
access_token:string,
token_type:string
}
export type ServerStatusResponse = {
status:string,
loggined:boolean
}
export type PasswordSend = {
password:string
password:string,
}
export type ChangePassword = {

View File

@@ -1,7 +1,7 @@
import { showNotification } from "@mantine/notifications";
import { ImCross } from "react-icons/im";
import { TiTick } from "react-icons/ti"
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, RegexAddForm, ServerStatusResponse, PasswordSend, ChangePassword } from "./models";
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, RegexAddForm, ServerStatusResponse, PasswordSend, ChangePassword, LoginResponse, ServerResponseToken } from "./models";
var Buffer = require('buffer').Buffer
@@ -9,9 +9,12 @@ export const eventUpdateName = "update-info"
export async function getapi(path:string):Promise<any>{
return await new Promise((resolve, reject) => {
fetch(`/api/${path}`,{credentials: "same-origin"})
.then(res => {
fetch(`/api/${path}`,{
credentials: "same-origin",
headers: { "Authorization" : "Bearer " + window.localStorage.getItem("access_token")}
}).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))
@@ -22,7 +25,30 @@ export async function getapi(path:string):Promise<any>{
});
}
export async function postapi(path:string,data:any):Promise<any>{
export async function postapi(path:string,data:any,is_form:boolean=false):Promise<any>{
return await new Promise((resolve, reject) => {
fetch(`/api/${path}`, {
method: 'POST',
credentials: "same-origin",
cache: 'no-cache',
headers: {
'Content-Type': is_form ? 'application/x-www-form-urlencoded' : 'application/json',
"Authorization" : "Bearer " + window.localStorage.getItem("access_token")
},
body: is_form ? data : JSON.stringify(data)
}).then(res => {
if(res.status === 401) window.location.reload()
if(res.status === 406) resolve({status:"Wrong Password"})
if(!res.ok) reject(res.statusText)
res.json().then( res => resolve(res) ).catch( err => reject(err))
})
.catch(err => {
reject(err)
})
});
}
export async function postform(path:string,data:any):Promise<any>{
return await new Promise((resolve, reject) => {
fetch(`/api/${path}`, {
method: 'POST',
@@ -64,23 +90,29 @@ export async function serviceinfo(service_id:string){
}
export async function logout(){
const { status } = await getapi(`logout`) as ServerResponse;
return status === "ok"?undefined:status
window.localStorage.removeItem("access_token")
}
export async function setpassword(data:PasswordSend) {
const { status } = await postapi("set-password",data) as ServerResponse;
const { status, access_token } = await postapi("set-password",data) as ServerResponseToken;
if (access_token)
window.localStorage.setItem("access_token", access_token);
return status === "ok"?undefined:status
}
export async function changepassword(data:ChangePassword) {
const { status } = await postapi("change-password",data) as ServerResponse;
const { status, access_token } = await postapi("change-password",data) as ServerResponseToken;
console.log(access_token)
if (access_token)
window.localStorage.setItem("access_token", access_token);
return status === "ok"?undefined:status
}
export async function login(data:PasswordSend) {
const { status } = await postapi("login",data) as ServerResponse;
return status === "ok"?undefined:status
const from = "username=login&password=" + encodeURI(data.password);
const { status, access_token } = await postapi("login",from,true) as LoginResponse;
window.localStorage.setItem("access_token", access_token);
return status;
}
export async function deleteregex(regex_id:number){