anki-sync-server/src/ankisyncd/full_sync.py

96 lines
3.0 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
import logging
import os
from sqlite3 import dbapi2 as sqlite
import shutil
import sys
from webob.exc import HTTPBadRequest
from anki.db import DB
from anki.collection import Collection
logger = logging.getLogger("ankisyncd.media")
logger.setLevel(1)
class FullSyncManager:
def test_db(self, db: DB):
"""
:param anki.db.DB db: the database uploaded from the client.
"""
if db.scalar("pragma integrity_check") != "ok":
raise HTTPBadRequest(
"Integrity check failed for uploaded collection database file."
)
def upload(self, col: Collection, data: bytes, session) -> str:
"""
Uploads a sqlite database from the client to the sync server.
:param anki.collection.Collectio col:
:param bytes data: The binary sqlite database from the client.
:param .sync_app.SyncUserSession session: The current session.
"""
# Verify integrity of the received database file before replacing our
# existing db.
temp_db_path = session.get_collection_path() + ".tmp"
with open(temp_db_path, 'wb') as f:
f.write(data)
try:
with DB(temp_db_path) as test_db:
self.test_db(test_db)
except sqlite.Error as e:
raise HTTPBadRequest("Uploaded collection database file is "
"corrupt.")
# Overwrite existing db.
col.close()
try:
shutil.copyfile(temp_db_path, session.get_collection_path())
finally:
col.reopen()
2020-08-27 03:06:57 +08:00
# Reopen the media database
col.media.connect()
return "OK"
def download(self, col: Collection, session) -> bytes:
"""Download the binary database.
Performs a downgrade to database schema 11 before sending the database
to the client.
:param anki.collection.Collection col:
:param .sync_app.SyncUserSession session:
:return bytes: the binary sqlite3 database
"""
col.close(downgrade=True)
db_path = session.get_collection_path()
try:
with open(db_path, 'rb') as tmp:
data = tmp.read()
finally:
col.reopen()
# Reopen the media database
col.media.connect()
return data
def get_full_sync_manager(config):
if "full_sync_manager" in config and config["full_sync_manager"]: # load from config
import importlib
import inspect
module_name, class_name = config['full_sync_manager'].rsplit('.', 1)
module = importlib.import_module(module_name.strip())
class_ = getattr(module, class_name.strip())
if not FullSyncManager in inspect.getmro(class_):
raise TypeError('''"full_sync_manager" found in the conf file but it doesn''t
inherit from FullSyncManager''')
return class_(config)
else:
return FullSyncManager()