def exec(self) -> dict: # If the user is already authenticated if not auth.get_current_user().is_anonymous: raise self.forbidden( lang.t('auth_http_api@user_already_authenticated')) try: user = auth.sign_in(self.arg('driver'), self.args) r = {'status': True} if self.arg('access_token'): r['access_token'] = _get_access_token_info( auth.generate_access_token(user)) return r # User account is not active except (auth.error.UserNotActive, auth.error.UserNotConfirmed) as e: raise self.warning(e, 401) # Any other exception except Exception as e: # Don't expose reason of error to the outer world logger.error(e) raise self.unauthorized( lang.t('auth_http_api@authentication_error'))
def get_comments_count(self, thread_uid: str) -> int: """Get comments count for particular thread. """ short_name = reg.get('disqus.short_name') secret_key = reg.get('disqus.secret_key') if not short_name or not secret_key: return 0 count = 0 try: r = requests.get( 'https://disqus.com/api/3.0/forums/listThreads.json', { 'api_secret': secret_key, 'forum': short_name, 'thread': 'ident:' + thread_uid, 'limit': 1, }).json() if r['code'] == 0 and r['response']: count = r['response'][0]['posts'] except Exception as e: logger.error(e) return count
def _cleanup_tmp_files(): success, failed = _util.cleanup_files(_reg.get('paths.tmp'), 86400) # 24h for f_path in success: _logger.debug('Obsolete tmp file removed: {}'.format(f_path)) for f_path, e in failed: _logger.error('Error while removing obsolete tmp file {}: {}'.format(f_path, e))
def do_send(msg: Message): try: engine = _SMTP('localhost') engine.sendmail(msg._from_addr, msg._to_addrs, str(msg)) log_msg = "Message '{}' has been sent to {}.".format(msg.subject, msg.to_addrs) _logger.info(log_msg) except Exception as e: _logger.error('Unable to send message to {}: {}'.format(msg.to_addrs, e), exc_info=e)
def exec(self): try: _api.dispense_bot(self.arg('bot_uid')).process_update( types.Update(json.loads(self.request.data))) except error.BotNotRegistered as e: logger.warn(str(e)) except Exception as e: logger.error(e)
def get_file(self, file_id: str) -> types.File: try: return types.File(self._request('getFile', { 'file_id': file_id, })) except error.ApiRequestError as e: logger.error(e) raise error.FileNotFound(file_id)
def on_cleanup(): success, failed = _util.cleanup_files(_reg.get('paths.session'), _reg.get('router.session_ttl', 86400)) for f_path in success: _logger.debug('Obsolete session file removed: {}'.format(f_path)) for f_path, e in failed: _logger.error('Error while removing obsolete session file {}: {}'.format(f_path, e))
def cleanup(): success, failed = _util.cleanup_files(_reg.get('paths.log'), _reg.get('logger.file_ttl', 2592000)) # 30d for f_path in success: _logger.debug('Obsolete log file removed: {}'.format(f_path)) for f_path, e in failed: _logger.error('Error while removing obsolete log file {}: {}'.format(f_path, e))
def pytsite_cleanup(): root = _path.join(_reg.get('paths.static'), 'image', 'resize') ttl = _reg.get('file_storage_odm.static_ttl', 2592000) # 1 month success, failed = _util.cleanup_files(root, ttl) for f_path in success: _logger.debug('Obsolete static file removed: {}'.format(f_path)) for f_path, e in failed: _logger.error('Error while removing obsolete static file {}: {}'.format(f_path, e))
def dispense(uid: str, request: Request = None) -> Form: """Dispense a form from the cache """ try: if not _FORMS.has(uid): raise KeyError(f"Form '{uid}' is not found") form = _FORMS.get(uid) form.request = request or router.request() return form except Exception as e: logger.error(e) raise RuntimeError('Unexpected form exception')
def _on_submit(self): from ._api import dispense_entity model = self.attr('model') try: # Ask entities to process deletion for eid in self.attr('eids', self.attr('ids', [])): dispense_entity(model, eid).odm_ui_d_form_submit() router.session().add_info_message( lang.t('odm_ui@operation_successful')) # Entity deletion was forbidden except errors.ForbidDeletion as e: logger.error(e) router.session().add_error_message( lang.t('odm_ui@entity_deletion_forbidden') + '. ' + str(e))
def get_comments_count(self, thread_uid: str) -> int: """Get comments count for particular thread. """ count = 0 try: r = _requests.get('https://graph.facebook.com/v2.4', { 'fields': 'share{comment_count}', 'id': thread_uid, }).json() if 'share' in r: count = int(r['share']['comment_count']) except Exception as e: _logger.error(e) return count
def call_command(self, name: str, msg: Union[types.Message, types.CallbackQuery], step: int = None): """Process an incoming command """ self.command_name = name if step is not None: self.command_step = step try: cmd_method = 'cmd_{}'.format(name) if hasattr(self, cmd_method): getattr(self, cmd_method)(msg) else: self.handle_command(name, msg) except error.CommandExecutionError as e: logger.error(e) self.send_message(e.msg, reply_markup=e.reply_markup)
def exec(self): del self.args['_pytsite_router_rule_name'] endpoint = '/' + self.args.pop('http_api_endpoint') current_path = router.current_path(False) request_method = router.request().method # Switch language language = router.request().headers.get('Accept-Language') # type: str if language: for lng in language.split(','): lng = lng.strip() if not lng.startswith('q=') and lang.is_defined(language): lang.set_current(language) break try: events.fire('http_api@pre_request') rule = _api.match(router.request().method, endpoint) events.fire('http_api@request') controller = rule.controller_class() # type: routing.Controller controller.request = self.request controller.args.update(self.args) controller.args.update(rule.args) controller.args['_pytsite_http_api_rule_name'] = rule.name controller.args.validate() response = controller.exec() return response if isinstance( response, http.Response) else http.JSONResponse(response) except (http.error.Base, errors.ForbidOperation) as e: if reg.get('debug'): logger.error(e) else: logger.error('{} {}: {}'.format(request_method, current_path, e.description)) if isinstance(e, errors.ForbidOperation): e = http.error.Forbidden(e) if e.response and isinstance(e.response, http.JSONResponse): response = e.response response.status_code = e.code else: # It is important to do `str(e.description)`, because `e.description` might be an exception response = http.JSONResponse({'error': str(e.description)}, e.code) return response except UserWarning as e: logger.warn('{} {}: {}'.format(request_method, current_path, e)) return http.JSONResponse({'warning': str(e)}, 200) except Exception as e: logger.error('{} {}: {}'.format(request_method, current_path, e), exc_info=e) return http.JSONResponse({'error': str(e)}, 500)
def dispense(request: _http.Request, uid: str) -> _form.Form: """Dispense a form """ try: # Determine form's class cid = uid.replace('cid:', '') if uid.startswith( 'cid:') else _cache.get_pool('form.form_cid').get(uid) cls = _util.get_module_attr(cid) # Prevent instantiating other classes via HTTP API if not issubclass(cls, _form.Form): raise RuntimeError('Form class is not found') # Instantiate form return cls(request) if uid.startswith('cid:') else cls(request, _uid=uid) except _cache.error.KeyNotExist: raise RuntimeError('Invalid form UID') # Hide all other exceptions info from outer world except Exception as e: _logger.error(e) raise RuntimeError('Unexpected form exception')
def _entity_save(args: dict): """Save an entity """ fields_data = args['fields_data'] collection = mongodb.get_collection(args['collection_name']) # Save data to the database try: # New entity if args['is_new']: collection.insert_one(fields_data) # Existing entity else: collection.replace_one({'_id': fields_data['_id']}, fields_data) # Update cache c_key = '{}.{}'.format(fields_data['_model'], fields_data['_id']) _ENTITIES_CACHE.put_hash(c_key, fields_data, _CACHE_TTL) except (bson_errors.BSONError, PyMongoError) as e: logger.error(e) logger.error('Document dump: {}'.format(fields_data)) raise e
def cron_1min(): """pytsite.cron.1min """ global _working if _working: _logger.warn('Content import is still working') return _working = True max_errors = _reg.get('content_import.max_errors', 13) max_items = _reg.get('content_import.max_items', 10) delay_errors = _reg.get('content_import.delay_errors', 120) importer_finder = _odm.find('content_import') \ .eq('enabled', True) \ .lt('paused_till', _datetime.now()) \ .sort([('errors', _odm.I_ASC)]) for importer in importer_finder.get(): # type: _model.ContentImport options = dict(importer.driver_opts) options.update({ 'content_author': importer.content_author, 'content_model': importer.content_model, 'content_language': importer.content_language, 'content_status': importer.content_status, 'content_section': importer.content_section, }) driver = _api.get_driver(importer.driver) items_imported = 0 try: _logger.info('Content import started. Driver: {}. Options: {}'.format(driver.get_name(), options)) # Get entities from driver and save them for entity in driver.get_entities(_frozendict(options)): if items_imported == max_items: break try: # Append additional tags if entity.has_field('tags'): for tag_title in importer.add_tags: tag = _tag.find_by_title(tag_title, language=importer.content_language) if not tag: tag = _tag.dispense(tag_title, language=importer.content_language).save() entity.f_add('tags', tag) # Save entity entity.save() # Notify listeners _events.fire('content_import@import', driver=driver, entity=entity) _logger.info("Content entity imported: '{}'".format(entity.f_get('title'))) items_imported += 1 # Entity was not successfully saved; make record in the log and skip to the next entity except Exception as e: # Delete already attached images to free space if entity.has_field('images') and entity.images: for img in entity.images: img.delete() _logger.error("Error while creating entity '{}'. {}".format(entity.title, str(e)), exc_info=e) # Mark that driver made its work without errors importer.f_set('errors', 0) _logger.info('Content import finished. Entities imported: {}.'.format(items_imported)) except Exception as e: # Increment errors counter importer.f_inc('errors') # Store info about error importer.f_set('last_error', str(e)) if importer.errors >= max_errors: # Disable if maximum errors count reached importer.f_set('enabled', False) else: # Pause importer importer.f_set('paused_till', _datetime.now() + _timedelta(minutes=delay_errors)) _logger.error(e) # Continue to the next importer continue finally: importer.save() _working = False
def on_pytsite_load(): update_info = _api.get_update_info() if not update_info: return # If there waiting updates exist, reload the application if _reg.get('env.type') == 'wsgi': _logger.warn('Application needs to be loaded in console to finish plugins update') return failed_plugins = [] # Call 'plugin_pre_install()' hooks for p_name, info in update_info.items(): v_to = _semver.Version(info['version_to']) try: # Check if the plugin is installed and loaded plugin = _api.get(p_name) # Call plugin_pre_install() hook if hasattr(plugin, 'plugin_pre_install') and callable(plugin.plugin_pre_install): plugin.plugin_pre_install() # Fire 'pre_install' event _events.fire('pytsite.plugman@pre_install', name=p_name, version=v_to) except _error.PluginNotLoaded as e: _logger.error(e) _console.print_warning(_lang.t('pytsite.plugman@plugin_install_error', { 'plugin': p_name, 'version': v_to, 'msg': str(e), })) failed_plugins.append(p_name) continue # Finish installing/updating plugins for p_name, info in update_info.items(): if p_name in failed_plugins: continue plugin = _api.get(p_name) v_from = _semver.Version(info['version_from']) v_to = _semver.Version(info['version_to']) try: _logger.info(_lang.t('pytsite.plugman@installing_plugin', { 'plugin': p_name, 'version': v_to, })) # Call plugin_install() hook if hasattr(plugin, 'plugin_install') and callable(plugin.plugin_install): plugin.plugin_install() # Fire 'install' event _events.fire('pytsite.plugman@install', name=p_name, version=v_to) _console.print_success(_lang.t('pytsite.plugman@plugin_install_success', { 'plugin': p_name, 'version': v_to, })) except Exception as e: _logger.error(e) _console.print_warning(_lang.t('pytsite.plugman@plugin_install_error', { 'plugin': p_name, 'version': v_to, 'msg': str(e), })) continue # Update plugin if v_from != '0.0.0': try: _console.print_info(_lang.t('pytsite.plugman@updating_plugin', { 'plugin': p_name, 'v_from': v_from, 'v_to': v_to, })) # Call plugin_update() hook if hasattr(plugin, 'plugin_update') and callable(plugin.plugin_update): plugin.plugin_update(v_from=v_from) # Fire 'update' event _events.fire('pytsite.plugman@update', name=p_name, v_from=v_from) _console.print_success(_lang.t('pytsite.plugman@plugin_update_success', { 'plugin': p_name, 'version': v_to, })) except Exception as e: _console.print_warning(_lang.t('pytsite.plugman@plugin_update_error', { 'plugin': p_name, 'version': v_to, 'msg': str(e), })) continue # Remove info from update queue _api.rm_update_info(p_name)