Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
23b08d4cf5 | |||
c66d325d8f | |||
833ebade22 | |||
6bbdc72162 | |||
839de42ab9 | |||
e0eec9630b | |||
a9b4a08357 | |||
7953235912 | |||
2cebb06f76 | |||
c9d9049ea2 | |||
23a575d61b | |||
ba3d3be2a9 | |||
f65a62606e | |||
ae16b99614 | |||
46ebdd4843 | |||
9f515b5660 | |||
40554545f2 | |||
be2bb856b0 | |||
489f6b223c | |||
6f3df5fc51 | |||
9c309dafc2 | |||
19423c7f84 | |||
a9b1e22b47 | |||
ca99215ac7 | |||
b7ba1cf4c1 | |||
a4e718067d | |||
2eecdbaf8f | |||
152ce7a170 | |||
ef5022ff64 | |||
c4800ddf55 | |||
6dcf5d757a | |||
00c5f5ac4e | |||
debee86627 | |||
e0ad3f0059 |
@ -4,6 +4,6 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/re
|
|||||||
RUN apk add git python3 py3-pip
|
RUN apk add git python3 py3-pip
|
||||||
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
RUN python3 -m pip install --upgrade pip && pip3 install --upgrade setuptools
|
RUN python3 -m pip install --upgrade pip && pip3 install --upgrade setuptools
|
||||||
COPY . /web-gpt
|
COPY ./project /web-gpt/project
|
||||||
RUN pip3 install -r /web-gpt/requirements.txt
|
RUN pip3 install -r /web-gpt/project/requirements.txt
|
||||||
CMD cd /web-gpt && flask --app=project run --host=0.0.0.0
|
CMD cd /web-gpt && waitress-serve --listen 0.0.0.0:5000 project:app
|
9
README.md
Normal file
9
README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
https://chat.w-q.top:520
|
||||||
|
|
||||||
|
OpenAI官方API的套壳网站。
|
||||||
|
|
||||||
|
试图规避ChatGPT的使用门槛,把科技带给更多人。
|
||||||
|
|
||||||
|
雷锋同志做好事从不留名。
|
||||||
|
|
||||||
|
如果一定要一个姓名,请叫我Prometheus.
|
13
config.ini
13
config.ini
@ -1,13 +0,0 @@
|
|||||||
[app]
|
|
||||||
NAME=APP_NAME
|
|
||||||
SECRET_KEY=SOME_RANDOM_STRING
|
|
||||||
SQLALCHEMY_DATABASE_URI=sqlite:///sqlite.db
|
|
||||||
#SQLALCHEMY_DATABASE_URI=mysql://username:password@server/db
|
|
||||||
|
|
||||||
[network]
|
|
||||||
PROXY=http://127.0.0.1:7890
|
|
||||||
|
|
||||||
[openai]
|
|
||||||
API_KEY=
|
|
||||||
MODEL_NAME=gpt-3.5-turbo
|
|
||||||
PROMPT=You are a helpful assistant
|
|
33
instance/config.yaml
Normal file
33
instance/config.yaml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
app:
|
||||||
|
NAME: web-gpt
|
||||||
|
SECRET_KEY: SOME_RANDOM_STRING
|
||||||
|
HOMEPAGE_NOTICE: |
|
||||||
|
1. 此网站基于openAI的API提供服务
|
||||||
|
2. 为了支持多轮对话,历史聊天会保存在服务端
|
||||||
|
3. 网站不做关键词过滤,但请不要违反相关法律
|
||||||
|
4. GPT生成的任何内容不保证准确性,请自行甄别
|
||||||
|
5. 项目仓库如下,欢迎提交代码
|
||||||
|
https://gitea.w-q.top:520/wangjiacai/web-gpt
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URI: sqlite:///sqlite.db
|
||||||
|
# SQLALCHEMY_DATABASE_URI: mysql://username:password@server/db
|
||||||
|
|
||||||
|
# LOGGING_LEVEL: CRITICAL | FATAL | ERROR | WARN | WARNING | INFO | DEBUG | NOTSET
|
||||||
|
LOGGING_LEVEL: INFO
|
||||||
|
|
||||||
|
|
||||||
|
network:
|
||||||
|
PROXY: http://127.0.0.1:7890
|
||||||
|
|
||||||
|
openai:
|
||||||
|
API_KEY:
|
||||||
|
MODEL_NAME: gpt-3.5-turbo
|
||||||
|
PROMPT: 你是一个有用的人工智能助理,你尽力确保回答的准确性,避免给出误导信息。
|
||||||
|
|
||||||
|
|
||||||
|
mail:
|
||||||
|
host:
|
||||||
|
port:
|
||||||
|
username:
|
||||||
|
from:
|
||||||
|
password:
|
@ -1,23 +1,37 @@
|
|||||||
from flask import Flask
|
from flask import Flask, request, send_from_directory
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
from configparser import ConfigParser
|
import yaml
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import os
|
||||||
|
import waitress
|
||||||
|
|
||||||
# init SQLAlchemy so we can use it later in our models
|
# init SQLAlchemy so we can use it later in our models
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
conf = ConfigParser()
|
|
||||||
conf.read("./config.ini")
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
print("project instance dir: ", app.instance_path)
|
||||||
|
config_file = app.instance_path+"/config.yaml"
|
||||||
|
print("config file path: ", config_file)
|
||||||
|
with open(config_file) as config_file:
|
||||||
|
conf = yaml.safe_load(config_file)
|
||||||
|
app.config['NAME'] = conf['app']['NAME']
|
||||||
app.config['SECRET_KEY'] = conf['app']['SECRET_KEY']
|
app.config['SECRET_KEY'] = conf['app']['SECRET_KEY']
|
||||||
|
app.config['LOGGING_LEVEL'] = conf['app']['LOGGING_LEVEL']
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = conf['app']['SQLALCHEMY_DATABASE_URI']
|
app.config['SQLALCHEMY_DATABASE_URI'] = conf['app']['SQLALCHEMY_DATABASE_URI']
|
||||||
|
app.config['HOMEPAGE_NOTICE'] = conf['app']['HOMEPAGE_NOTICE']
|
||||||
app.config['NETWORK_PROXY'] = conf['network']['PROXY']
|
app.config['NETWORK_PROXY'] = conf['network']['PROXY']
|
||||||
app.config['OPENAI_API_KEY'] = conf['openai']['API_KEY']
|
app.config['OPENAI_API_KEY'] = conf['openai']['API_KEY']
|
||||||
app.config['OPENAI_MODEL_NAME'] = conf['openai']['MODEL_NAME']
|
app.config['OPENAI_MODEL_NAME'] = conf['openai']['MODEL_NAME']
|
||||||
app.config['OPENAI_PROMPT'] = conf['openai']['PROMPT']
|
app.config['OPENAI_PROMPT'] = conf['openai']['PROMPT']
|
||||||
|
app.config['mail'] = conf['mail']
|
||||||
|
|
||||||
|
app.add_template_global(app.config['NAME'], "web_title")
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
@ -44,3 +58,45 @@ def create_app():
|
|||||||
app.register_blueprint(main_blueprint)
|
app.register_blueprint(main_blueprint)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
logger = logging.getLogger('waitress')
|
||||||
|
logger.setLevel(app.config['LOGGING_LEVEL'])
|
||||||
|
|
||||||
|
|
||||||
|
def get_actual_addr(request):
|
||||||
|
ip = request.remote_addr
|
||||||
|
if request.headers.getlist("X-Forwarded-For"):
|
||||||
|
ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||||
|
return ip
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/favicon.ico')
|
||||||
|
def favicon():
|
||||||
|
return send_from_directory(os.path.join(app.root_path, 'static'),
|
||||||
|
'favicon.ico', mimetype='image/vnd.microsoft.icon')
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def before_request():
|
||||||
|
timestamp = time.strftime('[%Y-%b-%d %H:%M]')
|
||||||
|
logger.info('%s > %s %s %s %s', timestamp, get_actual_addr(request),
|
||||||
|
request.method, request.scheme, request.full_path)
|
||||||
|
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def after_request(response):
|
||||||
|
timestamp = time.strftime('[%Y-%b-%d %H:%M]')
|
||||||
|
logger.info('%s < %s %s %s %s %s', timestamp, get_actual_addr(request),
|
||||||
|
request.method, request.scheme, request.full_path, response.status)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(Exception)
|
||||||
|
def exceptions(e):
|
||||||
|
tb = traceback.format_exc()
|
||||||
|
timestamp = time.strftime('[%Y-%b-%d %H:%M]')
|
||||||
|
logger.error('%s %s %s %s %s 5xx INTERNAL SERVER ERROR\n%s', timestamp,
|
||||||
|
get_actual_addr(request), request.method, request.scheme, request.full_path, tb)
|
||||||
|
return e.status_code
|
||||||
|
@ -1,14 +1,38 @@
|
|||||||
from flask_login import login_user, logout_user
|
from flask_login import login_user, logout_user
|
||||||
from flask import Blueprint, render_template, redirect, url_for, request, flash
|
from flask import Blueprint, render_template, redirect, url_for, request, flash, current_app
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from flask_login import login_required, current_user, login_manager
|
from flask_login import login_required, current_user, login_manager
|
||||||
from .models import User
|
from .models import User, Conversation
|
||||||
from . import db
|
from . import db
|
||||||
import time
|
import time
|
||||||
|
import hashlib
|
||||||
|
|
||||||
auth = Blueprint('auth', __name__)
|
auth = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def hash_to_digit(instr: str) -> str:
|
||||||
|
outstr = hashlib.md5(instr.encode('utf-8')).hexdigest()
|
||||||
|
outstr = f"{int(outstr, 16) % 1000000:0>6d}"
|
||||||
|
return outstr
|
||||||
|
|
||||||
|
|
||||||
|
def gen_activation_code(email: str) -> str:
|
||||||
|
current_time_slot = int(time.time() // 60)
|
||||||
|
s = email + current_app.config['SECRET_KEY'] + str(current_time_slot)
|
||||||
|
activation_code = hash_to_digit(s)
|
||||||
|
return activation_code
|
||||||
|
|
||||||
|
|
||||||
|
def check_activation_code(email: str, activation_code: str) -> bool:
|
||||||
|
current_time_slot = int(time.time() // 60)
|
||||||
|
for time_slot in range(current_time_slot-10, current_time_slot+1):
|
||||||
|
s = email + current_app.config['SECRET_KEY'] + str(time_slot)
|
||||||
|
expected_code = hash_to_digit(s)
|
||||||
|
if expected_code == activation_code:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/login')
|
@auth.route('/login')
|
||||||
def login():
|
def login():
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
@ -35,6 +59,22 @@ def login_post():
|
|||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('main.index'))
|
||||||
|
|
||||||
|
|
||||||
|
@auth.route('/activate', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def activate():
|
||||||
|
activation_code = request.form.get('activation_code')
|
||||||
|
if check_activation_code(current_user.email, activation_code):
|
||||||
|
account = User.query.filter_by(
|
||||||
|
id=current_user.id, email=current_user.email, name=current_user.name).first()
|
||||||
|
if account:
|
||||||
|
if db.session.query(User).filter(User.id == account.id).update({"isActivated": True}) and not db.session.commit():
|
||||||
|
time.sleep(0.05)
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
time.sleep(1)
|
||||||
|
flash("激活码不匹配")
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/signup')
|
@auth.route('/signup')
|
||||||
def signup():
|
def signup():
|
||||||
return render_template('signup.html')
|
return render_template('signup.html')
|
||||||
@ -53,12 +93,12 @@ def signup_post():
|
|||||||
flash('此邮箱已注册!')
|
flash('此邮箱已注册!')
|
||||||
return redirect(url_for('auth.signup'))
|
return redirect(url_for('auth.signup'))
|
||||||
if not (email):
|
if not (email):
|
||||||
flash('Email missing!')
|
flash('请输入邮箱!')
|
||||||
return redirect(url_for('auth.signup'))
|
return redirect(url_for('auth.signup'))
|
||||||
if not (name):
|
if not (name):
|
||||||
name = email
|
name = email
|
||||||
if not (password):
|
if not (password):
|
||||||
flash('Password missing!')
|
flash('请输入密码')
|
||||||
return redirect(url_for('auth.signup'))
|
return redirect(url_for('auth.signup'))
|
||||||
# create a new user with the form data. Hash the password so the plaintext version isn't saved.
|
# create a new user with the form data. Hash the password so the plaintext version isn't saved.
|
||||||
new_user = User(email=email,
|
new_user = User(email=email,
|
||||||
@ -74,6 +114,14 @@ def signup_post():
|
|||||||
# add the new user to the database
|
# add the new user to the database
|
||||||
db.session.add(new_user)
|
db.session.add(new_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
activation_code = gen_activation_code(new_user.email)
|
||||||
|
from . import smtp
|
||||||
|
content = f"Hi {new_user.name},欢迎注册!\n"
|
||||||
|
content += f"您的激活码是:\n{activation_code}\n\n"
|
||||||
|
content += "此激活码十分钟内有效,过期请联系管理员激活,谢谢!"
|
||||||
|
smtp.sendmail(new_user.email, "web-gpt激活码", content)
|
||||||
|
|
||||||
return redirect(url_for('auth.login'))
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
|
|
||||||
@ -119,6 +167,9 @@ def manage_post():
|
|||||||
account = User.query.filter_by(
|
account = User.query.filter_by(
|
||||||
id=id, email=email, name=name, role=role, isActivated=isActivated).first()
|
id=id, email=email, name=name, role=role, isActivated=isActivated).first()
|
||||||
if account:
|
if account:
|
||||||
|
db.session.query(Conversation).filter(
|
||||||
|
Conversation.userid == id).delete()
|
||||||
|
db.session.commit()
|
||||||
if db.session.query(User).filter(User.id == id).delete() and not db.session.commit():
|
if db.session.query(User).filter(User.id == id).delete() and not db.session.commit():
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
return "success"
|
return "success"
|
||||||
|
@ -2,14 +2,21 @@ from flask import Blueprint, render_template, current_app, request, flash, redir
|
|||||||
from flask_login import login_required, current_user, login_manager
|
from flask_login import login_required, current_user, login_manager
|
||||||
from .models import User, Conversation
|
from .models import User, Conversation
|
||||||
from . import db
|
from . import db
|
||||||
|
from datetime import datetime, timedelta
|
||||||
import openai
|
import openai
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
main = Blueprint('main', __name__)
|
main = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
logger = logging.getLogger('waitress')
|
||||||
|
|
||||||
|
|
||||||
@main.route('/')
|
@main.route('/')
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html', user=current_user)
|
|
||||||
|
notice = current_app.config['HOMEPAGE_NOTICE']
|
||||||
|
return render_template('index.html', user=current_user, homepage_notice=notice)
|
||||||
|
|
||||||
|
|
||||||
@main.route('/profile')
|
@main.route('/profile')
|
||||||
@ -75,15 +82,32 @@ def chat_post():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if current_user.is_authenticated and current_user.isActivated:
|
if current_user.is_authenticated and current_user.isActivated:
|
||||||
openai_resp = openai.ChatCompletion.create(
|
|
||||||
model=current_app.config['OPENAI_MODEL_NAME'],
|
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system",
|
{"role": "system",
|
||||||
"content": current_app.config['OPENAI_PROMPT']},
|
"content": current_app.config['OPENAI_PROMPT']},
|
||||||
{"role": "user", "content": msg}
|
|
||||||
]
|
]
|
||||||
|
chats_desc = Conversation.query.filter_by(
|
||||||
|
userid=current_user.id, useremail=current_user.email).order_by(Conversation.datetime.desc()).limit(20).all()
|
||||||
|
for chat in reversed(chats_desc):
|
||||||
|
if chat.datetime <= datetime.utcnow() - timedelta(seconds=300):
|
||||||
|
continue
|
||||||
|
if chat.response:
|
||||||
|
messages.append(
|
||||||
|
{"role": "assistant", "content": chat.response})
|
||||||
|
else:
|
||||||
|
messages.append(
|
||||||
|
{"role": "user", "content": chat.request})
|
||||||
|
try:
|
||||||
|
openai_resp = openai.ChatCompletion.create(
|
||||||
|
model=current_app.config['OPENAI_MODEL_NAME'],
|
||||||
|
messages=messages
|
||||||
)
|
)
|
||||||
msg_resp = openai_resp['choices'][0]['message']['content']
|
msg_resp = openai_resp['choices'][0]['message']['content']
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("OpenAI API request failed: %s", repr(e))
|
||||||
|
msg_resp = "请求错误,请尝试重发。如果持续错误,请联系管理员检查。" + "\n"+"错误信息: " + repr(e)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
if msg_resp:
|
if msg_resp:
|
||||||
response = {"message": msg_resp, "status": "success"}
|
response = {"message": msg_resp, "status": "success"}
|
||||||
else:
|
else:
|
||||||
|
@ -2,5 +2,7 @@ Flask==2.2.3
|
|||||||
Flask_Login==0.6.2
|
Flask_Login==0.6.2
|
||||||
flask_sqlalchemy==3.0.3
|
flask_sqlalchemy==3.0.3
|
||||||
openai==0.27.2
|
openai==0.27.2
|
||||||
|
PyYAML==6.0
|
||||||
SQLAlchemy==2.0.7
|
SQLAlchemy==2.0.7
|
||||||
|
waitress==2.1.2
|
||||||
Werkzeug==2.2.3
|
Werkzeug==2.2.3
|
31
project/smtp.py
Normal file
31
project/smtp.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import smtplib
|
||||||
|
import logging
|
||||||
|
import email
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.header import Header
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
mail_config = current_app.config['mail']
|
||||||
|
logger = logging.getLogger('waitress')
|
||||||
|
|
||||||
|
|
||||||
|
def sendmail(to: str, subject: str, message: str):
|
||||||
|
if not (mail_config['username'] and mail_config['password']):
|
||||||
|
logger.error("smtp email account is not configured")
|
||||||
|
mail = MIMEMultipart('alternative')
|
||||||
|
mail['Subject'] = Header(subject).encode()
|
||||||
|
mail['From'] = '%s <%s>' % (
|
||||||
|
Header(mail_config['from']).encode(), mail_config['username'])
|
||||||
|
mail['To'] = to
|
||||||
|
mail['Message-id'] = email.utils.make_msgid()
|
||||||
|
mail['Date'] = email.utils.formatdate()
|
||||||
|
|
||||||
|
mail.attach(MIMEText(message, "plain"))
|
||||||
|
try:
|
||||||
|
smtp = smtplib.SMTP_SSL(mail_config['host'], int(mail_config['port']))
|
||||||
|
smtp.login(mail_config['username'], mail_config['password'])
|
||||||
|
smtp.sendmail(mail_config['username'], to, mail.as_string())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("sendmail error: %s", repr(e))
|
||||||
|
smtp.quit()
|
BIN
project/static/favicon.ico
Normal file
BIN
project/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -6,11 +6,13 @@
|
|||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<title>Flask</title>
|
<title>{{ web_title }}</title>
|
||||||
|
<link rel="shortcut icon"
|
||||||
|
href="{{ url_for('static', filename='favicon.ico') }}"/>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"/>
|
href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"/>
|
||||||
<link rel="stylesheet"
|
<link href="https://cdn.staticfile.org/bulma/0.9.4/css/bulma.min.css"
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"/>
|
rel="stylesheet"/>
|
||||||
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
|
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
|
||||||
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -7,11 +7,16 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="fixed-bottom form-inline">
|
<div class="fixed-bottom form-inline">
|
||||||
<textarea id="msgbox" class="form-control" style="width:85%; float: left; margin-bottom: 20px;"></textarea>
|
<textarea id="msgbox"
|
||||||
|
class="form-control"
|
||||||
|
style="width:85%;
|
||||||
|
float: left;
|
||||||
|
margin-bottom: 20px"
|
||||||
|
placeholder="说点什么吧"></textarea>
|
||||||
<button id="btn-send"
|
<button id="btn-send"
|
||||||
class="btn btn-info"
|
class="btn btn-info"
|
||||||
type="button"
|
type="button"
|
||||||
style="width: 10%;"
|
style="width: 10%"
|
||||||
onclick="send_message()"
|
onclick="send_message()"
|
||||||
disabled>
|
disabled>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -37,8 +42,14 @@
|
|||||||
btn_send.disabled = true;
|
btn_send.disabled = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
|
||||||
<script>
|
var textarea = document.getElementById("msgbox")
|
||||||
|
textarea.addEventListener('keydown', function(e) {
|
||||||
|
if (e.ctrlKey && e.keyCode == 13) {
|
||||||
|
send_message()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function get_history() {
|
function get_history() {
|
||||||
var msgs
|
var msgs
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -99,7 +110,7 @@
|
|||||||
success: null,
|
success: null,
|
||||||
dataType: null
|
dataType: null
|
||||||
}).always(function (response) {
|
}).always(function (response) {
|
||||||
if (response?.status == "success") {
|
if (response && response.status == "success") {
|
||||||
var msg_list = document.getElementById("msg-list")
|
var msg_list = document.getElementById("msg-list")
|
||||||
var li = document.createElement('li');
|
var li = document.createElement('li');
|
||||||
var pre = document.createElement('pre');
|
var pre = document.createElement('pre');
|
||||||
|
@ -7,13 +7,36 @@
|
|||||||
你好,游客!
|
你好,游客!
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}<div class="notification is-danger">{{ messages[0] }}</div>{% endif %}
|
||||||
|
{% endwith %}
|
||||||
{% if user and user.is_authenticated %}
|
{% if user and user.is_authenticated %}
|
||||||
{% if user.isActivated %}
|
{% if user.isActivated %}
|
||||||
<a href="{{ url_for('main.chat') }}">
|
<a href="{{ url_for('main.chat') }}">
|
||||||
<button type="button" class="btn btn-primary">开始聊天</button>
|
<button type="button" class="btn btn-primary">开始聊天</button>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="text-warning">您的账号暂未激活,请等待管理员激活此账号。</p>
|
<div class="column is-4 is-offset-4">
|
||||||
|
<div class="box">
|
||||||
|
<p class="text-warning">您的账号暂未激活。</p>
|
||||||
|
<p class="text-warning">如未收到激活码邮件,请联系管理员处理</p>
|
||||||
|
<br/>
|
||||||
|
<form method="post" action="{{ url_for('auth.activate') }}">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input class="input is-large"
|
||||||
|
type="text"
|
||||||
|
name="activation_code"
|
||||||
|
placeholder="激活码"
|
||||||
|
autofocus=""
|
||||||
|
required="required"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="button is-block is-info is-large is-fullwidth">激活</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.login') }}">
|
<a href="{{ url_for('auth.login') }}">
|
||||||
@ -24,7 +47,10 @@
|
|||||||
<button type="button" class="btn btn-default">注册</button>
|
<button type="button" class="btn btn-default">注册</button>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% with messages = get_flashed_messages() %}
|
<div id="homepage-notice" class="row" style="margin-top: 100px;">
|
||||||
{% if messages %}<div class="notification is-danger">{{ messages[0] }}</div>{% endif %}
|
{% if homepage_notice %}
|
||||||
{% endwith %}
|
<h3 class="subtitle">公告栏</h3>
|
||||||
|
<pre>{{ homepage_notice }}</pre>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -65,11 +65,11 @@
|
|||||||
var account = obj.parentElement.parentElement
|
var account = obj.parentElement.parentElement
|
||||||
var data = {
|
var data = {
|
||||||
method: "update",
|
method: "update",
|
||||||
id: account?.children[0]?.innerHTML,
|
id: account.children[0].innerHTML,
|
||||||
email: account?.children[1]?.innerHTML,
|
email: account.children[1].innerHTML,
|
||||||
name: account?.children[2]?.innerHTML,
|
name: account.children[2].innerHTML,
|
||||||
role: account?.children[3]?.children[0].value,
|
role: account.children[3].children[0].value,
|
||||||
isActivated: account?.children[4].children[0].checked
|
isActivated: account.children[4].children[0].checked
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
@ -89,11 +89,11 @@
|
|||||||
var account = obj.parentElement.parentElement
|
var account = obj.parentElement.parentElement
|
||||||
var data = {
|
var data = {
|
||||||
method: "delete",
|
method: "delete",
|
||||||
id: account?.children[0]?.innerHTML,
|
id: account.children[0].innerHTML,
|
||||||
email: account?.children[1]?.innerHTML,
|
email: account.children[1].innerHTML,
|
||||||
name: account?.children[2]?.innerHTML,
|
name: account.children[2].innerHTML,
|
||||||
role: account?.children[3]?.children[0].value,
|
role: account.children[3].children[0].value,
|
||||||
isActivated: account?.children[4].children[0].checked
|
isActivated: account.children[4].children[0].checked
|
||||||
}
|
}
|
||||||
var ret = confirm("确认删除用户\"" + data.name + "\"吗?")
|
var ret = confirm("确认删除用户\"" + data.name + "\"吗?")
|
||||||
if (ret == true) {
|
if (ret == true) {
|
||||||
|
Reference in New Issue
Block a user