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
32 changes: 32 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Django environment
# Use "development" locally. Omit or set a production value in production.
PYTHON_ENV=development

# Django/API secrets
SECRET_KEY=replace-me
API_KEY=replace-me

# Used in highscores clean-code decoding
NEW_AES_KEY=0123456789abcdef

# Discord OAuth
DISCORD_CLIENT_SECRET=replace-me

# Email
SMTP_SERVER=smtp.example.com
SMTP_PASSWORD=replace-me

# Optional Discord webhook for highscores notifications
HIGHSCORES_WEBHOOK_URL=

# Polar subscriptions
POLAR_API_BASE_URL=https://api.polar.sh
POLAR_ACCESS_TOKEN=
POLAR_WEBHOOK_SECRET=
POLAR_CHECKOUT_SUCCESS_URL=
POLAR_CUSTOMER_PORTAL_URL=

# xRC server orchestrator
ORCHESTRATOR_API_BASE_URL=
ORCHESTRATOR_API_TOKEN=
ORCHESTRATOR_REQUEST_TIMEOUT_SECONDS=10
81 changes: 60 additions & 21 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,94 @@
name: 🚀 Deploy Django
name: Deploy Django

on:
push:
branches: [main]

jobs:
build-deploy:
name: 🎉 Build and Deploy
validate:
name: Validate

runs-on: ubuntu-latest

steps:
- name: 🚚 Get latest code
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v4

- name: 🔨 Set up Python 3.12
uses: actions/setup-python@v2
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.12
python-version: "3.12"
cache: pip

- name: 🛠 Install Dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: ▶ Run Tests
run: |
python manage.py test
- name: Check for missing migrations
run: python manage.py makemigrations --check --dry-run
env:
SECRET_KEY: ${{ secrets.BASE_KEY }}
SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
NEW_AES_KEY: ${{ secrets.NEW_AES_KEY }}
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: Apply migrations on a fresh database
run: python manage.py migrate --noinput
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: Run Django checks
run: python manage.py check
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: Run tests
run: pytest
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

build-deploy:
name: Build and Deploy
needs: validate

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip

- name: 📦 Collect Static Files
- name: Install dependencies
run: |
python manage.py collectstatic --noinput
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Collect static files
run: python manage.py collectstatic --noinput
env:
SECRET_KEY: ${{ secrets.BASE_KEY }}
SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
NEW_AES_KEY: ${{ secrets.NEW_AES_KEY }}
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}

- name: 🔃 Restart Server
- name: Restart server
run: |
mkdir tmp
touch tmp/restart.txt

- name: SFTP Deploy
- name: SFTP deploy
uses: easingthemes/ssh-deploy@v2
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
Expand Down
44 changes: 32 additions & 12 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,43 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: 🔨 Set up Python 3.12
uses: actions/setup-python@v2
- uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.12
python-version: "3.12"
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Check for missing migrations
run: python manage.py makemigrations --check --dry-run
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: Apply migrations on a fresh database
run: python manage.py migrate --noinput
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: Run Django checks
run: python manage.py check
env:
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret

- name: ▶ Run Tests
run: |
pytest
- name: Run tests
run: pytest
env:
SECRET_KEY: ${{ secrets.BASE_KEY }}
SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
NEW_AES_KEY: ${{ secrets.NEW_AES_KEY }}
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
SECRET_KEY: ci-secret-key
NEW_AES_KEY: 0123456789abcdef
DISCORD_CLIENT_SECRET: ci-discord-secret
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ dmypy.json

# Database
*.sqlite3
migrations/

.vscode/
.DS_Store
Expand Down
15 changes: 14 additions & 1 deletion SRCweb/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@
# SECURITY WARNING: keep the API Key used in production secret!
API_KEY = os.getenv("API_KEY") or "API_KEY"

# Polar subscriptions
POLAR_API_BASE_URL = os.getenv("POLAR_API_BASE_URL") or "https://api.polar.sh"
POLAR_ACCESS_TOKEN = os.getenv("POLAR_ACCESS_TOKEN") or ""
POLAR_WEBHOOK_SECRET = os.getenv("POLAR_WEBHOOK_SECRET") or ""
POLAR_CHECKOUT_SUCCESS_URL = os.getenv("POLAR_CHECKOUT_SUCCESS_URL") or ""
POLAR_CUSTOMER_PORTAL_URL = os.getenv("POLAR_CUSTOMER_PORTAL_URL") or ""

# xRC server orchestrator
ORCHESTRATOR_API_BASE_URL = os.getenv("ORCHESTRATOR_API_BASE_URL") or ""
ORCHESTRATOR_API_TOKEN = os.getenv("ORCHESTRATOR_API_TOKEN") or ""
ORCHESTRATOR_REQUEST_TIMEOUT_SECONDS = float(os.getenv("ORCHESTRATOR_REQUEST_TIMEOUT_SECONDS") or 10)

# Sends an email to admins when debug = false and a 500 server error occurs
ADMINS = [('Webmaster', 'webmaster@secondrobotics.org')]
ADMIN_EMAILS = [email for name, email in ADMINS]
Expand Down Expand Up @@ -73,7 +85,8 @@
# 'teamleague',
# 'ladder',
'discordoauth2',
'ranked'
'ranked',
'subscriptions',
]

REST_FRAMEWORK = {
Expand Down
2 changes: 2 additions & 0 deletions SRCweb/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
path('oauth2/', include('discordoauth2.urls')),
path('ranked/', include('ranked.urls')),
path('api/ranked/', include('ranked.api.urls', namespace="api-ranked")),
path('subscriptions/', include('subscriptions.urls')),
path('api/subscriptions/', include('subscriptions.api.urls', namespace="api-subscriptions")),
]

if settings.DEBUG:
Expand Down
53 changes: 53 additions & 0 deletions discordoauth2/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 3.2.25 on 2026-05-28 04:36

import discordoauth2.managers
import django.contrib.auth.validators
import django.core.validators
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]

operations = [
migrations.CreateModel(
name='User',
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('display_name', models.CharField(max_length=25, null=True, validators=[django.contrib.auth.validators.ASCIIUsernameValidator(), django.core.validators.MinLengthValidator(4)], verbose_name='display name')),
('id', models.BigIntegerField(primary_key=True, serialize=False, verbose_name='user id')),
('username', models.CharField(max_length=100, verbose_name='username')),
('discriminator', models.CharField(max_length=4, verbose_name='discriminator')),
('avatar', models.URLField(verbose_name='avatar')),
('public_flags', models.IntegerField(verbose_name='public flags')),
('flags', models.IntegerField(verbose_name='flags')),
('locale', models.CharField(max_length=100, verbose_name='locale')),
('mfa_enabled', models.BooleanField(verbose_name='multi-factor authentication enabled')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('verified', models.BooleanField(help_text='Whether the user has verified their email address.', verbose_name='email verified')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', discordoauth2.managers.DiscordUserOAuth2Manager()),
],
),
]
1 change: 1 addition & 0 deletions discordoauth2/migrations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading