#!/usr/bin/python3 # Launch as a eventlet.wsgi server instance. from distutils.util import strtobool import eventlet import eventlet.wsgi import getopt import os import signal import sys from . import store, changedetection_app, content_fetcher from . import __version__ # Only global so we can access it in the signal handler app = None datastore = None def sigterm_handler(_signo, _stack_frame): global app global datastore # app.config.exit.set() print('Shutdown: Got SIGTERM, DB saved to disk') datastore.sync_to_json() # raise SystemExit def main(): global datastore global app ssl_mode = False host = '' port = os.environ.get('PORT') or 5000 do_cleanup = False datastore_path = None # On Windows, create and use a default path. if os.name == 'nt': datastore_path = os.path.expandvars(r'%APPDATA%\changedetection.io') os.makedirs(datastore_path, exist_ok=True) else: # Must be absolute so that send_from_directory doesnt try to make it relative to backend/ datastore_path = os.path.join(os.getcwd(), "../datastore") try: opts, args = getopt.getopt(sys.argv[1:], "Ccsd:h:p:", "port") except getopt.GetoptError: print('backend.py -s SSL enable -h [host] -p [port] -d [datastore path]') sys.exit(2) create_datastore_dir = False for opt, arg in opts: if opt == '-s': ssl_mode = True if opt == '-h': host = arg if opt == '-p': port = int(arg) if opt == '-d': datastore_path = arg # Cleanup (remove text files that arent in the index) if opt == '-c': do_cleanup = True # Create the datadir if it doesnt exist if opt == '-C': create_datastore_dir = True # isnt there some @thingy to attach to each route to tell it, that this route needs a datastore app_config = {'datastore_path': datastore_path} if not os.path.isdir(app_config['datastore_path']): if create_datastore_dir: os.mkdir(app_config['datastore_path']) else: print( "ERROR: Directory path for the datastore '{}' does not exist, cannot start, please make sure the directory exists or specify a directory with the -d option.\n" "Or use the -C parameter to create the directory.".format(app_config['datastore_path']), file=sys.stderr) sys.exit(2) datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'], version_tag=__version__) app = changedetection_app(app_config, datastore) signal.signal(signal.SIGTERM, sigterm_handler) # Go into cleanup mode if do_cleanup: datastore.remove_unused_snapshots() app.config['datastore_path'] = datastore_path @app.context_processor def inject_version(): return dict(right_sticky="v{}".format(datastore.data['version_tag']), new_version_available=app.config['NEW_VERSION_AVAILABLE'], has_password=datastore.data['settings']['application']['password'] != False ) # Monitored websites will not receive a Referer header when a user clicks on an outgoing link. # @Note: Incompatible with password login (and maybe other features) for now, submit a PR! @app.after_request def hide_referrer(response): if strtobool(os.getenv("HIDE_REFERER", 'false')): response.headers["Referrer-Policy"] = "no-referrer" return response # Proxy sub-directory support # Set environment var USE_X_SETTINGS=1 on this script # And then in your proxy_pass settings # # proxy_set_header Host "localhost"; # proxy_set_header X-Forwarded-Prefix /app; if os.getenv('USE_X_SETTINGS'): print ("USE_X_SETTINGS is ENABLED\n") from werkzeug.middleware.proxy_fix import ProxyFix app.wsgi_app = ProxyFix(app.wsgi_app, x_prefix=1, x_host=1) if ssl_mode: # @todo finalise SSL config, but this should get you in the right direction if you need it. eventlet.wsgi.server(eventlet.wrap_ssl(eventlet.listen((host, port)), certfile='cert.pem', keyfile='privkey.pem', server_side=True), app) else: eventlet.wsgi.server(eventlet.listen((host, int(port))), app)