59 Commits

Author SHA1 Message Date
jadera 8393875963 change olhev date 2026-01-15 15:03:37 +02:00
SimeonPursiainen d5f67f4cc1 Edit GDPR link in Kaehmy site 2025-11-21 22:48:49 +02:00
J4DER4 ebd0bd9fa2 Merge branch 'Testing-audit-fixes-pt1' into 'main'
PIP upgrades for audit safetyscheck

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!117
2025-11-20 17:13:26 +00:00
J4DER4 c0a9321341 Merge branch 'jäsen_searchbar' into 'main'
added searchbar to jäsenrekisteri (check description)

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!116
2025-11-20 17:02:59 +00:00
jadera 6693279348 use strict version 2025-11-20 18:45:13 +02:00
jadera d0557ffb79 added more pip upgrades 2025-11-20 00:59:30 +02:00
jadera 91ee3bea6d testing pip upgrade to bump packages 2025-11-20 00:43:47 +02:00
jadera b1d6bf359f added searchbar to jäsenrekisteri
added searchbar
2025-11-19 23:37:48 +02:00
Justus Ojala 61ac177ce3 Made manual submit_id check and IntegrityError messages the same (were different to distinguish which is blocking the request when running locally) 2025-10-13 20:37:09 +03:00
Justus Ojala 9a2168e47f Add manual submit_id uniqueness check as it is not enforced on server 2025-10-13 20:20:29 +03:00
Justus Ojala a5732669da Merge branch 'signup-duplicate-reduction' into 'main'
Add submit_id to signup model

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!115
2025-10-13 19:45:46 +03:00
Justus Ojala 75800ee9ee Add submit_id to signup model 2025-10-13 19:33:26 +03:00
Justus Ojala 5782c20b4b Fix more name errors 2025-09-23 08:08:39 +03:00
Justus Ojala 167b0bfabf Fix more name errors 2025-09-23 07:52:01 +03:00
Justus Ojala 43c9a6d328 Merge branch 'print-syntax' into 'main'
This isn't C

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!113
2025-09-22 18:50:27 +03:00
Justus Ojala b3a159b3d8 This isn't C 2025-09-22 18:46:57 +03:00
Justus Ojala a7ed188dc8 Merge branch 'debugprints' into 'main'
added debugprints

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!112
2025-09-22 18:25:55 +03:00
Justus Ojala 162759dcb2 added debugprints 2025-09-22 18:22:08 +03:00
Justus Ojala 05e6ba01d9 Merge branch 'time-name-fix' into 'main'
Import time

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!111
2025-09-22 18:04:59 +03:00
Justus Ojala 8a061381f4 Import time 2025-09-22 18:01:27 +03:00
Justus Ojala d2fa7084da Merge branch 'setuptools' into 'main'
Setuptools

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!110
2025-09-22 17:45:46 +03:00
Justus Ojala e7278c8893 Remove poetry.lock from version control 2025-09-22 17:38:47 +03:00
Justus Ojala 52bf21c8ba Add setuptools dependency 2025-09-22 17:38:00 +03:00
Justus Ojala 0cbc794c75 Merge branch 'signup_duplicate_prevention' into 'main'
Added submission key checking to backend

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!106
2025-09-16 21:43:28 +03:00
Justus Ojala 90a0550775 Added submission key checking to backend 2025-09-16 21:24:52 +03:00
Aarni Halinen 0d458cf2ea Merge branch 'deps' into 'main'
Update some deps

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!105
2025-03-26 21:37:14 +02:00
Aarni Halinen 5155f52f29 update djangorestframework-simplejwt 2025-03-26 21:33:23 +02:00
Aarni Halinen a2ccc43a36 update gunicorn 2025-03-26 21:16:22 +02:00
Aarni Halinen a045c6ac89 update sentry-sdk 2025-03-26 21:13:17 +02:00
Aarni Halinen dcb2115cb5 poetry update 2025-03-26 21:12:51 +02:00
Aarni Halinen 5c7528ca6a Merge branch 'dev-setup' into 'main'
Update poetry and fix development tooling and docs

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!104
2025-03-26 21:07:01 +02:00
Aarni Halinen a3ab12619a EOF new line 2025-03-26 21:03:52 +02:00
Aarni Halinen c637ffb3f6 stop using env file in docker-compose 2025-03-26 20:58:05 +02:00
Aarni Halinen 41fd3043d0 update poetry to v2.1.1 2025-03-26 20:51:02 +02:00
Aarni Halinen 1331eeb1d7 change trunk and production branches 2025-03-06 02:36:14 +02:00
Aarni Halinen 1fd329f0c1 pick changes from templates 2025-03-06 02:31:56 +02:00
Aarni Halinen 52eb9e370c Revert "production deployments manually from develop branch"
This reverts commit 00e5eff8db.
2025-03-06 02:31:02 +02:00
Aarni Halinen 00e5eff8db production deployments manually from develop branch 2025-03-06 02:10:19 +02:00
SimeonPursiainen 7554c1e7e8 testing 2025-02-28 22:46:40 +02:00
Simeon Pursiainen 50fd4ff9f7 Merge branch 'update-python' into 'develop'
Update python to 3.12

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!99
2025-02-09 19:53:50 +00:00
Aarni Halinen ff8230d9c0 update node packages 2025-02-09 21:20:38 +02:00
Aarni Halinen 3f73fbec62 update node to v22 2025-02-09 21:08:18 +02:00
Aarni Halinen 916e0bdaf0 allow audit failure 2025-02-09 21:05:20 +02:00
Aarni Halinen 9e4a7c8569 poetry update 2025-02-09 20:56:21 +02:00
Aarni Halinen b5839da135 rm deprecated compose version 2025-02-09 20:54:05 +02:00
Aarni Halinen f562912492 migrate to new dependecy format 2025-02-09 20:54:04 +02:00
Aarni Halinen f0a5b6e8e7 update to python3.12 2025-02-09 19:47:10 +02:00
Aarni Halinen 8fc3ee534d fix markdown lint issues in README 2025-02-09 18:27:18 +02:00
Aarni Halinen 9b4fa56add uppercase AS in Dockerfile 2025-02-09 18:22:15 +02:00
Simeon Pursiainen 90ca91970d Update .gitlab-ci.yml file 2025-01-23 19:11:25 +00:00
Simeon Pursiainen 955072370f temporary fix 2025-01-23 19:05:17 +00:00
Simeon Pursiainen 709275c4d3 Edit email.html 2025-01-23 18:52:54 +00:00
Simeon Pursiainen 6f61c9dc32 Do audit only on push to master 2025-01-23 18:51:46 +00:00
Simeon Pursiainen f5432a1ff9 Edited Hafv date 2025-01-21 21:11:01 +00:00
Johannes Viirimäki f23ce6b39e use docker:25 based images and services 2024-09-30 07:36:45 +00:00
Johannes Viirimäki 3a8c455031 Update kaehmy.html 2024-09-30 07:25:41 +00:00
Johannes a6a973f008 update pip & pillow 2024-01-24 23:22:39 +02:00
Johannes 01f7911352 update packages 2024-01-24 22:02:05 +02:00
johannes 55507f89d1 change hafv date 2024-01-24 20:49:34 +02:00
31 changed files with 5280 additions and 5974 deletions
-6
View File
@@ -1,6 +0,0 @@
members/static/js/lib
infoscreen/static/js/lib
webapp/static/js/lib
static/js/lib
collected_static
venv
-251
View File
@@ -1,251 +0,0 @@
{
"env": {
"browser": true,
"jquery": true
},
"globals": {
"angular": true,
"noty": true,
"_": true,
"moment": true
},
"extends": "eslint:recommended",
"rules": {
"no-unused-vars": "warn",
"accessor-pairs": "error",
"array-bracket-spacing": "off",
"array-callback-return": "error",
"arrow-body-style": "error",
"arrow-parens": "error",
"arrow-spacing": "error",
"block-scoped-var": "off",
"block-spacing": "off",
"brace-style": "off",
"callback-return": "off",
"camelcase": "off",
"capitalized-comments": "off",
"class-methods-use-this": "error",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "off",
"complexity": "off",
"computed-property-spacing": "off",
"consistent-return": "off",
"consistent-this": "off",
"curly": "off",
"default-case": "off",
"dot-location": [
"error",
"property"
],
"dot-notation": "off",
"eol-last": "off",
"eqeqeq": "off",
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "off",
"func-style": "off",
"generator-star-spacing": "error",
"global-require": "off",
"guard-for-in": "off",
"handle-callback-err": "off",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"indent": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "off",
"keyword-spacing": "off",
"line-comment-position": "off",
"linebreak-style": "off",
"lines-around-comment": "off",
"lines-around-directive": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-ternary": "off",
"new-parens": "off",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-alert": "error",
"no-array-constructor": "off",
"no-await-in-loop": "error",
"no-bitwise": "off",
"no-caller": "error",
"no-catch-shadow": "off",
"no-confusing-arrow": "error",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty-function": "off",
"no-eq-null": "off",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "off",
"no-implicit-coercion": [
"error",
{
"boolean": false,
"number": false,
"string": false
}
],
"no-implicit-globals": "off",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-inner-declarations": [
"error",
"functions"
],
"no-invalid-this": "off",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "off",
"no-loop-func": "error",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-mixed-requires": "error",
"no-multi-assign": "off",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "off",
"no-native-reassign": "off",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "off",
"no-new": "error",
"no-new-func": "off",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "off",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-prototype-builtins": "off",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "off",
"no-return-await": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "off",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": "error",
"no-tabs": "off",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "off",
"no-trailing-spaces": "off",
"no-undef-init": "error",
"no-undefined": "off",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": [
"error",
{
"defaultAssignment": true
}
],
"no-unused-expressions": "off",
"no-use-before-define": "off",
"no-useless-call": "off",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-escape": "off",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "off",
"no-void": "off",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "off",
"operator-linebreak": "off",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"prefer-destructuring": [
"error",
{
"array": false,
"object": false
}
],
"prefer-numeric-literals": "error",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": "off",
"require-await": "error",
"require-jsdoc": "off",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": "off",
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": [
"error",
{
"nonwords": false,
"words": false
}
],
"spaced-comment": "off",
"strict": "off",
"symbol-description": "error",
"template-curly-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "off",
"vars-on-top": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "error",
"yoda": "off"
}
}
+2 -1
View File
@@ -11,4 +11,5 @@ node_modules/
.idea/ .idea/
*.code-workspace *.code-workspace
venv/ venv/
.venv/ .venv/
poetry.lock
+168 -165
View File
@@ -1,188 +1,191 @@
stages: stages:
- setup - setup
- audit - audit
- lint - lint
- test - test
- publish - publish
- deploy - deploy
- cleanup - cleanup
install: install:
image: node:14 image: node:22
stage: setup stage: setup
only: only:
- pushes - pushes
script: script:
- npm ci - npm ci
artifacts: artifacts:
paths: paths:
- node_modules - node_modules
expire_in: 1 week expire_in: 1 week
audit: audit:
image: python:3.9 image: python:3.12.9
stage: audit stage: audit
only: allow_failure: true
- pushes only:
needs: [] - pushes
before_script: needs: []
- pip install poetry==1.3.1 before_script:
- poetry config virtualenvs.create false - pip install pip==25.3
- poetry install --no-interaction --no-ansi - pip install poetry==2.1.1
script: - poetry config virtualenvs.create false
- safety check - poetry install --no-interaction --no-ansi
script:
- safety check
test: test:
image: python:3.9 image: python:3.12.9
stage: test stage: test
only: only:
- pushes - pushes
needs: [] needs: []
services: services:
- postgres:12 - postgres:12
variables: variables:
POSTGRES_DB: ci POSTGRES_DB: ci
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
DB_HOST: postgres DB_HOST: postgres
before_script: before_script:
- pip install poetry==1.3.1 - pip install pip==25.3
- poetry config virtualenvs.create false - pip install poetry==2.1.1
- poetry install --no-interaction --no-ansi - poetry config virtualenvs.create false
script: - poetry install --no-interaction --no-ansi
- python manage.py migrate --noinput script:
- python manage.py createdefaultadmin - python manage.py migrate --noinput
- python manage.py test - python manage.py createdefaultadmin
- python manage.py test
lint:py: lint:py:
image: python:3.9 image: python:3.12.9
stage: lint stage: lint
only: only:
- pushes - pushes
needs: [] needs: []
script: script:
- pip install black==22.3.0 - pip install black==22.3.0
- black --check . - black --check .
lint:js: lint:js:
image: node:14 image: node:22
stage: lint stage: lint
only: only:
- pushes - pushes
needs: ["install"] needs: ["install"]
script: script:
- npm run lint:js - npm run lint:js
lint:md: lint:md:
image: node:14 image: node:22
stage: lint stage: lint
only: only:
- pushes - pushes
needs: ["install"] needs: ["install"]
script: script:
- npm run lint:md - npm run lint:md
publish: publish:
image: docker:stable image: docker:25-cli
stage: publish stage: publish
needs: ["test", "lint:py", "lint:js", "lint:md"] needs: ["test", "lint:py", "lint:js", "lint:md"]
services: services:
- docker:stable-dind - docker:25-dind
only: only:
- develop - main
- master - production
script: script:
- docker info - docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build . -t "$IMAGE_NAME" - docker build . -t "$IMAGE_NAME"
- docker push "$IMAGE_NAME" - docker push "$IMAGE_NAME"
deploy:dev: deploy:dev:
image: docker:stable image: docker:25-cli
stage: deploy stage: deploy
only: only:
- develop - main
environment: environment:
name: dev name: dev
url: http://api.dev.sahkoinsinoorikilta.fi url: http://api.dev.sahkoinsinoorikilta.fi
variables: variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem - echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem - echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem - echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script: script:
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME" - docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
after_script: after_script:
- docker logout "$CI_REGISTRY" - docker logout "$CI_REGISTRY"
deploy:production: deploy:production:
stage: deploy stage: deploy
image: docker:stable image: docker:25-cli
only: only:
- master - production
environment: environment:
name: production name: production
url: https://api.sahkoinsinoorikilta.fi url: https://api.sahkoinsinoorikilta.fi
when: manual when: manual
variables: variables:
DOCKER_HOST: $CI_DOCKER_HOST DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem - echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem - echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem - echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
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: docker_prune:dev:
image: docker:stable image: docker:stable
stage: cleanup stage: cleanup
only: only:
- schedules - schedules
environment: environment:
name: dev name: dev
url: http://api.dev.sahkoinsinoorikilta.fi url: http://api.dev.sahkoinsinoorikilta.fi
variables: variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem - echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem - echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem - echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script: script:
- docker system prune - docker system prune
after_script: after_script:
- docker logout "$CI_REGISTRY" - docker logout "$CI_REGISTRY"
docker_prune:prod: docker_prune:prod:
image: docker:stable image: docker:stable
stage: cleanup stage: cleanup
only: only:
- schedules - schedules
environment: environment:
name: production name: production
url: https://api.sahkoinsinoorikilta.fi url: https://api.sahkoinsinoorikilta.fi
variables: variables:
DOCKER_HOST: $CI_DOCKER_HOST DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem - echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem - echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem - echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script: script:
- docker system prune - docker system prune
after_script: after_script:
- docker logout "$CI_REGISTRY" - docker logout "$CI_REGISTRY"
-1
View File
@@ -1 +0,0 @@
_
Executable → Regular
-3
View File
@@ -1,6 +1,3 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
-3
View File
@@ -1,6 +1,3 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
+1
View File
@@ -0,0 +1 @@
22.13.1
+1 -1
View File
@@ -1 +1 @@
3.9 3.12.9
+2
View File
@@ -0,0 +1,2 @@
python 3.12.9
poetry 2.1.1
+7 -6
View File
@@ -1,13 +1,13 @@
FROM python:3.9-slim-buster as builder FROM python:3.12.9-slim-bullseye AS builder
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
COPY . ./ COPY . ./
ENV POETRY_VERSION=2.1.1
ENV POETRY_VERSION=1.3.1 RUN pip install pip==25.3
RUN pip install "poetry==$POETRY_VERSION" RUN pip install "poetry==$POETRY_VERSION"
RUN poetry export --without-hashes > requirements.txt RUN poetry self add poetry-plugin-export
RUN poetry export --without-hashes --format=requirements.txt --output requirements.txt
FROM python:3.9-slim-buster as server FROM python:3.12.9-slim-bullseye AS server
WORKDIR /app WORKDIR /app
COPY . ./ COPY . ./
@@ -22,6 +22,7 @@ ENV PYTHONUNBUFFERED=1 \
PIP_DEFAULT_TIMEOUT=100 PIP_DEFAULT_TIMEOUT=100
RUN apt-get update && apt-get install --no-install-recommends -y build-essential RUN apt-get update && apt-get install --no-install-recommends -y build-essential
RUN pip install pip==25.3
RUN pip install --no-deps -r requirements.txt RUN pip install --no-deps -r requirements.txt
RUN python manage.py collectstatic --noinput RUN python manage.py collectstatic --noinput
+46 -24
View File
@@ -2,11 +2,12 @@
[Django](https://www.djangoproject.com/) backend containing multiple small applications and api for Next.js frontend. [Django](https://www.djangoproject.com/) backend containing multiple small applications and api for Next.js frontend.
* **Web app:** Backend for the main website. * **Web app:** Backend for the main website.
* **Member register:** Data table app for viewing and modifying the member register, member applications and membership payments. * **Member register:** Data table app for viewing and modifying the member register, member applications and membership payments.
* **Kaehmy:** Form for creating and listing kaehmys * **Kaehmy:** Form for creating and listing kaehmys
* **Ohlhafv:** Form for creating and listing ohlhafv challenges. * **Ohlhafv:** Form for creating and listing ohlhafv challenges.
* **Infoscreen:** Angular-based slideshow app for the guild room's screens. * **Infoscreen:** Angular-based slideshow app for the guild room's screens.
## Installation ## Installation
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:
@@ -14,13 +15,13 @@ Set up your SSH key authentication in GitLab Profile Settings. Then clone the re
```bash ```bash
git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git
cd web2.0-backend cd web2.0-backend
git checkout develop
``` ```
Copy env file for local use: Copy env file for local use:
```bash ```bash
cp .env.dev .env cp .env.dev .env
``` ```
### Poetry ### Poetry
@@ -29,48 +30,69 @@ For depedencies and virtual environment, we use [poetry](https://python-poetry.o
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry: First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
```bash ```bash
python3 -m pip install poetry==1.3.1 python -m pip install poetry==2.1.1
``` ```
The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD Install dependencies with
```bash ```bash
python3 -m poetry config virtualenvs.in-project true poetry install
```
Poetry is configured to install dependencies in a virtual environment, so you should see `.venv` folder in repo root.
Activate virtual environment in shell
```bash
eval $(poetry env activate)
``` ```
### Node ### 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: 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:
``` ```bash
npm install npm install
``` ```
TODO: List scripts See [Linting](#linting) for more info
### Database ### 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. 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: After installing docker use the following to create a database:
```bash ```bash
docker run --name sik.web.db -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:12 docker run --name sik.web.db -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:12
``` ```
## Development ## Development
Activate virtual environment in shell Install dependencies with
```bash
python3 -m poetry shell
```
Install dependencies
```bash ```bash
poetry install poetry install
``` ```
and make sure you are using Python from your virutal environment.
Virtual environment can be activated with
```bash
eval $(poetry env activate)
```
and you verify correct Python executable with
```bash
which python
# should return path similar to {your-system path}/web2.0-backend/.venv/bin/python
```
### Initializing data ### Initializing data
Run the following `manage.py` commands to initialize a new database. 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!
@@ -106,11 +128,11 @@ Example of creating a feature branch:
git checkout -b feature-branch-name 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 `main` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge.
Bugfixes do not need their own feature branches and can be pushed straight to `develop`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead. Bugfixes do not need their own feature branches and can be pushed straight to `main`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead.
Merge requests to `master` should be reviewed by multiple developers. Only a moderator can accept merge requests to `master`. Merge requests to `main` should be reviewed by multiple developers. Only a moderator can accept merge requests to `production`.
### Linting ### Linting
@@ -150,4 +172,4 @@ For more information about deployment check **[infra](https://gitlab.com/sahkoin
## 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 `main` or `production`.
+15 -7
View File
@@ -1,22 +1,30 @@
version: '3'
services: services:
db: db:
image: postgres:12 image: postgres:12
volumes: volumes:
- dbdata:/var/lib/postgresql/data - dbdata:/var/lib/postgresql/data
ports: ports:
- "5432:5432" - 5432:5432
environment: environment:
- POSTGRES_PASSWORD=postgres - POSTGRES_PASSWORD=postgres
web: web:
build: . build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend environment:
env_file: - DEPLOY_ENV=local
- .env - HOST=localhost
- DEBUG=True
- SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASSWD=postgres
- DB_HOST=db
- DB_PORT=5432
- EMAIL_API_KEY=
- GROUP_KEY=
- GOOGLE_CREDS='{}'
ports: ports:
- "8000:8000" - 8000:8000
depends_on: depends_on:
- db - db
+23
View File
@@ -0,0 +1,23 @@
import globals from "globals";
import js from "@eslint/js";
export default [
{
ignores: ["**/.venv/", "**/collected_static/", "**/static/js/lib/**"],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.jquery,
angular: true,
moment: true,
_: true
},
},
},
{
...js.configs.recommended
}
];
@@ -0,0 +1,65 @@
# Generated by Django 4.2.24 on 2025-10-13 14:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0011_delete_kaehmybaserole"),
]
operations = [
migrations.AlterField(
model_name="baserole",
name="category",
field=models.CharField(
choices=[
("board", "Board"),
("corporate", "Corporate affairs"),
("freshman", "Freshmen"),
("international", "International"),
("siwa", "SIK's free time"),
("media", "Media"),
("tech", "Technology"),
("wellbeing", "Wellbeing"),
("sikpaja", "Sik-paja"),
("ceremonies", "Ceremonies"),
("studies", "Studies"),
("sosso", "Sössö magazine"),
("pota", "PoTa"),
("alumni", "Alumni relations"),
("n", "N"),
("others", "Others"),
],
default="others",
max_length=255,
verbose_name="Category",
),
),
migrations.AlterField(
model_name="customrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
migrations.AlterField(
model_name="presetrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
]
+6 -1
View File
@@ -3,8 +3,13 @@
from django.contrib import admin from django.contrib import admin
from members.models import Member, Request, Payment from members.models import Member, Request, Payment
# Register your models here. # Register your models here.
admin.site.register(Member) class MemberAdmin(admin.ModelAdmin):
search_fields = ("first_name", "last_name", "email", "POR")
admin.site.register(Member, MemberAdmin)
admin.site.register(Request) admin.site.register(Request)
admin.site.register(Payment) admin.site.register(Payment)
+4756 -3742
View File
File diff suppressed because it is too large Load Diff
+9 -7
View File
@@ -9,7 +9,7 @@
"lint:py": "black --diff --check .", "lint:py": "black --diff --check .",
"lint:py:fix": "black .", "lint:py:fix": "black .",
"lint:py-type": "pyright", "lint:py-type": "pyright",
"prepare": "husky install" "prepare": "husky"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -17,13 +17,15 @@
}, },
"author": "SIK ry", "author": "SIK ry",
"license": "ISC", "license": "ISC",
"dependencies": { "devDependencies": {
"eslint": "^7.28.0", "@eslint/js": "^9.20.0",
"husky": "^6.0.0", "eslint": "^9.20.0",
"globals": "^15.14.0",
"husky": "^9.1.7",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pyright": "^1.1.149", "pyright": "^1.1.393",
"remark-cli": "^9.0.0", "remark-cli": "^12.0.1",
"remark-preset-lint-recommended": "^5.0.0" "remark-preset-lint-recommended": "^7.0.1"
}, },
"remarkConfig": { "remarkConfig": {
"plugins": [ "plugins": [
Generated
-1713
View File
File diff suppressed because it is too large Load Diff
+53 -38
View File
@@ -1,50 +1,65 @@
[tool.poetry] [project]
name = "web2.0-backend" authors = [
version = "0.1.0" {name = "Aarni Halinen", email = "aarni.halinen@sahkoinsinoorikilta.fi"},
]
description = "Backend for sahkoinsinoorikilta.fi" description = "Backend for sahkoinsinoorikilta.fi"
authors = ["Aarni Halinen aarni.halinen@sahkoinsinoorikilta.fi"] name = "web2.0-backend"
readme = "README.md"
requires-python = "~3.12"
version = "0.1.0"
[virtualenvs]
create = true
in-project = true
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" decorator = "^4.4.2"
decorator = "^4.0.9" Django = "^4.2.19"
Django = "^4.1"
requests = "^2.28.1"
django-cors-headers = "^3.13.0"
djangorestframework = "^3.12.4"
psycopg2-binary = "^2.9.3"
django-bootstrap3 = "^21.2"
django-tables2 = "^2.4.1"
django-modeltranslation = "^0.18.4"
django-auditlog = "^2.1.1"
django-phonenumber-field = {version = "^6.3.0", extras = ["phonenumbers"]}
django-autocomplete-light = "^3.4.1"
six = "^1.12.0"
pyexcel = "^0.7.0"
pyexcel-xlsx = "^0.6.0"
django-import-export = "^2.8.0"
openpyxl = "^2.6.4"
django-app-namespace-template-loader = "^0.4.1" django-app-namespace-template-loader = "^0.4.1"
django-filter = "^22.1" django-auditlog = "^2.1.1"
whitenoise = "^6.2.0" django-autocomplete-light = "^3.4.1"
jsonschema = "^4.9.0" django-bootstrap3 = "^21.2.0"
Markdown = "^3.2.2" django-cors-headers = "^3.13.0"
uWSGI = "^2.0.18" django-filter = "^22.1.0"
gunicorn = "^20.1.0" django-import-export = "^2.8.0"
Pillow = "^9.1.1" django-modeltranslation = "^0.18.4"
sendgrid = "^6.7.0" django-phonenumber-field = {version = "^6.4.0", extras = ["phonenumbers"]}
sentry-sdk = "^1.4.3"
django-polymorphic = "^3.1.0" django-polymorphic = "^3.1.0"
python-dotenv = "^0.20.0" django-tables2 = "^2.4.1"
djangorestframework-simplejwt = "^5.2.0" djangorestframework = "^3.12.4"
djangorestframework-simplejwt = "^5.5.0"
google-auth = "^2.9.1" google-auth = "^2.9.1"
google-api-python-client = "^2.54.0" google-api-python-client = "^2.54.0"
pyexcel-io = "^0.6.6" gunicorn = "^23.0.0"
jsonschema = "^4.9.0"
Markdown = "^3.2.2"
openpyxl = "^2.6.4"
Pillow = "^10.0.0"
psycopg2-binary = "^2.9.3"
pyexcel = "^0.7.0"
pyexcel-io = "^0.6.0"
pyexcel-xlsx = "^0.6.0"
python-dotenv = "^0.20.0"
requests = "^2.28.1"
sendgrid = "^6.7.0"
sentry-sdk = "^2.24.1"
six = "^1.12.0"
uWSGI = "^2.0.28"
whitenoise = "^6.2.0"
pyjwt = "^2.9.0"
setuptools = "^80.9.0"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
black = "^25.1.0"
coverage = "^6.4.2" coverage = "^6.4.2"
safety = "^2.1.1" safety = "^2.3.4"
black = "^22.6.0"
[tool.poetry]
package-mode = false
[tool.poetry.requires-plugins]
poetry-plugin-export = "^1.9"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
+1 -1
View File
@@ -12,5 +12,5 @@
"reportMissingImports": true, "reportMissingImports": true,
"reportMissingTypeStubs": false, "reportMissingTypeStubs": false,
"pythonVersion": "3.9" "pythonVersion": "3.12.9"
} }
+1 -1
View File
@@ -77,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%E2%80%93%20Toimihenkil%C3%B6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen. Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Suomeksi/Tietosuojaseloste%20%20Toimihenkilksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen.
{% endblocktrans %} {% endblocktrans %}
</span> </span>
<br> <br>
+2 -2
View File
@@ -5,6 +5,6 @@
{{ challenge.message }} {{ challenge.message }}
{% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 16.2" %}. {% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 12.2" %}.
{% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }} {% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }}
@@ -0,0 +1,32 @@
# Generated by Django 4.2.24 on 2025-10-13 14:48
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("webapp", "0082_delete_baserole"),
]
operations = [
migrations.AddField(
model_name="signup",
name="submit_id",
field=models.UUIDField(default=uuid.uuid4, editable=False, null=True),
),
migrations.AlterField(
model_name="basewebhook",
name="polymorphic_ctype",
field=models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="polymorphic_%(app_label)s.%(class)s_set+",
to="contenttypes.contenttype",
),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-10-13 15:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0083_signup_submit_id_alter_basewebhook_polymorphic_ctype"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-10-13 15:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0084_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.CharField(null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-10-13 15:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0085_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(editable=False, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.24 on 2025-10-13 15:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0086_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(editable=False, null=True, unique=True),
),
]
+2
View File
@@ -195,6 +195,8 @@ class Signup(models.Model):
email = models.EmailField(blank=True, null=True) email = models.EmailField(blank=True, null=True)
# Random unique identifier. Used for signup editing by the user. # Random unique identifier. Used for signup editing by the user.
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
# Random unique identifier generated by browser upon opening signup form. Used to prevent duplicate signups.
submit_id = models.UUIDField(null=True, editable=False, unique=True)
deleted = models.BooleanField(default=False) deleted = models.BooleanField(default=False)
def __str__(self): def __str__(self):
+2 -1
View File
@@ -7,6 +7,7 @@ class SignupSerializer(serializers.ModelSerializer):
source="signupForm", queryset=SignupForm.objects.all() source="signupForm", queryset=SignupForm.objects.all()
) )
list_name = serializers.CharField(read_only=True) list_name = serializers.CharField(read_only=True)
submit_id = serializers.UUIDField(required=False)
def add_extra_fields(self, validated_data): def add_extra_fields(self, validated_data):
questions = validated_data["signupForm"].questions questions = validated_data["signupForm"].questions
@@ -34,7 +35,7 @@ class SignupSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Signup model = Signup
fields = ("id", "signupForm_id", "answer", "list_name") fields = ("id", "submit_id", "signupForm_id", "answer", "list_name")
extra_kwargs = { extra_kwargs = {
"url": { "url": {
"view_name": "signup-detail", "view_name": "signup-detail",
+14
View File
@@ -1,6 +1,7 @@
"""Webapp views.""" """Webapp views."""
import json import json
import time
from jwt import decode from jwt import decode
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from django.utils import timezone from django.utils import timezone
@@ -11,6 +12,7 @@ from django.views.decorators.http import require_http_methods
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from django.db.models import Prefetch from django.db.models import Prefetch
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import IntegrityError
from rest_framework import routers from rest_framework import routers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
@@ -212,6 +214,14 @@ class SignupViewSet(ModelViewSet):
return self.partial_update(request, *args, **kwargs) return self.partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
# Temporary manual duplicate check as submit_id uniqueness is not enforced in deployment database
if "submit_id" in request.data and Signup.objects.filter(
submit_id=request.data["submit_id"]
):
return JsonResponse(
status=200, data={"message": "The submission has already been received"}
)
id = request.data["signupForm_id"] id = request.data["signupForm_id"]
try: try:
answer = request.data["answer"] answer = request.data["answer"]
@@ -226,6 +236,10 @@ class SignupViewSet(ModelViewSet):
return JsonResponse( return JsonResponse(
status=404, data={"error": f"SignupForm {id} not found"} status=404, data={"error": f"SignupForm {id} not found"}
) )
except IntegrityError:
return JsonResponse(
status=200, data={"message": "The submission has already been received"}
)
else: else:
return JsonResponse( return JsonResponse(
status=404, data={"error": f"SignupForm {id} not found"} status=404, data={"error": f"SignupForm {id} not found"}