diff --git a/Dockerfile b/Dockerfile index 0b0757d..185687a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,12 @@ FROM python:3.11-rc-alpine ENV cloud_name raincloud +ENV num_workers 5 ENV worker_timeout 300 COPY . /tmp/raincloud +RUN apk add redis RUN python -m venv /opt/venv RUN . /opt/venv/bin/activate && cd /tmp/raincloud && python -m pip install . RUN . /opt/venv/bin/activate && python -m pip install gunicorn @@ -13,4 +15,4 @@ RUN rm -rf /tmp/raincloud EXPOSE 8000/tcp -ENTRYPOINT . /opt/venv/bin/activate && gunicorn --timeout ${worker_timeout} --bind=0.0.0.0:8000 "raincloud:create_app(base_path='/var/www/raincloud',cloud_name='${cloud_name}')" \ No newline at end of file +ENTRYPOINT redis-server & echo $RANDOM$RANDOM | base64 > /var/raincloud_secret_key && . /opt/venv/bin/activate && gunicorn --bind=0.0.0.0:8000 --workers ${num_workers} --timeout ${worker_timeout} "raincloud:create_app(base_path='/var/www/raincloud', secret_key_path='/var/raincloud_secret_key', cloud_name='${cloud_name}')" \ No newline at end of file diff --git a/README.org b/README.org index 96431e5..8fcb551 100644 --- a/README.org +++ b/README.org @@ -11,7 +11,7 @@ *Features* - No users, just password protectable dynamic HTTP routes - - No database backend, just a flat directory structure + - Routes are defined by a flat directory structure - Permissions per route individually configurable via plain-text files ** Example @@ -50,47 +50,39 @@ First set up a [[https://redis.io/][Redis]] server which will be used for server-side session caching. Then a WSGI server like [[https://gunicorn.org/][Gunicorn]] can be used to serve /raincloud/ for example like this: - : $ gunicorn "raincloud:create_app(base_path='public', secret_key='i_am_a_key', redis_url='redis://127.0.0.1:6379/0')" + : $ gunicorn "raincloud:create_app(base_path='public', secret_key_path='secret_key', redis_url='redis://127.0.0.1:6379/0')" *** NixOS This repository is also a [[https://nixos.wiki/wiki/Flakes][Nix Flake]] which provides a [[https://nixos.org/][NixOS]] module. - It requres a running instance of a [[https://search.nixos.org/options?query=services.redis.servers][Redis server]]. + It requires a running instance of a [[https://search.nixos.org/options?query=services.redis.servers][Redis server]]. A minimal /raincloud/ instance can be setup for example like this: #+begin_src nix raincloud.nixosModule { - # Redis - services.redis.servers."raincloud" = { - enable = true; - databases = 1; - user="raincloud"; - }; - - # Raincloud services.raincloud = { enable = true; basePath = "/var/lib/raincloud"; - secretKey = "i_am_a_key"; - redisUrl = "unix:/run/redis-raincloud/redis.sock"; + secretKeyPath = "/var/lib/raincloud/secret_key"; + redisUrl = "unix:/run/redis-raincloud/redis.sock?db=0"; }; } #+end_src All configuration options are: - | Option | Description | Type | Default value | Example | - |-----------------+---------------------------------------------------------------+-------+----------------------------+-------------------------------| - | =address= | Bind address of the server | =str= | =127.0.0.1= | =0.0.0.0= | - | =port= | Port on which the server listens | =int= | =8000= | =5000= | - | =user= | User under which the server runs | =str= | =raincloud= | =alice= | - | =group= | Group under which the server runs | =str= | =raincloud= | =users= | - | =cloudName= | Name of the raincloud | =str= | =raincloud= | =bobsCloud= | - | =basePath= | Base path of the raincloud | =str= | | =/var/lib/raincloud= | - | =secretKey= | Flask secret key | =str= | | =i_am_a_key= | - | =redisUrl= | URL of Redis database | =str= | =redis://127.0.0.1:6379/0= | =redis://my_db_server:6379/0= | - | =numWorkers= | Number of Gunicorn workers (recommendation is: 2 x #CPUs + 1) | =int= | =5= | =17= | - | =workerTimeout= | Gunicorn worker timeout | =int= | =300= | =360= | + | Option | Description | Type | Default value | Example | + |-----------------+---------------------------------------------------------------+-------+----------------------------+----------------------------------------| + | =address= | Bind address of the server | =str= | =127.0.0.1= | =0.0.0.0= | + | =port= | Port on which the server listens | =int= | =8000= | =5000= | + | =user= | User under which the server runs | =str= | =raincloud= | =alice= | + | =group= | Group under which the server runs | =str= | =raincloud= | =users= | + | =cloudName= | Name of the raincloud | =str= | =raincloud= | =bobsCloud= | + | =basePath= | Base path of the raincloud | =str= | | =/var/lib/raincloud= | + | =secretKeyPath= | Path to file containing Flask secret key | =str= | | =/var/lib/raincloud/secret_key= | + | =redisUrl= | URL of Redis database | =str= | =redis://127.0.0.1:6379/0= | =unix:/run/redis-raincloud/redis.sock?db=0= | + | =numWorkers= | Number of Gunicorn workers (recommendation is: 2 x #CPUs + 1) | =int= | =5= | =17= | + | =workerTimeout= | Gunicorn worker timeout | =int= | =300= | =360= | *** Docker @@ -108,20 +100,19 @@ : $ docker run -p :8000 -v :/var/www/raincloud -e "cloud_name=podcloud" raincloud:latest - Similarly the environment variable =worker_timeout= can be set to increase the Gunicorn worker timeout in seconds. - It's default value is =300=. + The environment variables =num_workers= (default: =5=) and =worker_timeout= (default: =300=) can be set in the same way to set the number of Gunicorn workers and their timeout in seconds. ** Configuration /raincloud/ provides four configuration options which can be passed to =raincloud.create_app()=: - =base_path= :: Base path of the raincloud - - =secret_key= :: Flask secret key + - =secret_key_path= :: Path to file containing Flask secret key - =redis_url= :: URL of redis database (default: =redis://127.0.0.1:6379/0=) - =cloud_name= :: Cloud name (default: =raincloud=) Set them for example like this: - : >>> app = raincloud.create_app(base_path='/home/alice/public', secret_key='i_am_a_key', redis_url='redis://127.0.0.1:6379/0', cloud_name='raincloud') + : >>> app = raincloud.create_app(base_path='/home/alice/public', secret_key_path='/var/lib/raincloud/secret_key', redis_url='redis://127.0.0.1:6379/0', cloud_name='raincloud') *** =rc.conf= :properties: diff --git a/flake.nix b/flake.nix index f6f81a1..613b8f9 100644 --- a/flake.nix +++ b/flake.nix @@ -59,17 +59,20 @@ basePath = mkOption { type = types.str; + example = "/var/lib/raincloud"; description = "Base path of the raincloud"; }; - secretKey = mkOption { + secretKeyPath = mkOption { type = types.str; - description = "Flask secret key"; + example = "/var/lib/raincloud/secret_key"; + description = "Path to file containing Flask secret key"; }; redisUrl = mkOption { type = types.str; default = "redis://127.0.0.1:6379/0"; + example = "unix:/run/redis-raincloud/redis.sock"; description = "URL of Redis database"; }; @@ -115,10 +118,10 @@ PermissionsStartOnly = true; ExecStart = '' - ${gunicorn}/bin/gunicorn "raincloud:create_app('${cfg.basePath}', '${cfg.secretKey}', '${cfg.redisUrl}', '${cfg.cloudName}')" \ + ${gunicorn}/bin/gunicorn "raincloud:create_app('${cfg.basePath}', '${cfg.secretKeyPath}', '${cfg.redisUrl}', '${cfg.cloudName}')" \ + --bind=${cfg.address}:${toString cfg.port} \ --workers ${toString cfg.numWorkers} \ - --timeout ${toString cfg.workerTimeout} \ - --bind=${cfg.address}:${toString cfg.port} + --timeout ${toString cfg.workerTimeout} ''; }; }; diff --git a/raincloud/raincloud.py b/raincloud/raincloud.py index aa06bf6..7938e7f 100755 --- a/raincloud/raincloud.py +++ b/raincloud/raincloud.py @@ -18,12 +18,16 @@ import werkzeug def create_app( - base_path, secret_key, redis_url="redis://127.0.0.1:6379/0", cloud_name="raincloud" + base_path, + secret_key_path, + redis_url="redis://127.0.0.1:6379/0", + cloud_name="raincloud", ): # Create app app = Flask(__name__) - app.config["SECRET_KEY"] = secret_key + with open(secret_key_path, "r") as secret_key_file: + app.config["SECRET_KEY"] = secret_key_file.readline() # Create handlers dh = DirectoryHandler(base_path) diff --git a/raincloud/static/logo.svg b/raincloud/static/logo.svg deleted file mode 100644 index c06dcc2..0000000 --- a/raincloud/static/logo.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1a5dc97..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flask -redis diff --git a/setup.py b/setup.py index 73f62bc..506da8d 100644 --- a/setup.py +++ b/setup.py @@ -6,5 +6,5 @@ setup( packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=["flask"], + install_requires=["flask", "redis"], )