This commit is contained in:
martin 2018-05-31 16:01:32 +01:00
commit 642f90db1e
25 changed files with 1320 additions and 0 deletions

148
.gitignore vendored Normal file
View File

@ -0,0 +1,148 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
/src/bundle.js
/src/bundle.js.map
/setup/web/live/
/work/

50
docker-compose.yml Normal file
View File

@ -0,0 +1,50 @@
version: '3'
services:
PostgreSQL:
image: postgres:alpine
container_name: PostgreSQL
ports:
- 5432:5432
restart: always
env_file: .env
volumes:
- "$ROOT/postgresql:/var/lib/postgresql/data"
- "./setup/clustering/setup/postgres/start.sh:/docker-entrypoint-initdb.d/start.sh"
- ./setup/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
adminer:
image: adminer
container_name: adminer
restart: always
env_file: .env
depends_on:
- PostgreSQL
ports:
- 8080:8080
redis:
image: redis:alpine
container_name: redis
restart: always
volumes:
- "$ROOT/redis:/data"
web:
build: ./setup/web/.
container_name: web
command: npm run start
volumes:
- /user/app
ports:
- 9000:9000
depends_on:
- PostgreSQL
- redis
environment:
- HOST=${HOST}
- DATABASE=${POSTGRES_DB}
- USER=${POSTGRES_USER}
- PASSWORD=${POSTGRES_PASSWORD}
- DATABASE_URL=${DATABASE_URL}

View File

@ -0,0 +1,60 @@
version: '3'
services:
primary:
hostname: 'primary'
image: crunchydata/crunchy-postgres:centos7-10.3-1.8.2
ports:
- 5432
restart: always
env_file: .env
volumes:
- pg-primary-vol:/var/lib/postgresql/data
- ./setup/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
environment:
- POSTGRES_USER=comcardeuser
- POSTGRES_PASSWORD=example
- POSTGRES_DB=comcarde
- PGHOST=/tmp
- MAX_CONNECTIONS=10
- MAX_WAL_SENDERS=5
- PG_MODE=primary
- PG_PRIMARY_USER=primaryuser
- PG_PRIMARY_PASSWORD=password
- PG_DATABASE=testdb
- PG_USER=testuser
- PG_PASSWORD=password
- PG_ROOT_PASSWORD=password
- PG_PRIMARY_PORT=5432
networks:
- replication
- front
deploy:
placement:
constraints:
- node.labels.type == primary
- node.role == worker
adminer:
image: adminer
container_name: adminer
restart: always
env_file: .env
depends_on:
- primary
ports:
- 8080:8080
networks:
- front
networks:
replication:
front:
volumes:
pg-primary-vol:
pg-replica-vol:

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
env_check_err "POSTGRES_PASSWORD"
file_env 'POSTGRES_PASSWORD'
echo "$POSTGRES_PASSWORD"

View File

@ -0,0 +1,152 @@
# vim:set ft=dockerfile:
FROM alpine:3.7
# alpine includes "postgres" user/group in base install
# /etc/passwd:22:postgres:x:70:70::/var/lib/postgresql:/bin/sh
# /etc/group:34:postgres:x:70:
# the home directory for the postgres user, however, is not created by default
# see https://github.com/docker-library/postgres/issues/274
LABEL name="martind2000/clustergres"
RUN set -ex; \
postgresHome="$(getent passwd postgres)"; \
postgresHome="$(echo "$postgresHome" | cut -d: -f6)"; \
[ "$postgresHome" = '/var/lib/postgresql' ]; \
mkdir -p "$postgresHome"; \
chown -R postgres:postgres "$postgresHome"
# su-exec (gosu-compatible) is installed further down
# make the "en_US.UTF-8" locale so postgres will be utf-8 enabled by default
# alpine doesn't require explicit locale-file generation
ENV LANG en_US.utf8
RUN mkdir /docker-entrypoint-initdb.d
ENV PG_MAJOR 10
ENV PG_VERSION 10.4
ENV PG_SHA256 1b60812310bd5756c62d93a9f93de8c28ea63b0df254f428cd1cf1a4d9020048
RUN set -ex \
\
&& apk add --no-cache --virtual .fetch-deps \
ca-certificates \
openssl \
tar \
\
&& wget -O postgresql.tar.bz2 "https://ftp.postgresql.org/pub/source/v$PG_VERSION/postgresql-$PG_VERSION.tar.bz2" \
&& echo "$PG_SHA256 *postgresql.tar.bz2" | sha256sum -c - \
&& mkdir -p /usr/src/postgresql \
&& tar \
--extract \
--file postgresql.tar.bz2 \
--directory /usr/src/postgresql \
--strip-components 1 \
&& rm postgresql.tar.bz2 \
\
&& apk add --no-cache --virtual .build-deps \
bison \
coreutils \
dpkg-dev dpkg \
flex \
gcc \
# krb5-dev \
libc-dev \
libedit-dev \
libxml2-dev \
libxslt-dev \
make \
# openldap-dev \
openssl-dev \
# configure: error: prove not found
perl-utils \
# configure: error: Perl module IPC::Run is required to run TAP tests
perl-ipc-run \
# perl-dev \
# python-dev \
# python3-dev \
# tcl-dev \
util-linux-dev \
zlib-dev \
\
&& cd /usr/src/postgresql \
# update "DEFAULT_PGSOCKET_DIR" to "/var/run/postgresql" (matching Debian)
# see https://anonscm.debian.org/git/pkg-postgresql/postgresql.git/tree/debian/patches/51-default-sockets-in-var.patch?id=8b539fcb3e093a521c095e70bdfa76887217b89f
&& awk '$1 == "#define" && $2 == "DEFAULT_PGSOCKET_DIR" && $3 == "\"/tmp\"" { $3 = "\"/var/run/postgresql\""; print; next } { print }' src/include/pg_config_manual.h > src/include/pg_config_manual.h.new \
&& grep '/var/run/postgresql' src/include/pg_config_manual.h.new \
&& mv src/include/pg_config_manual.h.new src/include/pg_config_manual.h \
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
# explicitly update autoconf config.guess and config.sub so they support more arches/libcs
&& wget -O config/config.guess 'https://git.savannah.gnu.org/cgit/config.git/plain/config.guess?id=7d3d27baf8107b630586c962c057e22149653deb' \
&& wget -O config/config.sub 'https://git.savannah.gnu.org/cgit/config.git/plain/config.sub?id=7d3d27baf8107b630586c962c057e22149653deb' \
# configure options taken from:
# https://anonscm.debian.org/cgit/pkg-postgresql/postgresql.git/tree/debian/rules?h=9.5
&& ./configure \
--build="$gnuArch" \
# "/usr/src/postgresql/src/backend/access/common/tupconvert.c:105: undefined reference to `libintl_gettext'"
# --enable-nls \
--enable-integer-datetimes \
--enable-thread-safety \
--enable-tap-tests \
# skip debugging info -- we want tiny size instead
# --enable-debug \
--disable-rpath \
--with-uuid=e2fs \
--with-gnu-ld \
--with-pgport=5432 \
--with-system-tzdata=/usr/share/zoneinfo \
--prefix=/usr/local \
--with-includes=/usr/local/include \
--with-libraries=/usr/local/lib \
\
# these make our image abnormally large (at least 100MB larger), which seems uncouth for an "Alpine" (ie, "small") variant :)
# --with-krb5 \
# --with-gssapi \
# --with-ldap \
# --with-tcl \
# --with-perl \
# --with-python \
# --with-pam \
--with-openssl \
--with-libxml \
--with-libxslt \
&& make -j "$(nproc)" world \
&& make install-world \
&& make -C contrib install \
\
&& runDeps="$( \
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
| tr ',' '\n' \
| sort -u \
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
)" \
&& apk add --no-cache --virtual .postgresql-rundeps \
$runDeps \
bash \
su-exec \
# tzdata is optional, but only adds around 1Mb to image size and is recommended by Django documentation:
# https://docs.djangoproject.com/en/1.10/ref/databases/#optimizing-postgresql-s-configuration
tzdata \
&& apk del .fetch-deps .build-deps \
&& cd / \
&& rm -rf \
/usr/src/postgresql \
/usr/local/share/doc \
/usr/local/share/man \
&& find /usr/local -name '*.a' -delete
# make the sample config easier to munge (and "correct by default")
RUN sed -ri "s!^#?(listen_addresses)\s*=\s*\S+.*!\1 = '*'!" /usr/local/share/postgresql/postgresql.conf.sample
RUN mkdir -p /var/run/postgresql && chown -R postgres:postgres /var/run/postgresql && chmod 2777 /var/run/postgresql
ENV PGDATA /var/lib/postgresql/data
RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" # this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)
VOLUME /var/lib/postgresql/data
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 5432
CMD ["postgres"]

View File

@ -0,0 +1,162 @@
#!/usr/bin/env bash
set -Eeo pipefail
# TODO swap to -Eeuo pipefail above (after handling all potentially-unset variables)
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
if [ "${1:0:1}" = '-' ]; then
set -- postgres "$@"
fi
# allow the container to be started with `--user`
if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
mkdir -p "$PGDATA"
chown -R postgres "$PGDATA"
chmod 700 "$PGDATA"
mkdir -p /var/run/postgresql
chown -R postgres /var/run/postgresql
chmod 775 /var/run/postgresql
# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
if [ "$POSTGRES_INITDB_WALDIR" ]; then
mkdir -p "$POSTGRES_INITDB_WALDIR"
chown -R postgres "$POSTGRES_INITDB_WALDIR"
chmod 700 "$POSTGRES_INITDB_WALDIR"
fi
exec su-exec postgres "$BASH_SOURCE" "$@"
fi
if [ "$1" = 'postgres' ]; then
mkdir -p "$PGDATA"
chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
chmod 700 "$PGDATA" 2>/dev/null || :
# look specifically for PG_VERSION, as it is expected in the DB dir
if [ ! -s "$PGDATA/PG_VERSION" ]; then
# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
export LD_PRELOAD='/usr/lib/libnss_wrapper.so'
export NSS_WRAPPER_PASSWD="$(mktemp)"
export NSS_WRAPPER_GROUP="$(mktemp)"
echo "postgres:x:$(id -u):$(id -g):PostgreSQL:$PGDATA:/bin/false" > "$NSS_WRAPPER_PASSWD"
echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
fi
file_env 'POSTGRES_INITDB_ARGS'
if [ "$POSTGRES_INITDB_WALDIR" ]; then
export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
fi
eval "initdb --username=postgres $POSTGRES_INITDB_ARGS"
# unset/cleanup "nss_wrapper" bits
if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
fi
# check password first so we can output the warning before postgres
# messes it up
file_env 'POSTGRES_PASSWORD'
if [ "$POSTGRES_PASSWORD" ]; then
pass="PASSWORD '$POSTGRES_PASSWORD'"
authMethod=md5
else
# The - option suppresses leading tabs but *not* spaces. :)
cat >&2 <<-'EOWARN'
****************************************************
WARNING: No password has been set for the database.
This will allow anyone with access to the
Postgres port to access your database. In
Docker's default configuration, this is
effectively any other container on the same
system.
Use "-e POSTGRES_PASSWORD=password" to set
it in "docker run".
****************************************************
EOWARN
pass=
authMethod=trust
fi
{
echo
echo "host all all all $authMethod"
} >> "$PGDATA/pg_hba.conf"
# internal start of server in order to allow set-up using psql-client
# does not listen on external TCP/IP and waits until start finishes
PGUSER="${PGUSER:-postgres}" \
pg_ctl -D "$PGDATA" \
-o "-c listen_addresses=''" \
-w start
file_env 'POSTGRES_USER' 'postgres'
file_env 'POSTGRES_DB' "$POSTGRES_USER"
psql=( psql -v ON_ERROR_STOP=1 )
if [ "$POSTGRES_DB" != 'postgres' ]; then
"${psql[@]}" --username postgres <<-EOSQL
CREATE DATABASE "$POSTGRES_DB" ;
EOSQL
echo
fi
if [ "$POSTGRES_USER" = 'postgres' ]; then
op='ALTER'
else
op='CREATE'
fi
"${psql[@]}" --username postgres <<-EOSQL
$op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
EOSQL
echo
psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" )
echo
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
PGUSER="${PGUSER:-postgres}" \
pg_ctl -D "$PGDATA" -m fast -w stop
echo
echo 'PostgreSQL init process complete; ready for start up.'
echo
fi
fi
exec "$@"

View File

@ -0,0 +1 @@
FROM alpine

View File

54
setup/postgres/init.sql Normal file
View File

@ -0,0 +1,54 @@
-- SEQUENCE: public.test_id_seq
-- DROP SEQUENCE public.test_id_seq;
CREATE SEQUENCE public.test_id_seq;
ALTER SEQUENCE public.test_id_seq
OWNER TO comcardeuser;
-- Table: public.test
-- DROP TABLE public.test;
CREATE TABLE public.test
(
id integer NOT NULL DEFAULT nextval('test_id_seq'::regclass),
company_name character varying(100) COLLATE pg_catalog."default",
CONSTRAINT test_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.test
OWNER to comcardeuser;
-- FUNCTION: public.insert_company(character varying)
-- DROP FUNCTION public.insert_company(character varying);
CREATE OR REPLACE FUNCTION public.insert_company(
_name character varying)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
INSERT into test(company_name) Values(_name);
END;
$BODY$;
ALTER FUNCTION public.insert_company(character varying)
OWNER TO comcardeuser;

4
setup/postgres/setup.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
set -e
echo "Build Postgres"

0
setup/redis/redis.conf Normal file
View File

7
setup/web/Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM node:alpine
WORKDIR /usr/app
COPY . .
RUN npm install --unsafe-perm

33
setup/web/ecosystem.json Normal file
View File

@ -0,0 +1,33 @@
{
/**
* Application configuration section
* http://pm2.keymetrics.io/docs/usage/application-declaration/
*/
apps: [
// First application
{
"name": "Jubilee",
"script": "server.js",
"cwd": "/home/martind2000/dev/jubilee",
"watch": false,
"ignore_watch" : ["node_modules"],
"merge_logs" : true,
"autorestart" : true,
"restart_delay" : 3500,
"max_memory_restart" : "300M",
env: {
COMMON_VARIABLE: "true"
},
env_production: {
NODE_ENV: "production"
}
}
],
/**
* Deployment section
* http://pm2.keymetrics.io/docs/usage/deployment/
*/
deploy: {
}
}

View File

@ -0,0 +1,38 @@
'use strict';
const browserify = require('browserify');
const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const uglify = require('gulp-uglify-es').default;
const sourcemaps = require('gulp-sourcemaps');
const gutil = require('gulp-util');
const rename = require('gulp-rename');
const stripDebug = require('gulp-strip-debug');
gulp.task('bundleBackbone', function () {
// set up the browserify instance on a task basis
const b = browserify({
'debug': true,
'entries': './src/js/app.js'
});
return b.bundle()
.pipe(source('app.js'))
.pipe(buffer())
// .pipe(stripDebug())
.pipe(rename('bundle.js'))
.pipe(sourcemaps.init({ 'loadMaps': true }))
// Add transformation tasks to the pipeline here.
// .pipe(uglify())
.on('error', gutil.log)
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./live/js'));
});
gulp.task('buildBackbone', [ 'bundleBackbone'], function() {
gulp.watch('src/js/**/*.js', [ 'bundleBackbone']);
});

59
setup/web/gulp/build.js Normal file
View File

@ -0,0 +1,59 @@
const gulp = require('gulp'),
autoprefixer = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
concat = require('gulp-concat'),
cache = require('gulp-cache'),
htmlmin = require('gulp-htmlmin'),
inject = require('gulp-inject'),
del = require('del'),
htmlreplace = require('gulp-html-replace');
const scss = require('gulp-scss');
const sass = require('gulp-sass');
const googleWebFonts = require('gulp-google-webfonts');
const fontOptions = { };
gulp.task('styles', function() {
return gulp.src(['node_modules/backbone.modal/backbone.modal.css', 'node_modules/backbone.modal/backbone.modal.theme.css'])
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
/* .pipe(gulp.dest('dist/css'))*/
/* .pipe(rename({suffix: '.min'}))*/
.pipe(concat('style.min.css'))
.pipe(cssnano())
.pipe(gulp.dest('live/css'));
});
gulp.task('copy', function() {
gulp.src(['src/img/**/*']).pipe(gulp.dest('live/img'));
gulp.src(['src/gfx/**/*']).pipe(gulp.dest('live/gfx'));
gulp.src(['src/index.html']).pipe(gulp.dest('live'));
});
gulp.task('clean', function() {
return del(['live']);
});
gulp.task('customMUI', function() {
return gulp.src(['src/css/custom.scss'])
.pipe(sass({ 'outputStyle': 'compressed' }).on('error', sass.logError))
// .pipe(cssnano())
.pipe(rename('mui.custom.css'))
// .pipe(gulp.dest(`${dest}/css`));
.pipe(gulp.dest('live/css'));
});
gulp.task('vendor', function() {
return gulp.src([
'node_modules/muicss/dist/js/mui.min.js'
])
.pipe(concat('vendor.js'))
/* .pipe(uglify({ 'mangle': false }))*/
.pipe(gulp.dest(`live/js`));
});

7
setup/web/gulpfile.js Normal file
View File

@ -0,0 +1,7 @@
const gulp = require('gulp');
const requireDir = require('require-dir');
requireDir('./gulp');
gulp.task('default', ['bundleBackbone', 'styles', 'copy', 'customMUI', 'vendor']);

63
setup/web/package.json Normal file
View File

@ -0,0 +1,63 @@
{
"name": "pgtest",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "mocha",
"postinstall": "gulp default",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"backbone": "^1.3.3",
"browserify": "^16.1.0",
"eslint": "^4.19.0",
"express": "^4.16.3",
"hh-mm-ss": "^1.2.0",
"jquery": "^3.3.1",
"lodash": "^4.17.5",
"log4js": "^2.5.3",
"memory-cache": "^0.2.0",
"moment": "^2.21.0",
"muicss": "^0.9.38",
"pg-promise": "^8.4.4",
"request-json": "^0.6.3",
"strict-uri-encode": "^2.0.0",
"string": "^3.3.3",
"sugar": "^2.0.4",
"uglifyify": "^4.0.5",
"underscore": "^1.9.0"
},
"devDependencies": {
"expect.js": "^0.3.1",
"gulp": "^3.9.1",
"gulp-google-webfonts": "^1.0.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^3.1.0",
"gulp-scss": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-strip-debug": "^3.0.0",
"gulp-uglify-es": "^1.0.1",
"lodash.assign": "^4.2.0",
"mocha": "^5.0.4",
"node-fetch": "^2.1.1",
"require-dir": "^1.0.0",
"requirejs": "^2.3.5",
"sinon": "^4.4.6",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.11.0",
"whatwg-fetch": "^2.0.4",
"gulp-autoprefixer": "^5.0.0",
"gulp-bump": "^3.1.0",
"gulp-cache": "^1.0.2",
"gulp-concat": "^2.6.1",
"gulp-cssnano": "^2.1.2",
"gulp-html-replace": "^1.6.2",
"gulp-htmlmin": "^4.0.0",
"gulp-inject": "^4.3.1",
"gulp-uglify": "^3.0.0"
}
}

49
setup/web/server.js Normal file
View File

@ -0,0 +1,49 @@
const express = require('express');
const path = require('path');
const logger = require('log4js').getLogger('Server');
const db = require('./server/db-connector').dbConnection;
const dbTestdata = require('./server/db-testdata')(db);
const nameGen = require('./server/name-gen');
logger.level = 'debug';
const app = express();
const port = process.env.PORT || 9000;
const sitePath = 'live';
app.use(express.static(path.join(__dirname, sitePath)));
// app.get('/weather', cache('15 minutes'), (req, res) => {
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
app.get('/data', (req, res) => {
dbTestdata.sqlGetSimpleList().then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
});
});
app.listen(port, (err) => {
if (err)
return logger.error('Server error:', err);
logger.info(`Test Server is listening on ${port}`);
const i = setInterval(()=>{
dbTestdata.sqlInsertData(nameGen.gen()).then((d) => {
logger.debug(d);
}).catch((e) => {
logger.error(e);
});
}, 15000)
});

View File

@ -0,0 +1,22 @@
const pgp = require('pg-promise')();
const connectionString = process.env.DATABASE_URL;
/*const cn = {
connectionString:connectionString
};*/
const cn = {
host: process.env.HOST,
port: 5432,
database: process.env.DATABASE,
user: process.env.USER,
password: process.env.PASSWORD
};
console.log('>> connectionString', cn);
exports.dbConnection = pgp(cn);

View File

@ -0,0 +1,40 @@
const logger = require('log4js').getLogger('db-testdata');
logger.level = 'debug';
module.exports = function(db) {
const module = {};
module.sqlGetSimpleList = function(id) {
return new Promise(function(resolve, reject) {
db.manyOrNone('select * from "test";', [id])
.then(function(d) {
// console.log(d);
return resolve(d);
})
.catch((err)=> {
// console.log(err);
return reject(err);
});
});
};
module.sqlInsertData = function(name) {
logger.info('Insert ', name);
return new Promise(function(resolve, reject) {
db.func('insert_company',
[name])
.then(()=> {
return resolve('ok');
})
.catch((err)=> {
return reject(err);
});
});
};
return module;
};

View File

@ -0,0 +1,61 @@
/**
* Created by mdonnel on 20/04/2017.
*/
Array.prototype.random = function () {
return this[Math.floor((Math.random() * this.length))];
};
const left = ['Alabama',
'Alaska',
'Arizona',
'Maryland',
'Nevada',
'Mexico',
'Texas',
'Utah',
'Glasgow',
'Inverness',
'Edinburgh',
'Dumbarton',
'Balloch',
'Renton',
'Cardross',
'Dundee',
'Paisley',
'Hamilton',
'Greenock',
'Falkirk',
'Irvine',
'Renfrew',
'Erskine',
'London',
'Hammersmith',
'Islington',
'Silver', 'Black', 'Yellow', 'Purple', 'White', 'Pink', 'Red', 'Orange', 'Brown', 'Green', 'Blue',
'Amber', 'Aqua', 'Azure', 'Bronze', 'Coral', 'Copper', 'Crimson', 'Cyan', 'Ginger', 'Gold', 'Indigo', 'Jade'
];
const right = ['Aganju', 'Cygni', 'Akeron', 'Antares', 'Aragoth', 'Ardus', 'Carpenter',
'Cooper', 'Dahin', 'Capella', 'Endriago', 'Gallina', 'Fenris', 'Freya', 'Glenn', 'Grissom',
'Jotunheim', 'Kailaasa', 'Lagarto', 'Muspelheim', 'Nifleheim', 'Primus', 'Vega', 'Ragnarok',
'Shepard', 'Slayton', 'Tarsis', 'Mercury', 'Venus', 'Mars', 'Earth', 'Terra', 'Jupiter',
'Saturn', 'Uranus', 'Neptune', 'Pluto', 'Europa', 'Ganymede', 'Callisto', 'Titan', 'Juno',
'Eridanus', 'Scorpius', 'Crux', 'Cancer', 'Taurus', 'Lyra', 'Andromeda', 'Virgo', 'Aquarius',
'Cygnus', 'Corvus', 'Taurus', 'Draco', 'Perseus', 'Pegasus', 'Gemini', 'Columbia', 'Bootes',
'Orion', 'Deneb', 'Merope', 'Agate', 'Amber', 'Beryl', 'Calcite', 'Citrine', 'Coral', 'Diamond',
'Emerald', 'Garnet', 'Jade', 'Lapis', 'Moonstone', 'Obsidian', 'Onyx', 'Opal', 'Pearl', 'Quartz',
'Ruby', 'Sapphire', 'Topaz', 'Iron', 'Lead', 'Nickel', 'Copper', 'Zinc', 'Tin', 'Manes', 'Argon',
'Neon', 'Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India', 'Juliett',
'Kilo', 'Lima', 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform',
'Victor', 'Whisky', 'Xray', 'Yankee', 'Zulu'];
function gen(){
return (`${left.random() } ${ right.random() }`);
}
module.exports = {gen};

View File

@ -0,0 +1,137 @@
// import MUI colors
@import "./node_modules/muicss/lib/sass/mui/colors";
// customize MUI variables
$mui-primary-color: mui-color('pink', '500');
$mui-primary-color-dark: mui-color('blue-grey', '700');
$mui-primary-color-light: mui-color('blue-grey', '100');
$mui-accent-color: mui-color('deep-purple', '900');
$mui-accent-color-dark: mui-color('indigo', 'A100');
$mui-accent-color-light: mui-color('indigo', 'A400');
$mui-base-font-family: 'Roboto', "Helvetica Neue", Helvetica, Arial, Verdana, "Trebuchet MS";
$mui-base-font-weight: 400;
$mui-appbar-font-color: mui-color('black') !default;
$mui-link-font-color: mui-color('pink', '900') !default;
// import MUI SASS
@import "./node_modules/muicss/lib/sass/mui";
body {
background-color: mui-color('grey', '100');
}
#header {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 2;
transition: left 0.2s;
}
ul {
margin: 0;
padding: 0;
}
li {
display: inline;
margin: 0;
padding: 0 4px 0 0;
}
.dates {
padding: 2px;
border: solid 1px #80007e;
background-color: #ffffff;
}
#btc, #fx, #trend {
font-size: 85%;
}
.up, .ontime, .trendUp {
color: mui-color('green') !important;
}
.down, .delayed, .trendDown {
color: $mui-text-danger !important;
}
.nochange {
color: #000000;
}
.password {
border: 1px solid mui-color('grey', '400');
background-color: mui-color('grey', '200');
font-family: monospace;
white-space: pre;
}
.trendUp:before {
content: "";
}
.trendDown:before {
content: ''
}
.card {
position: relative;
background-color: #fff;
min-height: 48px;
margin: 8px;
border-bottom-color: #666666;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.itemRow {
background-color: #fff;
min-height: 48px;
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
line-height: 48px;
}
.cardTitle {
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
}
.entry {
height: 36px;
margin: 6px 0;
vertical-align: middle;
}
.time {
font-family: 'Roboto';
}
.titleBar {
font-family: 'Gotham Light';
font-size: 125%;
}
.tableBody {
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.unsliced {
height: 455px;
}
.sliced {
height: 300px;
}

44
setup/web/src/index.html Normal file
View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<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>PG TEST</title>
<link href="/css/mui.custom.css" rel="stylesheet" type="text/css"/>
<meta name="theme-color" content="#ffffff">
</head>
<body>
<div class="appPanel" data-id="main">
<div class="mui-container">
<div id="greet"></div>
<div id="location" class="mui-row" style="display: none;">PG TEST</div>
</div>
<div class="mui-container" id="viewFrame">
<div id="dataShell" class="mui-panel" style="display: ;">
<div id="dataTitle" class="mui--text-title cardTitle">Data</div>
<div id="data"></div>
</div>
</div>
</div>
<script src="js/vendor.js" async></script>
<script type='module' src="js/bundle.js" async></script>
<noscript>
<!-- anchor linking to external file -->
<h1>Javascript disabled</h1>
<p>This really requires javascript to work</p>
</noscript>
</body>
</html>

View File

@ -0,0 +1,97 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const DataItem = Backbone.Model.extend({
});
const DataCollection = Backbone.Collection.extend({
'model': DataItem
});
// const dataCollection = new dataCollection();
const DataItemView = Backbone.View.extend({
'tagName': 'div',
'className' : 'mui-row',
'template': _.template(`
<div class="mui-col-xs-4 mui-col-md-4"><%=id%></div>
<div class="mui-col-xs-8 mui-col-md-8"><%=company_name%></div>
`),
'initialize': function() {
this.render();
},
'render': function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
const DataListModel = Backbone.Model.extend({
'defaults' : function (obj) {
// return a new object
return {
'update' : new Date().getTime()
};
}, 'initialize': function() {
this.dataCollection = new DataCollection();
this.listenTo(this, 'change:update', this.onChange);
this.getData();
},
'onChange': function() {
this.getData();
},
'getData': function() {
request({
'url': `${window.loc.origin}/data`,
'method': 'GET', 'qs': {
}
}, function(err, res, body) {
if (err)
console.error(err);
else {
const fsJSON = JSON.parse(body);
this.dataCollection.reset(fsJSON);
this.logUpdate();
}
}.bind(this));
}, 'logUpdate': function() {
const time = new Date().getTime() ;
this.set('time', time);
this.timerID = setTimeout(
() => this.tick(),
30 * 1000
);
},
'tick': function() {
this.set('update', new Date().getTime());
}
});
const DataListView = Backbone.View.extend({
'initialize': function(options) {
this.model.dataCollection.bind('reset', this.render, this);
},
'render' : function() {
this.$el.empty();
this.model.dataCollection.each(function(item) {
const niView = new DataItemView({ 'model': item });
this.$el.append(niView.el);
}, this);
}
});
module.exports = { DataListModel, DataListView };

19
setup/web/src/js/app.js Normal file
View File

@ -0,0 +1,19 @@
require('muicss');
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { DataListModel, DataListView } = require('./DataList');
var app = app || {};
(function () {
window.loc = new URL(window.location);
app.dataList = new DataListView({ 'model': new DataListModel(), 'el':'#data' });
})();