Upgrading exports (#283)

* Upgrading export capabilities
* Only apply sqlite hacks for sqlite

This fixes #250, #246
Adds export.py to save CTFs without needing to actually spin up CTFd
Also forcing charset properly for MySQL
selenium-screenshot-testing
Kevin Chung 2017-06-16 17:49:37 -04:00 committed by GitHub
parent 34237e6292
commit f0c44ed6d6
4 changed files with 40 additions and 5 deletions

View File

@ -41,14 +41,20 @@ def create_app(config='CTFd.config.Config'):
if url.drivername == 'postgres': if url.drivername == 'postgres':
url.drivername = 'postgresql' url.drivername = 'postgresql'
if url.drivername.startswith('mysql'):
url.query['charset'] = 'utf8mb4'
# Creates database if the database database does not exist # Creates database if the database database does not exist
if not database_exists(url): if not database_exists(url):
if url.drivername.startswith('mysql'): if url.drivername.startswith('mysql'):
url.query['charset'] = 'utf8mb4'
create_database(url, encoding='utf8mb4') create_database(url, encoding='utf8mb4')
else: else:
create_database(url) create_database(url)
# This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
# This is mostly so we can force MySQL's charset
app.config['SQLALCHEMY_DATABASE_URI'] = str(url)
# Register database # Register database
db.init_app(app) db.init_app(app)

View File

@ -63,11 +63,8 @@ def admin_import_ctf():
import_ctf(backup, segments=segments.split(',')) import_ctf(backup, segments=segments.split(','))
else: else:
import_ctf(backup) import_ctf(backup)
except TypeError:
errors.append('The backup file is invalid')
except IntegrityError as e:
errors.append(e.message)
except Exception as e: except Exception as e:
print(e)
errors.append(type(e).__name__) errors.append(type(e).__name__)
if errors: if errors:

View File

@ -792,6 +792,15 @@ def import_ctf(backup, segments=None, erase=False):
saved = json.loads(data) saved = json.loads(data)
for entry in saved['results']: for entry in saved['results']:
entry_id = entry.pop('id', None) entry_id = entry.pop('id', None)
# This is a hack to get SQlite to properly accept datetime values from dataset
# See Issue #246
if get_config('SQLALCHEMY_DATABASE_URI').startswith('sqlite'):
for k, v in entry.items():
if isinstance(v, six.string_types):
try:
entry[k] = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S')
except ValueError as e:
pass
table.insert(entry) table.insert(entry)
else: else:
continue continue

23
export.py Normal file
View File

@ -0,0 +1,23 @@
from CTFd import create_app
from CTFd.utils import ctf_name, export_ctf
import datetime
import sys
import shutil
import zipfile
app = create_app()
with app.app_context():
backup = export_ctf()
if len(sys.argv) > 1:
with open(sys.argv[1], 'wb') as target:
shutil.copyfileobj(backup, target)
else:
ctf_name = ctf_name()
day = datetime.datetime.now().strftime("%Y-%m-%d")
full_name = "{}.{}.zip".format(ctf_name, day)
with open(full_name, 'wb') as target:
shutil.copyfileobj(backup, target)