Compare commits
16 Commits
master
...
feature/we
Author | SHA1 | Date |
---|---|---|
|
93a86f8122 | |
|
3c633d23ea | |
|
22587e4e2a | |
|
437ebf3e25 | |
|
7548ddcbc5 | |
|
fc0b06a1be | |
|
ac951aba32 | |
|
fb4e3c0e46 | |
|
e9f19fbddf | |
|
d0042f196e | |
|
c32db349d9 | |
|
d7a42d03f7 | |
|
392cda25ea | |
|
594c0f54a9 | |
|
c45be9a476 | |
|
202c35b517 |
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
"babel-plugin-transform-runtime",
|
||||||
|
"babel-plugin-dynamic-import-node"
|
||||||
|
]
|
||||||
|
}
|
|
@ -22,10 +22,15 @@ build/Release
|
||||||
# Dependency directory
|
# Dependency directory
|
||||||
# Deployed apps should consider commenting this line out:
|
# Deployed apps should consider commenting this line out:
|
||||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||||
node_modules
|
**/node_modules/**
|
||||||
|
!**/node_modules/.gitkeep
|
||||||
|
|
||||||
# Webpack output
|
# Webpack output
|
||||||
/dist
|
packages/*/dist
|
||||||
|
packages/*/lib
|
||||||
|
|
||||||
|
# Intermediate build files (cache, etc.)
|
||||||
|
/build
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,12 @@ build/Release
|
||||||
# Dependency directory
|
# Dependency directory
|
||||||
# Deployed apps should consider commenting this line out:
|
# Deployed apps should consider commenting this line out:
|
||||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||||
node_modules
|
**/node_modules/**
|
||||||
|
!**/node_modules/.gitkeep
|
||||||
|
|
||||||
# Webpack output
|
# Webpack output
|
||||||
/dist
|
packages/*/dist
|
||||||
|
packages/*/lib
|
||||||
|
|
||||||
|
# Intermediate build files (cache, etc.)
|
||||||
|
/build
|
||||||
|
|
|
@ -12,8 +12,19 @@ env:
|
||||||
es6: true
|
es6: true
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
no-underscore-dangle: 'off'
|
no-console: off
|
||||||
no-plusplus:
|
no-plusplus:
|
||||||
- error
|
- error
|
||||||
- allowForLoopAfterthoughts: true
|
- allowForLoopAfterthoughts: true
|
||||||
|
no-underscore-dangle: 'off'
|
||||||
react/no-array-index-key: 0
|
react/no-array-index-key: 0
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
- files:
|
||||||
|
- "config/**"
|
||||||
|
- "**/webpack.config*.js"
|
||||||
|
- "**/.babelrc*"
|
||||||
|
rules:
|
||||||
|
import/no-extraneous-dependencies:
|
||||||
|
- error
|
||||||
|
- devDependencies: true
|
||||||
|
|
|
@ -20,10 +20,14 @@ coverage
|
||||||
build/Release
|
build/Release
|
||||||
|
|
||||||
# Dependency directory
|
# Dependency directory
|
||||||
# Deployed apps should consider commenting these line out:
|
# Deployed apps should consider commenting this line out:
|
||||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||||
node_modules/**
|
**/node_modules/**
|
||||||
!node_modules/.gitkeep
|
!**/node_modules/.gitkeep
|
||||||
|
|
||||||
# Webpack output
|
# Webpack output
|
||||||
/dist
|
packages/*/dist
|
||||||
|
packages/*/lib
|
||||||
|
|
||||||
|
# Intermediate build files (cache, etc.)
|
||||||
|
/build
|
||||||
|
|
32
.travis.yml
|
@ -1,16 +1,16 @@
|
||||||
sudo: false
|
# sudo: false
|
||||||
|
#
|
||||||
language: node_js
|
# language: node_js
|
||||||
node_js:
|
# node_js:
|
||||||
- 6
|
# - 6
|
||||||
|
#
|
||||||
before_install:
|
# before_install:
|
||||||
- npm install codecov.io coveralls
|
# - npm install codecov.io coveralls
|
||||||
|
#
|
||||||
after_success:
|
# after_success:
|
||||||
- cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
|
# - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
|
||||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
# - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||||
|
#
|
||||||
branches:
|
# branches:
|
||||||
only:
|
# only:
|
||||||
- master
|
# - master
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
FROM icedream/caddy
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
COPY ./dist/ /data
|
|
|
@ -1,29 +1,29 @@
|
||||||
node("docker && linux && amd64") {
|
// node("docker && linux && amd64") {
|
||||||
checkout scm
|
// checkout scm
|
||||||
|
//
|
||||||
docker.image("node:8.2.1").inside {
|
// docker.image("node:8.2.1").inside {
|
||||||
// Install dependencies
|
// // Install dependencies
|
||||||
sh "npm install"
|
// sh "npm install"
|
||||||
|
//
|
||||||
// Build website with npm
|
// // Build website with npm
|
||||||
sh "npm run build"
|
// sh "npm run build"
|
||||||
archive "dist/**"
|
// archive "dist/**"
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Build docker image to be deployed
|
// // Build docker image to be deployed
|
||||||
def image = docker.build("docker.dreamnetwork.oss:5000/icedream/carl-kittelberger-website:${env.BRANCH_NAME ?: "latest"}", "-f Dockerfile.dist .")
|
// def image = docker.build("docker.dreamnetwork.oss:5000/icedream/carl-kittelberger-website:${env.BRANCH_NAME ?: "latest"}", "-f Dockerfile.dist .")
|
||||||
|
//
|
||||||
// @NOTE - https://issues.jenkins-ci.org/browse/JENKINS-42152?focusedCommentId=307976&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-307976
|
// // @NOTE - https://issues.jenkins-ci.org/browse/JENKINS-42152?focusedCommentId=307976&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-307976
|
||||||
image.push()
|
// image.push()
|
||||||
if (env.BRANCH_NAME) {
|
// if (env.BRANCH_NAME) {
|
||||||
switch(env.BRANCH_NAME) {
|
// switch(env.BRANCH_NAME) {
|
||||||
case "master":
|
// case "master":
|
||||||
image.push("latest")
|
// image.push("latest")
|
||||||
break
|
// break
|
||||||
default:
|
// default:
|
||||||
image.push()
|
// image.push()
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
image.push("${sh(script: "git describe --tags --always", returnStdout: true).trim()}")
|
// image.push("${sh(script: "git describe --tags --always", returnStdout: true).trim()}")
|
||||||
}
|
// }
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import autoprefixer from 'autoprefixer';
|
||||||
|
|
||||||
|
export default class Environment {
|
||||||
|
constructor(options) {
|
||||||
|
this.development = true;
|
||||||
|
this.production = false;
|
||||||
|
this.server = false;
|
||||||
|
|
||||||
|
this.autoprefixerTargets = [
|
||||||
|
'> 1%',
|
||||||
|
'last 4 versions',
|
||||||
|
'Firefox ESR',
|
||||||
|
'ios >= 8',
|
||||||
|
];
|
||||||
|
this.locales = ['en'];
|
||||||
|
|
||||||
|
if (options !== undefined && options !== null) {
|
||||||
|
this.input(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input(options) {
|
||||||
|
if (options) {
|
||||||
|
switch (true) {
|
||||||
|
case typeof (options) === 'string': // string
|
||||||
|
this.inputString(options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Array.isArray(options): // array
|
||||||
|
options.forEach((arg) => { this.input(arg); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // object
|
||||||
|
Object.keys(options).forEach((k) => {
|
||||||
|
this[k] = options[k] || this[k];
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (process.env.NODE_ENV) {
|
||||||
|
this.inputString(process.env.NODE_ENV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputString(env) {
|
||||||
|
switch (env.toLowerCase()) {
|
||||||
|
case 'development':
|
||||||
|
this.development = true;
|
||||||
|
this.production = false;
|
||||||
|
break;
|
||||||
|
case 'production':
|
||||||
|
this.development = false;
|
||||||
|
this.production = true;
|
||||||
|
break;
|
||||||
|
case 'server':
|
||||||
|
this.server = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('Unknown environment:', env);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
styleLoaders(...preprocessingLoaders) {
|
||||||
|
const {
|
||||||
|
production,
|
||||||
|
autoprefixerTargets,
|
||||||
|
server,
|
||||||
|
|
||||||
|
ExtractTextPlugin, // @HACK
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
if (!ExtractTextPlugin) {
|
||||||
|
throw new Error('Need a valid ExtractTextPlugin fed into the environment object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let cssLoaders = [
|
||||||
|
{
|
||||||
|
loader: 'style-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
importLoaders: 1,
|
||||||
|
sourceMap: true,
|
||||||
|
modules: true,
|
||||||
|
localIdentName: production
|
||||||
|
? '[name]__[local]--[hash:base64:5]'
|
||||||
|
: '[name]__[local]--[hash:base64:5]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
ident: 'postcss',
|
||||||
|
plugins: [
|
||||||
|
autoprefixer({
|
||||||
|
browsers: autoprefixerTargets,
|
||||||
|
grid: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
sourceMap: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].filter(loader => loader !== false);
|
||||||
|
|
||||||
|
if (preprocessingLoaders && preprocessingLoaders.length > 0) {
|
||||||
|
cssLoaders.push(
|
||||||
|
{
|
||||||
|
loader: 'resolve-url-loader',
|
||||||
|
options: {
|
||||||
|
fail: true,
|
||||||
|
silent: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...preprocessingLoaders.map(loader => Object.assign({}, loader, {
|
||||||
|
options: Object.assign({}, loader.options || {}, {
|
||||||
|
sourceMap: true,
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
const fallback = cssLoaders.shift();
|
||||||
|
cssLoaders = ExtractTextPlugin.extract({
|
||||||
|
fallback,
|
||||||
|
use: cssLoaders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return cssLoaders;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Plugin for HtmlPlugin which inlines content for an extracted Webpack manifest
|
||||||
|
* into the HTML in a <script> tag before other emitted asssets are injected by
|
||||||
|
* HtmlPlugin itself.
|
||||||
|
*/
|
||||||
|
export default function injectManifestPlugin() {
|
||||||
|
this.plugin('compilation', (compilation) => {
|
||||||
|
compilation.plugin('html-webpack-plugin-before-html-processing', (data, cb) => {
|
||||||
|
Object.keys(compilation.assets).forEach((key) => {
|
||||||
|
if (!key.startsWith('manifest.')) { return; }
|
||||||
|
const { children } = compilation.assets[key];
|
||||||
|
if (children && children[0]) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data.html = data.html.replace(/^(\s*)<\/body>/m, `$1<script>${children[0]._value}</script>\n$1</body>`);
|
||||||
|
// Remove the manifest from HtmlPlugin's assets to prevent a <script>
|
||||||
|
// tag being created for it.
|
||||||
|
const manifestIndex = data.assets.js.indexOf(data.assets.publicPath + key);
|
||||||
|
data.assets.js.splice(manifestIndex, 1);
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
delete data.assets.chunks.manifest;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cb(null, data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -28,13 +28,16 @@ services:
|
||||||
build: docker/node
|
build: docker/node
|
||||||
command: sh ./docker/web.sh
|
command: sh ./docker/web.sh
|
||||||
volumes:
|
volumes:
|
||||||
- ".:/src:ro"
|
- ".:/src"
|
||||||
- "web_npm_cache:/var/cache/npm"
|
- "web_npm_cache:/var/cache/npm"
|
||||||
- "/src/node_modules"
|
- "/src/node_modules"
|
||||||
|
- "/src/packages/frontend/node_modules"
|
||||||
|
- "/src/packages/backend/node_modules"
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
working_dir: /src
|
working_dir: /src
|
||||||
environment:
|
environment:
|
||||||
|
NODE_ENV: development
|
||||||
NPM_CONFIG_CACHE: /var/cache/npm
|
NPM_CONFIG_CACHE: /var/cache/npm
|
||||||
PORT: "3000"
|
PORT: "3000"
|
||||||
MYSQL_HOST: db
|
MYSQL_HOST: db
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:7-alpine
|
FROM node:8.2-alpine
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
|
@ -11,7 +11,7 @@ RUN \
|
||||||
tar \
|
tar \
|
||||||
make
|
make
|
||||||
|
|
||||||
RUN npm install npm@5.x \
|
# RUN npm install npm@5.x \
|
||||||
&& rm -rf /usr/local/lib/node_modules \
|
# && rm -rf /usr/local/lib/node_modules \
|
||||||
&& mv node_modules /usr/local/lib \
|
# && mv node_modules /usr/local/lib \
|
||||||
&& npm install -g npm@5.x
|
# && npm install -g npm@5.x
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
npm install
|
npm install --unsafe-perm
|
||||||
npm start
|
npm start
|
||||||
|
|
34
index.js
|
@ -1,34 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
const express = require('express');
|
|
||||||
const fs = require('fs');
|
|
||||||
const bodyParser = require('body-parser');
|
|
||||||
|
|
||||||
const frontendDir = path.resolve(__dirname, 'dist');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.set('port', (process.env.PORT || 3000));
|
|
||||||
|
|
||||||
fs.stat(frontendDir, (err) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(`Serving static build from ${frontendDir}`);
|
|
||||||
console.log('Run `npm run clean` to return to development mode');
|
|
||||||
app.use('/', express.static(frontendDir));
|
|
||||||
} else {
|
|
||||||
console.log('Serving development build with nwb middleware');
|
|
||||||
console.log('Run `npm run build` to create a production build');
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
||||||
app.use(require('nwb/express')(express, {
|
|
||||||
entry: 'src/index.jsx',
|
|
||||||
reload: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
|
||||||
|
|
||||||
app.listen(app.get('port'), () => {
|
|
||||||
console.log(`Server started: http://localhost:${app.get('port')}/`);
|
|
||||||
});
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"lerna": "2.1.0",
|
||||||
|
"packages": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"version": "0.2.0"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
This file is placed here to avoid Docker from setting up a root-owned host
|
||||||
|
volume directory here.
|
105
nwb.config.js
|
@ -1,105 +0,0 @@
|
||||||
const { ContextReplacementPlugin } = require('webpack');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
type: 'react-app',
|
|
||||||
|
|
||||||
babel: {
|
|
||||||
loose: false,
|
|
||||||
presets: [
|
|
||||||
'babel-preset-env',
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
'babel-plugin-transform-class-properties',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
webpack: {
|
|
||||||
aliases: {
|
|
||||||
'moment-timezone': 'moment-timezone/builds/moment-timezone-with-data-2012-2022.js',
|
|
||||||
},
|
|
||||||
|
|
||||||
autoprefixer: '> 1%, last 4 versions, Firefox ESR, ios >= 8',
|
|
||||||
|
|
||||||
config(config) {
|
|
||||||
// Change config as you wish
|
|
||||||
|
|
||||||
// Select rules that match stylesheet files
|
|
||||||
const matchingLoaders = [
|
|
||||||
'sass-loader',
|
|
||||||
].map(require.resolve);
|
|
||||||
|
|
||||||
const newConfig = Object.assign({}, config);
|
|
||||||
newConfig.module.rules = config.module.rules.map((rule) => {
|
|
||||||
let enableSourceMap = false;
|
|
||||||
if (!rule.use) {
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
return Object.assign({}, rule, {
|
|
||||||
use: rule.use.reduce((result, loaderDescriptor) => {
|
|
||||||
const resolvedLoader = require.resolve(loaderDescriptor.loader);
|
|
||||||
|
|
||||||
// Make sure resolve-url-loader is inserted right before compatible loaders
|
|
||||||
if (matchingLoaders.indexOf(resolvedLoader) >= 0) {
|
|
||||||
result.push({
|
|
||||||
loader: require.resolve('resolve-url-loader'),
|
|
||||||
options: {
|
|
||||||
fail: true,
|
|
||||||
silent: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
enableSourceMap = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalLoaderDescriptor = Object.assign({}, loaderDescriptor);
|
|
||||||
|
|
||||||
// Avoid PostCSS discarding all source map information
|
|
||||||
if (enableSourceMap || resolvedLoader === require.resolve('postcss-loader')) {
|
|
||||||
finalLoaderDescriptor.options = finalLoaderDescriptor.options || {};
|
|
||||||
finalLoaderDescriptor.options.sourceMap = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(finalLoaderDescriptor);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, []),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return newConfig;
|
|
||||||
},
|
|
||||||
|
|
||||||
extra: {
|
|
||||||
plugins: [
|
|
||||||
new ContextReplacementPlugin(/moment[/\\]locale$/, /en\.js/),
|
|
||||||
],
|
|
||||||
|
|
||||||
resolve: {
|
|
||||||
extensions: [
|
|
||||||
'.jsx',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
publicPath: '',
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
babel: {
|
|
||||||
test: /\.jsx?/,
|
|
||||||
},
|
|
||||||
'sass-css': {
|
|
||||||
modules: true,
|
|
||||||
localIdentName: '[name]__[local]__[hash:base64:5]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// @HACK - workaround for https://github.com/webpack/webpack/issues/1866
|
|
||||||
|
|
||||||
function batchresolve(arr) {
|
|
||||||
return arr.map(require.resolve);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.babel.presets = batchresolve(module.exports.babel.presets);
|
|
||||||
module.exports.babel.plugins = batchresolve(module.exports.babel.plugins);
|
|
||||||
|
|
61
package.json
|
@ -1,24 +1,26 @@
|
||||||
{
|
{
|
||||||
"name": "vizon-countdown-website",
|
"name": "@vizonweb/root",
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"description": "Website for a countdown to the next draw on VIzon",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "./index.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nwb build-react-app ./src --preact",
|
"build": "lerna run build",
|
||||||
"clean": "nwb clean-app",
|
"clean": "lerna run clean",
|
||||||
"docker-compose": "docker-compose -f docker-compose.local.yml",
|
"docker-compose": "docker-compose -f docker-compose.local.yml",
|
||||||
"docker:down:clean": "npm run -s docker:down -- --rmi all -v",
|
"docker:down:clean": "npm run -s docker:down -- --rmi all -v",
|
||||||
"docker:down": "npm run -s docker-compose -- down",
|
"docker:down": "npm run -s docker-compose -- down",
|
||||||
|
"docker:logs": "npm run -s docker-compose -- logs",
|
||||||
|
"docker:logs:follow": "npm run -s docker:logs -- -f",
|
||||||
|
"docker:restart": "npm run -s docker-compose -- restart",
|
||||||
"docker:up:daemon": "npm run -s docker:up -- -d",
|
"docker:up:daemon": "npm run -s docker:up -- -d",
|
||||||
"docker:up": "npm run -s docker-compose -- up --build",
|
"docker:up": "npm run -s docker-compose -- up --build",
|
||||||
"docker": "npm run -s docker:up",
|
"docker": "npm run -s docker:up",
|
||||||
|
"lerna": "lerna",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"prepare": "npm run -s build",
|
"postinstall": "lerna bootstrap --hoist",
|
||||||
"start": "node .",
|
"start": "cd packages/backend && npm run start",
|
||||||
"test:coverage": "nwb test-react --coverage",
|
"test:coverage": "lerna run test:coverage",
|
||||||
"test:watch": "nwb test-react --server",
|
"test:watch": "lerna run test:watch",
|
||||||
"test": "nwb test-react"
|
"test": "lerna run test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -31,43 +33,24 @@
|
||||||
"draw",
|
"draw",
|
||||||
"countdown"
|
"countdown"
|
||||||
],
|
],
|
||||||
"bin": "./index.js",
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"index.js",
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^7.2.3",
|
"autoprefixer": "^7.1.2",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"chalk": "^2.1.0",
|
||||||
"babel-preset-env": "^1.6.0",
|
"eslint": "^4.5.0",
|
||||||
"eslint-config-airbnb": "^15.1.0",
|
"eslint-config-airbnb": "^15.1.0",
|
||||||
"eslint-plugin-babel": "^4.1.2",
|
"eslint-plugin-babel": "^4.1.2",
|
||||||
"eslint-plugin-import": "^2.7.0",
|
"eslint-plugin-import": "^2.7.0",
|
||||||
"eslint-plugin-json": "^1.2.0",
|
"eslint-plugin-json": "^1.2.0",
|
||||||
"eslint-plugin-jsx-a11y": "^5.1.1",
|
"eslint-plugin-jsx-a11y": "5.x",
|
||||||
"eslint-plugin-react": "^7.2.1",
|
"eslint-plugin-react": "^7.3.0",
|
||||||
"eslint": "^4.5.0",
|
"lerna": "^2.1.0",
|
||||||
"file-loader": "^0.11.2",
|
"nodemon": "^1.11.0",
|
||||||
"moment-timezone": "^0.5.13",
|
"rimraf": "^2.6.1",
|
||||||
"normalize-scss": "^7.0.0",
|
"slash": "^1.0.0"
|
||||||
"nwb-sass": "^0.8.1",
|
|
||||||
"nwb": "^0.18.10",
|
|
||||||
"preact-compat": "^3.17.0",
|
|
||||||
"preact": "^8.2.1",
|
|
||||||
"prop-types": "^15.5.10",
|
|
||||||
"react-dom": "^15.6.1",
|
|
||||||
"react-fontawesome": "^1.6.1",
|
|
||||||
"react-helmet": "^5.1.3",
|
|
||||||
"react-router": "^4.1.2",
|
|
||||||
"react": "^15.6.1",
|
|
||||||
"resolve-url-loader": "^2.1.0",
|
|
||||||
"webfontloader": "^1.6.28"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.17.2",
|
"moment-timezone": "^0.5.13"
|
||||||
"express": "^4.15.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es6": {
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"spec": true,
|
||||||
|
"debug": false,
|
||||||
|
"modules": false
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"spec": true,
|
||||||
|
"debug": false,
|
||||||
|
"modules": "commonjs"
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
"babel-plugin-dynamic-import-node"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package-lock=true
|
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"name": "@vizonweb/backend",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"description": "VIzon lottery website",
|
||||||
|
"private": true,
|
||||||
|
"main": "./dist/node/index.js",
|
||||||
|
"jsnext:main": "./dist/es6/index.js",
|
||||||
|
"module": "./dist/es6/index.js",
|
||||||
|
"bin": {
|
||||||
|
"vizonweb": "./dist/node/index.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build:es6": "bnr build:es6",
|
||||||
|
"build:node": "bnr build:node",
|
||||||
|
"build": "run-p build:*",
|
||||||
|
"clean": "rimraf build dist",
|
||||||
|
"start": "bnr start",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"prepublishOnly": "npm run -s build"
|
||||||
|
},
|
||||||
|
"betterScripts": {
|
||||||
|
"start": {
|
||||||
|
"command": "babel-node ./src",
|
||||||
|
"env": {
|
||||||
|
"BABEL_ENV": "node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build:es6": {
|
||||||
|
"command": "babel --out-dir dist/es6 src/",
|
||||||
|
"env": {
|
||||||
|
"BABEL_ENV": "es6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build:node": {
|
||||||
|
"command": "babel --out-dir dist/node src/",
|
||||||
|
"env": {
|
||||||
|
"BABEL_ENV": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.icedream.tech:2222/icedream/vizon-countdown-website.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"website",
|
||||||
|
"vizon",
|
||||||
|
"rizon",
|
||||||
|
"draw",
|
||||||
|
"countdown",
|
||||||
|
"backend"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-cli": "^6.26.0",
|
||||||
|
"babel-eslint": "^7.2.3",
|
||||||
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
|
"babel-preset-env": "^1.6.0",
|
||||||
|
"better-npm-run": "^0.1.0",
|
||||||
|
"chalk": "^2.1.0",
|
||||||
|
"debug": "^3.0.1",
|
||||||
|
"eslint": "^4.5.0",
|
||||||
|
"eslint-config-airbnb": "^15.1.0",
|
||||||
|
"eslint-plugin-babel": "^4.1.2",
|
||||||
|
"eslint-plugin-import": "^2.7.0",
|
||||||
|
"eslint-plugin-json": "^1.2.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "5.x",
|
||||||
|
"nodemon": "^1.11.0",
|
||||||
|
"npm-run-all": "^4.1.0",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"slash": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.17.2",
|
||||||
|
"express": "^4.15.4",
|
||||||
|
"moment-timezone": "^0.5.13",
|
||||||
|
"mysql": "^2.14.1",
|
||||||
|
"@vizonweb/frontend": "0.2.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
import path from 'path';
|
||||||
|
import mysql from 'mysql';
|
||||||
|
import express from 'express';
|
||||||
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
|
// const fs = require('fs');
|
||||||
|
import frontend from '@vizonweb/frontend';
|
||||||
|
|
||||||
|
import bodyParser from 'body-parser';
|
||||||
|
|
||||||
|
/* Database setup */
|
||||||
|
|
||||||
|
const dbPool = mysql.createPool({
|
||||||
|
host: process.env.MYSQL_HOST || 'localhost',
|
||||||
|
user: process.env.MYSQL_USER || 'root',
|
||||||
|
password: process.env.MYSQL_PASSWORD || undefined,
|
||||||
|
database: process.env.MYSQL_DATABASE || 'vizon',
|
||||||
|
socketPath: process.env.MYSQL_SOCKET_PATH || undefined,
|
||||||
|
timezone: process.env.TZ || 'local',
|
||||||
|
|
||||||
|
debug: process.env.NODE_ENV === 'development',
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableNames = {
|
||||||
|
drawings: process.env.MYSQL_TABLE_DRAWINGS || 'vizon_drawings',
|
||||||
|
users: process.env.MYSQL_TABLE_USERS || 'vizon_users',
|
||||||
|
rankings: 'vizon_web_rankings',
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Frontend server setup */
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.set('port', (process.env.PORT || 3000));
|
||||||
|
|
||||||
|
app.use('/', frontend());
|
||||||
|
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Additional middleware which will set headers that we need on each request.
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
// Disable caching so we'll always get the latest comments.
|
||||||
|
res.setHeader('Cache-Control', 'no-cache');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
function calculateRankings(id) {
|
||||||
|
db.query('SELECT * FROM `vizon_web_rankings` WHERE `vizon_drawings_id`=?',
|
||||||
|
[id],
|
||||||
|
(err, results, fields) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Failed to query rankings from database:', err);
|
||||||
|
res.status(500).json({
|
||||||
|
error: 'Database query failed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
app.post('/api/result/:id', (req, res) => {
|
||||||
|
res.status(501).json({});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/api/result/:id', (req, res) => {
|
||||||
|
res.status(501).json({});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/api/status', (req, res) => {
|
||||||
|
res.status(501).json({});
|
||||||
|
// dbPool.getConnection((err, db) => {
|
||||||
|
// if (err) {
|
||||||
|
// console.error('Failed to connect to database:', err);
|
||||||
|
// res.status(500).json({
|
||||||
|
// error: 'Database connection failed',
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// db.query('SELECT * FROM ?? ORDER BY ?? DESC LIMIT 0, 1', [
|
||||||
|
// tableNames.drawings,
|
||||||
|
// 'id',
|
||||||
|
// ], (qerr, results, fields) => {
|
||||||
|
// if (qerr) {
|
||||||
|
// console.error('Failed to request drawings:', err);
|
||||||
|
// res.status(500).json({
|
||||||
|
// error: 'Database query failed',
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const row = results[0];
|
||||||
|
//
|
||||||
|
// const id = row.id;
|
||||||
|
//
|
||||||
|
// const date = moment(row.drawing_date);
|
||||||
|
//
|
||||||
|
// const numbers = [
|
||||||
|
// row.first,
|
||||||
|
// row.second,
|
||||||
|
// row.third,
|
||||||
|
// row.fourth,
|
||||||
|
// row.fifth,
|
||||||
|
// row.sixth,
|
||||||
|
// ].map(i => parseInt(i, 10));
|
||||||
|
//
|
||||||
|
// const ranking = [
|
||||||
|
// // @TODO - calculate ranking from mysql tables {drawings} and {bets}
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// res.json({
|
||||||
|
// lastDrawing: {
|
||||||
|
// id,
|
||||||
|
// date,
|
||||||
|
// numbers,
|
||||||
|
// ranking,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(app.get('port'), () => {
|
||||||
|
console.log(`Server started: http://localhost:${app.get('port')}/`);
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"debug": true
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
"babel-plugin-transform-runtime",
|
||||||
|
"babel-plugin-dynamic-import-node"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
overrides:
|
||||||
|
- files:
|
||||||
|
- src/website/**/*.js
|
||||||
|
rules:
|
||||||
|
no-console: error
|
||||||
|
import/no-extraneous-dependencies:
|
||||||
|
- error
|
||||||
|
- devDependencies: true
|
|
@ -0,0 +1 @@
|
||||||
|
package-lock=false
|
|
@ -0,0 +1,2 @@
|
||||||
|
This file is placed here to avoid Docker from setting up a root-owned host
|
||||||
|
volume directory here.
|
|
@ -0,0 +1,127 @@
|
||||||
|
{
|
||||||
|
"name": "@vizonweb/frontend",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"description": "VIzon lottery website",
|
||||||
|
"private": true,
|
||||||
|
"browser": "./browser/index.js",
|
||||||
|
"main": "./lib/middleware.node/index.js",
|
||||||
|
"jsnext:main": "./lib/middleware.es6/index.js",
|
||||||
|
"module": "./lib/middleware.es6/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build:website:development": "npm run -s build:website -- --env development",
|
||||||
|
"build:website:production": "npm run -s build:website -- --env production",
|
||||||
|
"build:website:watch": "run-p build:website -- --watch",
|
||||||
|
"build:website": "webpack",
|
||||||
|
"build:middleware:es6": "bnr build:middleware:es6",
|
||||||
|
"build:middleware:node": "bnr build:middleware:node",
|
||||||
|
"build:middleware": "run-p build:middleware:*",
|
||||||
|
"build:production": "bnr build:production",
|
||||||
|
"build": "run-p build:website build:middleware",
|
||||||
|
"clean": "rimraf build lib",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"prepublish": "npm run -s build:middleware",
|
||||||
|
"prepublishOnly": "npm run -s build:production"
|
||||||
|
},
|
||||||
|
"betterScripts": {
|
||||||
|
"build:middleware:es6": {
|
||||||
|
"command": "babel --out-dir lib/middleware.es6 src/middleware",
|
||||||
|
"env": {
|
||||||
|
"BABEL_ENV": "es6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build:middleware:node": {
|
||||||
|
"command": "babel --out-dir lib/middleware.node src/middleware",
|
||||||
|
"env": {
|
||||||
|
"BABEL_ENV": "node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"build:production": {
|
||||||
|
"command": "npm run -s build",
|
||||||
|
"env": {
|
||||||
|
"NODE_ENV": "production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.icedream.tech:2222/icedream/vizon-countdown-website.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"website",
|
||||||
|
"vizon",
|
||||||
|
"rizon",
|
||||||
|
"draw",
|
||||||
|
"countdown",
|
||||||
|
"frontend"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"webpack.config.babel.js",
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^7.1.2",
|
||||||
|
"babel-cli": "^6.26.0",
|
||||||
|
"babel-eslint": "^7.2.3",
|
||||||
|
"babel-loader": "^7.1.2",
|
||||||
|
"babel-plugin-dynamic-import-node": "^1.0.2",
|
||||||
|
"babel-plugin-dynamic-import-webpack": "^1.0.1",
|
||||||
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
|
"babel-plugin-transform-react-constant-elements": "^6.23.0",
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"babel-preset-env": "^1.6.0",
|
||||||
|
"babel-preset-react": "^6.24.1",
|
||||||
|
"better-npm-run": "^0.1.0",
|
||||||
|
"case-sensitive-paths-webpack-plugin": "^2.1.1",
|
||||||
|
"chalk": "^2.1.0",
|
||||||
|
"css-loader": "^0.28.5",
|
||||||
|
"debug": "^3.0.1",
|
||||||
|
"eslint": "^4.5.0",
|
||||||
|
"eslint-config-airbnb": "^15.1.0",
|
||||||
|
"eslint-plugin-babel": "^4.1.2",
|
||||||
|
"eslint-plugin-import": "^2.7.0",
|
||||||
|
"eslint-plugin-json": "^1.2.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "5.x",
|
||||||
|
"eslint-plugin-react": "^7.3.0",
|
||||||
|
"eventsource-polyfill": "^0.9.6",
|
||||||
|
"extract-text-webpack-plugin": "^3.0.0",
|
||||||
|
"file-loader": "^0.11.2",
|
||||||
|
"html-webpack-plugin": "^2.30.1",
|
||||||
|
"moment-timezone": "^0.5.13",
|
||||||
|
"node-sass": "^4.5.3",
|
||||||
|
"nodemon": "^1.11.0",
|
||||||
|
"normalize-scss": "^7.0.0",
|
||||||
|
"npm-run-all": "^4.1.0",
|
||||||
|
"postcss-loader": "^2.0.6",
|
||||||
|
"preact": "^8.2.4",
|
||||||
|
"preact-compat": "^3.17.0",
|
||||||
|
"progress-bar-webpack-plugin": "^1.10.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react": "^15.6.1",
|
||||||
|
"react-dom": "^15.6.1",
|
||||||
|
"react-fontawesome": "^1.6.1",
|
||||||
|
"react-helmet": "^5.1.3",
|
||||||
|
"react-hot-loader": "^3.0.0-beta.7",
|
||||||
|
"react-router": "^4.2.0",
|
||||||
|
"resolve-url-loader": "^2.1.0",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"sass-loader": "^6.0.6",
|
||||||
|
"slash": "^1.0.0",
|
||||||
|
"style-loader": "^0.18.2",
|
||||||
|
"url-loader": "^0.5.9",
|
||||||
|
"webfontloader": "^1.6.28",
|
||||||
|
"webpack": "^3.5.5",
|
||||||
|
"webpack-dev-middleware": "^1.12.0",
|
||||||
|
"webpack-hot-middleware": "^2.18.2",
|
||||||
|
"webpack-merge": "^4.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.17.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es6": {
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"spec": true,
|
||||||
|
"debug": false,
|
||||||
|
"modules": false
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"node": true,
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"spec": true,
|
||||||
|
"debug": false,
|
||||||
|
"modules": "commonjs"
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
"babel-plugin-dynamic-import-node"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Implements the express middleware to serve the frontend.
|
||||||
|
*
|
||||||
|
* - Should use webpack-dev-server middleware with hot reloading if running in
|
||||||
|
* development mode.
|
||||||
|
* - Should just serve the files from the htdocs folder if running in production
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
import { Router, static as staticMiddleware } from 'express';
|
||||||
|
|
||||||
|
const websitePath = resolve(__dirname, '..', 'website');
|
||||||
|
|
||||||
|
export default function frontendRouter() {
|
||||||
|
const app = new Router();
|
||||||
|
|
||||||
|
switch ((process.env.NODE_ENV || 'production').toString().toLowerCase()) {
|
||||||
|
case 'development':
|
||||||
|
console.log('Serving development build with webpack dev middleware');
|
||||||
|
|
||||||
|
console.log('Loading development packages...');
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
import('babel-register'),
|
||||||
|
import('babel-polyfill'),
|
||||||
|
import('webpack'),
|
||||||
|
import('webpack-dev-middleware'),
|
||||||
|
import('webpack-hot-middleware'),
|
||||||
|
import('util'),
|
||||||
|
import('body-parser'),
|
||||||
|
])
|
||||||
|
.then((loadedModules) => {
|
||||||
|
const babelRegister = loadedModules[0];
|
||||||
|
// const babelPolyfill = loadedModules[1];
|
||||||
|
const webpack = loadedModules[2];
|
||||||
|
const webpackDevMiddleware = loadedModules[3];
|
||||||
|
const webpackHotMiddleware = loadedModules[4];
|
||||||
|
const { inspect } = loadedModules[5];
|
||||||
|
const bodyParser = loadedModules[6];
|
||||||
|
|
||||||
|
console.log('Setting up babel-register...');
|
||||||
|
|
||||||
|
babelRegister({
|
||||||
|
babelrc: true,
|
||||||
|
extensions: ['.es6', '.es', '.babel.js', '.jsx', '.js'],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Loading webpack configuration...');
|
||||||
|
|
||||||
|
return import('../../webpack.config.babel')
|
||||||
|
.then((generateWebpackConfig) => {
|
||||||
|
let webpackConfig = generateWebpackConfig;
|
||||||
|
if (webpackConfig.default) {
|
||||||
|
// ES6 module imported in CJS, default export.
|
||||||
|
webpackConfig = webpackConfig.default;
|
||||||
|
}
|
||||||
|
if (typeof (webpackConfig) === 'function') {
|
||||||
|
webpackConfig = webpackConfig([
|
||||||
|
process.env.NODE_ENV,
|
||||||
|
{
|
||||||
|
server: true,
|
||||||
|
},
|
||||||
|
].filter(v => !!v));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Webpack configuration loaded.');
|
||||||
|
|
||||||
|
const compiler = webpack(webpackConfig);
|
||||||
|
|
||||||
|
app.use(webpackDevMiddleware(compiler, webpackConfig.devServer));
|
||||||
|
app.use(webpackHotMiddleware(compiler, {
|
||||||
|
log: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.get('/webpackConfig', (req, res) => {
|
||||||
|
res.setHeader('content-type', 'text/plain');
|
||||||
|
res.send(inspect(webpackConfig, { depth: 7, colors: false }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
console.warn('The frontend router will not work due to the error above.');
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // production mode
|
||||||
|
console.log(`Serving static build from ${websitePath}`);
|
||||||
|
app.use('/', staticMiddleware(websitePath));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["babel-preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"browsers": ">1%",
|
||||||
|
"uglify": false
|
||||||
|
},
|
||||||
|
"modules": false
|
||||||
|
}],
|
||||||
|
"babel-preset-react"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-react-constant-elements",
|
||||||
|
"babel-plugin-transform-class-properties",
|
||||||
|
["babel-plugin-transform-runtime", {
|
||||||
|
"helpers": false,
|
||||||
|
"polyfill": false,
|
||||||
|
"regenerator": true
|
||||||
|
}],
|
||||||
|
"babel-plugin-syntax-dynamic-import"
|
||||||
|
]
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import moment from 'moment-timezone';
|
||||||
import 'react-fontawesome';
|
import 'react-fontawesome';
|
||||||
import WebFont from 'webfontloader';
|
import WebFont from 'webfontloader';
|
||||||
import Countdown from './Countdown';
|
import Countdown from './Countdown';
|
||||||
|
import Drawing from './Drawing';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
import Footer from './Footer';
|
import Footer from './Footer';
|
||||||
import getUpcomingDate from './getUpcomingDate';
|
import getUpcomingDate from './getUpcomingDate';
|
||||||
|
@ -44,6 +45,7 @@ class App extends React.Component {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
|
this.interval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateUpcomingDate() {
|
calculateUpcomingDate() {
|
||||||
|
@ -55,7 +57,19 @@ class App extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUpcomingDate() {
|
updateUpcomingDate() {
|
||||||
this.setState({ nextUpcomingDate: this.calculateUpcomingDate() });
|
const { getNow } = this.props;
|
||||||
|
|
||||||
|
const nextUpcomingDate = this.calculateUpcomingDate();
|
||||||
|
|
||||||
|
if (this.interval) {
|
||||||
|
const interval = moment.duration(nextUpcomingDate.diff(getNow()))
|
||||||
|
.asMilliseconds();
|
||||||
|
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = setInterval(this.updateUpcomingDate.bind(this), interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ nextUpcomingDate });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -79,6 +93,19 @@ class App extends React.Component {
|
||||||
The next VIzon draw is on {nextUpcomingDate.format('dddd')}, {nextUpcomingDate.format('L LT z')}.
|
The next VIzon draw is on {nextUpcomingDate.format('dddd')}, {nextUpcomingDate.format('L LT z')}.
|
||||||
</p>
|
</p>
|
||||||
<Countdown date={nextUpcomingDate} />
|
<Countdown date={nextUpcomingDate} />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Last Drawing:
|
||||||
|
</p>
|
||||||
|
<Drawing
|
||||||
|
numbers={[1, 2, 3, 4, 5, 6]}
|
||||||
|
ranking={[
|
||||||
|
{ rank: '1st', winners: 0 },
|
||||||
|
{ rank: '2nd', winners: 0 },
|
||||||
|
{ rank: '3rd', winners: 0 },
|
||||||
|
{ rank: 'Consolation', winners: 4 },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer>
|
<Footer>
|
|
@ -9,9 +9,6 @@ import style from './Countdown.sass';
|
||||||
export default class Countdown extends React.Component {
|
export default class Countdown extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
date: PropTypes.instanceOf(moment).isRequired,
|
date: PropTypes.instanceOf(moment).isRequired,
|
||||||
locale: PropTypes.string,
|
|
||||||
minimumIntegerDigits: PropTypes.number,
|
|
||||||
useGrouping: PropTypes.bool,
|
|
||||||
getNow: PropTypes.func,
|
getNow: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,39 +54,26 @@ export default class Countdown extends React.Component {
|
||||||
|
|
||||||
const size = 160; // @HACK
|
const size = 160; // @HACK
|
||||||
|
|
||||||
const { locale, minimumIntegerDigits, useGrouping } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<div className={style.countdown}>
|
<div className={style.countdown}>
|
||||||
<ProgressCircle size={size} max={3} progress={days} stroke="rgb(76,114,163)">
|
<ProgressCircle size={size} max={3} progress={days} stroke="rgb(76,114,163)">
|
||||||
<DigitBlock suffix="d">
|
<DigitBlock suffix="d">
|
||||||
{Math.floor(days).toLocaleString(locale, {
|
{Math.floor(days).toString().padStart(2, '0')}
|
||||||
minimumIntegerDigits,
|
|
||||||
useGrouping,
|
|
||||||
})}
|
|
||||||
</DigitBlock>
|
</DigitBlock>
|
||||||
</ProgressCircle>
|
</ProgressCircle>
|
||||||
<ProgressCircle size={size} max={24} progress={hours} stroke="rgb(60, 144, 60)">
|
<ProgressCircle size={size} max={24} progress={hours} stroke="rgb(60, 144, 60)">
|
||||||
<DigitBlock suffix="h">
|
<DigitBlock suffix="h">
|
||||||
{Math.floor(hours).toLocaleString(locale, {
|
{Math.floor(hours).toString().padStart(2, '0')}
|
||||||
minimumIntegerDigits,
|
|
||||||
useGrouping,
|
|
||||||
})}
|
|
||||||
</DigitBlock>
|
</DigitBlock>
|
||||||
</ProgressCircle>
|
</ProgressCircle>
|
||||||
<ProgressCircle size={size} max={60} progress={minutes} stroke="rgb(160, 154, 40)">
|
<ProgressCircle size={size} max={60} progress={minutes} stroke="rgb(160, 154, 40)">
|
||||||
<DigitBlock suffix="m">
|
<DigitBlock suffix="m">
|
||||||
{Math.floor(minutes).toLocaleString(locale, {
|
{Math.floor(minutes).toString().padStart(2, '0')}
|
||||||
minimumIntegerDigits,
|
|
||||||
useGrouping,
|
|
||||||
})}
|
|
||||||
</DigitBlock>
|
</DigitBlock>
|
||||||
</ProgressCircle>
|
</ProgressCircle>
|
||||||
<ProgressCircle size={size} max={60} progress={seconds} stroke="rgb(193,63,88)">
|
<ProgressCircle size={size} max={60} progress={seconds} stroke="rgb(193,63,88)">
|
||||||
<DigitBlock suffix="s">
|
<DigitBlock suffix="s">
|
||||||
{Math.floor(seconds).toLocaleString(locale, {
|
{Math.floor(seconds).toString().padStart(2, '0')}
|
||||||
minimumIntegerDigits,
|
|
||||||
useGrouping,
|
|
||||||
})}
|
|
||||||
</DigitBlock>
|
</DigitBlock>
|
||||||
</ProgressCircle>
|
</ProgressCircle>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import style from './Drawing.sass';
|
||||||
|
|
||||||
|
const Numbers = ({ numbers }) => (
|
||||||
|
<div className={style.numbers}>
|
||||||
|
{
|
||||||
|
numbers.map((number, index) => (
|
||||||
|
<div key={index} className={style.number}>
|
||||||
|
{number}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
Numbers.propTypes = {
|
||||||
|
numbers: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Drawing extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
ranking: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
rank: PropTypes.string,
|
||||||
|
winners: PropTypes.int,
|
||||||
|
})).isRequired,
|
||||||
|
numbers: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { ranking, numbers } = this.props;
|
||||||
|
return (
|
||||||
|
<div className={style.drawing}>
|
||||||
|
<Numbers numbers={numbers} />
|
||||||
|
<div className={style.ranking}>
|
||||||
|
{
|
||||||
|
ranking.map(({ rank, winners }) => (
|
||||||
|
<div key={rank} className={style.rank}>
|
||||||
|
<div className={style.rankName}>{rank}</div>
|
||||||
|
<div className={style.winners}>{winners > 0 ? winners : 'No'} winners</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
.drawing
|
||||||
|
|
||||||
|
.numbers
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.number
|
||||||
|
flex: 1 0 auto
|
||||||
|
text-align: center
|
||||||
|
font-size: 2.5em
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.ranking
|
||||||
|
display: table
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.rank
|
||||||
|
display: table-row
|
||||||
|
|
||||||
|
.rankName, .winners
|
||||||
|
display: table-cell
|
||||||
|
|
||||||
|
.rankName
|
||||||
|
text-align: right
|
||||||
|
width: 50%
|
||||||
|
padding-right: .25em
|
||||||
|
|
||||||
|
.winners
|
||||||
|
text-align: left
|
||||||
|
width: 50%
|
||||||
|
padding-left: .25em
|
|
@ -16,7 +16,6 @@ Footer.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Footer.defaultProps = {
|
Footer.defaultProps = {
|
||||||
children: null,
|
children: null,
|
||||||
className: null,
|
className: null,
|
|
@ -0,0 +1,21 @@
|
||||||
|
$debug: false
|
||||||
|
|
||||||
|
@if ($debug)
|
||||||
|
*
|
||||||
|
background-color: rgba(255, 0, 255, 0.1)
|
||||||
|
border: red 1px solid
|
||||||
|
box-sizing: border-box
|
||||||
|
position: relative
|
||||||
|
&:after
|
||||||
|
content: attr(class)
|
||||||
|
z-index: 99999
|
||||||
|
box-shadow: 0 0 10px black
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
top: 0
|
||||||
|
background: white
|
||||||
|
border: black 1px solid
|
||||||
|
padding: 4px
|
||||||
|
font-size: 12px
|
||||||
|
font-weight: bold
|
||||||
|
font-family: monospace
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import { AppContainer } from 'react-hot-loader';
|
||||||
|
|
||||||
|
import './index.sass';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
// Create div node to render app in
|
||||||
|
const rootContainer = document.createElement('div');
|
||||||
|
document.body.appendChild(rootContainer);
|
||||||
|
|
||||||
|
const runRender = (Component) => {
|
||||||
|
console.log('Rendering app...');
|
||||||
|
render(<AppContainer>
|
||||||
|
<Component />
|
||||||
|
</AppContainer>, rootContainer);
|
||||||
|
};
|
||||||
|
|
||||||
|
runRender(App);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept('./App', () => { runRender(App); });
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
@import ~normalize-scss/sass/normalize
|
@import ~normalize-scss/sass/normalize
|
||||||
@import fonts/colaborate
|
@import fonts/colaborate
|
||||||
|
@import debug
|
||||||
|
|
||||||
body
|
body
|
||||||
font-family: sans-serif
|
font-family: sans-serif
|
|
@ -0,0 +1,309 @@
|
||||||
|
import {
|
||||||
|
DefinePlugin,
|
||||||
|
NamedModulesPlugin,
|
||||||
|
HashedModuleIdsPlugin,
|
||||||
|
LoaderOptionsPlugin,
|
||||||
|
ContextReplacementPlugin,
|
||||||
|
HotModuleReplacementPlugin,
|
||||||
|
NoEmitOnErrorsPlugin,
|
||||||
|
|
||||||
|
optimize,
|
||||||
|
} from 'webpack';
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import { isatty } from 'tty';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import _debug from 'debug';
|
||||||
|
import slash from 'slash';
|
||||||
|
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
|
||||||
|
import ExtractTextPlugin from 'extract-text-webpack-plugin';
|
||||||
|
import HtmlPlugin from 'html-webpack-plugin';
|
||||||
|
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
|
||||||
|
|
||||||
|
import Environment from '../../config/webpack/environment';
|
||||||
|
import injectManifestPlugin from '../../config/webpack/injectManifestPlugin';
|
||||||
|
|
||||||
|
const debug = _debug('webpack:config');
|
||||||
|
debug.generated = _debug('webpack:config:generated');
|
||||||
|
|
||||||
|
debug.generated('filename:', __filename);
|
||||||
|
debug.generated('dirname:', __dirname);
|
||||||
|
|
||||||
|
const {
|
||||||
|
CommonsChunkPlugin,
|
||||||
|
UglifyJsPlugin,
|
||||||
|
ModuleConcatenationPlugin,
|
||||||
|
} = optimize;
|
||||||
|
|
||||||
|
const websiteSubfolder = 'website';
|
||||||
|
|
||||||
|
export default (options) => {
|
||||||
|
const environment = new Environment({
|
||||||
|
ExtractTextPlugin, // @HACK
|
||||||
|
});
|
||||||
|
|
||||||
|
environment.input(options);
|
||||||
|
|
||||||
|
debug.generated(environment);
|
||||||
|
|
||||||
|
const {
|
||||||
|
development,
|
||||||
|
production,
|
||||||
|
server,
|
||||||
|
locales,
|
||||||
|
autoprefixerTargets,
|
||||||
|
} = environment;
|
||||||
|
|
||||||
|
const baseOutputFilename = development
|
||||||
|
? 'assets/dev/[name].dev.[ext]'
|
||||||
|
// Always use a hash (in production) to prevent files with the same name causing issues
|
||||||
|
: 'assets/prod/[chunkhash:2]/[name].[chunkhash:8].[ext]';
|
||||||
|
|
||||||
|
const webpackChunkFilename = baseOutputFilename
|
||||||
|
.replace(/\[ext(.*?)\]/g, 'js');
|
||||||
|
const webpackOutputFilename = webpackChunkFilename;
|
||||||
|
|
||||||
|
const assetOutputFilename = baseOutputFilename
|
||||||
|
.replace(/\[chunkhash(.*?)\]/g, '[hash$1]');
|
||||||
|
|
||||||
|
const cssOutputFileName = baseOutputFilename
|
||||||
|
.replace(/\[ext(.*?)\]/g, 'css')
|
||||||
|
.replace(/\[chunkhash(.*?)\]/g, '[contenthash$1]');
|
||||||
|
const cssOutputRebasePath = `${slash(path.relative(path.dirname(cssOutputFileName), ''))}/`;
|
||||||
|
|
||||||
|
// Default options for url-loader
|
||||||
|
const urlLoaderOptions = {
|
||||||
|
limit: 1, // Don't inline anything (but empty files) by default
|
||||||
|
name: assetOutputFilename,
|
||||||
|
publicPath: cssOutputRebasePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
name: 'frontend',
|
||||||
|
target: 'web',
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
// inline: true,
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
historyApiFallback: true,
|
||||||
|
hot: true,
|
||||||
|
noInfo: true,
|
||||||
|
overlay: true,
|
||||||
|
publicPath: '',
|
||||||
|
quiet: false,
|
||||||
|
watchOptions: {
|
||||||
|
ignored: /node_modules/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.jsx?/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
// Look for babel configuration in project directory
|
||||||
|
babelrc: false,
|
||||||
|
// Cache transformations to the filesystem (in default temp dir)
|
||||||
|
cacheDirectory: true,
|
||||||
|
|
||||||
|
presets: [
|
||||||
|
['babel-preset-env', {
|
||||||
|
targets: {
|
||||||
|
browsers: autoprefixerTargets,
|
||||||
|
uglify: false,
|
||||||
|
},
|
||||||
|
// spec: true,
|
||||||
|
// debug: development,
|
||||||
|
modules: false, // do not transpile modules, webpack 2+ does that
|
||||||
|
}],
|
||||||
|
'babel-preset-react',
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'babel-plugin-transform-react-constant-elements',
|
||||||
|
'babel-plugin-transform-class-properties',
|
||||||
|
['babel-plugin-transform-runtime', {
|
||||||
|
helpers: false,
|
||||||
|
polyfill: false,
|
||||||
|
regenerator: true,
|
||||||
|
}],
|
||||||
|
'babel-plugin-dynamic-import-webpack',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...[
|
||||||
|
/\.(gif|png|webp)$/i, // graphics
|
||||||
|
/\.svg$/i, // svg
|
||||||
|
/\.jpe?g$/i, // jpeg
|
||||||
|
/\.(mp4|ogg|webm)$/i, // video
|
||||||
|
/\.(eot|otf|ttf|woff|woff2)$/i, // fonts
|
||||||
|
/\.(wav|mp3|m4a|aac|oga)$/i, // audio
|
||||||
|
].map(test => ({
|
||||||
|
test,
|
||||||
|
loader: 'url-loader',
|
||||||
|
options: urlLoaderOptions,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: environment.styleLoaders(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.s[ac]ss$/,
|
||||||
|
use: environment.styleLoaders(
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
strictExportPresence: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
output: {
|
||||||
|
filename: webpackOutputFilename,
|
||||||
|
chunkFilename: webpackChunkFilename,
|
||||||
|
path: path.join(__dirname, 'lib', websiteSubfolder),
|
||||||
|
publicPath: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
// Show progress as a bar during build
|
||||||
|
isatty(1) && new ProgressBarPlugin({
|
||||||
|
complete: chalk.white('\u2588'),
|
||||||
|
incomplete: chalk.grey('\u2591'),
|
||||||
|
format: `:bar ${chalk.cyan.bold(':percent')} Webpack build: ${chalk.grey(':msg')}`,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Enforce case-sensitive import paths
|
||||||
|
new CaseSensitivePathsPlugin(),
|
||||||
|
// Replace specified expressions with values
|
||||||
|
new DefinePlugin({
|
||||||
|
'process.env.__DEV__': JSON.stringify(development),
|
||||||
|
'process.env.__PROD__': JSON.stringify(production),
|
||||||
|
'process.env.__SERVER__': JSON.stringify(server),
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'development'),
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Dev server build
|
||||||
|
...[
|
||||||
|
// Hot module reloading
|
||||||
|
new HotModuleReplacementPlugin(),
|
||||||
|
new NoEmitOnErrorsPlugin(),
|
||||||
|
|
||||||
|
// Use paths as names when serving
|
||||||
|
new NamedModulesPlugin(),
|
||||||
|
].filter(() => server),
|
||||||
|
|
||||||
|
// If we're not serving, we're creating a static build
|
||||||
|
...[// Extract imported stylesheets out into .css files
|
||||||
|
new ExtractTextPlugin({
|
||||||
|
allChunks: true,
|
||||||
|
filename: cssOutputFileName,
|
||||||
|
}),
|
||||||
|
].filter(() => !server),
|
||||||
|
|
||||||
|
// Move modules imported from node_modules/ into a vendor chunk when enabled
|
||||||
|
new CommonsChunkPlugin({
|
||||||
|
name: 'vendor',
|
||||||
|
minChunks(module) {
|
||||||
|
return (module.resource && module.resource.includes('node_modules'));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// If we're generating an HTML file, we must be building a web app, so
|
||||||
|
// configure deterministic hashing for long-term caching.
|
||||||
|
|
||||||
|
// Generate stable module ids instead of having Webpack assign integers.
|
||||||
|
// NamedModulesPlugin allows for easier debugging and
|
||||||
|
// HashedModuleIdsPlugin does this without adding too much to bundle
|
||||||
|
// size.
|
||||||
|
development
|
||||||
|
? new NamedModulesPlugin()
|
||||||
|
: new HashedModuleIdsPlugin(),
|
||||||
|
|
||||||
|
// The Webpack manifest is normally folded into the last chunk, changing
|
||||||
|
// its hash - prevent this by extracting the manifest into its own
|
||||||
|
// chunk - also essential for deterministic hashing.
|
||||||
|
new CommonsChunkPlugin({ name: 'manifest' }),
|
||||||
|
|
||||||
|
// Inject the Webpack manifest into the generated HTML as a <script>
|
||||||
|
injectManifestPlugin,
|
||||||
|
|
||||||
|
// Production builds
|
||||||
|
...[
|
||||||
|
// JavaScript minification
|
||||||
|
new LoaderOptionsPlugin({ debug: false, minimize: true }),
|
||||||
|
new UglifyJsPlugin({
|
||||||
|
compress: {
|
||||||
|
warnings: false,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
},
|
||||||
|
sourceMap: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Hoisting
|
||||||
|
new ModuleConcatenationPlugin(),
|
||||||
|
].filter(() => production),
|
||||||
|
|
||||||
|
new HtmlPlugin({
|
||||||
|
template: path.join(__dirname, 'src', websiteSubfolder, 'index.html'),
|
||||||
|
filename: 'index.html',
|
||||||
|
hash: false,
|
||||||
|
inject: true,
|
||||||
|
compile: true,
|
||||||
|
favicon: false,
|
||||||
|
minify: false,
|
||||||
|
cache: true,
|
||||||
|
showErrors: true,
|
||||||
|
chunks: 'all',
|
||||||
|
excludeChunks: [],
|
||||||
|
title: 'VIzon Countdown',
|
||||||
|
xhtml: false,
|
||||||
|
chunksSortMode: 'dependency',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Only include selected locales for moment.js
|
||||||
|
new ContextReplacementPlugin(/moment[/\\]locale$/, new RegExp(`^\\.\\/(${locales.join('|')})$`)),
|
||||||
|
].filter(plugin => plugin !== false),
|
||||||
|
resolve: {
|
||||||
|
extensions: [
|
||||||
|
'.js', '.json', '.jsx',
|
||||||
|
],
|
||||||
|
alias: {
|
||||||
|
'moment-timezone': 'moment-timezone/builds/moment-timezone-with-data-2012-2022.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolveLoader: {
|
||||||
|
modules: ['node_modules', 'nwb'],
|
||||||
|
},
|
||||||
|
devtool: server ? 'cheap-module-source-map' : 'source-map',
|
||||||
|
entry: {
|
||||||
|
app: [
|
||||||
|
...[
|
||||||
|
'eventsource-polyfill',
|
||||||
|
'react-hot-loader/patch',
|
||||||
|
'webpack-hot-middleware/client',
|
||||||
|
].filter(() => server).map(require.resolve),
|
||||||
|
path.join(__dirname, 'src', websiteSubfolder),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
// Use preact instead of react (only on static build)
|
||||||
|
Object.assign(config.resolve.alias, {
|
||||||
|
react: !server ? 'preact-compat\\dist\\preact-compat' : undefined,
|
||||||
|
'react-dom': !server ? 'preact-compat\\dist\\preact-compat' : undefined,
|
||||||
|
'create-react-class': !server ? 'preact-compat/lib/create-react-class' : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.generated(config);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
|
@ -0,0 +1,91 @@
|
||||||
|
CREATE OR REPLACE VIEW `vizon_web_rankings` AS
|
||||||
|
SELECT
|
||||||
|
bets.id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
COUNT(*) AS richtige
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
vizon_users_id AS id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
FIRST AS NUMBER
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
vizon_users_id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
SECOND
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
vizon_users_id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
third
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
vizon_users_id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
fourth
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
vizon_users_id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
fifth
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
vizon_users_id,
|
||||||
|
vizon_drawings_id,
|
||||||
|
sixth
|
||||||
|
FROM
|
||||||
|
vizon_bets
|
||||||
|
) bets,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
FIRST AS NUMBER
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
SECOND
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
third
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
fourth
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
fifth
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
sixth
|
||||||
|
FROM
|
||||||
|
vizon_drawings
|
||||||
|
) drawings
|
||||||
|
WHERE
|
||||||
|
bets.vizon_drawings_id = drawings.id AND bets.number = drawings.number
|
||||||
|
GROUP BY
|
||||||
|
bets.id,
|
||||||
|
vizon_drawings_id
|
|
@ -1,11 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { render } from 'react-dom';
|
|
||||||
|
|
||||||
import './index.sass';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
// Create div node to render app in
|
|
||||||
const rootContainer = document.createElement('div');
|
|
||||||
document.body.appendChild(rootContainer);
|
|
||||||
|
|
||||||
render(<App />, rootContainer);
|
|