mirror of
https://github.com/DarkflameUniverse/NexusDashboard.git
synced 2025-04-26 08:06:21 +00:00
Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
823ec2008f | ||
![]() |
f3e2254330 | ||
![]() |
d6b0a91e4d | ||
![]() |
d698e650ad | ||
![]() |
cde585fad8 | ||
![]() |
8b70f259c0 | ||
![]() |
de50bc7278 | ||
![]() |
2e4bd04d09 | ||
![]() |
ccc793a129 | ||
![]() |
69823be5c8 | ||
![]() |
3027534b16 | ||
![]() |
09096fe1c4 | ||
![]() |
9bfa55ac8e | ||
![]() |
1dee96c04f | ||
![]() |
d005b497e6 | ||
![]() |
259efc81fd | ||
![]() |
b9fc039a7e | ||
![]() |
abc8af89c5 | ||
![]() |
5ae2769ad2 | ||
![]() |
b7e48bb656 | ||
![]() |
7633053490 | ||
![]() |
e6c452d000 | ||
![]() |
13376d0c1f | ||
![]() |
bb63cfb8f5 | ||
![]() |
a10b8d7975 | ||
![]() |
1c9ee91b78 | ||
![]() |
5c2721cd65 | ||
![]() |
8ec3803786 | ||
![]() |
a578f8a53c | ||
![]() |
7ca62fe478 | ||
![]() |
01e304a041 | ||
![]() |
368c0819bd | ||
![]() |
ab8119c5b8 | ||
![]() |
c9ad415f13 | ||
![]() |
a7a68d2fe1 | ||
![]() |
b17928b050 | ||
![]() |
ee65f67fe3 | ||
![]() |
5d1b79334a | ||
![]() |
e726f59114 | ||
![]() |
8826a34ebc | ||
![]() |
a3d492df91 | ||
![]() |
4a58e963a5 | ||
![]() |
8012780eba |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -3,7 +3,7 @@ name: ci
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "**"
|
- "main"
|
||||||
tags:
|
tags:
|
||||||
- "v*.*.*"
|
- "v*.*.*"
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@ -34,7 +34,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
# generate Docker tags based on the following events/attributes
|
# generate Docker tags based on the following events/attributes
|
||||||
@ -46,7 +46,7 @@ jobs:
|
|||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
FROM python:3.8-slim-buster
|
FROM python:3.11-slim-bookworm
|
||||||
|
|
||||||
RUN apt update
|
RUN apt update
|
||||||
RUN apt -y install zip
|
RUN apt -y install zip
|
||||||
|
11
README.md
11
README.md
@ -83,11 +83,12 @@ docker run -d \
|
|||||||
-e APP_SECRET_KEY='<secret_key>' \
|
-e APP_SECRET_KEY='<secret_key>' \
|
||||||
-e APP_DATABASE_URI='mysql+pymysql://<username>:<password>@<host>:<port>/<database>' \
|
-e APP_DATABASE_URI='mysql+pymysql://<username>:<password>@<host>:<port>/<database>' \
|
||||||
# you can include other optional Environment Variables from below like this
|
# you can include other optional Environment Variables from below like this
|
||||||
-e REQUIRE_PLAY_KEY=True
|
-e REQUIRE_PLAY_KEY=True \
|
||||||
-p 8000:8000/tcp
|
-p 8000:8000/tcp \
|
||||||
-v /path/to/unpacked/client:/app/luclient:r \
|
-v /path/to/logs:/logs:rw /
|
||||||
|
-v /path/to/unpacked/client:/app/luclient:ro \
|
||||||
-v /path/to/cachedir:/app/cache:rw \
|
-v /path/to/cachedir:/app/cache:rw \
|
||||||
aronwk/nexus-dashboard:latest
|
ghcr.io/darkflameuniverse/nexusdashboard:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
* `/app/luclient` must be mapped to the location of an unpacked client
|
* `/app/luclient` must be mapped to the location of an unpacked client
|
||||||
@ -97,7 +98,7 @@ docker run -d \
|
|||||||
|
|
||||||
### Environmental Variables
|
### Environmental Variables
|
||||||
|
|
||||||
Please Reference `app/settings_exmaple.py` to see all the variables
|
Please Reference `app/settings_example.py` to see all the variables
|
||||||
|
|
||||||
* Required:
|
* Required:
|
||||||
* APP_SECRET_KEY (Must be provided)
|
* APP_SECRET_KEY (Must be provided)
|
||||||
|
@ -5,7 +5,6 @@ from flask_assets import Environment
|
|||||||
from webassets import Bundle
|
from webassets import Bundle
|
||||||
import time
|
import time
|
||||||
from app.models import db, migrate, PlayKey
|
from app.models import db, migrate, PlayKey
|
||||||
from app.schemas import ma
|
|
||||||
from app.forms import CustomUserManager
|
from app.forms import CustomUserManager
|
||||||
from flask_user import user_registered, current_user, user_logged_in
|
from flask_user import user_registered, current_user, user_logged_in
|
||||||
from flask_wtf.csrf import CSRFProtect
|
from flask_wtf.csrf import CSRFProtect
|
||||||
@ -19,7 +18,9 @@ from app.commands import (
|
|||||||
load_property,
|
load_property,
|
||||||
gen_image_cache,
|
gen_image_cache,
|
||||||
gen_model_cache,
|
gen_model_cache,
|
||||||
fix_clone_ids
|
fix_clone_ids,
|
||||||
|
remove_buffs,
|
||||||
|
find_missing_commendation_items
|
||||||
)
|
)
|
||||||
from app.models import Account, AccountInvitation, AuditLog
|
from app.models import Account, AccountInvitation, AuditLog
|
||||||
|
|
||||||
@ -95,6 +96,8 @@ def create_app():
|
|||||||
app.cli.add_command(gen_image_cache)
|
app.cli.add_command(gen_image_cache)
|
||||||
app.cli.add_command(gen_model_cache)
|
app.cli.add_command(gen_model_cache)
|
||||||
app.cli.add_command(fix_clone_ids)
|
app.cli.add_command(fix_clone_ids)
|
||||||
|
app.cli.add_command(remove_buffs)
|
||||||
|
app.cli.add_command(find_missing_commendation_items)
|
||||||
|
|
||||||
register_logging(app)
|
register_logging(app)
|
||||||
register_settings(app)
|
register_settings(app)
|
||||||
@ -126,8 +129,6 @@ def register_extensions(app):
|
|||||||
"""
|
"""
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
migrate.init_app(app, db)
|
migrate.init_app(app, db)
|
||||||
ma.init_app(app)
|
|
||||||
|
|
||||||
scheduler.init_app(app)
|
scheduler.init_app(app)
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|
||||||
@ -202,19 +203,23 @@ def register_settings(app):
|
|||||||
# Load environment specific settings
|
# Load environment specific settings
|
||||||
app.config['TESTING'] = False
|
app.config['TESTING'] = False
|
||||||
app.config['DEBUG'] = False
|
app.config['DEBUG'] = False
|
||||||
|
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
||||||
|
"pool_pre_ping": True,
|
||||||
|
"pool_size": 10,
|
||||||
|
"max_overflow": 2,
|
||||||
|
"pool_recycle": 300,
|
||||||
|
"pool_pre_ping": True,
|
||||||
|
"pool_use_lifo": True
|
||||||
|
}
|
||||||
|
|
||||||
# always pull these two from the env
|
|
||||||
app.config['SECRET_KEY'] = os.getenv(
|
app.config['SECRET_KEY'] = os.getenv(
|
||||||
'APP_SECRET_KEY',
|
'APP_SECRET_KEY',
|
||||||
app.config['APP_SECRET_KEY']
|
app.config['APP_SECRET_KEY']
|
||||||
|
|
||||||
)
|
)
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv(
|
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv(
|
||||||
'APP_DATABASE_URI',
|
'APP_DATABASE_URI',
|
||||||
app.config['APP_DATABASE_URI']
|
app.config['APP_DATABASE_URI']
|
||||||
)
|
)
|
||||||
|
|
||||||
# try to get overides, otherwise just use what we have already
|
|
||||||
app.config['USER_ENABLE_REGISTER'] = os.getenv(
|
app.config['USER_ENABLE_REGISTER'] = os.getenv(
|
||||||
'USER_ENABLE_REGISTER',
|
'USER_ENABLE_REGISTER',
|
||||||
app.config['USER_ENABLE_REGISTER']
|
app.config['USER_ENABLE_REGISTER']
|
||||||
@ -239,22 +244,16 @@ def register_settings(app):
|
|||||||
'USER_REQUIRE_INVITATION',
|
'USER_REQUIRE_INVITATION',
|
||||||
app.config['USER_REQUIRE_INVITATION']
|
app.config['USER_REQUIRE_INVITATION']
|
||||||
)
|
)
|
||||||
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
|
||||||
"pool_pre_ping": True,
|
|
||||||
"pool_size": 10,
|
|
||||||
"max_overflow": 2,
|
|
||||||
"pool_recycle": 300,
|
|
||||||
"pool_pre_ping": True,
|
|
||||||
"pool_use_lifo": True
|
|
||||||
}
|
|
||||||
app.config['MAIL_SERVER'] = os.getenv(
|
app.config['MAIL_SERVER'] = os.getenv(
|
||||||
'MAIL_SERVER',
|
'MAIL_SERVER',
|
||||||
app.config['MAIL_SERVER']
|
app.config['MAIL_SERVER']
|
||||||
)
|
)
|
||||||
app.config['MAIL_PORT'] = os.getenv(
|
app.config['MAIL_PORT'] = int(
|
||||||
'MAIL_USE_SSL',
|
os.getenv(
|
||||||
|
'MAIL_PORT',
|
||||||
app.config['MAIL_PORT']
|
app.config['MAIL_PORT']
|
||||||
)
|
)
|
||||||
|
)
|
||||||
app.config['MAIL_USE_SSL'] = os.getenv(
|
app.config['MAIL_USE_SSL'] = os.getenv(
|
||||||
'MAIL_USE_SSL',
|
'MAIL_USE_SSL',
|
||||||
app.config['MAIL_USE_SSL']
|
app.config['MAIL_USE_SSL']
|
||||||
@ -308,6 +307,42 @@ def register_settings(app):
|
|||||||
app.config['CACHE_LOCATION']
|
app.config['CACHE_LOCATION']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Recaptcha settings
|
||||||
|
if "RECAPTCHA_ENABLE" not in app.config:
|
||||||
|
app.config['RECAPTCHA_ENABLE'] = False
|
||||||
|
app.config['RECAPTCHA_ENABLE'] = os.getenv(
|
||||||
|
'RECAPTCHA_ENABLE',
|
||||||
|
app.config['RECAPTCHA_ENABLE']
|
||||||
|
)
|
||||||
|
if "RECAPTCHA_PUBLIC_KEY" not in app.config:
|
||||||
|
app.config['RECAPTCHA_PUBLIC_KEY'] = ''
|
||||||
|
app.config['RECAPTCHA_PUBLIC_KEY'] = os.getenv(
|
||||||
|
'RECAPTCHA_PUBLIC_KEY',
|
||||||
|
app.config['RECAPTCHA_PUBLIC_KEY']
|
||||||
|
)
|
||||||
|
if "RECAPTCHA_PRIVATE_KEY" not in app.config:
|
||||||
|
app.config['RECAPTCHA_PRIVATE_KEY'] = ''
|
||||||
|
app.config['RECAPTCHA_PRIVATE_KEY'] = os.getenv(
|
||||||
|
'RECAPTCHA_PRIVATE_KEY',
|
||||||
|
app.config['RECAPTCHA_PRIVATE_KEY']
|
||||||
|
)
|
||||||
|
# Optional
|
||||||
|
if "RECAPTCHA_API_SERVER" in app.config:
|
||||||
|
app.config['RECAPTCHA_API_SERVER'] = os.getenv(
|
||||||
|
'RECAPTCHA_API_SERVER',
|
||||||
|
app.config['RECAPTCHA_API_SERVER']
|
||||||
|
)
|
||||||
|
if "RECAPTCHA_PARAMETERS" in app.config:
|
||||||
|
app.config['RECAPTCHA_PARAMETERS'] = os.getenv(
|
||||||
|
'RECAPTCHA_PARAMETERS',
|
||||||
|
app.config['RECAPTCHA_PARAMETERS']
|
||||||
|
)
|
||||||
|
if "RECAPTCHA_DATA_ATTRS" in app.config:
|
||||||
|
app.config['RECAPTCHA_DATA_ATTRS'] = os.getenv(
|
||||||
|
'RECAPTCHA_DATA_ATTRS',
|
||||||
|
app.config['RECAPTCHA_DATA_ATTRS']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def gm_level(gm_level):
|
def gm_level(gm_level):
|
||||||
|
@ -19,16 +19,12 @@ from app.models import (
|
|||||||
db,
|
db,
|
||||||
Friends
|
Friends
|
||||||
)
|
)
|
||||||
from app.schemas import AccountSchema
|
|
||||||
from app import gm_level, log_audit
|
from app import gm_level, log_audit
|
||||||
from app.forms import EditGMLevelForm, EditEmailForm
|
from app.forms import EditGMLevelForm, EditEmailForm
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
|
||||||
accounts_blueprint = Blueprint('accounts', __name__)
|
accounts_blueprint = Blueprint('accounts', __name__)
|
||||||
|
|
||||||
account_schema = AccountSchema()
|
|
||||||
|
|
||||||
|
|
||||||
@accounts_blueprint.route('/', methods=['GET'])
|
@accounts_blueprint.route('/', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
@ -256,6 +252,9 @@ def get():
|
|||||||
# Delete
|
# Delete
|
||||||
# </a>
|
# </a>
|
||||||
|
|
||||||
|
if not current_app.config["USER_ENABLE_EMAIL"]:
|
||||||
|
account["2"] = '''N/A'''
|
||||||
|
|
||||||
if account["4"]:
|
if account["4"]:
|
||||||
account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
|
account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
|
||||||
else:
|
else:
|
||||||
@ -271,20 +270,11 @@ def get():
|
|||||||
else:
|
else:
|
||||||
account["6"] = '''<h2 class="far fa-check-square text-success"></h2>'''
|
account["6"] = '''<h2 class="far fa-check-square text-success"></h2>'''
|
||||||
|
|
||||||
if current_app.config["USER_ENABLE_EMAIL"]:
|
if not current_app.config["USER_ENABLE_EMAIL"]:
|
||||||
if account["8"]:
|
account["8"] = '''<h2 class="far fa-times-circle text-muted"></h2>'''
|
||||||
|
elif account["8"]:
|
||||||
account["8"] = '''<h2 class="far fa-check-square text-success"></h2>'''
|
account["8"] = '''<h2 class="far fa-check-square text-success"></h2>'''
|
||||||
else:
|
else:
|
||||||
account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
|
account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
|
||||||
else:
|
|
||||||
# shift columns to fill in gap of 2
|
|
||||||
account["2"] = account["3"]
|
|
||||||
account["3"] = account["4"]
|
|
||||||
account["4"] = account["5"]
|
|
||||||
account["5"] = account["6"]
|
|
||||||
account["6"] = account["7"]
|
|
||||||
# remove last two columns
|
|
||||||
del account["7"]
|
|
||||||
del account["8"]
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -3,20 +3,17 @@ from flask_user import login_required, current_user
|
|||||||
from datatables import ColumnDT, DataTables
|
from datatables import ColumnDT, DataTables
|
||||||
import time
|
import time
|
||||||
from app.models import CharacterInfo, CharacterXML, Account, db
|
from app.models import CharacterInfo, CharacterXML, Account, db
|
||||||
from app.schemas import CharacterInfoSchema
|
|
||||||
from app.forms import RescueForm, CharXMLUploadForm
|
from app.forms import RescueForm, CharXMLUploadForm
|
||||||
from app import gm_level, log_audit
|
from app import gm_level, log_audit
|
||||||
from app.luclient import translate_from_locale
|
from app.luclient import translate_from_locale
|
||||||
import xmltodict
|
import xmltodict
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import json
|
import json
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
|
||||||
character_blueprint = Blueprint('characters', __name__)
|
character_blueprint = Blueprint('characters', __name__)
|
||||||
|
|
||||||
character_schema = CharacterInfoSchema()
|
|
||||||
|
|
||||||
|
|
||||||
@character_blueprint.route('/', methods=['GET'])
|
@character_blueprint.route('/', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
@ -103,6 +100,88 @@ def view(id):
|
|||||||
character_json=character_json
|
character_json=character_json
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@character_blueprint.route('/chardata/<id>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def chardata(id):
|
||||||
|
|
||||||
|
character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()
|
||||||
|
|
||||||
|
if character_data == {}:
|
||||||
|
abort(404)
|
||||||
|
return
|
||||||
|
|
||||||
|
if current_user.gm_level < 3:
|
||||||
|
if character_data.account_id and character_data.account_id != current_user.id:
|
||||||
|
abort(403)
|
||||||
|
return
|
||||||
|
character_json = xmltodict.parse(
|
||||||
|
CharacterXML.query.filter(
|
||||||
|
CharacterXML.id == id
|
||||||
|
).first().xml_data.replace("\"stt=", "\" stt="),
|
||||||
|
attr_prefix="attr_"
|
||||||
|
)
|
||||||
|
|
||||||
|
# print json for reference
|
||||||
|
# with open("errorchar.json", "a") as file:
|
||||||
|
# file.write(
|
||||||
|
# json.dumps(character_json, indent=4)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# stupid fix for jinja parsing
|
||||||
|
character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items")
|
||||||
|
# sort by items slot index
|
||||||
|
if type(character_json["obj"]["inv"]["holdings"]["in"]) == list:
|
||||||
|
for inv in character_json["obj"]["inv"]["holdings"]["in"]:
|
||||||
|
if "i" in inv.keys() and type(inv["i"]) == list:
|
||||||
|
inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s']))
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'partials/_charxml.html.j2',
|
||||||
|
character_data=character_data,
|
||||||
|
character_json=character_json
|
||||||
|
)
|
||||||
|
|
||||||
|
@character_blueprint.route('/inventory/<id>/<inventory_id>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def inventory(id, inventory_id):
|
||||||
|
|
||||||
|
character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first()
|
||||||
|
|
||||||
|
if character_data == {}:
|
||||||
|
abort(404)
|
||||||
|
return
|
||||||
|
|
||||||
|
if current_user.gm_level < 3:
|
||||||
|
if character_data.account_id and character_data.account_id != current_user.id:
|
||||||
|
abort(403)
|
||||||
|
return
|
||||||
|
character_json = xmltodict.parse(
|
||||||
|
CharacterXML.query.filter(
|
||||||
|
CharacterXML.id == id
|
||||||
|
).first().xml_data.replace("\"stt=", "\" stt="),
|
||||||
|
attr_prefix="attr_"
|
||||||
|
)
|
||||||
|
|
||||||
|
# print json for reference
|
||||||
|
# with open("errorchar.json", "a") as file:
|
||||||
|
# file.write(
|
||||||
|
# json.dumps(character_json, indent=4)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# stupid fix for jinja parsing
|
||||||
|
character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items")
|
||||||
|
# sort by items slot index
|
||||||
|
if type(character_json["obj"]["inv"]["holdings"]["in"]) == list:
|
||||||
|
for inv in character_json["obj"]["inv"]["holdings"]["in"]:
|
||||||
|
if "i" in inv.keys() and type(inv["i"]) == list:
|
||||||
|
inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s']))
|
||||||
|
for inventory in character_json["obj"]["inv"]["holdings"]["in"]:
|
||||||
|
if inventory["attr_t"] == inventory_id:
|
||||||
|
return render_template(
|
||||||
|
'partials/charxml/_inventory.html.j2',
|
||||||
|
inventory=inventory
|
||||||
|
)
|
||||||
|
return "No Items in Inventory", 404
|
||||||
|
|
||||||
@character_blueprint.route('/view_xml/<id>', methods=['GET'])
|
@character_blueprint.route('/view_xml/<id>', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@ -238,7 +317,7 @@ def upload(id):
|
|||||||
flash("You accept all consequences from these actions", "danger")
|
flash("You accept all consequences from these actions", "danger")
|
||||||
log_audit(f"Updated {character_data.id}'s xml data")
|
log_audit(f"Updated {character_data.id}'s xml data")
|
||||||
return redirect(url_for('characters.view', id=id))
|
return redirect(url_for('characters.view', id=id))
|
||||||
form.char_xml.data = character_data.xml_data
|
form.char_xml.data = minidom.parseString(character_data.xml_data).toprettyxml(indent=" ")
|
||||||
return render_template("character/upload.html.j2", form=form)
|
return render_template("character/upload.html.j2", form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import random
|
|||||||
import string
|
import string
|
||||||
import datetime
|
import datetime
|
||||||
from flask_user import current_app
|
from flask_user import current_app
|
||||||
from app import db
|
from app import db, luclient
|
||||||
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC, Mail
|
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC, Mail, CharacterXML
|
||||||
import pathlib
|
import pathlib
|
||||||
import zlib
|
import zlib
|
||||||
from wand import image
|
from wand import image
|
||||||
@ -15,7 +15,7 @@ from multiprocessing import Pool
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
import time
|
import time
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
@click.command("init_db")
|
@click.command("init_db")
|
||||||
@click.argument('drop_tables', nargs=1)
|
@click.argument('drop_tables', nargs=1)
|
||||||
@ -180,6 +180,20 @@ def load_property(zone, player):
|
|||||||
)
|
)
|
||||||
new_prop_content.save()
|
new_prop_content.save()
|
||||||
|
|
||||||
|
@click.command("remove_buffs")
|
||||||
|
@with_appcontext
|
||||||
|
def remove_buffs():
|
||||||
|
"""Clears all buff from all characters"""
|
||||||
|
chars = CharacterXML.query.all()
|
||||||
|
for char in chars:
|
||||||
|
character_xml = ET.XML(char.xml_data.replace("\"stt=", "\" stt="))
|
||||||
|
dest = character_xml.find(".//dest")
|
||||||
|
if dest:
|
||||||
|
buff = character_xml.find(".//buff")
|
||||||
|
if buff:
|
||||||
|
dest.remove(buff)
|
||||||
|
char.xml_data = ET.tostring(character_xml)
|
||||||
|
char.save()
|
||||||
|
|
||||||
@click.command("gen_image_cache")
|
@click.command("gen_image_cache")
|
||||||
def gen_image_cache():
|
def gen_image_cache():
|
||||||
@ -267,3 +281,41 @@ def find_or_create_account(name, email, password, gm_level=9):
|
|||||||
db.session.add(play_key)
|
db.session.add(play_key)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return # account
|
return # account
|
||||||
|
|
||||||
|
@click.command("find_missing_commendation_items")
|
||||||
|
@with_appcontext
|
||||||
|
def find_missing_commendation_items():
|
||||||
|
data = dict()
|
||||||
|
lots = set()
|
||||||
|
reward_items = luclient.query_cdclient("Select reward_item1 from Missions;")
|
||||||
|
for reward_item in reward_items:
|
||||||
|
lots.add(reward_item[0])
|
||||||
|
reward_items = luclient.query_cdclient("Select reward_item2 from Missions;")
|
||||||
|
for reward_item in reward_items:
|
||||||
|
lots.add(reward_item[0])
|
||||||
|
reward_items = luclient.query_cdclient("Select reward_item3 from Missions;")
|
||||||
|
for reward_item in reward_items:
|
||||||
|
lots.add(reward_item[0])
|
||||||
|
reward_items = luclient.query_cdclient("Select reward_item4 from Missions;")
|
||||||
|
for reward_item in reward_items:
|
||||||
|
lots.add(reward_item[0])
|
||||||
|
lots.remove(0)
|
||||||
|
lots.remove(-1)
|
||||||
|
|
||||||
|
for lot in lots:
|
||||||
|
itemcompid = luclient.query_cdclient(
|
||||||
|
"Select component_id from ComponentsRegistry where component_type = 11 and id = ?;",
|
||||||
|
[lot],
|
||||||
|
one=True
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
itemcomp = luclient.query_cdclient(
|
||||||
|
"Select commendationLOT, commendationCost from ItemComponent where id = ?;",
|
||||||
|
[itemcompid],
|
||||||
|
one=True
|
||||||
|
)
|
||||||
|
if itemcomp[0] is None or itemcomp[1] is None:
|
||||||
|
data[lot] = {"name": luclient.get_lot_name(lot)}
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
|
||||||
|
72
app/forms.py
72
app/forms.py
@ -1,17 +1,15 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm, Recaptcha, RecaptchaField
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from flask_user.forms import (
|
from flask_user.forms import (
|
||||||
unique_email_validator,
|
unique_email_validator,
|
||||||
password_validator,
|
LoginForm,
|
||||||
unique_username_validator
|
RegisterForm
|
||||||
)
|
)
|
||||||
from flask_user import UserManager
|
from flask_user import UserManager
|
||||||
from wtforms.widgets import TextArea, NumberInput
|
from wtforms.widgets import TextArea, NumberInput
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
StringField,
|
StringField,
|
||||||
HiddenField,
|
|
||||||
PasswordField,
|
|
||||||
BooleanField,
|
BooleanField,
|
||||||
SubmitField,
|
SubmitField,
|
||||||
validators,
|
validators,
|
||||||
@ -24,69 +22,43 @@ from app.models import PlayKey
|
|||||||
|
|
||||||
|
|
||||||
def validate_play_key(form, field):
|
def validate_play_key(form, field):
|
||||||
"""Validates a field for a valid phone number
|
"""Validates a field for a valid play kyey
|
||||||
Args:
|
Args:
|
||||||
form: REQUIRED, the field's parent form
|
form: REQUIRED, the field's parent form
|
||||||
field: REQUIRED, the field with data
|
field: REQUIRED, the field with data
|
||||||
Returns:
|
Returns:
|
||||||
None, raises ValidationError if failed
|
None, raises ValidationError if failed
|
||||||
"""
|
"""
|
||||||
# jank to get the fireign key that we need back into the field
|
# jank to get the foreign key that we need back into the field
|
||||||
if current_app.config["REQUIRE_PLAY_KEY"]:
|
if current_app.config["REQUIRE_PLAY_KEY"]:
|
||||||
field.data = PlayKey.key_is_valid(key_string=field.data)
|
field.data = PlayKey.key_is_valid(key_string=field.data)
|
||||||
return
|
return True
|
||||||
|
|
||||||
|
class CustomRecaptcha(Recaptcha):
|
||||||
|
def __call__(self, form, field):
|
||||||
|
if not current_app.config.get("RECAPTCHA_ENABLE", False):
|
||||||
|
return True
|
||||||
|
return super(CustomRecaptcha, self).__call__(form, field)
|
||||||
|
|
||||||
|
|
||||||
class CustomUserManager(UserManager):
|
class CustomUserManager(UserManager):
|
||||||
def customize(self, app):
|
def customize(self, app):
|
||||||
self.RegisterFormClass = CustomRegisterForm
|
self.RegisterFormClass = CustomRegisterForm
|
||||||
|
self.LoginFormClass = CustomLoginForm
|
||||||
|
|
||||||
|
class CustomRegisterForm(RegisterForm):
|
||||||
class CustomRegisterForm(FlaskForm):
|
|
||||||
"""Registration form"""
|
|
||||||
next = HiddenField()
|
|
||||||
reg_next = HiddenField()
|
|
||||||
|
|
||||||
# Login Info
|
|
||||||
email = StringField(
|
|
||||||
'E-Mail',
|
|
||||||
validators=[
|
|
||||||
Optional(),
|
|
||||||
validators.Email('Invalid email address'),
|
|
||||||
unique_email_validator,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
username = StringField(
|
|
||||||
'Username',
|
|
||||||
validators=[
|
|
||||||
DataRequired(),
|
|
||||||
unique_username_validator,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
play_key_id = StringField(
|
play_key_id = StringField(
|
||||||
'Play Key',
|
'Play Key',
|
||||||
validators=[
|
validators=[validate_play_key]
|
||||||
Optional(),
|
)
|
||||||
validate_play_key,
|
recaptcha = RecaptchaField(
|
||||||
]
|
validators=[CustomRecaptcha()]
|
||||||
)
|
)
|
||||||
|
|
||||||
password = PasswordField('Password', validators=[
|
class CustomLoginForm(LoginForm):
|
||||||
DataRequired(),
|
recaptcha = RecaptchaField(
|
||||||
password_validator,
|
validators=[CustomRecaptcha()]
|
||||||
validators.length(max=40, message="The maximum length of the password is 40 characters due to game client limitations")
|
)
|
||||||
])
|
|
||||||
retype_password = PasswordField('Retype Password', validators=[
|
|
||||||
validators.EqualTo('password', message='Passwords did not match'),
|
|
||||||
validators.length(max=40, message="The maximum length of the password is 40 characters due to game client limitations")
|
|
||||||
])
|
|
||||||
|
|
||||||
invite_token = HiddenField('Token')
|
|
||||||
|
|
||||||
submit = SubmitField('Register')
|
|
||||||
|
|
||||||
|
|
||||||
class CreatePlayKeyForm(FlaskForm):
|
class CreatePlayKeyForm(FlaskForm):
|
||||||
|
|
||||||
|
@ -2,16 +2,12 @@ from flask import render_template, Blueprint, send_from_directory
|
|||||||
from flask_user import current_user, login_required
|
from flask_user import current_user, login_required
|
||||||
|
|
||||||
from app.models import Account, CharacterInfo, ActivityLog
|
from app.models import Account, CharacterInfo, ActivityLog
|
||||||
from app.schemas import AccountSchema, CharacterInfoSchema
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
main_blueprint = Blueprint('main', __name__)
|
main_blueprint = Blueprint('main', __name__)
|
||||||
|
|
||||||
account_schema = AccountSchema()
|
|
||||||
char_info_schema = CharacterInfoSchema()
|
|
||||||
|
|
||||||
|
|
||||||
@main_blueprint.route('/', methods=['GET'])
|
@main_blueprint.route('/', methods=['GET'])
|
||||||
def index():
|
def index():
|
||||||
|
@ -4,10 +4,9 @@ from flask_user import UserMixin
|
|||||||
from wtforms import ValidationError
|
from wtforms import ValidationError
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from flask_sqlalchemy import BaseQuery
|
from flask_sqlalchemy.query import Query
|
||||||
from sqlalchemy.dialects import mysql
|
from sqlalchemy.dialects import mysql
|
||||||
from sqlalchemy.exc import OperationalError, StatementError
|
from sqlalchemy.exc import OperationalError, StatementError
|
||||||
from sqlalchemy.types import JSON
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
@ -15,7 +14,7 @@ import string
|
|||||||
|
|
||||||
# retrying query to work around python trash collector
|
# retrying query to work around python trash collector
|
||||||
# killing connections of other gunicorn workers
|
# killing connections of other gunicorn workers
|
||||||
class RetryingQuery(BaseQuery):
|
class RetryingQuery(Query):
|
||||||
__retry_count__ = 3
|
__retry_count__ = 3
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -1018,7 +1017,7 @@ class Reports(db.Model):
|
|||||||
__tablename__ = 'reports'
|
__tablename__ = 'reports'
|
||||||
|
|
||||||
data = db.Column(
|
data = db.Column(
|
||||||
JSON(),
|
mysql.MEDIUMBLOB(),
|
||||||
nullable=False
|
nullable=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ from flask_user import login_required, current_user
|
|||||||
from datatables import ColumnDT, DataTables
|
from datatables import ColumnDT, DataTables
|
||||||
import time
|
import time
|
||||||
from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account, Mail
|
from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account, Mail
|
||||||
from app.schemas import PropertySchema
|
|
||||||
from app import gm_level, log_audit
|
from app import gm_level, log_audit
|
||||||
from app.luclient import query_cdclient
|
from app.luclient import query_cdclient
|
||||||
from app.forms import RejectPropertyForm
|
from app.forms import RejectPropertyForm
|
||||||
@ -24,9 +23,6 @@ import pathlib
|
|||||||
|
|
||||||
property_blueprint = Blueprint('properties', __name__)
|
property_blueprint = Blueprint('properties', __name__)
|
||||||
|
|
||||||
property_schema = PropertySchema()
|
|
||||||
|
|
||||||
|
|
||||||
@property_blueprint.route('/', methods=['GET'])
|
@property_blueprint.route('/', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
|
@ -4,8 +4,7 @@ from app.models import CharacterInfo, Account, CharacterXML, Reports
|
|||||||
from app.luclient import get_lot_name
|
from app.luclient import get_lot_name
|
||||||
from app import gm_level, scheduler
|
from app import gm_level, scheduler
|
||||||
from sqlalchemy.orm import load_only
|
from sqlalchemy.orm import load_only
|
||||||
import datetime
|
import xmltodict, gzip, json, datetime
|
||||||
import xmltodict
|
|
||||||
|
|
||||||
reports_blueprint = Blueprint('reports', __name__)
|
reports_blueprint = Blueprint('reports', __name__)
|
||||||
|
|
||||||
@ -44,6 +43,8 @@ def index():
|
|||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
def items_by_date(date):
|
def items_by_date(date):
|
||||||
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data
|
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data
|
||||||
|
data = gzip.decompress(data)
|
||||||
|
data = json.loads(data.decode('utf-8'))
|
||||||
return render_template('reports/items/by_date.html.j2', data=data, date=date)
|
return render_template('reports/items/by_date.html.j2', data=data, date=date)
|
||||||
|
|
||||||
|
|
||||||
@ -62,6 +63,8 @@ def items_graph(start, end):
|
|||||||
datasets = []
|
datasets = []
|
||||||
# get stuff ready
|
# get stuff ready
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
|
entry.data = gzip.decompress(entry.data)
|
||||||
|
entry.data = json.loads(entry.data.decode('utf-8'))
|
||||||
labels.append(entry.date.strftime("%m/%d/%Y"))
|
labels.append(entry.date.strftime("%m/%d/%Y"))
|
||||||
for key in entry.data:
|
for key in entry.data:
|
||||||
items[key] = get_lot_name(key)
|
items[key] = get_lot_name(key)
|
||||||
@ -104,6 +107,8 @@ def items_graph(start, end):
|
|||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
def currency_by_date(date):
|
def currency_by_date(date):
|
||||||
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data
|
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data
|
||||||
|
data = gzip.decompress(data)
|
||||||
|
data = json.loads(data.decode('utf-8'))
|
||||||
return render_template('reports/currency/by_date.html.j2', data=data, date=date)
|
return render_template('reports/currency/by_date.html.j2', data=data, date=date)
|
||||||
|
|
||||||
|
|
||||||
@ -121,6 +126,8 @@ def currency_graph(start, end):
|
|||||||
datasets = []
|
datasets = []
|
||||||
# get stuff ready
|
# get stuff ready
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
|
entry.data = gzip.decompress(entry.data)
|
||||||
|
entry.data = json.loads(entry.data.decode('utf-8'))
|
||||||
labels.append(entry.date.strftime("%m/%d/%Y"))
|
labels.append(entry.date.strftime("%m/%d/%Y"))
|
||||||
for character in characters:
|
for character in characters:
|
||||||
data = []
|
data = []
|
||||||
@ -155,6 +162,8 @@ def currency_graph(start, end):
|
|||||||
@gm_level(3)
|
@gm_level(3)
|
||||||
def uscore_by_date(date):
|
def uscore_by_date(date):
|
||||||
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first().data
|
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first().data
|
||||||
|
data = gzip.decompress(data)
|
||||||
|
data = json.loads(data.decode('utf-8'))
|
||||||
return render_template('reports/uscore/by_date.html.j2', data=data, date=date)
|
return render_template('reports/uscore/by_date.html.j2', data=data, date=date)
|
||||||
|
|
||||||
|
|
||||||
@ -172,6 +181,8 @@ def uscore_graph(start, end):
|
|||||||
datasets = []
|
datasets = []
|
||||||
# get stuff ready
|
# get stuff ready
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
|
entry.data = gzip.decompress(entry.data)
|
||||||
|
entry.data = json.loads(entry.data.decode('utf-8'))
|
||||||
labels.append(entry.date.strftime("%m/%d/%Y"))
|
labels.append(entry.date.strftime("%m/%d/%Y"))
|
||||||
for character in characters:
|
for character in characters:
|
||||||
data = []
|
data = []
|
||||||
@ -236,19 +247,31 @@ def gen_item_report():
|
|||||||
if "i" in inv.keys() and type(inv["i"]) == list and (int(inv["attr_t"]) == 0 or int(inv["attr_t"]) == 1):
|
if "i" in inv.keys() and type(inv["i"]) == list and (int(inv["attr_t"]) == 0 or int(inv["attr_t"]) == 1):
|
||||||
for item in inv["i"]:
|
for item in inv["i"]:
|
||||||
if item["attr_l"] in report_data:
|
if item["attr_l"] in report_data:
|
||||||
|
if ("attr_c" in item):
|
||||||
report_data[item["attr_l"]]["item_count"] = report_data[item["attr_l"]]["item_count"] + int(item["attr_c"])
|
report_data[item["attr_l"]]["item_count"] = report_data[item["attr_l"]]["item_count"] + int(item["attr_c"])
|
||||||
else:
|
else:
|
||||||
|
report_data[item["attr_l"]]["item_count"] = report_data[item["attr_l"]]["item_count"] + 1
|
||||||
|
else:
|
||||||
|
if ("attr_c" in item):
|
||||||
report_data[item["attr_l"]] = {"item_count": int(item["attr_c"]), "chars": {}}
|
report_data[item["attr_l"]] = {"item_count": int(item["attr_c"]), "chars": {}}
|
||||||
|
else:
|
||||||
|
report_data[item["attr_l"]] = {"item_count": 1, "chars": {}}
|
||||||
if name in report_data[item["attr_l"]]["chars"]:
|
if name in report_data[item["attr_l"]]["chars"]:
|
||||||
|
if ("attr_c" in item):
|
||||||
report_data[item["attr_l"]]["chars"][name] = report_data[item["attr_l"]]["chars"][name] + int(item["attr_c"])
|
report_data[item["attr_l"]]["chars"][name] = report_data[item["attr_l"]]["chars"][name] + int(item["attr_c"])
|
||||||
else:
|
else:
|
||||||
|
report_data[item["attr_l"]]["chars"][name] = report_data[item["attr_l"]]["chars"][name] + 1
|
||||||
|
else:
|
||||||
|
if ("attr_c" in item):
|
||||||
report_data[item["attr_l"]]["chars"][name] = int(item["attr_c"])
|
report_data[item["attr_l"]]["chars"][name] = int(item["attr_c"])
|
||||||
|
else:
|
||||||
|
report_data[item["attr_l"]]["chars"][name] = 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"REPORT::ITEMS - ERROR PARSING CHARACTER {char_xml.id}")
|
current_app.logger.error(f"REPORT::ITEMS - ERROR PARSING CHARACTER {char_xml.id}")
|
||||||
current_app.logger.error(f"REPORT::ITEMS - {e}")
|
current_app.logger.error(f"REPORT::ITEMS - {e}")
|
||||||
|
|
||||||
new_report = Reports(
|
new_report = Reports(
|
||||||
data=report_data,
|
data=gzip.compress(json.dumps(report_data).encode('utf-8')),
|
||||||
report_type="items",
|
report_type="items",
|
||||||
date=date
|
date=date
|
||||||
)
|
)
|
||||||
@ -296,7 +319,7 @@ def gen_currency_report():
|
|||||||
current_app.logger.error(f"REPORT::CURRENCY - {e}")
|
current_app.logger.error(f"REPORT::CURRENCY - {e}")
|
||||||
|
|
||||||
new_report = Reports(
|
new_report = Reports(
|
||||||
data=report_data,
|
data=gzip.compress(json.dumps(report_data).encode('utf-8')),
|
||||||
report_type="currency",
|
report_type="currency",
|
||||||
date=date
|
date=date
|
||||||
)
|
)
|
||||||
@ -344,7 +367,7 @@ def gen_uscore_report():
|
|||||||
current_app.logger.error(f"REPORT::U-SCORE - {e}")
|
current_app.logger.error(f"REPORT::U-SCORE - {e}")
|
||||||
|
|
||||||
new_report = Reports(
|
new_report = Reports(
|
||||||
data=report_data,
|
data=gzip.compress(json.dumps(report_data).encode('utf-8')),
|
||||||
report_type="uscore",
|
report_type="uscore",
|
||||||
date=date
|
date=date
|
||||||
)
|
)
|
||||||
|
130
app/schemas.py
130
app/schemas.py
@ -1,130 +0,0 @@
|
|||||||
from flask_marshmallow import Marshmallow
|
|
||||||
from app.models import (
|
|
||||||
PlayKey,
|
|
||||||
PetNames,
|
|
||||||
Mail,
|
|
||||||
UGC,
|
|
||||||
PropertyContent,
|
|
||||||
Property,
|
|
||||||
CharacterXML,
|
|
||||||
CharacterInfo,
|
|
||||||
Account,
|
|
||||||
AccountInvitation,
|
|
||||||
ActivityLog,
|
|
||||||
CommandLog
|
|
||||||
)
|
|
||||||
ma = Marshmallow()
|
|
||||||
|
|
||||||
|
|
||||||
class PlayKeySchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = PlayKey
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = True
|
|
||||||
|
|
||||||
|
|
||||||
class PetNamesSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = PetNames
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
|
|
||||||
class MailSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = Mail
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
|
|
||||||
class UGCSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = UGC
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
|
|
||||||
class PropertyContentSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = PropertyContent
|
|
||||||
include_relationships = True
|
|
||||||
load_instance = True
|
|
||||||
include_fk = True
|
|
||||||
|
|
||||||
ugc = ma.Nested(UGCSchema)
|
|
||||||
|
|
||||||
|
|
||||||
class PropertySchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = Property
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
properties_contents = ma.Nested(PropertyContentSchema, many=True)
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterXMLSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = CharacterXML
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterInfoSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = CharacterInfo
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
charxml = ma.Nested(CharacterXMLSchema)
|
|
||||||
properties_owner = ma.Nested(PropertySchema, many=True)
|
|
||||||
pets = ma.Nested(PetNamesSchema, many=True)
|
|
||||||
mail = ma.Nested(MailSchema, many=True)
|
|
||||||
|
|
||||||
|
|
||||||
class AccountSchema(ma.SQLAlchemyAutoSchema):
|
|
||||||
class Meta:
|
|
||||||
model = Account
|
|
||||||
include_relationships = False
|
|
||||||
load_instance = True
|
|
||||||
include_fk = False
|
|
||||||
|
|
||||||
play_key = ma.Nested(PlayKeySchema)
|
|
||||||
charinfo = ma.Nested(CharacterInfoSchema, many=True)
|
|
||||||
|
|
||||||
|
|
||||||
class AccountInvitationSchema(ma.SQLAlchemyAutoSchema): # noqa
|
|
||||||
class Meta:
|
|
||||||
model = AccountInvitation
|
|
||||||
include_relationships = True
|
|
||||||
load_instance = True
|
|
||||||
include_fk = True
|
|
||||||
|
|
||||||
invite_by_user = ma.Nested(AccountSchema)
|
|
||||||
|
|
||||||
|
|
||||||
class ActivityLogSchema(ma.SQLAlchemyAutoSchema): # noqa
|
|
||||||
class Meta:
|
|
||||||
model = ActivityLog
|
|
||||||
include_relationships = True
|
|
||||||
load_instance = True
|
|
||||||
include_fk = True
|
|
||||||
|
|
||||||
character = ma.Nested(CharacterInfoSchema())
|
|
||||||
|
|
||||||
|
|
||||||
class CommandLogSchema(ma.SQLAlchemyAutoSchema): # noqa
|
|
||||||
class Meta:
|
|
||||||
model = CommandLog
|
|
||||||
include_relationships = True
|
|
||||||
load_instance = True
|
|
||||||
include_fk = True
|
|
||||||
|
|
||||||
character = ma.Nested(CharacterInfoSchema())
|
|
@ -61,3 +61,13 @@ USER_AFTER_LOGOUT_ENDPOINT = "main.index"
|
|||||||
|
|
||||||
# Option will be removed once this feature is full implemeted
|
# Option will be removed once this feature is full implemeted
|
||||||
ENABLE_CHAR_XML_UPLOAD = False
|
ENABLE_CHAR_XML_UPLOAD = False
|
||||||
|
|
||||||
|
# Recaptcha settings
|
||||||
|
# See: https://flask-wtf.readthedocs.io/en/1.2.x/form/#recaptcha
|
||||||
|
RECAPTCHA_ENABLE = False
|
||||||
|
RECAPTCHA_PUBLIC_KEY = ''
|
||||||
|
RECAPTCHA_PRIVATE_KEY = ''
|
||||||
|
# Optional
|
||||||
|
# RECAPTCHA_API_SERVER = ''
|
||||||
|
# RECAPTCHA_PARAMETERS = ''
|
||||||
|
RECAPTCHA_DATA_ATTRS = {'theme': 'white', 'size': 'invisible'}
|
||||||
|
@ -14,17 +14,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
{% if config.USER_ENABLE_EMAIL %}
|
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
{% endif %}
|
|
||||||
<th>GM Level</th>
|
<th>GM Level</th>
|
||||||
<th>Locked</th>
|
<th>Locked</th>
|
||||||
<th>Banned</th>
|
<th>Banned</th>
|
||||||
<th>Muted</th>
|
<th>Muted</th>
|
||||||
<th>Registered</th>
|
<th>Registered</th>
|
||||||
{% if config.USER_ENABLE_EMAIL %}
|
|
||||||
<th>Email Confirmed</th>
|
<th>Email Confirmed</th>
|
||||||
{% endif %}
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
|
@ -1,188 +0,0 @@
|
|||||||
{% extends "bootstrap/base.html" %}
|
|
||||||
{% block title %}Key Creation{% endblock %}
|
|
||||||
|
|
||||||
{% block navbar %}
|
|
||||||
<nav class="navbar navbar-default">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<a class="navbar-brand" href="{{ url_for('dashboard') }}">Dashboard</a>
|
|
||||||
</div>
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
</ul>
|
|
||||||
<ul class="nav navbar-nav" style="float: right">
|
|
||||||
<li class="active"><a href="#">Welcome {{ current_user.username }}!</a></li>
|
|
||||||
<li><a href="{{ url_for('logout') }}">Logout</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
{% endblock navbar %}}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{# LOGO #}
|
|
||||||
<div class="container" style="margin-top: 50px">
|
|
||||||
|
|
||||||
{# Display logo #}
|
|
||||||
<div style="margin-bottom: 50px">
|
|
||||||
<img
|
|
||||||
src="{{ url_for('static', filename=resources.LOGO) }}"
|
|
||||||
class="center-block img-responsive mx-auto d-block"
|
|
||||||
alt="Logo"
|
|
||||||
width="100"
|
|
||||||
height="100"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Key creation #}
|
|
||||||
<div class="container">
|
|
||||||
<div class="text-center">
|
|
||||||
<h3>Key Creation</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
|
|
||||||
{# If the error value is set, display the error in red text #}
|
|
||||||
{% if error %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
{{ error }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# If the message value is set, display the message in green text #}
|
|
||||||
{% if message %}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
{{ message }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Form which takes in Admin Username, Admin Password, and the amount of keys to create. #}
|
|
||||||
<form action="{{ url_for('dashboard') }}" method="post">
|
|
||||||
{# Key count input #}
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="key_count">Generate keys</label>
|
|
||||||
<input type="number" class="form-control" name="key_count" placeholder="Enter number of keys...">
|
|
||||||
<small class="form-text text-muted">Number of keys to create.</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Submit button #}
|
|
||||||
<div class="form-group">
|
|
||||||
<button type="submit" class="btn btn-primary">Generate Keys</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{# If the keys value is set, create a list for each key in keys #}
|
|
||||||
{% if keys %}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
<ul>
|
|
||||||
{% for key in keys %}
|
|
||||||
<li>{{ key }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Activity graphs #}
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<h3>Activity</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-6">
|
|
||||||
|
|
||||||
<canvas id="sessions_graph" width="400" height="400"></canvas>
|
|
||||||
<canvas id="play_time_graph" width="400" height="400"></canvas>
|
|
||||||
<canvas id="zone_play_time_graph" width="400" height="400"></canvas>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-lg-3">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Make a get request to the server to load the activity data
|
|
||||||
$.get("{{ url_for('load_activities') }}", function(data) {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make a get request to the server to get the activity data for "sessions"
|
|
||||||
$.get("{{ url_for('activity_data', name='sessions') }}", function(data) {
|
|
||||||
// Load data as a json object
|
|
||||||
data = JSON.parse(data);
|
|
||||||
var ctx = document.getElementById('sessions_graph').getContext('2d');
|
|
||||||
var myChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: data.labels,
|
|
||||||
datasets: data.datasets
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make a get request to the server to get the activity data for "play_time"
|
|
||||||
$.get("{{ url_for('activity_data', name='play_time') }}", function(data) {
|
|
||||||
// Load data as a json object
|
|
||||||
data = JSON.parse(data);
|
|
||||||
var ctx = document.getElementById('play_time_graph').getContext('2d');
|
|
||||||
var myChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: data.labels,
|
|
||||||
datasets: data.datasets
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make a get request to the server to get the activity data for "zone_play_time"
|
|
||||||
$.get("{{ url_for('activity_data', name='zone_play_time') }}", function(data) {
|
|
||||||
// Load data as a json object
|
|
||||||
data = JSON.parse(data);
|
|
||||||
var ctx = document.getElementById('zone_play_time_graph').getContext('2d');
|
|
||||||
var myChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: data.labels,
|
|
||||||
datasets: data.datasets
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock scripts %}}
|
|
@ -84,6 +84,35 @@
|
|||||||
<script type="text/javascript" src="{{ url_for('static', filename='bootstrap-4.2.1/js/bootstrap.bundle.min.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='bootstrap-4.2.1/js/bootstrap.bundle.min.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='datatables/datatables.min.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='datatables/datatables.min.js') }}"></script>
|
||||||
<script type="sytylesheet" src="{{ url_for('static', filename='datatables/datatables.min.css') }}"></script>
|
<script type="sytylesheet" src="{{ url_for('static', filename='datatables/datatables.min.css') }}"></script>
|
||||||
|
<script>
|
||||||
|
// set the active nav-link item
|
||||||
|
$(function () {
|
||||||
|
let target_nav = '#{{request.endpoint}}'.replace('\.', '-');
|
||||||
|
$(target_nav).addClass('active');
|
||||||
|
});
|
||||||
|
// make tooltips with data work
|
||||||
|
$(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
})
|
||||||
|
function setInnerHTML(elm, html) {
|
||||||
|
elm.innerHTML = html;
|
||||||
|
$("body").tooltip({ selector: '[data-toggle=tooltip]' });
|
||||||
|
Array.from(elm.querySelectorAll("script"))
|
||||||
|
.forEach( oldScriptEl => {
|
||||||
|
const newScriptEl = document.createElement("script");
|
||||||
|
|
||||||
|
Array.from(oldScriptEl.attributes).forEach( attr => {
|
||||||
|
newScriptEl.setAttribute(attr.name, attr.value)
|
||||||
|
});
|
||||||
|
|
||||||
|
const scriptText = document.createTextNode(oldScriptEl.innerHTML);
|
||||||
|
newScriptEl.appendChild(scriptText);
|
||||||
|
|
||||||
|
oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -9,10 +9,22 @@
|
|||||||
{% endblock content_before %}
|
{% endblock content_before %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3 style="color: orange;">PROCEED WITH CAUTION</h3>
|
<style>
|
||||||
|
.blink {
|
||||||
|
animation: blinker .5s linear infinite;
|
||||||
|
color: red;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
@keyframes blinker {
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h3 class="text-center blink">PROCEED WITH CAUTION</h3>
|
||||||
<form method=post>
|
<form method=post>
|
||||||
{{ form.csrf_token }}
|
{{ form.csrf_token }}
|
||||||
<div class="card shadow-sm mx-auto pb-3 bg-dark border-primary" style="width: 20rem;">
|
<div class="card shadow-sm mx-auto pb-3 bg-dark border-primary" >
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{ helper.render_field(form.char_xml) }}
|
{{ helper.render_field(form.char_xml) }}
|
||||||
{{ helper.render_submit_field(form.submit) }}
|
{{ helper.render_submit_field(form.submit) }}
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
{% include 'partials/_character.html.j2' %}
|
{% include 'partials/_character.html.j2' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="col-sm" id="charxml">
|
||||||
{% include 'partials/_charxml.html.j2'%}
|
Loading Character Data
|
||||||
|
{% include 'partials/_loading.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
@ -32,3 +33,14 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock content_after %}
|
{% endblock content_after %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ super() }}
|
||||||
|
<script>
|
||||||
|
fetch({{ url_for("characters.chardata", id=character_data.id)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("charxml"), text);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock js %}
|
||||||
|
@ -63,6 +63,11 @@
|
|||||||
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# recaptcha #}
|
||||||
|
{% if config.RECAPTCHA_ENABLE %}
|
||||||
|
{{ render_field(form.recaptcha, tabindex=250) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{# Submit button #}
|
{# Submit button #}
|
||||||
{{ render_submit_field(form.submit, tabindex=180) }}
|
{{ render_submit_field(form.submit, tabindex=180) }}
|
||||||
</form>
|
</form>
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# recaptcha #}
|
||||||
|
{% if config.RECAPTCHA_ENABLE %}
|
||||||
|
{{ form.recaptcha }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{# Submit button #}
|
{# Submit button #}
|
||||||
{{ render_submit_field(login_form.submit, tabindex=180) }}
|
{{ render_submit_field(login_form.submit, tabindex=180) }}
|
||||||
</form>
|
</form>
|
||||||
@ -63,6 +68,11 @@
|
|||||||
{{ render_field(register_form.retype_password, tabindex=240) }}
|
{{ render_field(register_form.retype_password, tabindex=240) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# recaptcha #}
|
||||||
|
{% if config.RECAPTCHA_ENABLE %}
|
||||||
|
{{ register_form.recaptcha }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ render_submit_field(register_form.submit, tabindex=280) }}
|
{{ render_submit_field(register_form.submit, tabindex=280) }}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -47,6 +47,11 @@
|
|||||||
{{ render_field(form.retype_password, tabindex=240) }}
|
{{ render_field(form.retype_password, tabindex=240) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# recaptcha #}
|
||||||
|
{% if config.RECAPTCHA_ENABLE %}
|
||||||
|
{{ render_field(form.recaptcha, tabindex=250) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -82,7 +82,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col text-right">
|
<div class="col text-right">
|
||||||
Source
|
Source
|
||||||
|
@ -11,9 +11,11 @@
|
|||||||
<div class="col text-right">
|
<div class="col text-right">
|
||||||
U-Score: {{ character_json.obj.char.attr_ls }}
|
U-Score: {{ character_json.obj.char.attr_ls }}
|
||||||
</div>
|
</div>
|
||||||
|
{% if "lvl" in character_json.obj %}
|
||||||
<div class="col">
|
<div class="col">
|
||||||
Level: {{ character_json.obj.lvl.attr_l }}
|
Level: {{ character_json.obj.lvl.attr_l }}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -112,99 +114,33 @@
|
|||||||
<div class="tab-content mt-3" id="nav-invContent">
|
<div class="tab-content mt-3" id="nav-invContent">
|
||||||
<div class="tab-pane fade show active" id="nav-items" role="tabpanel" aria-labelledby="nav-items-tab">
|
<div class="tab-pane fade show active" id="nav-items" role="tabpanel" aria-labelledby="nav-items-tab">
|
||||||
{# Inv ID 0 - Index: 0 #}
|
{# Inv ID 0 - Index: 0 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "0" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-vault" role="tabpanel" aria-labelledby="nav-vault-tab">
|
<div class="tab-pane fade" id="nav-vault" role="tabpanel" aria-labelledby="nav-vault-tab">
|
||||||
{# Inv ID 1 - Index: 1 #}
|
{# Inv ID 1 - Index: 1 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "1" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-vault-models" role="tabpanel" aria-labelledby="nav-vault-models-tab">
|
<div class="tab-pane fade" id="nav-vault-models" role="tabpanel" aria-labelledby="nav-vault-models-tab">
|
||||||
{# Inv ID 14 - Index: 10 #}
|
{# Inv ID 14 - Index: 10 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "14" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-bricks" role="tabpanel" aria-labelledby="nav-bricks-tab">
|
<div class="tab-pane fade" id="nav-bricks" role="tabpanel" aria-labelledby="nav-bricks-tab">
|
||||||
{# Inv ID 2 - Index: 2 #}
|
{# Inv ID 2 - Index: 2 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "2" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-models" role="tabpanel" aria-labelledby="nav-models-tab">
|
<div class="tab-pane fade" id="nav-models" role="tabpanel" aria-labelledby="nav-models-tab">
|
||||||
{# Inv ID 5 - Index: 6 #}
|
{# Inv ID 5 - Index: 6 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "5" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="nav-behaviors" role="tabpanel" aria-labelledby="nav-behaviors-tab">
|
<div class="tab-pane fade" id="nav-behaviors" role="tabpanel" aria-labelledby="nav-behaviors-tab">
|
||||||
{# Inv ID 7 - Index: 8 #}
|
{# Inv ID 7 - Index: 8 #}
|
||||||
{% for item in character_json.obj.inv.holdings.in %}
|
Loading Inventory
|
||||||
{% if item.attr_t == "7" %}
|
{% include 'partials/_loading.html' %}
|
||||||
{% if item.i is iterable and (item.i is not string and item.i is not mapping) %}
|
|
||||||
{% for inv_item in item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
{% with inv_item=item.i %}
|
|
||||||
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -263,3 +199,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=0)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-items"), text);
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=1)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-vault"), text);
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=14)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-vault-models"), text);
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=2)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-bricks"), text);
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=5)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-models"), text);
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch({{ url_for("characters.inventory", id=character_data.id, inventory_id=7)|tojson }})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
setInnerHTML(document.getElementById("nav-behaviors"), text);
|
||||||
|
})
|
||||||
|
</script>
|
21
app/templates/partials/_loading.html
Normal file
21
app/templates/partials/_loading.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s;">
|
||||||
|
<span class="sr-only">Loading 0</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 1s;">
|
||||||
|
<span class="sr-only">Loading 1</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 2s;">
|
||||||
|
<span class="sr-only">Loading 2</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 3s;">
|
||||||
|
<span class="sr-only">Loading 3</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 4s;">
|
||||||
|
<span class="sr-only">Loading 4</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 5s;">
|
||||||
|
<span class="sr-only">Loading 5</span>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-grow text-light" role="status" style="animation-duration: 6s; animation-delay: 6s;">
|
||||||
|
<span class="sr-only">Loading 6</span>
|
||||||
|
</div>
|
@ -77,6 +77,14 @@
|
|||||||
{{ property.performance_cost }}
|
{{ property.performance_cost }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col text-right">
|
||||||
|
Clone ID:
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{{ property.clone_id }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% if request.endpoint != "properties.view" %}
|
{% if request.endpoint != "properties.view" %}
|
||||||
<br/>
|
<br/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -4,25 +4,29 @@
|
|||||||
alt="{{ inv_item.attr_l|get_lot_name }}"
|
alt="{{ inv_item.attr_l|get_lot_name }}"
|
||||||
class="border p-1 border-primary rounded m-1"
|
class="border p-1 border-primary rounded m-1"
|
||||||
width="60"
|
width="60"
|
||||||
|
{% if 'attr_eq' in inv_item %}
|
||||||
{% if inv_item.attr_eq == "true" %}style="background-color:#d16f05;"{% endif %}
|
{% if inv_item.attr_eq == "true" %}style="background-color:#d16f05;"{% endif %}
|
||||||
|
{% endif %}
|
||||||
height="60"
|
height="60"
|
||||||
data-html="true"
|
data-html="true"
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
data-placement="left"
|
data-placement="left"
|
||||||
title="{% include 'partials/charxml/_item_tooltip.html.j2' %}"
|
title="{% include 'partials/charxml/_item_tooltip.html.j2' %}"
|
||||||
>
|
>
|
||||||
{% if inv_item.attr_c != "1" %}
|
{% if 'attr_c' in inv_item %}
|
||||||
<span class="inventory-count text-bold">
|
<span class="inventory-count text-bold">
|
||||||
{%if inv_item.attr_c|int > 999 %}
|
{%if inv_item.attr_c|int > 999 %}
|
||||||
+999
|
+999
|
||||||
{% else %}
|
{% elif inv_item.attr_c|int > 1 %}
|
||||||
{{ inv_item.attr_c }}
|
{{ inv_item.attr_c }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if 'attr_b' in inv_item %}
|
||||||
{% if inv_item.attr_b == "true" %}
|
{% if inv_item.attr_b == "true" %}
|
||||||
<span class="inventory-lock">
|
<span class="inventory-lock">
|
||||||
<i class='fas fa-lock'></i>
|
<i class='fas fa-lock'></i>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
9
app/templates/partials/charxml/_inventory.html.j2
Normal file
9
app/templates/partials/charxml/_inventory.html.j2
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% if inventory.i is iterable and (inventory.i is not string and inventory.i is not mapping) %}
|
||||||
|
{% for inv_item in inventory.i %}
|
||||||
|
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% with inv_item=inventory.i %}
|
||||||
|
{% include 'partials/charxml/_inv_grid.html.j2' %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
@ -66,7 +66,8 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'attr_c' in inv_item %}
|
||||||
{%if inv_item.attr_c|int > 999 %}
|
{%if inv_item.attr_c|int > 999 %}
|
||||||
<br />Count: {{ inv_item.attr_c|numberFormat }}
|
<br />Count: {{ inv_item.attr_c|numberFormat }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
@ -22,8 +22,8 @@ logger = logging.getLogger('alembic.env')
|
|||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
config.set_main_option(
|
config.set_main_option(
|
||||||
'sqlalchemy.url',
|
'sqlalchemy.url',
|
||||||
str(current_app.extensions['migrate'].db.get_engine().url).replace(
|
current_app.config.get('SQLALCHEMY_DATABASE_URI')
|
||||||
'%', '%%'))
|
)
|
||||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
164
migrations/versions/1164e037907f_compressss_reports.py
Normal file
164
migrations/versions/1164e037907f_compressss_reports.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
"""compressss reports
|
||||||
|
|
||||||
|
Revision ID: 1164e037907f
|
||||||
|
Revises: a6e42ef03da7
|
||||||
|
Create Date: 2023-11-18 01:38:00.127472
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
from sqlalchemy.orm.session import Session
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
from sqlalchemy.types import JSON
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
import gzip
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '1164e037907f'
|
||||||
|
down_revision = 'a6e42ef03da7'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
class ReportsUpgradeNew(Base):
|
||||||
|
__tablename__ = 'reports'
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
|
||||||
|
data = sa.Column(
|
||||||
|
mysql.MEDIUMBLOB(),
|
||||||
|
nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
report_type = sa.Column(
|
||||||
|
sa.VARCHAR(35),
|
||||||
|
nullable=False,
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
date = sa.Column(
|
||||||
|
sa.Date(),
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
sa.session.add(self)
|
||||||
|
sa.session.commit()
|
||||||
|
sa.session.refresh(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportsUpgradeOld(Base):
|
||||||
|
__tablename__ = 'reports_old'
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
|
||||||
|
data = sa.Column(
|
||||||
|
JSON(),
|
||||||
|
nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
report_type = sa.Column(
|
||||||
|
sa.VARCHAR(35),
|
||||||
|
nullable=False,
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
date = sa.Column(
|
||||||
|
sa.Date(),
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class ReportsDowngradeOld(Base):
|
||||||
|
__tablename__ = 'reports'
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
|
||||||
|
data = sa.Column(
|
||||||
|
mysql.MEDIUMBLOB(),
|
||||||
|
nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
report_type = sa.Column(
|
||||||
|
sa.VARCHAR(35),
|
||||||
|
nullable=False,
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
date = sa.Column(
|
||||||
|
sa.Date(),
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
sa.session.add(self)
|
||||||
|
sa.session.commit()
|
||||||
|
sa.session.refresh(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportsDowngradeNew(Base):
|
||||||
|
__tablename__ = 'reports_old'
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
|
||||||
|
data = sa.Column(
|
||||||
|
JSON(),
|
||||||
|
nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
report_type = sa.Column(
|
||||||
|
sa.VARCHAR(35),
|
||||||
|
nullable=False,
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
date = sa.Column(
|
||||||
|
sa.Date(),
|
||||||
|
primary_key=True,
|
||||||
|
autoincrement=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
|
||||||
|
op.rename_table('reports', 'reports_old')
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = Session(bind=bind)
|
||||||
|
reports = session.query(ReportsUpgradeOld)
|
||||||
|
op.create_table('reports',
|
||||||
|
sa.Column('data', mysql.MEDIUMBLOB(), nullable=False),
|
||||||
|
sa.Column('report_type', sa.VARCHAR(length=35), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('date', sa.Date(), autoincrement=False, nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('report_type', 'date')
|
||||||
|
)
|
||||||
|
# insert records
|
||||||
|
new_reports = []
|
||||||
|
# insert records
|
||||||
|
for report in reports:
|
||||||
|
new_reports.append({
|
||||||
|
"data":gzip.compress(json.dumps(report.data).encode('utf-8')),
|
||||||
|
"report_type":report.report_type,
|
||||||
|
"date":report.date
|
||||||
|
})
|
||||||
|
op.bulk_insert(ReportsUpgradeNew.__table__, new_reports)
|
||||||
|
op.drop_table('reports_old')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('reports')
|
||||||
|
op.create_table('reports',
|
||||||
|
sa.Column('data', JSON(), nullable=False),
|
||||||
|
sa.Column('report_type', sa.VARCHAR(length=35), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('date', sa.Date(), autoincrement=False, nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('report_type', 'date')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
@ -77,11 +77,11 @@ def upgrade():
|
|||||||
sa.Column('created_at', mysql.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
sa.Column('created_at', mysql.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||||
sa.Column('play_key_id', mysql.INTEGER(), nullable=True),
|
sa.Column('play_key_id', mysql.INTEGER(), nullable=True),
|
||||||
sa.Column('mute_expire', mysql.BIGINT(unsigned=True), server_default='0', nullable=False),
|
sa.Column('mute_expire', mysql.BIGINT(unsigned=True), server_default='0', nullable=False),
|
||||||
sa.ForeignKeyConstraint(['play_key_id'], ['play_keys.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint('id'),
|
||||||
sa.UniqueConstraint('name')
|
sa.UniqueConstraint('name')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
op.create_foreign_key(None, 'accounts', 'play_keys', ['play_key_id'], ['id'], ondelete='CASCADE')
|
||||||
op.add_column('accounts', sa.Column('active', sa.BOOLEAN(), server_default='1', nullable=False))
|
op.add_column('accounts', sa.Column('active', sa.BOOLEAN(), server_default='1', nullable=False))
|
||||||
op.add_column('accounts', sa.Column('email_confirmed_at', sa.DateTime(), nullable=True))
|
op.add_column('accounts', sa.Column('email_confirmed_at', sa.DateTime(), nullable=True))
|
||||||
op.add_column('accounts', sa.Column('email', sa.Unicode(length=255), server_default='', nullable=True))
|
op.add_column('accounts', sa.Column('email', sa.Unicode(length=255), server_default='', nullable=True))
|
||||||
|
@ -1,61 +1,20 @@
|
|||||||
alembic==1.7.5
|
alembic==1.7.5
|
||||||
APScheduler==3.8.1
|
|
||||||
astroid==2.9.1
|
|
||||||
autopep8==1.6.0
|
autopep8==1.6.0
|
||||||
bcrypt==3.2.0
|
|
||||||
blinker==1.4
|
|
||||||
cffi==1.14.6
|
|
||||||
click==8.0.1
|
|
||||||
colorama==0.4.4
|
|
||||||
cryptography==36.0.0
|
|
||||||
dnspython==2.1.0
|
|
||||||
dominate==2.6.0
|
|
||||||
email-validator==1.1.3
|
email-validator==1.1.3
|
||||||
Flask==2.0.1
|
Flask==3.0.0
|
||||||
Flask-APScheduler==1.12.3
|
Flask-APScheduler==1.12.3
|
||||||
Flask-Assets==2.0
|
Flask-Assets==2.1.0
|
||||||
Flask-Login==0.5.0
|
|
||||||
Flask-Mail==0.9.1
|
|
||||||
flask-marshmallow==0.14.0
|
|
||||||
Flask-Migrate==3.1.0
|
Flask-Migrate==3.1.0
|
||||||
Flask-SQLAlchemy==2.5.1
|
Flask-SQLAlchemy==3.1.1
|
||||||
Flask-User==1.0.2.2
|
Flask-User==1.0.2.2
|
||||||
Flask-WTF==1.0.0
|
Flask-WTF==1.2.1
|
||||||
greenlet==1.1.0
|
gunicorn==21.2.0
|
||||||
idna==3.3
|
|
||||||
isort==5.10.1
|
|
||||||
itsdangerous==2.0.1
|
|
||||||
Jinja2==3.0.1
|
|
||||||
lazy-object-proxy==1.7.1
|
|
||||||
libsass==0.21.0
|
libsass==0.21.0
|
||||||
Mako==1.2.2
|
|
||||||
MarkupSafe==2.0.1
|
|
||||||
marshmallow==3.14.1
|
|
||||||
marshmallow-sqlalchemy==0.26.1
|
|
||||||
mccabe==0.6.1
|
|
||||||
passlib==1.7.4
|
|
||||||
platformdirs==2.4.1
|
|
||||||
pycodestyle==2.8.0
|
|
||||||
pycparser==2.20
|
|
||||||
pydocstyle==6.1.1
|
|
||||||
pyflakes==2.4.0
|
|
||||||
pylama==8.3.3
|
|
||||||
pylint==2.12.2
|
|
||||||
PyMySQL==1.0.2
|
PyMySQL==1.0.2
|
||||||
python-dateutil==2.8.2
|
SQLAlchemy==2.0.23
|
||||||
pytz==2021.3
|
|
||||||
pytz-deprecation-shim==0.1.0.post0
|
|
||||||
six==1.16.0
|
|
||||||
snowballstemmer==2.2.0
|
|
||||||
SQLAlchemy==1.4.22
|
|
||||||
sqlalchemy-datatables==2.0.1
|
sqlalchemy-datatables==2.0.1
|
||||||
toml==0.10.2
|
|
||||||
tzdata==2021.5
|
|
||||||
tzlocal==4.1
|
|
||||||
visitor==0.1.3
|
|
||||||
Wand==0.6.7
|
Wand==0.6.7
|
||||||
webassets==2.0
|
webassets==2.0
|
||||||
Werkzeug==2.0.1
|
Werkzeug==3.0.1
|
||||||
wrapt==1.13.3
|
|
||||||
WTForms==3.0.0
|
WTForms==3.0.0
|
||||||
xmltodict==0.12.0
|
xmltodict==0.12.0
|
||||||
|
16
wsgi.py
16
wsgi.py
@ -1,23 +1,19 @@
|
|||||||
from sys import platform
|
|
||||||
from app import create_app
|
from app import create_app
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
|
|
||||||
@app.shell_context_processor
|
@app.shell_context_processor
|
||||||
def make_shell_context():
|
def make_shell_context():
|
||||||
"""Extend the Flask shell context."""
|
"""Extend the Flask shell context."""
|
||||||
return {'app': app}
|
return {'app': app}
|
||||||
|
|
||||||
running_directly = __name__ == "wsgi" or __name__ == "__main__"
|
|
||||||
running_under_gunicorn = not running_directly and 'gunicorn' in __name__ and 'linux' in platform
|
|
||||||
|
|
||||||
# Configure development running
|
if __name__ == '__main__':
|
||||||
if running_directly:
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
app.run(host='0.0.0.0')
|
app.run(host='0.0.0.0')
|
||||||
|
else:
|
||||||
# Configure production running
|
|
||||||
if running_under_gunicorn:
|
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
gunicorn_logger = logging.getLogger('gunicorn.error')
|
gunicorn_logger = logging.getLogger('gunicorn.error')
|
||||||
@ -27,7 +23,3 @@ if running_under_gunicorn:
|
|||||||
file_handler.setFormatter(formatter)
|
file_handler.setFormatter(formatter)
|
||||||
app.logger.addHandler(file_handler)
|
app.logger.addHandler(file_handler)
|
||||||
app.logger.setLevel(gunicorn_logger.level)
|
app.logger.setLevel(gunicorn_logger.level)
|
||||||
|
|
||||||
# Error out if nothing has been setup
|
|
||||||
if not running_directly and not running_under_gunicorn:
|
|
||||||
raise RuntimeError('Unsupported WSGI server')
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user