Commit be9f84d6 authored by Nikolai R Kristiansen's avatar Nikolai R Kristiansen

Bump python deps and upgrade everything

Replace gulp with webpack
Switch to npm from yarn
Add eslint and prettier
parent 45621280
......@@ -6,4 +6,5 @@ db.sqlite3
venv
node_modules
bower_components
staticfiles
\ No newline at end of file
staticfiles
static/dist/
\ No newline at end of file
Kassa helps you register purchased membership cards and memberships. It's a tool for volunteers and bar employees at The Norwegian Student Society.
## TODO
* replace nunjucks with something that is maintained
* autoprefixer
* bootstrap 4.x
* Loading state on submit buttons
* Galtinn/LDAP auth
## Installation
apt install libldap2-dev libsasl2-dev # pyldap deps
python3 -m venv venv
. venv/bin/activate
pip install -U pip wheel
pip install -r requirements.txt
python manage.py migrate
```shell script
apt install libldap2-dev libsasl2-dev # pyldap deps
python3 -m venv venv
. venv/bin/activate
pip install -U pip wheel
pip install -r requirements.txt
python manage.py migrate
```
### Development tasks
```shell script
npm run build:templates # regenerate nunjucks templates
```
## Configuration
Create a user in Galtinn with the following permissions:
* galtinn | User | Can view User
......@@ -22,14 +31,20 @@ Create a user in Galtinn with the following permissions:
* galtinn | Order | Can view Order
Fetch GALTINN_API_KEY:
```shell script
curl -H "Content-Type: application/json" -X POST -d '{"username":"apiuser","password":"test"}' GALTINN_API_URL/auth/obtain-token/
```
If developing you can disable calls to Tekstmelding in local_settings.py:
TEKSTMELDING_ENABLED = False
```python
TEKSTMELDING_ENABLED = False
```
## LDAP
sudo docker run -e LDAP_DOMAIN=neuf.no -e LDAP_ORGANISATION="Neuf" -e LDAP_ADMIN_PASSWORD="toor" -p 389:389 -d nikolaik/openldap
ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f test/testdata.ldif # Testdata
# Verify import
ldapsearch -x -b dc=neuf,dc=no
# Login with test@example.com:test
\ No newline at end of file
```shell script
sudo docker run -e LDAP_DOMAIN=neuf.no -e LDAP_ORGANISATION="Neuf" -e LDAP_ADMIN_PASSWORD="toor" -p 389:389 -d nikolaik/openldap
ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f test/testdata.ldif # Testdata
# Verify import
ldapsearch -x -b dc=neuf,dc=no
# Login with test@example.com:test
```
from django.conf import settings
def sentry(request):
return {'SENTRY_DSN': settings.SENTRY_DSN or '', 'SENTRY_ENVIRONMENT': settings.SENTRY_ENVIRONMENT}
{% load staticfiles raven %}
{% load static %}
<!DOCTYPE html>
<html>
<head lang="en">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Registrer medlemskap{% endblock %}</title>
<link rel="shortcut icon" href="{% static "dist/images/favicon.ico" %}">
<!--<link rel="stylesheet" href="{% static "dist/styles/vendor.css" %}">-->
<link rel="stylesheet" href="{% static "dist/styles/app.css" %}">
<script src="{% static "dist/scripts/vendor.js" %}"></script>
<script src="{% static "dist/scripts/templates.js" %}"></script>
<script src="{% static "dist/scripts/app.js" %}"></script>
<script src="https://cdn.ravenjs.com/3.5.1/raven.min.js"></script>
<link rel="stylesheet" href="{% static "dist/main.css" %}">
</head>
<body>
<div class="toast-wrap"></div>
......@@ -24,11 +20,15 @@
</div>
<footer class="footer">
<div class="container">
<div class="credits"><p><a href="https://studentersamfundet.no" title="Det Norske Studentersamfund"><img src="{% static "dist/images/logo.png" %}" width="70" height="70"></a></p>
<div class="credits"><p><a href="https://studentersamfundet.no" title="Det Norske Studentersamfund"><img src="{% static "dist/images/logo.png" %}" width="70" height="70" alt="DNS logo"></a></p>
Laget med <span class="love" title="kærlighed"></span> av <a href="http://kak.studentersamfundet.no/" title="Kommunikasjonsavdelingen">KAK</a></div>
</div>
</footer>
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
{% if SENTRY_DSN %}
<script src="https://browser.sentry-cdn.com/5.10.2/bundle.min.js" integrity="sha384-ssBfXiBvlVC7bdA/VX03S88B5MwXQWdnpJRbUYFPgswlOBwETwTp6F3SMUNpo9M9" crossorigin="anonymous"></script>
<script>Sentry.init({ dsn: '{{ SENTRY_DSN }}', environment: '{{ SENTRY_ENVIRONMENT }}' });</script>
{% endif %}
<script src="{% static "dist/main.js" %}"></script>
</body>
</html>
\ No newline at end of file
from django.conf.urls import url
from django.urls import path, re_path
from apps.kassa.views import (register, user_search, check_card, check_phone_number, register_card_and_membership,
renew_membership)
urlpatterns = [
url(r'^$', register, name='register'),
url(r'^user-search/$', user_search, name='user-search'),
url(r'^check-phone-number/$', check_phone_number, name='check-phone-number'),
url(r'^check-card/$', check_card, name='check-card'),
url(r'^register-card-membership/$', register_card_and_membership, name='register-card-and-membership'),
url(r'^renew-membership/$', renew_membership, name='renew-membership'),
re_path(r'^$', register, name='register'),
path('user-search/', user_search, name='user-search'),
path('check-phone-number/', check_phone_number, name='check-phone-number'),
path('check-card/', check_card, name='check-card'),
path('register-card-membership/', register_card_and_membership, name='register-card-and-membership'),
path('renew-membership/', renew_membership, name='renew-membership'),
]
from contextlib import contextmanager as _contextmanager
from fabric.api import run, sudo, env, cd, prefix
import os
env.use_ssh_config = True
env.hosts = ['dreamcast.neuf.no']
env.project_path = '/var/www/neuf.no/kassa'
env.user = 'gitdeploy'
env.activate = 'source {}/venv/bin/activate'.format(env.project_path)
from fabric import Connection
from invoke import task
@_contextmanager
def virtualenv():
with cd(env.project_path), prefix(env.activate):
yield
@task()
def deploy(c):
"""Make sure proxy_user is set to your neuf username."""
project_path = '/var/www/neuf.no/kassa'
proxy_user = os.getenv('DEPLOY_USER', os.getenv('USER'))
c = Connection(host='gitdeploy@dreamcast.neuf.no', gateway=Connection('login.neuf.no', user=proxy_user))
def deploy():
with virtualenv():
run('git pull') # Get source
run('pip install -r requirements.txt') # install deps in virtualenv
run('umask 022; python manage.py collectstatic --noinput') # Collect static
run('python manage.py migrate') # Run DB migrations
with c.cd(project_path), c.prefix('source {}/venv/bin/activate'.format(project_path)):
c.run('git pull') # Get source
c.run('pip install -U pip')
c.run('pip install -r requirements.txt') # install deps in virtualenv
with c.cd('static'): # install and compile frontend deps
c.run('npm i')
c.run('npm run build')
c.run('python manage.py collectstatic --noinput -i node_modules') # Collect static
c.run('python manage.py migrate') # Run DB migrations
# Reload
sudo('/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP', shell=False)
# Reload gunicorn
c.sudo('/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP', shell=False)
@task
def build(c):
with c.cd('static'):
c.run('npm i')
c.run('npm run build')
......@@ -11,8 +11,6 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import raven
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
......@@ -41,20 +39,18 @@ INSTALLED_APPS = (
'bootstrapform',
'django_extensions',
'corsheaders',
'raven.contrib.django.raven_compat',
)
LOCAL_APPS = ('apps.kassa',)
INSTALLED_APPS += LOCAL_APPS
MIDDLEWARE_CLASSES = (
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
......@@ -75,6 +71,7 @@ TEMPLATES = [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'apps.kassa.context_processors.sentry',
],
},
},
......@@ -124,11 +121,6 @@ TEKSTMELDING_ACTIVATION_SMS_TEMPLATE = 'Velkommen som medlem! Registrer deg her:
CORS_ORIGIN_ALLOW_ALL = True
RAVEN_CONFIG = {
'dsn': os.getenv('RAVEN_DSN'),
'release': raven.fetch_git_sha(BASE_DIR)
}
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
......@@ -139,10 +131,6 @@ LOGGING = {
},
},
'handlers': {
'sentry': {
'level': 'WARNING', # To capture more than ERROR, change to WARNING, INFO, etc.
'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
......@@ -152,31 +140,18 @@ LOGGING = {
'loggers': {
'root': {
'level': 'WARNING',
'handlers': ['sentry', 'console'],
},
'django': {
'level': 'DEBUG',
'handlers': ['console'],
'propagate': True,
},
'django.db.backends': {
'level': 'ERROR',
'handlers': ['console'],
'propagate': False,
},
'raven': {
'level': 'DEBUG',
'handlers': ['console'],
'propagate': False,
},
'sentry.errors': {
'level': 'DEBUG',
'handlers': ['console'],
'propagate': False,
},
},
}
SENTRY_DSN = None
SENTRY_ENVIRONMENT = 'dev'
# Local settings
try:
......
from django.conf.urls import include, url
from django.contrib import admin
from django.urls import path
from django.contrib.auth import urls as auth_urls
from apps.kassa import urls as kassa_urls
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
path('admin/', admin.site.urls),
url('^', include(kassa_urls)),
url('^accounts/', include('django.contrib.auth.urls'))
path('accounts/', include(auth_urls))
]
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
{
"env": {
"browser": true
},
"extends": [
"airbnb-base",
"plugin:prettier/recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"rules": {
"no-plusplus": "off",
"no-console": "off",
"no-underscore-dangle": "off",
"no-param-reassign": "off",
"no-restricted-syntax": "off",
},
"settings": {
"import/resolver": {
"node": {}, // https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-509384041
"webpack": {
"config": "webpack.config.js"
}
}
}
,
"plugins": ["prettier"],
"parser": "babel-eslint"
}
{
"singleQuote": true,
"printWidth": 120,
"arrowParens": "always"
}
This diff is collapsed.
$icon-font-path: '../fonts/';
$icon-font-path: './fonts/';
$green-color: #5cb85c;
$red-color: #ff433d;
$yellow-color: #f8ff5b;
$active-color: darkred;
/* Vendor styles */
@import "../../bower_components/bootstrap-sass/assets/stylesheets/_bootstrap.scss";
@import "../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss";
/* Global styles */
body {
......
This diff is collapsed.
export const identity = (v) => v;
{
"name": "kassa",
"private": true,
"dependencies": {
"jquery": "3.2.1",
"underscore": "1.8.3",
"list.js": "1.5.0",
"bootstrap-sass": "3.3.7",
"nunjucks": "~3.0.1",
"moment": "~2.18.1"
},
"devDependencies": {},
"resolutions": {
"jquery": "3.2.1"
},
"overrides": {
"bootstrap-sass": {
"main": [
"assets/stylesheets/_bootstrap.scss",
"assets/fonts/bootstrap/*",
"assets/javascripts/bootstrap.js"
]
}
}
}
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
{
"name": "kassa",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "webpack -p --env production --display-error-details --mode production",
"build:templates": "echo '/* eslint-disable */' > app/templates.js && nunjucks-precompile app/templates >> app/templates.js",
"start": "webpack-dev-server --hot --env development --mode development",
"format": "eslint --fix \"app/**/*.js\"",
"lint": "eslint --quiet \"app/**/*.js\""
},
"dependencies": {
"jshint": "^2.9.5"
"bootstrap-sass": "^3.4.1",
"jquery": "^3.4.1",
"list.js": "^1.5.0",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"nunjucks": "^3.2.0"
},
"devDependencies": {
"browser-sync": "^2.7.6",
"del": "^3.0.0",
"gulp": "^3.8.11",
"gulp-autoprefixer": "^4.0.0",
"gulp-concat": "~2.6.1",
"gulp-csso": "^3.0.0",
"gulp-filter": "^5.0.0",
"gulp-flatten": "^0.3.1",
"gulp-imagemin": "^3.3.0",
"gulp-jshint": "^2.0.4",
"gulp-load-plugins": "^1.5.0",
"gulp-nunjucks": "^3.0.0",
"gulp-plumber": "~1.1.0",
"gulp-rename": "~1.2.2",
"gulp-sass": "~3.1.0",
"gulp-size": "^2.1.0",
"gulp-uglify": "^3.0.0",
"gulp-useref": "^3.1.2",
"gulp-util": "~3.0.4",
"jshint-stylish": "^2.0.0",
"main-bower-files": "^2.8.0",
"opn": "^5.1.0"
"@babel/core": "^7.7.7",
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.4",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.4.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-prettier": "^6.7.0",
"eslint-import-resolver-babel-module": "^5.1.0",
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-prettier": "^3.1.2",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.0",
"prettier": "^1.19.1",
"sass-loader": "^8.0.0",
"style-loader": "^1.1.2",
"url-loader": "^3.0.0",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
},
"engines": {
"node": ">=0.10.0"
"node": "12"
}
}
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const baseStyleLoader = (env) => (env === 'production' ? MiniCssExtractPlugin.loader : require.resolve('style-loader'));
module.exports = (env, argv) => {
return {
entry: './app/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
extensions: ['.jsx', '.js', '.json', '.scss', '.css'],
modules: ['node_modules']
},
plugins: [
new MiniCssExtractPlugin(),
env === 'production' ? new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) : null,
env !== 'production' ? new HtmlWebpackPlugin({ template: './app/index.html' }) : null,
new webpack.ProvidePlugin({
jQuery: 'jquery'
})
].filter(Boolean),
module: {
rules: [
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: require.resolve('file-loader'),
options: {}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: require.resolve('file-loader')
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: require.resolve('babel-loader')
},
{
test: /\.css$/,
loaders: [baseStyleLoader(env), require.resolve('css-loader')]
},
{
test: /\.scss$/,
loaders: [
baseStyleLoader(env),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: false,
sourceMap: true
}
},
{
loader: require.resolve('sass-loader'),
options: {
sassOptions: {
precision: 8
}
}
}
]
}
]
},
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
historyApiFallback: true,
overlay: {
errors: true,
warnings: false
},
port: 8001,
publicPath: '/',
proxy: {
'/api': 'http://localhost:8000'
// TODO: get urls
},
stats: {
assets: false,
modules: false,
chunks: false
}
},
devtool: env === 'production' ? 'source-map' : 'cheap-module-eval-source-map'
};
};
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment