Merge branch 'develop' into django-v4

This commit is contained in:
Ojakoo
2022-12-21 18:34:39 +02:00
34 changed files with 1588 additions and 1399 deletions
+4 -2
View File
@@ -1,11 +1,13 @@
DEPLOY_ENV=local DEPLOY_ENV=local
SENTRY_DSN= SENTRY_DSN=
HOST=api.dev.sahkoinsinoorikilta.fi HOST=localhost
DEBUG=True DEBUG=True
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp( SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
DB_NAME=postgres DB_NAME=postgres
DB_USER=postgres DB_USER=postgres
DB_PASSWD=postgres DB_PASSWD=postgres
DB_HOST=db DB_HOST=localhost
DB_PORT=5432 DB_PORT=5432
EMAIL_API_KEY= EMAIL_API_KEY=
GROUP_KEY=
GOOGLE_CREDS='{}'
+1 -1
View File
@@ -10,4 +10,4 @@ DB_HOST=db
DB_PORT=5432 DB_PORT=5432
EMAIL_API_KEY= EMAIL_API_KEY=
GROUP_KEY= GROUP_KEY=
GOOGLE_CREDS_JSON='{}' GOOGLE_CREDS='{}'
+60 -3
View File
@@ -5,10 +5,13 @@ stages:
- test - test
- publish - publish
- deploy - deploy
- cleanup
install: install:
image: node:14 image: node:14
stage: setup stage: setup
only:
- pushes
script: script:
- npm ci - npm ci
artifacts: artifacts:
@@ -19,6 +22,8 @@ install:
audit: audit:
image: python:3.9 image: python:3.9
stage: audit stage: audit
only:
- pushes
needs: [] needs: []
before_script: before_script:
- pip install poetry==1.1.13 - pip install poetry==1.1.13
@@ -30,6 +35,8 @@ audit:
test: test:
image: python:3.9 image: python:3.9
stage: test stage: test
only:
- pushes
needs: [] needs: []
services: services:
- postgres:12 - postgres:12
@@ -51,6 +58,8 @@ test:
lint:py: lint:py:
image: python:3.9 image: python:3.9
stage: lint stage: lint
only:
- pushes
needs: [] needs: []
script: script:
- pip install black==22.3.0 - pip install black==22.3.0
@@ -59,6 +68,8 @@ lint:py:
lint:js: lint:js:
image: node:14 image: node:14
stage: lint stage: lint
only:
- pushes
needs: ["install"] needs: ["install"]
script: script:
- npm run lint:js - npm run lint:js
@@ -66,13 +77,15 @@ lint:js:
lint:md: lint:md:
image: node:14 image: node:14
stage: lint stage: lint
only:
- pushes
needs: ["install"] needs: ["install"]
script: script:
- npm run lint:md - npm run lint:md
publish: publish:
stage: publish
image: docker:stable image: docker:stable
stage: publish
needs: ["test", "lint:py", "lint:js", "lint:md"] needs: ["test", "lint:py", "lint:js", "lint:md"]
services: services:
- docker:stable-dind - docker:stable-dind
@@ -86,8 +99,8 @@ publish:
- docker push "$IMAGE_NAME" - docker push "$IMAGE_NAME"
deploy:dev: deploy:dev:
stage: deploy
image: docker:stable image: docker:stable
stage: deploy
only: only:
- develop - develop
environment: environment:
@@ -128,4 +141,48 @@ deploy:production:
script: script:
- docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME" - docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME"
after_script: after_script:
- docker logout "$CI_REGISTRY" - docker logout "$CI_REGISTRY"
docker_prune:dev:
image: docker:stable
stage: cleanup
only:
- schedules
environment:
name: dev
url: http://api.dev.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- docker system prune
after_script:
- docker logout "$CI_REGISTRY"
docker_prune:prod:
image: docker:stable
stage: cleanup
only:
- schedules
environment:
name: production
url: https://api.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- docker system prune
after_script:
- docker logout "$CI_REGISTRY"
+1 -1
View File
@@ -4,7 +4,7 @@
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
source "${VIRTUAL_ENV}/bin/activate" . "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
+1 -1
View File
@@ -4,7 +4,7 @@
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
source "${VIRTUAL_ENV}/bin/activate" . "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
+60 -40
View File
@@ -1,24 +1,13 @@
# SIKWEB 2.0 # Web 2.0 Backend
A modern web app using a Django backend and an Angular frontend. [Django](https://www.djangoproject.com/) backend containing multiple small applications and api for Next.js frontend.
## Components * **Web app:** Backend for the main website.
* **Member register:** Data table app for viewing and modifying the member register, member applications and membership payments.
### Infoscreen * **Kaehmy:** Form for creating and listing kaehmys
* **Ohlhafv:** Form for creating and listing ohlhafv challenges.
Angular-based slideshow app for the guild room's screens. * **Infoscreen:** Angular-based slideshow app for the guild room's screens.
## Installation
### Member register
Data table app for viewing and modifying the member register, member applications and membership payments.
### Web app
Mostly static website with an event calendar and news feed.
## Accessing the source
### Clone this repository and enter it
Set up your SSH key authentication in GitLab Profile Settings. Then clone the repository and checkout the development branch: Set up your SSH key authentication in GitLab Profile Settings. Then clone the repository and checkout the development branch:
@@ -28,17 +17,53 @@ cd web2.0-backend
git checkout develop git checkout develop
``` ```
## Development Copy env file for local use:
```bash
cp .env.dev .env
```
### Poetry ### Poetry
For depedencies and virtual environment, we use [poetry](https://python-poetry.org/). The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with For depedencies and virtual environment, we use [poetry](https://python-poetry.org/).
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
```bash
python3 -m pip install poetry
```
The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with
```bash ```bash
poetry config virtualenvs.in-project true poetry config virtualenvs.in-project true
``` ```
#### CMDs ### Node
We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm). After installing install dependencies:
```
npm install
```
TODO: List scripts
### Database
To run a local development database **[docker](https://docs.docker.com/engine/install/)** is recommended. If you want to additianally use a db management tool **[pgAdmin](https://www.pgadmin.org/download/)** is nice.
After installing docker use the following to create a database:
```bash
docker run --name postgres:12 -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:12
```
## Development
Activate virtual environment in shell
```bash
python3 -m poetry shell
```
Install dependencies Install dependencies
@@ -46,12 +71,6 @@ Install dependencies
poetry install poetry install
``` ```
Activate virtual environment in shell
```bash
poetry shell
```
### npm scripts ### npm scripts
We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm). We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
@@ -60,12 +79,13 @@ TODO: List scripts
### Initializing data ### Initializing data
Run the following `manage.py` commands. Do not run these in production without thinking! Run the following `manage.py` commands to initialize a new database. Do not run these in production without thinking!
```bash ```bash
python manage.py createdefaultadmin # creates an admin user python manage.py migrate # run migrations
python manage.py initialize # creates user groups python manage.py createdefaultadmin # creates an admin user
python manage.py createdummydata # creates dummy members to the member register python manage.py initialize # creates user groups
python manage.py createdummydata # creates dummy members to the member register
``` ```
### Running ### Running
@@ -74,10 +94,6 @@ python manage.py createdummydata # creates dummy members to the member regis
python manage.py runserver 0.0.0.0:8000 python manage.py runserver 0.0.0.0:8000
``` ```
Using address `0.0.0.0` will bind to all IP addresses. Using `localhost` will only bind to your machine.
#### Visit the page
Visit [https://localhost:8000](https://localhost:8000) in your browser! Visit [https://localhost:8000](https://localhost:8000) in your browser!
### Development workflow ### Development workflow
@@ -87,7 +103,7 @@ When you start working on a feature, create a feature branch for your changes. T
Example of creating a feature branch: Example of creating a feature branch:
```bash ```bash
git checkout -b feature-error-page git checkout -b feature-branch-name
``` ```
When your changes are ready and the code works without errors, submit a merge request to `develop` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge. When your changes are ready and the code works without errors, submit a merge request to `develop` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge.
@@ -98,16 +114,18 @@ Merge requests to `master` should be reviewed by multiple developers. Only a mod
### Linting ### Linting
Lint python files using `pycodestyle` with Lint python files using `black` with
```bash ```bash
pycodestyle --config=pycodestyle.cfg --count . npm run lint:py # check changes
npm run lint:py:fix # fix errors
``` ```
Lint javascript and markdown using `eslint` and `remark` with Lint javascript and markdown using `eslint` and `remark` with
```bash ```bash
npm test npm run lint:md # markdown
npm run lint:js # javascript
``` ```
Use an editor with linting capabilities to write pretty code that passes linting. Examples include _VSCode_, _Atom_ and _Pycharm_. Use an editor with linting capabilities to write pretty code that passes linting. Examples include _VSCode_, _Atom_ and _Pycharm_.
@@ -128,6 +146,8 @@ Tests are located in `tests.py` under every subproject.
Project is run in production with Docker. See `Dockerfile` for details. Project is run in production with Docker. See `Dockerfile` for details.
For more information about deployment check **[infra](https://gitlab.com/sahkoinsinoorikilta/vtmk/infra)** repository.
## GitLab CI ## GitLab CI
All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `develop` or `master`. All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `develop` or `master`.
+4 -4
View File
@@ -6,15 +6,15 @@ from kaehmy.models import PresetRole, CustomRole, Application, Comment, BaseRole
class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple): class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
option_template_name = "kaehmy/checkbox_option.html" option_template_name = "checkbox_option.html"
def create_option( def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None self, name, formIterator, label, selected, index, subindex=None, attrs=None
): ):
dic = super(CheckboxSelectMultiple, self).create_option( dic = super(CheckboxSelectMultiple, self).create_option(
name, value, label, selected, index, subindex, attrs name, formIterator, label, selected, index, subindex, attrs
) )
description = PresetRole.objects.get(id=value).description description = PresetRole.objects.get(id=formIterator.value).description
dic["description"] = description dic["description"] = description
return dic return dic
+4 -1
View File
@@ -13,6 +13,7 @@ class BaseRole(models.Model):
is_board = models.BooleanField(_("Board member")) is_board = models.BooleanField(_("Board member"))
CATEGORIES = ( CATEGORIES = (
("board", _("Board")),
("corporate", _("Corporate affairs")), ("corporate", _("Corporate affairs")),
("freshman", _("Freshmen")), ("freshman", _("Freshmen")),
("international", _("International")), ("international", _("International")),
@@ -20,11 +21,13 @@ class BaseRole(models.Model):
("media", _("Media")), ("media", _("Media")),
("tech", _("Technology")), ("tech", _("Technology")),
("wellbeing", _("Wellbeing")), ("wellbeing", _("Wellbeing")),
("elepaja", _("Elepaja")), ("sikpaja", _("Sik-paja")),
("ceremonies", _("Ceremonies")), ("ceremonies", _("Ceremonies")),
("studies", _("Studies")), ("studies", _("Studies")),
("sosso", _("Sössö magazine")), ("sosso", _("Sössö magazine")),
("pota", _("PoTa")),
("alumni", _("Alumni relations")), ("alumni", _("Alumni relations")),
("n", _("N")),
("others", _("Others")), ("others", _("Others")),
) )
category = models.CharField( category = models.CharField(
+4 -6
View File
@@ -5,12 +5,6 @@
margin-right: auto; margin-right: auto;
} }
body {
max-width: 1000px;
margin-left: auto !important;
margin-right: auto !important;
}
div.tooltip-inner { div.tooltip-inner {
max-width: 25rem; max-width: 25rem;
} }
@@ -28,6 +22,10 @@ div.tooltip-inner {
.kaehmy-content { .kaehmy-content {
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
} }
p { p {
+2 -5
View File
@@ -3,13 +3,9 @@
} }
footer { footer {
/* position: absolute; */
bottom: 0; bottom: 0;
width: 100%; width: 100%;
height: 60px; /* Set the fixed height of the footer here */ margin: 1rem;
/* line-height: 60px; /* Vertically center the text there */
margin-top: 2rem;
margin-bottom: 1rem;
} }
footer .container .col .nav .nav-item { footer .container .col .nav .nav-item {
@@ -26,6 +22,7 @@ footer .container .col .nav .nav-item {
.lang-select { .lang-select {
width: 10rem; width: 10rem;
margin-bottom: 1rem;
display: inline-block; display: inline-block;
} }
+18 -27
View File
@@ -1,37 +1,28 @@
.header-content { .kaehmy-header {
background-color: #0c2938;
} }
.header-content .logo { .kaehmy-header-content {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.header-content .logo img {
display: block;
height: auto;
margin: auto;
}
.kaehmy-banner {
max-width: 1000px; max-width: 1000px;
width: 100%;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
@media screen and (min-width: 1000px) {
.kaehmy_header-content {
position: absolute;
left: 0;
top: 0;
background-color: #0c2938;
width: 100%;
}
.kaehmy_header {
margin-bottom: 331px;
}
}
.kaehmy-banner-image { .kaehmy-banner-image {
width: 100%; max-height: 10rem;
max-width: 100%;
} }
.heading {
display: flex;
place-content: center;
flex-direction: column;
text-align: center;
margin: 1rem;
}
+7 -3
View File
@@ -1,11 +1,15 @@
.kaehmy_navigation { .kaehmy_navigation {
margin-bottom: 10px; margin-bottom: 10px;
}
.navbar-border {
border-bottom: 2px solid #282b3b; border-bottom: 2px solid #282b3b;
} }
.navbar-light .navbar-nav .nav-link { .navbar-light .navbar-nav .nav-link {
color: black; color: black;
} }
.navbar {
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
+10 -10
View File
@@ -4,6 +4,7 @@ from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
import logging import logging
from sikweb.settings import URL from sikweb.settings import URL
@@ -64,14 +65,15 @@ def comment(request, *args, **kwargs):
if form.is_valid(): if form.is_valid():
comment = form.save() comment = form.save()
name = comment.name name = comment.name
url = f"https://{URL}/kaehmy"
to_email = comment.parent.email to_email = comment.parent.email
subject = "Kaehmyysi tai kommenttiisi on vastattu!" subject = "Kaehmyysi tai kommenttiisi on vastattu!"
email_body = ( message = render_to_string(
f"{name.capitalize()} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n" "kaehmy/email_comment.html", {"name": name, "url": url}
"Käy lukemassa viesti osoitteessa https://{URL}/kaehmy"
) )
send_email(to=to_email, subject=subject, body=email_body)
send_email(to=to_email, subject=subject, body=message, html=True)
logging.debug(f"Sent kaehmy comment email to recipient <{to_email}>") logging.debug(f"Sent kaehmy comment email to recipient <{to_email}>")
return redirect("/kaehmy") return redirect("/kaehmy")
@@ -129,16 +131,14 @@ def submit(request, *args, **kwargs):
url = f"https://{URL}/kaehmy" url = f"https://{URL}/kaehmy"
name = form.cleaned_data.get("name", "Anonymous") name = form.cleaned_data.get("name", "Anonymous")
email_body = (
f"Moikka {name}!\r\n\r\nHienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.\r\n"
"Mahdollisista kommenteista tulee ilmoitus sähköpostitse.\r\n\r\n"
"Käy katsomassa kaehmytilanne osoitteessa {url}"
)
to_email = form.cleaned_data.get("email", "") to_email = form.cleaned_data.get("email", "")
subject = "Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle" subject = "Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle"
message = render_to_string(
"kaehmy/email_kaehmy.html", {"name": name, "url": url}
)
send_email(to_email, subject, email_body) send_email(to=to_email, subject=subject, body=message, html=True)
logging.debug(f"Sent kaehmy email to recipient <{to_email}>") logging.debug(f"Sent kaehmy email to recipient <{to_email}>")
processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy") processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy")
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Generated
+5 -8
View File
@@ -241,14 +241,14 @@ Django = ">=3.2"
[[package]] [[package]]
name = "django-phonenumber-field" name = "django-phonenumber-field"
version = "6.3.0" version = "6.4.0"
description = "An international phone number field for django models." description = "An international phone number field for django models."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
Django = ">=2.2" Django = ">=3.2"
phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phonenumbers\""} phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phonenumbers\""}
[package.extras] [package.extras]
@@ -314,7 +314,7 @@ test = ["cryptography", "pytest-cov", "pytest-django", "pytest-xdist", "pytest",
[[package]] [[package]]
name = "dparse" name = "dparse"
version = "0.5.1" version = "0.6.2"
description = "A parser for Python dependency files" description = "A parser for Python dependency files"
category = "dev" category = "dev"
optional = false optional = false
@@ -322,11 +322,11 @@ python-versions = ">=3.5"
[package.dependencies] [package.dependencies]
packaging = "*" packaging = "*"
pyyaml = "*"
toml = "*" toml = "*"
[package.extras] [package.extras]
pipenv = ["pipenv"] pipenv = ["pipenv"]
conda = ["pyyaml"]
[[package]] [[package]]
name = "et-xmlfile" name = "et-xmlfile"
@@ -1092,10 +1092,7 @@ djangorestframework = [
{file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"}, {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"},
] ]
djangorestframework-simplejwt = [] djangorestframework-simplejwt = []
dparse = [ dparse = []
{file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"},
{file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"},
]
et-xmlfile = [ et-xmlfile = [
{file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
{file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
+17 -2
View File
@@ -10,8 +10,23 @@ fi
if test -f "$DB_PASSWD_FILE"; then if test -f "$DB_PASSWD_FILE"; then
export DB_PASSWD=$(cat $DB_PASSWD_FILE) export DB_PASSWD=$(cat $DB_PASSWD_FILE)
fi fi
if test -f "$GOOGLE_CREDS_JSON"; then if test -f "$G_PRIVATE_KEY_ID_FILE"; then
export GOOGLE_CREDS_JSON=$(cat $GOOGLE_CRED_JSON_FILE) export G_PRIVATE_KEY_ID=$(cat $G_PRIVATE_KEY_ID_FILE)
fi
if test -f "$G_PRIVATE_KEY_FILE"; then
export G_PRIVATE_KEY="$(cat $G_PRIVATE_KEY_FILE)"
fi
if test -f "$G_CLIENT_EMAIL_FILE"; then
export G_CLIENT_EMAIL=$(cat $G_CLIENT_EMAIL_FILE)
fi
if test -f "$G_CLIENT_ID_FILE"; then
export G_CLIENT_ID=$(cat $G_CLIENT_ID_FILE)
fi
if test -f "$G_CLIENT_URL_FILE"; then
export G_CLIENT_URL=$(cat $G_CLIENT_URL_FILE)
fi
if test -f "$GROUP_KEY_FILE"; then
export GROUP_KEY=$(cat $GROUP_KEY_FILE)
fi fi
# Collect static files # Collect static files
+13 -1
View File
@@ -82,7 +82,19 @@ DATABASES = {
# Google api settings # Google api settings
GROUP_KEY = os.getenv("GROUP_KEY", "") GROUP_KEY = os.getenv("GROUP_KEY", "")
GOOGLE_SERVICE_ACCOUNT = json.loads(os.getenv("GOOGLE_CREDS_JSON", "{}"))
GOOGLE_CREDS = {
"type": "service_account",
"project_id": "web2-backend",
"private_key_id": os.getenv("G_PRIVATE_KEY_ID", ""),
"private_key": os.getenv("G_PRIVATE_KEY", ""),
"client_email": os.getenv("G_CLIENT_EMAIL", ""),
"client_id": os.getenv("G_CLIENT_ID", ""),
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": os.getenv("G_CLIENT_URL", ""),
}
# JWT authentication # JWT authentication
SIMPLE_JWT = { SIMPLE_JWT = {
+24
View File
@@ -29,15 +29,39 @@ services:
- FRONTEND_URL=dev.sahkoinsinoorikilta.fi - FRONTEND_URL=dev.sahkoinsinoorikilta.fi
- DEBUG=True - DEBUG=True
- EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY - EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY
- G_PRIVATE_KEY_ID_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY_ID
- G_PRIVATE_KEY_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY
- G_CLIENT_EMAIL_FILE=/run/secrets/BACKEND_G_CLIENT_EMAIL
- G_CLIENT_ID_FILE=/run/secrets/BACKEND_G_CLIENT_ID
- G_CLIENT_URL_FILE=/run/secrets/BACKEND_G_CLIENT_URL
- GROUP_KEY_FILE=/run/secrets/BACKEND_GROUP_KEY
- DB_HOST=db - DB_HOST=db
- DB_PORT=5432 - DB_PORT=5432
secrets: secrets:
- DJANGO_EMAIL_API_KEY - DJANGO_EMAIL_API_KEY
- BACKEND_G_PRIVATE_KEY_ID
- BACKEND_G_PRIVATE_KEY
- BACKEND_G_CLIENT_EMAIL
- BACKEND_G_CLIENT_ID
- BACKEND_G_CLIENT_URL
- BACKEND_GROUP_KEY
secrets: secrets:
DJANGO_EMAIL_API_KEY: DJANGO_EMAIL_API_KEY:
external: true external: true
BACKEND_G_PRIVATE_KEY_ID:
external: true
BACKEND_G_PRIVATE_KEY:
external: true
BACKEND_G_CLIENT_EMAIL:
external: true
BACKEND_G_CLIENT_ID:
external: true
BACKEND_G_CLIENT_URL:
external: true
BACKEND_GROUP_KEY:
external: true
volumes: volumes:
dbdata: dbdata:
+25 -4
View File
@@ -34,13 +34,24 @@ services:
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY - SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD - DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY - EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
- GOOGLE_CREDS_JSON=/run/secrets/GOOGLE_CREDS_JSON - G_PRIVATE_KEY_ID_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY_ID
- G_PRIVATE_KEY_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY
- G_CLIENT_EMAIL_FILE=/run/secrets/BACKEND_G_CLIENT_EMAIL
- G_CLIENT_ID_FILE=/run/secrets/BACKEND_G_CLIENT_ID
- G_CLIENT_URL_FILE=/run/secrets/BACKEND_G_CLIENT_URL
- GROUP_KEY_FILE=/run/secrets/BACKEND_GROUP_KEY
secrets: secrets:
- BACKEND_SECRET_KEY - BACKEND_SECRET_KEY
- BACKEND_DB_PASSWD - BACKEND_DB_PASSWD
- BACKEND_EMAIL_API_KEY - BACKEND_EMAIL_API_KEY
- GOOGLE_CREDS_JSON - BACKEND_G_PRIVATE_KEY_ID
- BACKEND_G_PRIVATE_KEY
- BACKEND_G_CLIENT_EMAIL
- BACKEND_G_CLIENT_ID
- BACKEND_G_CLIENT_URL
- BACKEND_GROUP_KEY
secrets: secrets:
BACKEND_SECRET_KEY: BACKEND_SECRET_KEY:
external: true external: true
@@ -48,5 +59,15 @@ secrets:
external: true external: true
BACKEND_EMAIL_API_KEY: BACKEND_EMAIL_API_KEY:
external: true external: true
GOOGLE_CREDS_JSON: BACKEND_G_PRIVATE_KEY_ID:
EXTERNAL: true external: true
BACKEND_G_PRIVATE_KEY:
external: true
BACKEND_G_CLIENT_EMAIL:
external: true
BACKEND_G_CLIENT_ID:
external: true
BACKEND_G_CLIENT_URL:
external: true
BACKEND_GROUP_KEY:
external: true
+1 -1
View File
@@ -13,7 +13,7 @@
{% block body %} {% block body %}
{% block header %} {% block header %}
<div class="kaehmy_header"> <div class="kaehmy-header">
{% include "kaehmy/header.html" %} {% include "kaehmy/header.html" %}
</div> </div>
{% endblock header %} {% endblock header %}
+10
View File
@@ -0,0 +1,10 @@
{% load i18n %}
<p>
Hei!
</p>
<p>
{{ name }} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.
Käy lukemassa viesti
<a href={{ url }}>täältä.</a>
</p>
+13
View File
@@ -0,0 +1,13 @@
{% load i18n %}
<p>
Moikka {{ name }}!
</p>
<p>
Hienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.
Mahdollisista kommenteista tulee ilmoitus sähköpostitse.
</p>
<p>
Käy katsomassa kaehmytilanne
<a href={{ url }}>täältä.</a>
</p>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "kaehmy/base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
+9 -2
View File
@@ -1,7 +1,14 @@
{% load i18n %} {% load i18n %}
<div class="kaehmy_header-content"> <div class="kaehmy-header-content center">
<div class="kaehmy-banner logo"> <div class="kaehmy-banner logo">
<a href="/kaehmy"><img class="kaehmy-banner-image" src="/static/kaehmy/img/kaehmy_banner.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a> <a href="/kaehmy">
<img class="kaehmy-banner-image" src="https://static.sahkoinsinoorikilta.fi/logot-ja-grafiikka/web/side/SIK_RGB_W_side.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry">
</a>
</div>
<div class="kaehmy-banner heading">
<p style="color:#D57A2D; font-size:2rem">{% blocktrans %}Kähmyt ovat auki!{% endblocktrans %}</p>
<p style="color:#BFDBD9; font-size:1rem">{% blocktrans %}Haku hallitukseen 24.10. mennessä ja toimihenkilöksi 18.11 mennessä.{% endblocktrans %}</p>
<p style="color:#BFDBD9; font-size:1.5rem">{% blocktrans %}Hae nyt!{% endblocktrans %}</p>
</div> </div>
</div> </div>
+7 -5
View File
@@ -28,10 +28,12 @@
</p> </p>
<h5>{% trans "Päivämääriä & deadlineja" %}</h5> <h5>{% trans "Päivämääriä & deadlineja" %}</h5>
<ul> <ul>
<li><strong>25.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li> <li><strong>11.10.</strong> {% blocktrans %}Toimikuntamessut @OK20{% endblocktrans %}</li>
<li><strong>01.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li> <li><strong>24.10.</strong> {% blocktrans %}Deadline hallitusvirkoihin hakemiselle.{% endblocktrans %}</li>
<li><strong>09.11.</strong> {% blocktrans %}Toimikunta-appro{% endblocktrans %}</li> <li><strong>25.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li>
<li><strong>17.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li> <li><strong>07.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li>
<li><strong>18.11.</strong> {% blocktrans %}Deadline toimivirkoihin hakemiselle.{% endblocktrans %}</li>
<li><strong>24.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li>
</ul> </ul>
<form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %} <form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %}
{% bootstrap_field form.name %} {% bootstrap_field form.name %}
@@ -75,7 +77,7 @@
<input type="checkbox" required name="gdpr" value="1"> <input type="checkbox" required name="gdpr" value="1">
<span>{% blocktrans %} <span>{% blocktrans %}
Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Tietosuojaseloste%20%23U2013%20Toimihenkil%23U00f6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen. Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Tietosuojaseloste%20%E2%80%93%20Toimihenkil%C3%B6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen.
{% endblocktrans %} {% endblocktrans %}
</span> </span>
{% buttons %} {% buttons %}
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="card" style="margin-top: 0.5rem; margin-bottom: 0"> <div class="card" style="margin-top: 0.5rem; margin-bottom: 0">
<div class="card-block"> <div class="card-block">
<h4>{{ message.name }}</h4> <h4>{{ message.name }}</h4>
<p>{{ message.message|linebreaks|urlize }}</p> <p>{{ message.message|linebreaks|urlize }}</p>
<h6 class="card-subtitle mb-2 text-muted">{{ message.timestamp }}</h6> <h6 class="card-subtitle mb-2 text-muted">{{ message.timestamp }}</h6>
+2 -2
View File
@@ -1,8 +1,8 @@
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
<div class="kaehmy_navigation"> <div class="kaehmy_navigation bg-faded">
<nav class="navbar-border navbar navbar-toggleable-md navbar-light bg-faded"> <nav class="navbar navbar-toggleable-md navbar-light">
<div class="navbar-nav"> <div class="navbar-nav">
<a class="nav-item nav-link" href="/kaehmy">{% trans "List kaehmys" %}</a> <a class="nav-item nav-link" href="/kaehmy">{% trans "List kaehmys" %}</a>
<a class="nav-item nav-link" href="/kaehmy/new">{% trans "New kaehmy" %} <span class="sr-only">(current)</span></a> <a class="nav-item nav-link" href="/kaehmy/new">{% trans "New kaehmy" %} <span class="sr-only">(current)</span></a>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "members/base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
View File
+13 -2
View File
@@ -25,7 +25,7 @@ from sikweb.settings import (
DEFAULT_EMAIL_FROM_ADDR, DEFAULT_EMAIL_FROM_ADDR,
ENABLE_AUTOMATIC_EMAILS, ENABLE_AUTOMATIC_EMAILS,
GROUP_KEY, GROUP_KEY,
GOOGLE_SERVICE_ACCOUNT, GOOGLE_CREDS,
) )
from datetime import timedelta from datetime import timedelta
@@ -136,7 +136,7 @@ def add_to_mailinglist(email: str):
# create credentials, with subject is used to impersonate admin account # create credentials, with subject is used to impersonate admin account
# jas_manager has groups editor rights in google admin # jas_manager has groups editor rights in google admin
credentials = service_account.Credentials.from_service_account_info( credentials = service_account.Credentials.from_service_account_info(
info=GOOGLE_SERVICE_ACCOUNT, scopes=SCOPES info=GOOGLE_CREDS, scopes=SCOPES
).with_subject("jas_manager@sahkoinsinoorikilta.fi") ).with_subject("jas_manager@sahkoinsinoorikilta.fi")
service = build("admin", "directory_v1", credentials=credentials) service = build("admin", "directory_v1", credentials=credentials)
@@ -157,3 +157,14 @@ def add_to_mailinglist(email: str):
) )
send_email(to, subject, body) send_email(to, subject, body)
except ValueError as err:
logging.exception("Formatting of google credentials is incorrect")
if DEPLOY_ENV == "production":
to = "ilari.ojakorpi@sahkoinsinoorikilta.fi"
subject = "Web error: Failed adding to google groups"
body = "Google credential formatted incorretly\nEmail that was not added: {}\n\nAdd user manually to jäsenet groups.".format(
email
)
send_email(to, subject, body)