diff --git a/Pipfile b/Pipfile index 89bb351..ddfa874 100644 --- a/Pipfile +++ b/Pipfile @@ -19,13 +19,10 @@ user-agents = "*" emoji-country-flag = "*" rules = "*" gunicorn = "*" -psycopg2 = "*" +psycopg2-binary = "*" redis = "*" django-redis-cache = "*" pycountry = "*" -[requires] -python_version = "3.6" - [pipenv] allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock index e9fe5c0..c913d2f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,12 +1,10 @@ { "_meta": { "hash": { - "sha256": "c7c9248fad70f86dda59b61f823f0992276c6cb9ee7ead05ae5719077c0c0c1f" + "sha256": "2cd3e33ea333a40476d2030b6be66826e93c3a4de67032655061725835c92f09" }, "pipfile-spec": 6, - "requires": { - "python_version": "3.6" - }, + "requires": {}, "sources": [ { "name": "pypi", @@ -127,14 +125,6 @@ ], "version": "==2.9" }, - "importlib-metadata": { - "hashes": [ - "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", - "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" - ], - "markers": "python_version < '3.8'", - "version": "==1.6.0" - }, "kombu": { "hashes": [ "sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76", @@ -155,21 +145,38 @@ ], "version": "==3.1.0" }, - "psycopg2": { + "psycopg2-binary": { "hashes": [ - "sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535", - "sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7", - "sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c", - "sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080", - "sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81", - "sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72", - "sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52", - "sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e", - "sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf", - "sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9", - "sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb", - "sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055", - "sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818" + "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac", + "sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a", + "sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5", + "sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04", + "sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1", + "sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5", + "sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce", + "sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434", + "sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9", + "sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057", + "sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98", + "sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522", + "sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505", + "sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa", + "sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3", + "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f", + "sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4", + "sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4", + "sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266", + "sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66", + "sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38", + "sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3", + "sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389", + "sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab", + "sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb", + "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6", + "sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d", + "sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162", + "sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e", + "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd" ], "index": "pypi", "version": "==2.8.5" @@ -292,13 +299,6 @@ ], "index": "pypi", "version": "==5.0.1" - }, - "zipp": { - "hashes": [ - "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", - "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" - ], - "version": "==3.1.0" } }, "develop": { diff --git a/README.md b/README.md index 0bc7ff9..d05b793 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,9 @@ To install Shynet using the simplest possible setup, follow these instructions. 2. Have a PostgreSQL server ready to go. This can be on the same machine as the deployment, or elsewhere. You'll just need a username, password, and host. (For info on how to setup a PostgreSQL server on Ubuntu, follow [this guide](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04)). -3. Configure an environment file for Shynet. Be sure to swap out the variables below with the correct values for your setup. (The comments refer to the lines that follow.) +3. Configure an environment file for Shynet. (For example, create a file called `.env`.) Be sure to swap out the variables below with the correct values for your setup. (The comments refer to the lines that follow. Note that Docker is weird with quotes, so it tends to be better to omit them from your env file.) -```env +``` # Database DB_NAME= DB_USER= @@ -108,33 +108,41 @@ DJANGO_SECRET_KEY= DEBUG=False CELERY_TASK_ALWAYS_EAGER=True # For better security, set this to your deployment's domain. Comma separated. -ALLOWED_HOSTS="*" +ALLOWED_HOSTS=* # Set to True (capitalized) if you want people to be able to sign up for your Shynet instance (not recommended) SIGNUPS_ENABLED=False # Change as required -TIME_ZONE="America/New_York" +TIME_ZONE=America/New_York # Set to "False" if you will not be serving content over HTTPS SCRIPT_USE_HTTPS=True +``` -# The following settings are OPTIONAL and not necessary for most basic deployments -REDIS_CACHE_LOCATION="redis://redis.default.svc.cluster.local/0" +For more advanced deployments, you may consider adding the following settings to your environment file. **The following settings are optional, and not required for simple deployments.** + +```env +# Email settings +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +EMAIL_HOST= +SERVER_EMAIL=Shynet + +# Redis and queue settings; not necessary for single-instance deployments +REDIS_CACHE_LOCATION=redis://redis.default.svc.cluster.local/0 # If set, make sure CELERY_TASK_ALWAYS_EAGER is False -CELERY_BROKER_URL="redis://redis.default.svc.cluster.local/1" -EMAIL_HOST_USER="" -EMAIL_HOST_PASSWORD="" -EMAIL_HOST="" -SERVER_EMAIL="Shynet " +CELERY_BROKER_URL=redis://redis.default.svc.cluster.local/1 ``` 4. Setup the Shynet database by running `docker run --env-file= milesmcc/shynet:latest python manage.py migrate`. -5. Create your admin account by running `docker run -i --env-file= milesmcc/shynet:latest python manage.py createsuperuser`. +5. Create your admin account by running `docker run --env-file= milesmcc/shynet:latest python manage.py registeradmin `. The command will print a temporary password that you'll be able to use to log in. -6. Launch the Shynet server by running `docker run --env-file= milesmcc/shynet:latest`. +6. Configure Shynet's hostname (e.g. `shynet.example.com` or `localhost:8000`) by running `docker run --env-file= milesmcc/shynet:latest python manage.py hostname ""`. This doesn't affect Shynet's bind port; instead, it determines what hostname to inject into the tracking script. (So you'll want to use the "user-facing" hostname here.) -7. With the server still running, visit it in a web browser. Go to `http:///admin` and log in with the credentials you setup in step 5. Click on "Sites", then "example.com". Update the values to match your deployment (the domain will be the domain where you'll _eventually_ host Shynet from, and the display name will be used to whitelabel Shynet throughout the management interface). When you're ready, click "Save". +7. Name your Shynet instance by running `docker run --env-file= milesmcc/shynet:latest python manage.py whitelabel ""`. This could be something like "My Shynet Server" or "Acme Analytics"—whatever suits you. -8. Visit your service's homepage, and verify everything looks right! +8. Launch the Shynet server by running `docker run --env-file= milesmcc/shynet:latest`. You may need to bind Docker's port 8000 (where Shynet runs) to your local port 8000; this can be done using the flag `-p 8000:8000`. + +9. Visit your service's homepage, and verify everything looks right! You should see a login prompt. Log in with the credentials from step 5. You'll probably be prompted to "confirm your email"—if you haven't set up an email server, the confirmation email will be printed to the console instead. **Next steps:** while out of the scope of this short guide, next steps include setting up Shynet behind a reverse proxy (be it your own [Nginx server](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) or [Cloudflare](https://cloudflare.com)), making it run in the background, and integrating it on your sites. Integration instructions are available on each service's management page. diff --git a/shynet/core/management/commands/__init__.py b/shynet/core/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shynet/core/management/commands/hostname.py b/shynet/core/management/commands/hostname.py new file mode 100644 index 0000000..47b7541 --- /dev/null +++ b/shynet/core/management/commands/hostname.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings +from django.contrib.sites.models import Site +from core.models import User +from django.utils.crypto import get_random_string +import uuid +import traceback + + +class Command(BaseCommand): + help = "Configures the Shynet hostname" + + def add_arguments(self, parser): + parser.add_argument( + "hostname", + type=str, + ) + + def handle(self, *args, **options): + site = Site.objects.get(pk=settings.SITE_ID) + site.domain = options.get("hostname") + site.save() + self.stdout.write( + self.style.SUCCESS( + f"Successfully set the hostname to '{options.get('hostname')}'" + ) + ) \ No newline at end of file diff --git a/shynet/core/management/commands/registeradmin.py b/shynet/core/management/commands/registeradmin.py new file mode 100644 index 0000000..941a117 --- /dev/null +++ b/shynet/core/management/commands/registeradmin.py @@ -0,0 +1,29 @@ +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings +from django.contrib.sites.models import Site +from core.models import User +from django.utils.crypto import get_random_string +import uuid +import traceback + + +class Command(BaseCommand): + help = "Creates an admin user with an auto-generated password" + + def add_arguments(self, parser): + parser.add_argument( + "email", + type=str, + ) + + def handle(self, *args, **options): + email = options.get("email") + password = get_random_string() + User.objects.create_superuser( + str(uuid.uuid4()), email=email, password=password + ) + self.stdout.write( + self.style.SUCCESS("Successfully created a Shynet superuser") + ) + self.stdout.write(f"Email address: {email}") + self.stdout.write(f"Password: {password}") diff --git a/shynet/core/management/commands/whitelabel.py b/shynet/core/management/commands/whitelabel.py new file mode 100644 index 0000000..eeed75d --- /dev/null +++ b/shynet/core/management/commands/whitelabel.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings +from django.contrib.sites.models import Site +from core.models import User +from django.utils.crypto import get_random_string +import uuid +import traceback + + +class Command(BaseCommand): + help = "Configures a Shynet whitelabel" + + def add_arguments(self, parser): + parser.add_argument( + "name", + type=str, + ) + + def handle(self, *args, **options): + site = Site.objects.get(pk=settings.SITE_ID) + site.name = options.get("name") + site.save() + self.stdout.write( + self.style.SUCCESS( + f"Successfully set the whitelabel to '{options.get('name')}'" + ) + ) \ No newline at end of file