Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/deploy-to-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Deploy To Production

on:
workflow_run:
workflows: ["Build and Publish"]
types:
- completed

jobs:
shut-down-current-version:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Retrieving up to date docker-compose.yaml
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: (cd /home/TheCompound && docker-compose down)

download-updated-docker-compose:
needs: shut-down-current-version
runs-on: ubuntu-latest
steps:
- name: Retrieving up to date docker-compose.yaml
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: curl -o /home/TheCompound/docker-compose.yml https://raw.githubusercontent.com/SethAngell/TheVirtualCompound/main/.ci/docker-compose.yml

spin-up-new-version-of-the-compound:
needs: [shut-down-current-version, download-updated-docker-compose]
if: always()
runs-on: ubuntu-latest
steps:
- name: Retrieving up to date docker-compose.yaml
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.SSH_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: (cd /home/TheCompound && docker-compose up -d)
37 changes: 37 additions & 0 deletions .github/workflows/notify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Notify

# Only trigger, when the build workflow succeeded
on:
workflow_run:
workflows: ["Deploy To Production"]
types:
- completed

jobs:
see-if-site-is-back-online:
name: healthcheck
runs-on: ubuntu-latest
steps:
- name: Give site some time to collectstatic and deploy
run: sleep 60s
shell: bash
- name: Attempt to connect to personal landing page
run: curl --connect-timeout 30 --retry 10 --retry-delay 5 https://sethangell.com
shell: bash

send-notification:
runs-on: ubuntu-latest
needs: see-if-site-is-back-online
if: always()
steps:
- name: Post Pipeline Status
uses: twilio-labs/actions-sms@v1
with:
fromPhoneNumber: ${{ secrets.FROM_NUMBER }}
toPhoneNumber: ${{ secrets.TO_NUMBER }}
message: "The Virtual Compound CI pipeline is complete. Status of Deployment is ${{ github.event.workflow_run.conclusion }}. Status of post deployment healthcheck is ${{ steps.healthcheck.conclusion }}. See https://sethangell.com"
env:
TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }}
TWILIO_API_KEY: ${{ secrets.TWILIO_API_KEY }}
TWILIO_API_SECRET: ${{ secrets.TWILIO_API_SECRET }}

7 changes: 7 additions & 0 deletions app/TheCompound/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@
BASE_DIR / "project_static",
]

# ===============================
# = = = = Email Settings = = = =
if DEBUG is True:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
else:
raise NotImplementedError("Prod Email Is Not Yet Available")

# ===============================
# = = = Deployment Settings = = =
if DEBUG is False:
Expand Down
1 change: 1 addition & 0 deletions app/TheCompound/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
path("admin/", admin.site.urls),
path("", include("landing_page.urls")),
path("blog/", include("blog.urls")),
path("accounts/", include("accounts.urls")),
]
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Expand Down
7 changes: 6 additions & 1 deletion app/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.contrib.auth.admin import UserAdmin

from .forms import CustomUserChangeForm, CustomUserCreationForm
from .models import CustomUser, Domain
from .models import CustomUser, Domain, Invitation


class CustomUserAdmin(UserAdmin):
Expand Down Expand Up @@ -49,5 +49,10 @@ class DomainAdmin(admin.ModelAdmin):
pass


class InvitationAdmin(admin.ModelAdmin):
pass


admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Domain, DomainAdmin)
admin.site.register(Invitation, InvitationAdmin)
17 changes: 16 additions & 1 deletion app/accounts/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth.forms import UserChangeForm, UserCreationForm

from .models import CustomUser
from .models import CustomUser, Invitation


class CustomUserCreationForm(UserCreationForm):
Expand All @@ -19,3 +20,17 @@ class Meta:
"email",
"name",
)


class NewInvitationForm(forms.ModelForm):
class Meta:
model = Invitation
fields = ("linked_domain", "email")


class NewUserFromInviteForm(forms.Form):
email = forms.EmailField()
name = forms.CharField(max_length=128)
invite_code = forms.CharField(max_length=128)
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by Django 4.0.2 on 2022-07-10 15:51

import uuid

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("accounts", "0003_customuser_name"),
]

operations = [
migrations.AlterField(
model_name="domain",
name="name",
field=models.CharField(max_length=128),
),
migrations.AlterField(
model_name="domain",
name="user",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.CreateModel(
name="Invitation",
fields=[
(
"email",
models.EmailField(
max_length=254,
primary_key=True,
serialize=False,
unique=True,
verbose_name="email address",
),
),
(
"invitation_code",
models.UUIDField(default=uuid.uuid4, editable=False),
),
(
"linked_domain",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="accounts.domain",
),
),
],
),
]
18 changes: 17 additions & 1 deletion app/accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import uuid

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
Expand All @@ -21,8 +23,22 @@ def __str__(self):


class Domain(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True
)
name = models.CharField(max_length=128)

def __str__(self):
return self.name


class Invitation(models.Model):
email = models.EmailField(_("email address"), unique=True, primary_key=True)
invitation_code = models.UUIDField(default=uuid.uuid4, editable=False)
linked_domain = models.ForeignKey(
Domain, on_delete=models.CASCADE, blank=True, null=True
)

def save(self, *args, **kwargs):
self.email = self.email.lower()
super(Invitation, self).save(*args, **kwargs)
101 changes: 101 additions & 0 deletions app/accounts/templates/accounts/accept_invite.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{% extends "_base.html" %}
{% load static %}
{% load timeline_helper %}
{% block meta %}
<meta property="og:title" content="Create a super secret invite" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://doublel.studio/accounts/invite_user" />
{% endblock meta %}

{% block links %}
<link rel="stylesheet" href="{% static "css/tailwind-output.css" %}">
{% endblock %}
{% block style %}
h1, h2, a {
font-family: 'Rubik', sans-serif;
}

p {
font-family: 'Karla', sans-serif;
}

{% endblock %}
{% block font %}
<link defer href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Karla&family=Rubik:wght@500;700&display=swap" rel="stylesheet">
<script defer src="https://kit.fontawesome.com/273c56c702.js" crossorigin="anonymous"></script>
{% endblock %}
{% block title %}Accept Invite{% endblock title %}
{% block content %}
{% if errors %}
<script>alert("{{errors}}")</script>
{% endif %}
<div class="w-11/12 lg:w-3/6 h-screen flex flex-col mx-auto mt-5 justify-center items-center">
<div id="form-container" class="w-fit rounded-lg h-fit p-10 flex flex-col items-start border-seth-blue-600 border-2">
<div class="flex flex-row space-x-1 mb-5 text-slate-900 text-black">
<h4>🤫 Shhhhhhh....What's the secret password?</h4>
</div>

<form action={% url 'accept_invitation' %} method="post" class="w-full">
{% csrf_token %}
<div class="flex flex-col justify-center">
<label class="block mb-2">
<span id="email-label" class="block text-sm font-medium text-slate-700" aria-label-for="email">Email: </span>
<div class="mt-1">
<input type="email" name="email" id="email" class="peer px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:shadow-none" placeholder="you@example.com">
{% if "Invalid Email:" in errors %}
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">{{errors.email}}</p>
{% endif %}
</div>
</label>
<label class="block mb-2">
<span id="name-label" class="block text-sm font-medium text-slate-700" aria-label-for="name">Name: </span>
<div class="mt-1">
<input type="text" name="name" id="name" class="peer px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:shadow-none" placeholder="Steve Wozniak">
</div>
</label>
<label class="block mb-2">
<span id="password1-label" class="block text-sm font-medium text-slate-700" aria-label-for="password1">Password: </span>
<div class="mt-1">
<input type="password" name="password1" id="password1" class="peer px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:shadow-none">
</div>
</label>
<label class="block mb-2">
<span id="password2-label" class="block text-sm font-medium text-slate-700" aria-label-for="password1">Confirm Password: </span>
<div class="mt-1">
<input type="password" name="password2" id="password2" class="peer px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:shadow-none">
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">Passwords do not match :(</p>
</div>
</label>
<label class="block mb-2">
<span id="invite-code-label" class="block text-sm font-medium text-slate-700" aria-label-for="invite_code">Invitation Code: </span>
<div class="mt-1">
<input type="text" name="invite_code" id="invite_code" class="peer px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1 invalid:border-pink-500 invalid:text-pink-600 focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:shadow-none" placeholder="00000000-0000-0000-0000-000000000000">
{% if "Invalid Invite Code:" in errors %}
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">{{errors.code}}</p>
{% endif %}
</div>
</label>
<input class="mt-8 w-min bg-slate-500 hover:bg-slate-600 focus:outline-none focus:ring focus:ring-pink-300 active:bg-slate-700 px-5 py-2 text-sm leading-5 rounded-full font-semibold text-slate-50" type="submit" value="Signup">
</div>
</form>
</div>
</div>
<script>

document.getElementById("password2").addEventListener("keyup", function(event) {
var password1_input_value = document.getElementById("password1").value;
var password2_input_value = event.target.value;

console.log("HI!");

if (password1_input_value !== password2_input_value) {
document.getElementById("password2").setCustomValidity("Invalid Value.");
} else {
document.getElementById("password2").setCustomValidity("");
}
});


</script>

{% endblock content %}
11 changes: 11 additions & 0 deletions app/accounts/templates/accounts/emails/plaintext-invite.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Hey! Welcome To The Site

Create an account here link-to-web.site

Youre unique signup code is {{ invite.invitation_code }}

Make sure to signup with your emails address: {{ invite.email }}

Your site will be available at https://{{ invite.domain.name }}

Bye Now!
11 changes: 11 additions & 0 deletions app/accounts/templates/accounts/emails/styled-invite.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<h1>Hey! Welcome To The Site</h1>

<p>Create an account here link-to-web.site</p>

<p>Youre unique signup code is {{ invite.invitation_code }}</p>

<p>Make sure to signup with your emails address: {{ invite.email }}</p>

<p>Your site will be available at https://{{ invite.linked_domain.name }}</p>

<b>Bye Now!</b>
Loading