commit 145978beef7a285461eb33e31816342373c79c2d
parent 278e5554d0c6a20fbd5dddd162bd67ab5bd29254
Author: Oshgnacknak <osh@oshgnacknak.de>
Date: Sat, 7 Mar 2020 19:16:06 +0100
Added public routes + Cleaned up environment variables via Docker
Diffstat:
8 files changed, 189 insertions(+), 50 deletions(-)
diff --git a/Dockerfile b/Dockerfile
@@ -5,6 +5,11 @@ WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
+ENV COOKIE_PASSWORD 'CHANGE ME ASAP'
+ENV MONGO_URL 'mongodb://mongo/SMTL'
+ENV MINOR_BIRTH_YEAR 2002
+ENV SENIOR_BIRTH_YEAR 1961
+
EXPOSE 3000
COPY . .
diff --git a/PlayerRoute.js b/PlayerRoute.js
@@ -0,0 +1,40 @@
+const Player = require('./models/Player')
+
+const birthAttr = year => {
+ if (year >= process.env.MINOR_BIRTH_YEAR) {
+ return 'J'
+ } else if (year < process.env.SENIOR_BIRTH_YEAR) {
+ return 'S'
+ }
+ return ''
+}
+
+const genderAttr = gender => {
+ return gender === 'FEMALE' ? 'W' : ''
+}
+
+module.exports = app => {
+
+ app.route('/players')
+
+ .get((reg, res) =>
+ Player.find({ approved: true })
+ .sort({ dwz: -1 })
+ .then(players =>
+ players.map(player => {
+ const { name, birth_year, gender, club, dwz } = player;
+ const attr = birthAttr(birth_year) + genderAttr(gender)
+ return { name, club, dwz, attr }
+ }))
+ .then(data => res.json(data))
+ .catch(err => console.log(err))
+ )
+
+ .post((req, res) => {
+ const { name, email, birth_year, gender, club, dwz } = req.body;
+ new Player({ name, email, birth_year, gender, club, dwz })
+ .save()
+ .then(() => res.json({}))
+ .catch(err => res.status(400).send(err))
+ })
+}
diff --git a/README.md b/README.md
@@ -2,3 +2,11 @@
This webapp is for for the 'Stadtmeisterschaft' players list and the singup.
+## Docker
+
+### Environment Variables
+
+- COOKIE_PASSWORD: secret password to encrypt cookies
+- MINOR_BIRTH_YEAR: Year of birth at which players count a being minor
+- SENIOR_BIRTH_YEAR: Year of birth before which some players count a being senior
+- MONGO_URL: Url to connect to mongo database
diff --git a/admin.js b/admin.js
@@ -56,11 +56,10 @@ const adminBro = new AdminBro({
}
})
-const cookiePassword = process.env.COOKIE_PASSWORD
-if (!cookiePassword) {
+const { COOKIE_PASSWORD } = process.env
+if (!COOKIE_PASSWORD) {
throw new Error('process.env.COOKIE_PASSWORD must be set')
}
-
module.exports = AdminBroExpress.buildAuthenticatedRouter(adminBro, {
authenticate: async (email, password) => {
const user = await User.findOne({ email })
@@ -72,5 +71,5 @@ module.exports = AdminBroExpress.buildAuthenticatedRouter(adminBro, {
}
return false
},
- cookiePassword: cookiePassword
+ cookiePassword: COOKIE_PASSWORD
})
diff --git a/docker-compose.yml b/docker-compose.yml
@@ -1,12 +1,12 @@
version: '3.1'
services:
- mongo:
- image: mongo
- restart: always
- smtl:
- build: .
- environment:
- COOKIE_PASSWORD: 'Pm9yr39FZamjga8fPUy34/4g7rm0e8uaTqsIxZ/U7TE='
- ports:
- - '3000:3000'
+ mongo:
+ image: mongo
+ restart: always
+ smtl:
+ build: .
+ volumes:
+ - './public:/usr/src/app/public'
+ ports:
+ - '3000:3000'
diff --git a/index.js b/index.js
@@ -6,16 +6,12 @@ const favicon = require('serve-favicon')
const bcrypt = require('bcrypt')
const User = require('./models/User')
-const Player = require('./models/Player')
-const PORT = process.env.PORT || 3000
-const MINOR_BIRTH_YEAR = process.env.MINOR_BIRTH_YEAR || 2002
-const SENIOR_BIRTH_YEAR = process.env.SENIOR_BIRTH_YEAR || 1961
-const MONGO_URL = process.env.MONGO_URL || 'mongodb://mongo/SMTL'
+const PORT = 3000
const app = express()
mongoose.Promise = global.Promise
-mongoose.connect(MONGO_URL, { useNewUrlParser: true })
+mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true })
.then(() => {
console.log('MongoDB Connected successfully...')
return User.countDocuments({})
@@ -40,36 +36,7 @@ app.use('/admin', require('./admin'))
app.use(favicon('public/vereinslogo1.gif'))
app.use(bodyParser.json())
-const birthAttr = year => {
- if (year >= MINOR_BIRTH_YEAR) {
- return 'J'
- } else if (year < SENIOR_BIRTH_YEAR) {
- return 'S'
- }
- return ''
-}
-const genderAttr = gender => {
- return gender === 'FEMALE' ? 'W' : ''
-}
-app.route('/players')
- .get((reg, res) =>
- Player.find({ approved: true })
- .then(players =>
- players.map(player => {
- const { name, birth_year, gender, club, dwz } = player;
- const attr = birthAttr(birth_year) + genderAttr(gender)
- return { name, club, dwz, attr }
- }))
- .then(data => res.json(data))
- .catch(err => console.log(err))
- )
- .post((req, res) => {
- const { name, email, birth_year, gender, club, dwz } = req.body;
- new Player({ name, email, birth_year, gender, club, dwz })
- .save()
- .then(() => res.json({}))
- .catch(err => res.status(400).send(err))
- })
+require('./PlayerRoute')(app)
app.listen(PORT, () => {
console.log('SMTL server started on: ' + PORT)
diff --git a/models/Player.js b/models/Player.js
@@ -19,7 +19,6 @@ const PlayerSchema = new Schema({
type: Number,
required: true,
min: 1,
- max: 2020,
validate: [ Number.isInteger ]
},
gender: {
diff --git a/public/index.html b/public/index.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
+ <title>Langener Stadtmeisterschaft 2020</title>
+
+ <style>
+ h1 {
+ text-align: center;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <h1>Langener Stadtmeisterschaft 2020</h1>
+
+ <h2>Teilnehmerliste</h2>
+
+ <table class="table table-responsive">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Verein</th>
+ <th>DWZ</th>
+ <th>Attr.</th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+ </table>
+
+ <h2>Anmeldeformular</h2>
+
+ <form>
+ <div class="form-group">
+ <label for="email">Email Adresse</label>
+ <input type="email" class="form-control" name="email" required="true" aria-describedby="emailHelp" placeholder="email@example.com">
+ <small id="emailHelp" class="form-text text-muted">Ihre Email Adresse wird nicht veröffentlicht</small>
+ </div>
+ <div class="form-group">
+ <label for="name">Name</label>
+ <input type="text" class="form-control" name="name" required="true" placeholder="Name, Vorname" />
+ </div>
+ <div class="form-group">
+ <label for="birth_year">Geburtsjahr</label>
+ <input type="number" class="form-control" name="birth_year" required="true" placeholder="1648" />
+ </div>
+ <div class="form-group">
+ <label for="gender">Geschlecht</label>
+ <select class="form-control" name="gender">
+ <option value="MALE">Männlich</option>
+ <option value="FIMALE">Weiblich</option>
+ <option value="OTHER">Divers</option>
+ </select>
+ </div>
+ <div class="form-group">
+ <label for="club">Verein</label>
+ <input type="text" class="form-control" name="club" placeholder="SK Langen" />
+ </div>
+ <div class="form-group">
+ <label for="dwz">DWZ</label>
+ <input type="number" class="form-control" name="dwz" value="0" />
+ </div>
+ <button type="submit" class="btn btn-primary">Teilnehmen</button>
+ </form>
+ </div>
+
+ <script defer>
+ const table = document.querySelector('table tbody')
+
+ fetch('/players?no_cache=' + Date.now())
+ .then(res => res.json())
+ .then(players => {
+ table.innerHTML = ''
+ players.forEach(player => {
+ const tr = document.createElement('tr');
+ [ 'name', 'club', 'dwz', 'attr' ].forEach(key => {
+ const td = document.createElement('td')
+ td.innerText = player[key]
+ tr.appendChild(td)
+ })
+ table.appendChild(tr)
+ })
+ })
+
+ const form = document.querySelector('form')
+ form.addEventListener('submit', e => {
+ e.preventDefault()
+ const data = Object.fromEntries(new FormData(form))
+
+ fetch('/players', {
+ method: 'POST',
+ body: JSON.stringify(data),
+ headers: { 'content-type': 'application/json' }
+ })
+ .then(res => {
+ if (!res.ok) {
+ throw res
+ }
+ return res.json()
+ })
+ .then(res => {
+ alert('Ihre Teilnahme wurde erfolgreich erfasst!\nBitte haben Sie Geduld, bis sie bearbeitet wurde.')
+ form.reset()
+ })
+ .catch(err => err.json().then(errors => {
+ Array.from(form.elements)
+ .filter(e => errors.errors.hasOwnProperty(e.name))
+ .forEach(e => {
+ e.setCustomValidity(errors.errors[e.name].message)
+ e.checkValidity();
+ e.addEventListener('change', () => e.setCustomValidity(''))
+ });
+ }))
+ })
+
+ </script>
+ </body>
+</html>