from flask import Flask from flask_sqlalchemy import SQLAlchemy from sqlalchemy import MetaData from flask_migrate import Migrate import version from libforget.cachebust import cachebust import mimetypes import libforget.brotli import libforget.img_proxy from werkzeug.middleware.proxy_fix import ProxyFix app = Flask(__name__) default_config = { "SQLALCHEMY_TRACK_MODIFICATIONS": False, "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2:///forget", "HTTPS": True, "SENTRY_CONFIG": {}, "REPO_URL": "https://github.com/codl/forget", "CHANGELOG_URL": "https://github.com/codl/forget/blob/{hash}/CHANGELOG.markdown", "REDIS_URI": "redis://", } app.config.update(default_config) app.config.from_pyfile('config.py', True) metadata = MetaData(naming_convention={ "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s" }) db = SQLAlchemy(app, metadata=metadata) migrate = Migrate(app, db) if 'CELERY_BROKER' not in app.config: uri = app.config['REDIS_URI'] if uri.startswith('unix://'): uri = uri.replace('unix', 'redis+socket', 1) app.config['CELERY_BROKER'] = uri sentry = None if 'SENTRY_DSN' in app.config: from raven.contrib.flask import Sentry app.config['SENTRY_CONFIG']['release'] = version.get_versions()['version'] sentry = Sentry(app, dsn=app.config['SENTRY_DSN']) url_for = cachebust(app) @app.context_processor def inject_static(): def static(filename, **kwargs): return url_for('static', filename=filename, **kwargs) return {'st': static} @app.after_request def install_security_headers(resp): csp = ("default-src 'none';" "img-src 'self';" "style-src 'self' 'unsafe-inline';" "frame-ancestors 'none';" ) if 'SENTRY_DSN' in app.config: csp += "script-src 'self' https://cdn.ravenjs.com/;" csp += "connect-src 'self' https://sentry.io/;" else: csp += "script-src 'self' 'unsafe-eval';" csp += "connect-src 'self';" if 'CSP_REPORT_URI' in app.config: csp += "report-uri " + app.config.get('CSP_REPORT_URI') if app.config.get('HTTPS'): resp.headers.set('strict-transport-security', 'max-age={}'.format(60*60*24*365)) csp += "; upgrade-insecure-requests" resp.headers.set('Content-Security-Policy', csp) resp.headers.set('referrer-policy', 'no-referrer') resp.headers.set('x-content-type-options', 'nosniff') resp.headers.set('x-frame-options', 'DENY') resp.headers.set('x-xss-protection', '1') return resp mimetypes.add_type('image/webp', '.webp') libforget.brotli.brotli(app) imgproxy = ( libforget.img_proxy.ImgProxyCache(redis_uri=app.config.get('REDIS_URI'))) app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)