KodaChat is generating..
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,7 @@ __pycache__/
|
||||
*.log
|
||||
|
||||
# Виртуальное окружение
|
||||
# venv/
|
||||
.venv/
|
||||
# env/
|
||||
|
||||
# IDE
|
||||
|
||||
13
GPT_chat.code-workspace
Normal file
13
GPT_chat.code-workspace
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "GPT_chat",
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"path": "../Free-GPT4-WEB-API/src/data"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
1
data/cookies.json
Normal file
1
data/cookies.json
Normal file
@@ -0,0 +1 @@
|
||||
{"field": "value"}
|
||||
1
data/proxies.json
Normal file
1
data/proxies.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"protocol": "socks5", "username": "usr", "password": "pass", "ip": "0.0.0.0", "port": "8080"}, {"protocol": "https", "username": "usr", "password": "pass", "ip": "0.0.0.1", "port": "443"}]
|
||||
BIN
data/settings.db
Normal file
BIN
data/settings.db
Normal file
Binary file not shown.
1
har_and_cookies/.nodriver_is_open
Normal file
1
har_and_cookies/.nodriver_is_open
Normal file
@@ -0,0 +1 @@
|
||||
1756455950.493793
|
||||
1
har_and_cookies/auth_Copilot.json
Normal file
1
har_and_cookies/auth_Copilot.json
Normal file
@@ -0,0 +1 @@
|
||||
{"api_key": null, "cookies": {}}
|
||||
1
har_and_cookies/auth_OpenaiChat.json
Normal file
1
har_and_cookies/auth_OpenaiChat.json
Normal file
File diff suppressed because one or more lines are too long
564
main.py
Normal file
564
main.py
Normal file
@@ -0,0 +1,564 @@
|
||||
import re
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import sqlite3
|
||||
from uuid import uuid4
|
||||
import threading
|
||||
|
||||
# GPT Library
|
||||
import g4f
|
||||
from g4f.api import run_api
|
||||
|
||||
# Server
|
||||
from flask import Flask, redirect, render_template
|
||||
from flask import request
|
||||
import getpass
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
UPLOAD_FOLDER = 'data/'
|
||||
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 # 16 MB
|
||||
|
||||
# Settings file path
|
||||
SETTINGS_FILE = "./data/settings.db"
|
||||
PROXIES_FILE = "./data/proxies.json"
|
||||
|
||||
# Available providers
|
||||
PROVIDERS = {
|
||||
"Auto": "",
|
||||
# "Acytoo": g4f.Provider.Acytoo,
|
||||
# "Aichat": g4f.Provider.Aichat,
|
||||
# "Ails": g4f.Provider.Ails,
|
||||
# "BlackBox": g4f.Provider.Blackbox,
|
||||
# "Chatgpt4o": g4f.Provider.Chatgpt4o,
|
||||
# "ChatGpt": g4f.Provider.ChatGpt,
|
||||
# "ChatGptt" : g4f.Provider.ChatGptt,
|
||||
# "DeepInfraChat": g4f.Provider.DeepInfraChat,
|
||||
# "Glider": g4f.Provider.Glider,
|
||||
# "H2o": g4f.Provider.H2o,
|
||||
# "HuggingChat": g4f.Provider.HuggingChat,
|
||||
# "Opchatgpts": g4f.Provider.Opchatgpts,
|
||||
# "OpenAssistant": g4f.Provider.OpenAssistant,
|
||||
# "OpenaiChat": g4f.Provider.OpenaiChat,
|
||||
# "Raycast": g4f.Provider.Raycast,
|
||||
# "Theb": g4f.Provider.Theb,
|
||||
# "Wewordle": g4f.Provider.Wewordle,
|
||||
# "You": g4f.Provider.You,
|
||||
# "Yqcloud": g4f.Provider.Yqcloud,
|
||||
# "Pizzagpt": g4f.Provider.Pizzagpt,
|
||||
# "HuggingChat": g4f.Provider.HuggingChat,
|
||||
# "HuggingFace": g4f.Provider.HuggingFace
|
||||
|
||||
}
|
||||
|
||||
GENERIC_MODELS = ["gpt-3.5-turbo", "gpt-4", "gpt-4o","gpt-4o-mini"]
|
||||
|
||||
|
||||
print(
|
||||
"""
|
||||
FreeGPT4 Web API - A Web API for GPT-4
|
||||
Repo: github.com/aledipa/FreeGPT4-WEB-API
|
||||
By: Alessandro Di Pasquale
|
||||
|
||||
GPT4Free Credits: github.com/xtekky/gpt4free
|
||||
""",
|
||||
)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--remove-sources",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Remove the sources from the response",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-gui",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Use a graphical interface for settings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--private-mode",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Use a private token to access the API",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-proxies",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Use one or more proxies to avoid being blocked or banned",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-history",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Enable the history of the messages",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password",
|
||||
action='store',
|
||||
required=False,
|
||||
help="Set or change the password for the settings page [mandatory in docker envirtonment]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cookie-file",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Use a cookie file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--file-input",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Add the file as input support",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Change the port (default: 5500)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Change the model (default: gpt_4)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--provider",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Change the provider (default: Bing)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--keyword",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Add the keyword support",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--system-prompt",
|
||||
action='store',
|
||||
required=False,
|
||||
type=str,
|
||||
help="Use a system prompt to 'customize' the answers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-fast-api",
|
||||
action='store_true',
|
||||
required=False,
|
||||
help="Use the fast API standard (PORT 1337 - compatible with OpenAI integrations)",
|
||||
)
|
||||
|
||||
args, unknown = parser.parse_known_args()
|
||||
|
||||
# Get the absolute path of the script
|
||||
script_path = os.path.abspath(__file__)
|
||||
# Get the directory of the script
|
||||
script_dir = os.path.dirname(script_path)
|
||||
# Change the current working directory
|
||||
os.chdir(script_dir)
|
||||
|
||||
# Loads the proxies from the file
|
||||
if (args.enable_proxies and os.path.exists(PROXIES_FILE)):
|
||||
proxies = json.load(open(PROXIES_FILE))
|
||||
else:
|
||||
proxies = None
|
||||
|
||||
# Crates the Fast API Thread
|
||||
t = threading.Thread(target=run_api, name="fastapi")
|
||||
|
||||
# Loads the settings from the file
|
||||
def load_settings(file=SETTINGS_FILE):
|
||||
conn = sqlite3.connect(file)
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT * FROM settings")
|
||||
data = c.fetchone()
|
||||
conn.close()
|
||||
# Converts the data to a dictionary
|
||||
data = {
|
||||
"keyword": data[1],
|
||||
"file_input": data[2],
|
||||
"port": data[3],
|
||||
"provider": data[4],
|
||||
"model": data[5],
|
||||
"cookie_file": data[6],
|
||||
"token": data[7],
|
||||
"remove_sources": data[8],
|
||||
"system_prompt": data[9],
|
||||
"message_history": data[10],
|
||||
"proxies": data[11],
|
||||
"password": data[12],
|
||||
"fast_api": data[13]
|
||||
}
|
||||
print(data)
|
||||
return data
|
||||
|
||||
def start_fast_api():
|
||||
print("[!] FAST API PORT: 1336")
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
# Updates the settings
|
||||
data = load_settings()
|
||||
if (args.keyword == None):
|
||||
args.keyword = data["keyword"]
|
||||
|
||||
if (args.file_input == False):
|
||||
args.file_input = data["file_input"]
|
||||
|
||||
if (args.port == None):
|
||||
args.port = data["port"]
|
||||
|
||||
if (args.provider == None):
|
||||
args.provider = data["provider"]
|
||||
|
||||
if (args.model == None):
|
||||
args.model = data["model"]
|
||||
|
||||
if (args.cookie_file == None):
|
||||
args.cookie_file = data["cookie_file"]
|
||||
|
||||
if (args.private_mode and data["token"] == ""):
|
||||
token = str(uuid4())
|
||||
data["token"] = token
|
||||
elif (data["token"] != ""):
|
||||
token = data["token"]
|
||||
args.private_mode = True
|
||||
|
||||
if (args.remove_sources == False):
|
||||
args.remove_sources = data["remove_sources"]
|
||||
|
||||
if (args.system_prompt == None):
|
||||
args.system_prompt = data["system_prompt"]
|
||||
|
||||
if (args.enable_history == False):
|
||||
args.enable_history = data["message_history"]
|
||||
|
||||
if (args.enable_proxies == False):
|
||||
args.enable_proxies = data["proxies"]
|
||||
|
||||
if (args.enable_fast_api or data["fast_api"]):
|
||||
start_fast_api()
|
||||
|
||||
|
||||
# Initializes the message history
|
||||
message_history = [{"role": "system", "content": args.system_prompt}]
|
||||
|
||||
if (args.enable_gui):
|
||||
# Asks for password to set to lock the settings page
|
||||
# Checks if settings.db contains a password
|
||||
try:
|
||||
set_password = True
|
||||
if (args.password != None):
|
||||
password = args.password
|
||||
confirm_password = password
|
||||
else:
|
||||
if (data["password"] == ""):
|
||||
password = getpass.getpass("Settings page password:\n > ")
|
||||
confirm_password = getpass.getpass("Confirm password:\n > ")
|
||||
else:
|
||||
set_password = False
|
||||
|
||||
if (set_password):
|
||||
if(password == "" or confirm_password == ""):
|
||||
print("[X] Password cannot be empty")
|
||||
exit()
|
||||
|
||||
if ((password != confirm_password) and (data["password"] == "")):
|
||||
print("[X] Passwords don't match")
|
||||
exit()
|
||||
else:
|
||||
password = generate_password_hash(password)
|
||||
confirm_password = generate_password_hash(confirm_password)
|
||||
print("[V] Password set.")
|
||||
try:
|
||||
conn = sqlite3.connect(SETTINGS_FILE)
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE settings SET password = ?", (password,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("[V] Password saved.")
|
||||
except Exception as error:
|
||||
print("[X] Error:", error)
|
||||
exit()
|
||||
except Exception as error:
|
||||
print("[X] Error:", error)
|
||||
exit()
|
||||
else:
|
||||
print("[!] GUI disabled")
|
||||
|
||||
# Saves the settings to the file
|
||||
def save_settings(request, file):
|
||||
conn = sqlite3.connect(file)
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE settings SET file_input = ?", (bool(request.form["file_input"] == "true"),))
|
||||
c.execute("UPDATE settings SET remove_sources = ?", (bool(request.form["remove_sources"] == "true"),))
|
||||
c.execute("UPDATE settings SET port = ?", (request.form["port"],))
|
||||
c.execute("UPDATE settings SET model = ?", (request.form["model"],))
|
||||
c.execute("UPDATE settings SET keyword = ?", (request.form["keyword"],))
|
||||
c.execute("UPDATE settings SET provider = ?", (request.form["provider"],))
|
||||
c.execute("UPDATE settings SET system_prompt = ?", (request.form["system_prompt"],))
|
||||
c.execute("UPDATE settings SET message_history = ?", (bool(request.form["message_history"] == "true"),))
|
||||
c.execute("UPDATE settings SET proxies = ?", (bool(request.form["proxies"] == "true"),))
|
||||
c.execute("UPDATE settings SET fast_api = ?", (bool(request.form["fast_api"] == "true"),))
|
||||
if (len(request.form["new_password"]) > 0):
|
||||
c.execute("UPDATE settings SET password = ?", (generate_password_hash(request.form["new_password"]),))
|
||||
|
||||
file = request.files["cookie_file"]
|
||||
if (bool(request.form["private_mode"] == "true")):
|
||||
c.execute("UPDATE settings SET token = ?", (request.form["token"],))
|
||||
args.private_mode = True
|
||||
else:
|
||||
c.execute("UPDATE settings SET token = ''")
|
||||
args.private_mode = False
|
||||
#checks if the file is not empty
|
||||
if file.filename != '':
|
||||
#checks if the file is a json file
|
||||
if file.filename.endswith('.json'):
|
||||
#saves the file
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
#updates the cookie_file
|
||||
c.execute("UPDATE settings SET cookie_file = ?", (os.path.join(app.config['UPLOAD_FOLDER'], filename),))
|
||||
else:
|
||||
print("The file is not a json file")
|
||||
|
||||
if (args.enable_proxies or bool(request.form["proxies"] == "true")):
|
||||
# Extracts the proxies from the form
|
||||
proxies = []
|
||||
i = 1
|
||||
while True:
|
||||
if (("proxy_" + str(i)) in request.form):
|
||||
proxy = request.form["proxy_" + str(i)]
|
||||
if (proxy != ""):
|
||||
# Checks if the proxy syntax is correct
|
||||
if (proxy.count(":") == 3 and proxy.count("@") == 1):
|
||||
proxy = {
|
||||
"protocol": proxy.split("://")[0],
|
||||
"username": proxy.split("://")[1].split(":")[0],
|
||||
"password": proxy.split("://")[1].split(":")[1].split("@")[0],
|
||||
"ip": proxy.split("://")[1].split(":")[1].split("@")[1],
|
||||
"port": proxy.split("://")[1].split(":")[2]
|
||||
}
|
||||
proxies.append(proxy)
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
|
||||
# Saves the proxies to the file proxies.json
|
||||
with open(PROXIES_FILE, "w") as pf:
|
||||
json.dump(proxies, pf)
|
||||
pf.close()
|
||||
|
||||
if (bool(request.form["fast_api"] == "true") and not args.enable_fast_api):
|
||||
start_fast_api()
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
|
||||
# Loads the settings from the file and updates the args
|
||||
def apply_settings(file):
|
||||
data = load_settings(file)
|
||||
args.keyword = data["keyword"]
|
||||
args.file_input = data["file_input"]
|
||||
args.port = data["port"]
|
||||
args.provider = data["provider"]
|
||||
args.model = data["model"]
|
||||
args.cookie_file = data["cookie_file"]
|
||||
args.token = data["token"]
|
||||
args.remove_sources = data["remove_sources"]
|
||||
args.system_prompt = data["system_prompt"]
|
||||
args.enable_history = data["message_history"]
|
||||
args.enable_proxies = data["proxies"]
|
||||
args.password = data["password"]
|
||||
args.enable_fast_api = data["fast_api"]
|
||||
return
|
||||
|
||||
|
||||
@app.route("/", methods=["GET", "POST"])
|
||||
async def index() -> str:
|
||||
"""
|
||||
Main function
|
||||
"""
|
||||
|
||||
# Starts the bot and gets the input
|
||||
print("Initializing...")
|
||||
question = None
|
||||
|
||||
print("start")
|
||||
if request.method == "GET":
|
||||
question = request.args.get(args.keyword) #text
|
||||
print(args.private_mode)
|
||||
if (args.private_mode and request.args.get("token") != data["token"]):
|
||||
return "<p id='response'>Invalid token</p>"
|
||||
print("get")
|
||||
else:
|
||||
file = request.files["file"]
|
||||
text = file.read().decode("utf-8")
|
||||
question = text
|
||||
print("Post reading the file", question)
|
||||
|
||||
print("ici")
|
||||
if question is None:
|
||||
return "<p id='response'>Please enter a question</p>"
|
||||
|
||||
# Gets the response from the bot
|
||||
# print(PROVIDERS[args.provider].params) # supported args
|
||||
print("\nCookies: " + str(len(args.cookie_file)))
|
||||
print("\nInput: " + question)
|
||||
if (len(args.cookie_file) != 0):
|
||||
try:
|
||||
cookies = json.load(open(args.cookie_file)) # Loads the cookies from the file
|
||||
print("COOKIES: "+str(cookies))
|
||||
if (len(cookies) == 0):
|
||||
cookies = {"a": "b"} # Dummy cookies
|
||||
except Exception as error:
|
||||
print("[X] Error:", error)
|
||||
exit()
|
||||
else:
|
||||
cookies = {"a": "b"} # Dummy cookies
|
||||
|
||||
|
||||
if (args.enable_history):
|
||||
print("History enabled")
|
||||
message_history.append({"role": "user", "content": question})
|
||||
else:
|
||||
print("History disabled")
|
||||
message_history.clear()
|
||||
message_history.append({"role": "system", "content": args.system_prompt})
|
||||
message_history.append({"role": "user", "content": question})
|
||||
|
||||
proxy = None
|
||||
if (args.enable_proxies):
|
||||
# Extracts a proxy from the list
|
||||
proxy = random.choice(proxies)
|
||||
# Formats the proxy like https://user:password@host:port
|
||||
proxy = f"{proxy['protocol']}://{proxy['username']}:{proxy['password']}@{proxy['ip']}:{proxy['port']}"
|
||||
print("Proxy: " + proxy)
|
||||
|
||||
if (args.provider == "Auto"):
|
||||
response = (
|
||||
await g4f.ChatCompletion.create_async(
|
||||
model=args.model,
|
||||
messages=message_history,
|
||||
cookies=cookies,
|
||||
proxy=proxy
|
||||
)
|
||||
)
|
||||
else:
|
||||
response = (
|
||||
await g4f.ChatCompletion.create_async(
|
||||
model=args.model,
|
||||
provider=args.provider,
|
||||
messages=message_history,
|
||||
cookies=cookies,
|
||||
proxy=proxy
|
||||
)
|
||||
)
|
||||
|
||||
print(response)
|
||||
|
||||
#Joins the response into a single string
|
||||
resp_str = ""
|
||||
for message in response:
|
||||
resp_str += message
|
||||
|
||||
# Cleans the response from the resources links
|
||||
if (args.remove_sources):
|
||||
if re.search(r"\[\^[0-9]+\^\]\[[0-9]+\]", resp_str):
|
||||
resp_str = resp_str.split("\n\n")
|
||||
if len(resp_str) > 1:
|
||||
resp_str.pop(0)
|
||||
resp_str = re.sub(r"\[\^[0-9]+\^\]\[[0-9]+\]", "", str(resp_str[0]))
|
||||
# Returns the response
|
||||
return resp_str
|
||||
# return "<p id='response'>" + resp + "</p>" # Uncomment if preferred
|
||||
|
||||
def auth():
|
||||
# Reads the password from the data file
|
||||
data = load_settings()
|
||||
# Checks if the password is set
|
||||
if (data["password"] != ""):
|
||||
if (request.method == "POST"):
|
||||
password = request.form["password"]
|
||||
if (check_password_hash(data["password"], password)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
async def login():
|
||||
if (args.enable_gui):
|
||||
return render_template("login.html", **locals())
|
||||
else:
|
||||
return "The GUI is disabled. In order to enable it, use the --enable-gui argument."
|
||||
|
||||
@app.route("/settings", methods=["GET", "POST"])
|
||||
async def settings():
|
||||
if (request.method == "GET"):
|
||||
return redirect("/login", code=302)
|
||||
if (auth()):
|
||||
try:
|
||||
providers=PROVIDERS
|
||||
generic_models=GENERIC_MODELS
|
||||
data = load_settings()
|
||||
proxies = json.loads(open(PROXIES_FILE).read())
|
||||
return render_template("settings.html", **locals())
|
||||
except Exception as error:
|
||||
print("[X] Error:", error)
|
||||
return "Error: " + str(error)
|
||||
else:
|
||||
return render_template("login.html", **locals())
|
||||
|
||||
@app.route("/save", methods=["POST"])
|
||||
async def save():
|
||||
if (auth()):
|
||||
try:
|
||||
if (request.method == "POST"):
|
||||
save_settings(request, SETTINGS_FILE)
|
||||
apply_settings(SETTINGS_FILE)
|
||||
|
||||
return "New settings saved and applied successfully!"
|
||||
else:
|
||||
return render_template("login.html", **locals())
|
||||
except Exception as error:
|
||||
print("[X] Error:", error)
|
||||
return "Error: " + str(error)
|
||||
else:
|
||||
return render_template("login.html", **locals())
|
||||
|
||||
@app.route("/models", methods=["GET"])
|
||||
async def get_models():
|
||||
provider = request.args.get("provider")
|
||||
if (provider == "Auto"):
|
||||
return GENERIC_MODELS
|
||||
try:
|
||||
return PROVIDERS[provider].models
|
||||
except:
|
||||
return ["default"]
|
||||
|
||||
@app.route("/generatetoken", methods=["GET", "POST"])
|
||||
async def get_token():
|
||||
return str(uuid4())
|
||||
|
||||
if __name__ == "__main__":
|
||||
# # Starts the server, change the port if needed
|
||||
app.run("0.0.0.0", port=args.port, debug=False)
|
||||
13
requirements.txt
Normal file
13
requirements.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
curl_cffi
|
||||
aiohttp
|
||||
aiohttp_socks
|
||||
auth
|
||||
Flask[async]
|
||||
g4f
|
||||
Werkzeug>=3.0.3
|
||||
aiohttp_socks
|
||||
nodriver
|
||||
# pysqlite3
|
||||
python-multipart
|
||||
uvicorn
|
||||
fastapi
|
||||
73
static/css/style.css
Normal file
73
static/css/style.css
Normal file
@@ -0,0 +1,73 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
|
||||
/* font-family: 'Inter', sans-serif; */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Inter", sans-serif;
|
||||
background-color: #e4f2f4;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
textarea::placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.inter {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.darkblue {
|
||||
color: #034953;
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: #007080;
|
||||
}
|
||||
|
||||
.label_gray {
|
||||
color: #c8d9de;
|
||||
}
|
||||
|
||||
.label_green {
|
||||
color: #035306;
|
||||
}
|
||||
|
||||
.label_red {
|
||||
color: #8b0000;
|
||||
}
|
||||
|
||||
.border_red {
|
||||
border-color: #8b0000;
|
||||
}
|
||||
|
||||
.label_darkorange {
|
||||
color: #8b7b00;
|
||||
}
|
||||
|
||||
.darkblue_bg {
|
||||
background-color: #034953;
|
||||
}
|
||||
|
||||
.darkgreen_bg {
|
||||
background-color: #035306;
|
||||
}
|
||||
|
||||
.darkred_bg {
|
||||
background-color: #8b0000;
|
||||
}
|
||||
|
||||
.blue_bg {
|
||||
background-color: #007080;
|
||||
}
|
||||
|
||||
.lightpink_bg {
|
||||
background-color: #fff2f2;
|
||||
}
|
||||
|
||||
.fileholder {
|
||||
background-color: white !important;
|
||||
}
|
||||
BIN
static/img/copy(Gregor_Cresnar).png
Normal file
BIN
static/img/copy(Gregor_Cresnar).png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
static/img/delete(Anggara).png
Normal file
BIN
static/img/delete(Anggara).png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
BIN
static/img/favicon(Nicoladipa).png
Normal file
BIN
static/img/favicon(Nicoladipa).png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
252
static/js/script.js
Normal file
252
static/js/script.js
Normal file
@@ -0,0 +1,252 @@
|
||||
// Turns on the button
|
||||
function turnOn(idOn, idOff, set) {
|
||||
document.getElementById(idOff).style.color = "#C8D9DE";
|
||||
document.getElementById(idOn).style.color = "#035306";
|
||||
document.getElementById(set).style.color = "#035306";
|
||||
}
|
||||
|
||||
// Turns off the button
|
||||
function turnOff(idOn, idOff, set) {
|
||||
document.getElementById(idOff).style.color = "#8B0000";
|
||||
document.getElementById(idOn).style.color = "#C8D9DE";
|
||||
document.getElementById(set).style.color = "#8B0000";
|
||||
}
|
||||
|
||||
// Changes the button text and color
|
||||
function changeButton(buttonID) {
|
||||
document.getElementById(buttonID).style.backgroundColor = "#035306";
|
||||
document.getElementById(buttonID).textContent = "Change";
|
||||
}
|
||||
|
||||
// Hides an element and shows another
|
||||
function replaceElement(idToHide, idToShow) {
|
||||
document.getElementById(idToHide).hidden = true;
|
||||
document.getElementById(idToShow).hidden = false;
|
||||
}
|
||||
|
||||
// Shows an element
|
||||
function showElement(idToShow) {
|
||||
element = document.getElementById(idToShow);
|
||||
element.hidden = false;
|
||||
}
|
||||
|
||||
// Hides an element
|
||||
function hideElement(idToHide) {
|
||||
element = document.getElementById(idToHide);
|
||||
element.hidden = true;
|
||||
}
|
||||
|
||||
// Hides a composed warning
|
||||
function hideWarning(idToHide) {
|
||||
document.getElementById(idToHide).classList.add("hidden");
|
||||
}
|
||||
|
||||
// Shows a composed warning
|
||||
function showWarning(idToShow) {
|
||||
document.getElementById(idToShow).classList.remove("hidden");
|
||||
}
|
||||
|
||||
// Copies the text in the given id to the clipboard
|
||||
function copyTextToClipboardFromName(name) {
|
||||
var copyText = document.getElementsByName(name)[0].value;
|
||||
navigator.clipboard.writeText(copyText);
|
||||
}
|
||||
|
||||
// Replaces the element in the given ID with the given text
|
||||
function replaceElementWithText(id, text) {
|
||||
var element = document.getElementById(id);
|
||||
var originalElement = element;
|
||||
// Creates a new element
|
||||
var newElement = document.createElement("p");
|
||||
// Adds some content to the new element
|
||||
newElement.textContent = text;
|
||||
newElement.classList.add("darkblue");
|
||||
newElement.classList.add("inline-block");
|
||||
newElement.id = id;
|
||||
// Replaces the old element with the new one
|
||||
element.parentNode.replaceChild(newElement, element);
|
||||
|
||||
return originalElement;
|
||||
}
|
||||
|
||||
function copy(name, id, text) {
|
||||
copyTextToClipboardFromName(name);
|
||||
var oldElement = replaceElementWithText(id, text);
|
||||
setTimeout(
|
||||
(param) => {
|
||||
var textElement = document.getElementById(id);
|
||||
textElement.parentNode.replaceChild(param, textElement);
|
||||
},
|
||||
2000,
|
||||
oldElement,
|
||||
);
|
||||
}
|
||||
|
||||
function createToken(targetID) {
|
||||
$.ajax({
|
||||
url: "/generatetoken",
|
||||
data: {},
|
||||
success: function (response) {
|
||||
var tokenText = document.getElementById(targetID).innerText;
|
||||
if (tokenText.length == 0) {
|
||||
document.getElementById(targetID).innerText = response;
|
||||
document.getElementById("token").value = response;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function addProxy() {
|
||||
// Gets the number of proxy input fields in 'proxy_list_div'
|
||||
var proxyListDiv = document.getElementById("proxy_list_div");
|
||||
var proxyList = proxyListDiv.getElementsByTagName("input");
|
||||
|
||||
// Checks if there's already an empty proxy input field
|
||||
for (var i = 0; i < proxyList.length; i++) {
|
||||
if (proxyList[i].value.length == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// adds a new proxy input field
|
||||
var newProxyDiv = document.createElement("div");
|
||||
newProxyDiv.classList.add("flex", "justify-start", "mb-3");
|
||||
|
||||
var newProxy = document.createElement("input");
|
||||
newProxy.type = "text";
|
||||
newProxy.name = "proxy_" + (proxyList.length + 1);
|
||||
newProxy.id = newProxy.name;
|
||||
newProxy.classList.add(
|
||||
"input",
|
||||
"outline-none",
|
||||
"py-1",
|
||||
"px-2",
|
||||
"rounded-lg",
|
||||
"inter",
|
||||
"w-full",
|
||||
);
|
||||
newProxy.placeholder = "protocol://user:password@host:port";
|
||||
newProxy.setAttribute("onchange", "checkAllProxies()");
|
||||
newProxyDiv.appendChild(newProxy);
|
||||
|
||||
// adds the 'delete' image button to the new proxy input field
|
||||
var deleteButton = document.createElement("img");
|
||||
deleteButton.src = "static/img/delete(Anggara).png";
|
||||
deleteButton.classList.add(
|
||||
"inline-block",
|
||||
"mx-2",
|
||||
"p-1",
|
||||
"hover:brightness-125",
|
||||
);
|
||||
deleteButton.width = 32;
|
||||
deleteButton.onclick = function () {
|
||||
newProxyDiv.remove();
|
||||
};
|
||||
newProxyDiv.appendChild(deleteButton);
|
||||
|
||||
document.getElementById("proxy_list_div").appendChild(newProxyDiv);
|
||||
}
|
||||
|
||||
function deleteElement(id) {
|
||||
document.getElementById(id).remove();
|
||||
}
|
||||
|
||||
// Checks if the proxy syntax is correct
|
||||
function checkProxySyntax(proxy) {
|
||||
var proxyRegex =
|
||||
/^((http|https|socks4|socks5):\/\/)?([a-zA-Z0-9]+:[a-zA-Z0-9]+@)?([a-zA-Z0-9.-]+):([0-9]+)$/;
|
||||
return proxyRegex.test(proxy);
|
||||
}
|
||||
|
||||
// Warns the user if the proxy syntax is incorrect
|
||||
function warnProxySyntax(proxy, proxyID) {
|
||||
if (checkProxySyntax(proxy)) {
|
||||
document
|
||||
.getElementById(proxyID)
|
||||
.classList.remove("border_red", "label_red", "lightpink_bg");
|
||||
} else {
|
||||
document
|
||||
.getElementById(proxyID)
|
||||
.classList.add("border_red", "border", "label_red", "lightpink_bg");
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the syntax at every proxy input field at input change
|
||||
function checkAllProxies() {
|
||||
var proxyListDiv = document.getElementById("proxy_list_div");
|
||||
var proxyList = proxyListDiv.getElementsByTagName("input");
|
||||
for (var i = 0; i < proxyList.length; i++) {
|
||||
warnProxySyntax(proxyList[i].value, proxyList[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if new password and confirm password match
|
||||
function checkPasswordMatch() {
|
||||
var newPassword = document.getElementById("new_password").value;
|
||||
var confirmPassword = document.getElementById("confirm_password").value;
|
||||
if (newPassword == confirmPassword) {
|
||||
if (newPassword.length > 0) {
|
||||
replaceElement("error_password", "success_password");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
replaceElement("success_password", "error_password");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Opens the update password form
|
||||
function openPasswordUpdate() {
|
||||
replaceElement("open_password_update", "cancel_password_update");
|
||||
showElement("password_update");
|
||||
}
|
||||
|
||||
// Closes the update password form
|
||||
function cancelPasswordUpdate() {
|
||||
hideElement("password_update");
|
||||
hideElement("success_password");
|
||||
hideElement("error_password");
|
||||
replaceElement("cancel_password_update", "open_password_update");
|
||||
document.getElementById("new_password").value = "";
|
||||
document.getElementById("confirm_password").value = "";
|
||||
}
|
||||
|
||||
// Enables the save button if the password is correct
|
||||
function enableSaveButton() {
|
||||
var pass_length = document.getElementById("password").value.length;
|
||||
var isPasswordUpdateClosed = document.getElementById("password_update").hidden;
|
||||
if ((checkPasswordMatch() || isPasswordUpdateClosed) && pass_length > 0) {
|
||||
replaceElement('save_label_dummy', 'save_label');
|
||||
} else {
|
||||
replaceElement('save_label', 'save_label_dummy');
|
||||
}
|
||||
}
|
||||
|
||||
// Gets available models for choosen A.I. Provider
|
||||
$(document).ready(function () {
|
||||
$("#provider").change(function () {
|
||||
var inputValue = $(this).val();
|
||||
$.ajax({
|
||||
url: "/models",
|
||||
data: {
|
||||
provider: inputValue,
|
||||
},
|
||||
success: function (response) {
|
||||
var select = document.getElementById("model");
|
||||
|
||||
// Remove existing models
|
||||
while (select.firstChild) {
|
||||
select.removeChild(select.firstChild);
|
||||
}
|
||||
|
||||
// Add the new models
|
||||
for (var i = 0; i < response.length; i++) {
|
||||
var option = document.createElement("option");
|
||||
option.innerText = response[i];
|
||||
option.value = response[i];
|
||||
select.add(option);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
49
templates/login.html
Normal file
49
templates/login.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!-- flask login page -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon(Nicoladipa).png') }}" type="image/x-icon">
|
||||
<title>Login</title>
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}
|
||||
<!-- Logo on the left and title on the right -->
|
||||
<table class="w-50 m-7">
|
||||
<tr>
|
||||
<td class="m-7">
|
||||
<img src="{{ url_for('static', filename='img/favicon(Nicoladipa).png') }}" width="96" class="inline-block" alt="Logo">
|
||||
</td>
|
||||
<td class="m-7">
|
||||
<h1 class="title font-bold inter darkblue text-4xl">FreeGPT4</h1>
|
||||
<h2 class="title blue
|
||||
text-2xl">Server settings</h2>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="flex justify-center w-screen h-96">
|
||||
<div class="flex items-center">
|
||||
<form method="POST" action="/settings">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input outline-none py-3 px-4 rounded-lg inter w-56" type="password" name="password" placeholder="Your Password">
|
||||
</div>
|
||||
</div>
|
||||
<button class="button outline-none py-3 px-4 rounded-lg darkblue_bg inter text-white text-md mt-5 hover:opacity-95 w-56">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
<!-- script import -->
|
||||
<script src="{{ url_for('static', filename='js/script.js') }}" type="text/javascript"></script>
|
||||
</html>
|
||||
<!-- Path: src/templates/settings.html -->
|
||||
292
templates/settings.html
Normal file
292
templates/settings.html
Normal file
@@ -0,0 +1,292 @@
|
||||
<!-- flask settings page -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon(Nicoladipa).png') }}" type="image/x-icon">
|
||||
<title>Settings</title>
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}
|
||||
<!-- Logo on the left and title on the right -->
|
||||
<table class="w-50 m-7">
|
||||
<tr>
|
||||
<td class="m-7">
|
||||
<img src="{{ url_for('static', filename='img/favicon(Nicoladipa).png') }}" width="96" class="inline-block" alt="Logo">
|
||||
</td>
|
||||
<td class="m-7">
|
||||
<h1 class="title font-bold inter darkblue text-4xl">FreeGPT4</h1>
|
||||
<h2 class="title blue
|
||||
text-2xl">Server settings</h2>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<form class="m-7 mt-10 inter" action="/save" method="post" enctype=multipart/form-data>
|
||||
<table class="table-fixed w-full">
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_0"><b>File input support:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
{% if data['file_input'] %}
|
||||
<input type="radio" id="off_rad_0" name="file_input" value="false" hidden oninput="turnOff('on_0', 'off_0', 'set_0')">
|
||||
<label for="off_rad_0" id="off_0" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_0" id="file_input" name="file_input" value="true" hidden oninput="turnOn('on_0', 'off_0', 'set_0')" checked>
|
||||
<label for="on_rad_0" id="on_0" class="label_green"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_0" name="file_input" value="false" hidden oninput="turnOff('on_0', 'off_0', 'set_0')" checked>
|
||||
<label for="off_rad_0" id="off_0" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_0" id="file_input" name="file_input" value="true" hidden oninput="turnOn('on_0', 'off_0', 'set_0')">
|
||||
<label for="on_rad_0" id="on_0" class="label_gray"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_1"><b>Show sources:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
{% if data['remove_sources'] %}
|
||||
<input type="radio" id="off_rad_1" name="remove_sources" value="true" hidden oninput="turnOff('on_1', 'off_1', 'set_1')" checked>
|
||||
<label for="off_rad_1" id="off_1" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_1" name="remove_sources" value="false" hidden oninput="turnOn('on_1', 'off_1', 'set_1')">
|
||||
<label for="on_rad_1" id="on_1" class="label_gray"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_1" name="remove_sources" value="true" hidden oninput="turnOff('on_1', 'off_1', 'set_1')">
|
||||
<label for="off_rad_1" id="off_1" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_1" name="remove_sources" value="false" hidden oninput="turnOn('on_1', 'off_1', 'set_1')" checked>
|
||||
<label for="on_rad_1" id="on_1" class="label_green"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_2"><b>Message history:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
{% if data['message_history'] %}
|
||||
<input type="radio" id="off_rad_2" name="message_history" value="false" hidden oninput="turnOff('on_2', 'off_2', 'set_2')">
|
||||
<label for="off_rad_2" id="off_2" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_2" name="message_history" value="true" hidden oninput="turnOn('on_2', 'off_2', 'set_2')" checked>
|
||||
<label for="on_rad_2" id="on_2" class="label_green"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_2" name="message_history" value="false" hidden oninput="turnOff('on_2', 'off_2', 'set_2')" checked>
|
||||
<label for="off_rad_2" id="off_2" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_2" name="message_history" value="true" hidden oninput="turnOn('on_2', 'off_2', 'set_2')">
|
||||
<label for="on_rad_2" id="on_2" class="label_gray"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_3">
|
||||
<b>Private Mode:</b>
|
||||
<br>
|
||||
<p id="warning_0" class="text-sm label_darkorange" hidden> <b>Warning:</b> your current token will be deleted </p>
|
||||
<input name="token" id="token" value="{{ data['token'] }}" hidden/>
|
||||
{% if data['token']|length > 0 %}
|
||||
<span id="warning_1" class="text-sm blue inline-block">
|
||||
{% else %}
|
||||
<span id="warning_1" class="text-sm blue inline-block hidden">
|
||||
{% endif %}
|
||||
<b>Your Token: </b> <i id="token_text" onclick="copyTextToClipboardFromName('token');">{{ data['token'] }}</i>
|
||||
<img id="copy_icon" src="{{ url_for('static', filename='img/copy(Gregor_Cresnar).png') }}" width="16" class="inline-block" alt="Copy" onclick="copy('token', this.id, 'Copied!');"/>
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
{% if data['token'] %}
|
||||
<input type="radio" id="off_rad_3" name="private_mode" value="false" hidden oninput="turnOff('on_3', 'off_3', 'set_3'); hideWarning('warning_1'); showElement('warning_0');">
|
||||
<label for="off_rad_3" id="off_3" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_3" name="private_mode" value="true" hidden oninput="turnOn('on_3', 'off_3', 'set_3'); showWarning('warning_1'); hideElement('warning_0'); createToken('token_text')" checked>
|
||||
<label for="on_rad_3" id="on_3" class="label_green"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_3" name="private_mode" value="false" hidden oninput="turnOff('on_3', 'off_3', 'set_3'); hideWarning('warning_1'); showElement('warning_0');" checked>
|
||||
<label for="off_rad_3" id="off_3" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_3" name="private_mode" value="true" hidden oninput="turnOn('on_3', 'off_3', 'set_3'); showWarning('warning_1'); hideElement('warning_0'); createToken('token_text')">
|
||||
<label for="on_rad_3" id="on_3" class="label_gray"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_4">
|
||||
<b>Proxies:</b>
|
||||
<br>
|
||||
{% if data['proxies'] %}
|
||||
<div id="proxy_list" class="mt-3">
|
||||
{% else %}
|
||||
<div id="proxy_list" class="mt-3" hidden>
|
||||
{% endif %}
|
||||
<label id="add_proxy_button" class="button outline-none py-1 px-4 rounded-lg darkgreen_bg text-white text-md hover:opacity-95 w-24 mt-5" onclick="addProxy();">Add Proxy</label>
|
||||
<br>
|
||||
<p class="text-sm mb-2 mt-4"> <b> Your Proxies: </b></p>
|
||||
<div id="proxy_list_div">
|
||||
{% for proxy in proxies %}
|
||||
<!-- proxy form: protocol://user:password@ip:port -->
|
||||
<div class="flex justify-start mb-3" id="proxy_div_{{ loop.index }}">
|
||||
<input type="text" id="proxy_{{ loop.index }}" name="proxy_{{ loop.index }}" class="input outline-none py-1 px-2 rounded-lg inter w-full" onchange="warnProxySyntax(this.value, this.id);" placeholder="protocol://user:password@host:port" value="{{ proxy['protocol'] }}://{{ proxy['username'] }}:{{ proxy['password'] }}@{{ proxy['ip'] }}:{{ proxy['port'] }}">
|
||||
<img id="delete_icon_{{ loop.index }}" src="{{ url_for('static', filename='img/delete(Anggara).png') }}" width="32" class="inline-block mx-2 p-1 hover:brightness-125" alt="Delete" onclick="deleteElement('proxy_div_{{ loop.index }}');"/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg align-top">
|
||||
{% if data['proxies'] %}
|
||||
<input type="radio" id="off_rad_4" name="proxies" value="false" hidden oninput="turnOff('on_4', 'off_4', 'set_4'); hideElement('proxy_list');">
|
||||
<label for="off_rad_4" id="off_4" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_4" name="proxies" value="true" hidden oninput="turnOn('on_4', 'off_4', 'set_4'); showElement('proxy_list');" checked>
|
||||
<label for="on_rad_4" id="on_4" class="label_green"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_4" name="proxies" value="false" hidden oninput="turnOff('on_4', 'off_4', 'set_4'); hideElement('proxy_list');" checked>
|
||||
<label for="off_rad_4" id="off_4" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_4" name="proxies" value="true" hidden oninput="turnOn('on_4', 'off_4', 'set_4'); showElement('proxy_list');">
|
||||
<label for="on_rad_4" id="on_4" class="label_gray"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800" id="set_5">
|
||||
<b>Fast API:</b>
|
||||
<br>
|
||||
<p id="warning_2" class="text-sm label_darkorange" hidden> <b>Warning:</b> the Fast API shutdown will take effect only after you've manually restarted the server </p>
|
||||
</td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
{% if data['fast_api'] %}
|
||||
<input type="radio" id="off_rad_5" name="fast_api" value="false" hidden oninput="turnOff('on_5', 'off_5', 'set_5'); showElement('warning_2');">
|
||||
<label for="off_rad_5" id="off_5" class="label_gray"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_5" name="fast_api" value="true" hidden oninput="turnOn('on_5', 'off_5', 'set_5'); hideElement('warning_2');" checked>
|
||||
<label for="on_rad_5" id="on_5" class="label_green"><b>ON</b></label>
|
||||
{% else %}
|
||||
<input type="radio" id="off_rad_5" name="fast_api" value="false" hidden oninput="turnOff('on_5', 'off_5', 'set_5'); showElement('warning_2');" checked>
|
||||
<label for="off_rad_5" id="off_5" class="label_red"><b>OFF</b></label>
|
||||
<p class="inline-block mx-1"> - </p>
|
||||
<input type="radio" id="on_rad_5" name="fast_api" value="true" hidden oninput="turnOn('on_5', 'off_5', 'set_5'); hideElement('warning_2');">
|
||||
<label for="on_rad_5" id="on_5" class="label_gray"><b>ON</b></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800">
|
||||
<b>Server port:</b>
|
||||
<br>
|
||||
<p id="warning_3" class="text-sm label_darkorange" hidden> <b>Warning:</b> the port change will take effect only after you've manually restarted the server </p>
|
||||
</td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
<b>
|
||||
<input type="number" id="port" name="port" class="input outline-none py-1 px-2 rounded-lg inter w-24" placeholder="Port" value="{{ data['port'] }}" onchange="showElement('warning_3')">
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800"><b>A.I. Provider:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
<b>
|
||||
<select name="provider" id="provider" class="input outline-none py-1 px-2 rounded-lg inter w-24">
|
||||
<option value="{{ data['provider'] }}"> {{ data['provider'] }} - Default </option>
|
||||
{% for key, value in providers.items() %}
|
||||
<option value="{{ key }}"> {{ key }} </option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800"><b>A.I. Model:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
<b>
|
||||
<select name="model" id="model" class="input outline-none py-1 px-2 rounded-lg inter w-24">
|
||||
<option value="{{ data['model'] }}"> {{ data['model'] }} </option>
|
||||
{% if data['provider'] == "Auto" %}
|
||||
{% for model in generic_models %}
|
||||
<option value="{{ model }}"> {{ model }} </option>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for model in providers[data['provider']].models %}
|
||||
<option value="{{ model }}"> {{ model }} </option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800"><b>Cookies:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
<b>
|
||||
<input type="file" id="cookie_file" name="cookie_file" class="input outline-none py-1 px-2 rounded-lg inter w-24 fileholder" placeholder="Cookies" value="{{ data['cookie_file'] }}" accept=".json" oninput="changeButton('cookies_button')" hidden>
|
||||
{% if data['cookie_file']|length == 0 %}
|
||||
<label for="cookie_file" id="cookies_button" class="button outline-none py-1 px-4 rounded-lg darkblue_bg inter text-white text-md mt-5 hover:opacity-95 w-24">Upload</label>
|
||||
{% else %}
|
||||
<label for="cookie_file" id="change_cookies_button" class="button outline-none py-1 px-4 rounded-lg darkgreen_bg inter text-white text-md mt-5 hover:opacity-95 w-24">Change</label>
|
||||
{% endif %}
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800"><b>Input keyword:</b></td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg">
|
||||
<b>
|
||||
<input type="text" id="keyword" name="keyword" class="input outline-none py-1 px-2 rounded-lg inter w-24" placeholder="i.e. input" value="{{ data['keyword'] }}" suggested="text"></input>
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800 pb-3">
|
||||
<b>Password:</b>
|
||||
<div id="password_update" class="mt-4" hidden>
|
||||
<p class="text-sm"> <b> Set Password </b></p>
|
||||
<input type="password" id="new_password" name="new_password" class="input outline-none py-1 px-2 rounded-lg inter" placeholder="Your new Password" onchange="enableSaveButton();" autocomplete="new-password"></input>
|
||||
<p class="text-sm mt-4"> <b> Confirm Password </b></p>
|
||||
<input type="password" id="confirm_password" name="confirm_password" class="input outline-none py-1 px-2 rounded-lg inter" placeholder="New Password Again" onchange="enableSaveButton();" autocomplete="new-password"></input>
|
||||
<p id="error_password" class="text-sm label_red mt-1" hidden> <b> Error: </b> Passwords do not match </p>
|
||||
<p id="success_password" class="text-sm label_green mt-1" hidden>
|
||||
<b> Success: </b> Passwords match. <br>
|
||||
<i> When entering the old password below to confirm, the password will be updated </i>
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg align-top">
|
||||
<b>
|
||||
<label id="open_password_update" class="button outline-none py-1 px-4 rounded-lg darkgreen_bg inter text-white text-md mt-5 hover:opacity-95 w-24" onclick="openPasswordUpdate(); enableSaveButton();">Update</label>
|
||||
<label id="cancel_password_update" class="button outline-none py-1 px-4 rounded-lg darkred_bg inter text-white text-md mt-5 hover:opacity-95 w-24" onclick="cancelPasswordUpdate(); enableSaveButton();" hidden>Cancel</label>
|
||||
</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-1 fond-bold inter darkblue text-lg border-b border-slate-800">
|
||||
<b>System Prompt:</b>
|
||||
<textarea type="text" id="system_prompt" name="system_prompt" class="input outline-none py-1 px-2 my-2 rounded-lg inter w-full font-normal" placeholder="i.e. You're a virtual flight assistant, from now on follow your role for every answer.">{{ data['system_prompt'] }}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- enter password to confirm -->
|
||||
<div class="flex justify-start mt-12">
|
||||
<input type="password" id="password" name="password" class="input outline-none py-3 px-4 rounded-lg inter w-60" placeholder="Enter password to confirm" onchange="enableSaveButton();" autocomplete="current-password">
|
||||
</div>
|
||||
<!-- save and apply button -->
|
||||
<div class="flex justify-start">
|
||||
<button type="submit" id="save" hidden>Save and apply</button>
|
||||
<!-- dummy label gray -->
|
||||
<label for="save" id="save_label" class="button outline-none py-3 px-4 pl-5 rounded-lg darkblue_bg inter text-white text-md mt-3 hover:opacity-95 w-40" hidden>Save and apply</label>
|
||||
<label id="save_label_dummy" class="button outline-none py-3 px-4 pl-5 rounded-lg bg-slate-300 inter text-white text-md mt-3 w-40">Save and apply</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
<!-- script import -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/script.js') }}" type="text/javascript"></script>
|
||||
</html>
|
||||
<!-- Path: src/templates/settings.html -->
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
4
tests/test_launch.py
Normal file
4
tests/test_launch.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from src.FreeGPT4_Server import index
|
||||
|
||||
def test_index():
|
||||
assert index()
|
||||
Reference in New Issue
Block a user