From 37aea443d6f3974ee91c5d5a78cbbacda132fd68 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 10 Dec 2017 12:12:06 -0600 Subject: [PATCH] Sincrionizacion con SeaFile general --- source/app/conf.py.example | 12 +++ source/app/controllers/helper.py | 140 +++++++++++++++++++++++++++++++ source/app/controllers/util.py | 57 +++++++++---- source/app/settings.py | 5 ++ source/static/js/ui/admin.js | 2 +- 5 files changed, 199 insertions(+), 17 deletions(-) diff --git a/source/app/conf.py.example b/source/app/conf.py.example index 03166fb..0892815 100644 --- a/source/app/conf.py.example +++ b/source/app/conf.py.example @@ -11,3 +11,15 @@ DEFAULT_PASSWORD = 'blades3.3' #~ Establece una ruta accesible para el servidor web LOG_PATH = '/srv/empresa/logs/empresalibre.log' + +# ~ Establece los valores para sincronizar los backups de la base de datos +# ~ por ejemplo +# ~ SEAFILE_SERVER = { + # ~ 'URL': 'https://tu_servidor_seafile', + # ~ 'USER': 'tu_usuario', + # ~ 'PASS': 'tu_contraseña', + # ~ 'REPO': 'id_repo', +# ~ } + + +SEAFILE_SERVER = {} \ No newline at end of file diff --git a/source/app/controllers/helper.py b/source/app/controllers/helper.py index 0ccfc90..16875f7 100644 --- a/source/app/controllers/helper.py +++ b/source/app/controllers/helper.py @@ -12,6 +12,9 @@ from email.mime.text import MIMEText from email import encoders from email.utils import formatdate +import os +import requests + from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Image from reportlab.lib import colors from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle @@ -724,3 +727,140 @@ class ReportTemplate(BaseDocTemplate): return +class SeaFileAPI(object): + FILE_DOES_NOT_EXIST = 441 + + def __init__(self, url, username, password): + self._url = url + self._headers = self._get_auth(username, password) + + @property + def is_connect(self): + return bool(self._headers) + + def _open(self, path): + return open(path, 'rb') + + def _get_auth(self, username, password): + url = self._url + 'auth-token/' + data = { + 'username': username, + 'password': password, + } + resp = requests.post(url, data=data) + if resp.status_code != requests.codes.ok: + msg = 'Token Error: {}'.format(resp.status_code) + print (msg) + return {} + + headers = {'Authorization': 'Token {}'.format(resp.json()['token'])} + return headers + + def _get_upload_link(self, repo_id): + if not self._headers: + return '' + + url = '{}repos/{}/upload-link/'.format(self._url, repo_id) + resp = requests.get(url, headers=self._headers) + if resp.status_code != requests.codes.ok: + msg = 'Error: {}'.format(resp.status_code) + print (msg) + return '' + + return resp.json() + + def _get_update_link(self, repo_id): + if not self._headers: + return '' + + url = '{}repos/{}/update-link/'.format(self._url, repo_id) + data = {'p': '/'} + resp = requests.get(url, data=data, headers=self._headers) + if resp.status_code != requests.codes.ok: + msg = 'Error: {}'.format(resp.status_code) + print (msg) + return '' + + return resp.json() + + def _decrypt(self, repo_id, password): + if not self._headers: + return False + + url = '{}repos/{}/'.format(self._url, repo_id) + data = {'password': password} + resp = requests.post(url, data=data, headers=self._headers) + if resp.status_code != requests.codes.ok: + msg = 'Error: {}'.format(resp.status_code) + print (msg) + return False + + return True + + def upload_file(self, path_file, repo_id, password): + if not self._headers: + return False + + if password: + if not self._decrypt(repo_id, password): + return False + + upload_link = self._get_upload_link(repo_id) + data = { + 'filename': path_file, + 'parent_dir': '/', + 'relative_path': '', + } + files = {'file': self._open(path_file)} + + resp = requests.post( + upload_link, data=data, files=files, headers=self._headers) + if resp.status_code != requests.codes.ok: + msg = 'Upload Code: {}\n{}'.format(resp.status_code, resp.text) + print (msg) + return False + + return True + + def _info_path(self, path): + path, filename = os.path.split(path) + return path, filename + + def update_file(self, path_file, repo_id, password, create=True): + if not self._headers: + return False + + if password: + if not self._decrypt(repo_id, password): + return False + + update_link = self._get_update_link(repo_id) + _, filename = self._info_path(path_file) + files = { + 'file': (filename, self._open(path_file)), + 'filename': (None, filename), + 'target_file': (None, '/{}'.format(filename)) + } + + resp = requests.post(update_link, files=files, headers=self._headers) + if resp.status_code != requests.codes.ok: + msg = 'Update Code: {}\n{}'.format(resp.status_code, resp.text) + print (msg) + if resp.status_code == self.FILE_DOES_NOT_EXIST and create: + return self.upload_file(path_file, repo_id, password) + else: + return False + + return True + + def ping(self): + url = self._url + 'ping/' + resp = requests.get(url) + return resp.json() + + + def auth_ping(self): + url = self._url + 'auth/ping/' + resp = requests.get(url, headers=self._headers) + return resp.json() + diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 8eb9f8c..9162630 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -33,18 +33,13 @@ except: import pyqrcode from dateutil import parser -from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice +from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice, \ + SeaFileAPI from settings import DEBUG, log, template_lookup, COMPANIES, DB_SAT, \ PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \ PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES - -#~ def _get_hash(password): - #~ return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() - - -#~ def validate_password(hashed, password): - #~ return bcrypt.hashpw(password.encode(), hashed.encode()) == hashed.encode() +from settings import SEAFILE_SERVER def _call(args): @@ -1390,11 +1385,46 @@ def crear_db(nombre): def _to_seafile(path_db, data): - msg = '\tSincronizando backup...' - log.info(msg) + # ~ if DEBUG: + # ~ return + + _, filename = os.path.split(path_db) + if SEAFILE_SERVER: + msg = '\tSincronizando backup general...' + log.info(msg) + seafile = SeaFileAPI( + SEAFILE_SERVER['URL'], + SEAFILE_SERVER['USER'], + SEAFILE_SERVER['PASS']) + + if seafile.is_connect: + msg = '\tSincronizando: {} '.format(filename) + log.info(msg) + seafile.update_file( + path_db, SEAFILE_SERVER['REPO'], SEAFILE_SERVER['PASS']) + msg = '\tRespaldo general de {} sincronizado'.format(filename) + log.info(msg) + + if len(data) < 2: + return + if not data[0] or not data[1]: + return + + # ~ msg = '\tSincronizando backup particular...' + # ~ log.info(msg) + # ~ seafile = SeaFileAPI(SEAFILE_SERVER['URL'], data[0], data[1]) + # ~ if seafile.is_connect: + # ~ msg = '\tSincronizando: {} '.format(filename) + # ~ log.info(msg) + # ~ seafile.update_file( + # ~ path_db, SEAFILE_SERVER['REPO'], data[1]) + # ~ msg = '\tRespaldo partícular de {} sincronizado'.format(filename) + # ~ log.info(msg) + return +@run_in_thread def _backup_and_sync(rfc, data): msg = 'Generando backup de: {}'.format(rfc) log.info(msg) @@ -1413,11 +1443,6 @@ def _backup_and_sync(rfc, data): msg = '\tBackup generado...' log.info(msg) result = _call(sql).strip().split('|') - if len(result) < 2: - return - if not result[0] or not result[1]: - return - _to_seafile(path_bk, result) except Exception as e: log.info(e) @@ -1438,7 +1463,7 @@ def backup_dbs(): for rfc, data in rows: args = loads(data) - run_in_thread(_backup_and_sync(rfc, args)) + _backup_and_sync(rfc, args) return diff --git a/source/app/settings.py b/source/app/settings.py index f5b88b1..db494d8 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -14,6 +14,11 @@ try: except ImportError: DEFAULT_PASSWORD = 'blades3.3' +try: + from conf import SEAFILE_SERVER +except ImportError: + SEAFILE_SERVER = {} + DEBUG = DEBUG VERSION = '0.2.1' diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 27a267e..20a2a77 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -148,7 +148,7 @@ var emisor_otros_datos= [ {view: 'text', id: 'token_timbrado', name: 'token_timbrado', label: 'Token de Timbrado: '}, {view: 'text', id: 'token_soporte', - name: 'token_soporte', label: 'Token de Soporte: '}, + name: 'token_soporte', label: 'Token para Respaldos: '}, ]