diff --git a/.envdev b/.envdev index 4553071..8ba1f5d 100644 --- a/.envdev +++ b/.envdev @@ -6,7 +6,7 @@ MAIL_SMTP=smtp.example.com MAIL_USERNAME=foobar@example.com MAIL_PASSWORD=foobar -DB_HOSTADDR=127.0.0.1 +DB_HOSTNAME=127.0.0.1 DB_DATABASE=template DB_USERNAME=template DB_PASSWORD=pikachu diff --git a/.gitignore b/.gitignore index 42c7858..a521a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,4 @@ public/*.js public/*.css public/*.map public/*.gz +docker-compose.yml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cd1774f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM node:15 + +# Change working directory +WORKDIR "/app" + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install npm production packages +RUN npm install + +COPY . /app + +EXPOSE 3000 + +USER node + +ENTRYPOINT ["npm", "start"] \ No newline at end of file diff --git a/README.md b/README.md index ba5ac30..8b58de3 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,15 @@ To set up this template, please ensure mariadb is running on the host computer, This should get the template working in development mode. -# Deployment +# Setup Deployment -When deploying your project, please ensure that you're using HTTPS protocol. It's not hard to set up using [certbot](https://certbot.eff.org/). +Eventually, a clean install will be this easy: + +``` +git clone template-url +npm run configure +docker-compose up +``` # Microservices @@ -54,14 +60,7 @@ TODO: more info. - configuration script - Docker, docker, docker. - Start here page - -Eventually, a clean install will be this easy: - -``` -git clone template-url -npm run configure -npm start -``` +- LICENSE # Email settings diff --git a/configure-script.js b/configure-script.js new file mode 100644 index 0000000..9d44e3a --- /dev/null +++ b/configure-script.js @@ -0,0 +1,248 @@ +//setup +const readline = require('readline'); +const fs = require('fs'); + +var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); + +//manually promisify this (util didn't work) +const question = (prompt, def) => { + return new Promise((resolve, reject) => { + rl.question(`${prompt} (${def}): `, answer => { + resolve(answer || def); + }); + }); +}; + +//questions +(async () => { + //project configuration + const projectName = await question('Project Name', 'template'); + const projectWebAddress = await question('Project Web Address', 'example.com'); + const projectMailSMTP = await question('Project Mail SMTP', 'smtp.example.com'); + const projectMailUser = await question('Project Mail Username', 'foobar@example.com'); + const projectMailPass = await question('Project Mail Password', 'foobar'); + const projectDBUser = await question('Project Database Username', projectName); + const projectDBPass = await question('Project Database Password', 'pikachu'); + + //news configuration + const newsName = await question('News Name', 'news'); + const newsDBUser = await question('News Database Username', newsName); + const newsDBPass = await question('News Database Password', 'charizard'); + const newsKey = await question('News Query Key', 'key'); + + //TODO: chat configuration + + //database configuration + const databaseRootPassword = await question('Database Root Password', 'password'); + const databaseTimeZone = await question('Database Timezone', 'Australia/Sydney'); + + //traefic configuration + const supportEmail = await question('Support Email', 'postmaster@example.com'); + +const yml = ` +services: + ${projectName}: + build: . + ports: + - "3000:3000" + labels: + - "traefik.enable=true" + - "traefik.http.routers.${projectName}router.rule=Host('${projectWebAddress}')" + - "traefik.http.routers.${projectName}router.entrypoints=websecure" + - "traefik.http.routers.${projectName}router.tls.certresolver=myresolver" + - "traefik.http.routers.${projectName}router.service=${projectName}service@docker" + - "traefik.http.services.${projectName}service.loadbalancer.server.port=3000" + environment: + - WEB_PROTOCOL=https + - WEB_ADDRESS=localhost + - WEB_PORT=3000 + - MAIL_SMTP=${projectMailSMTP} + - MAIL_USERNAME=${projectMailUser} + - MAIL_PASSWORD=${projectMailPass} + - DB_HOSTNAME=database + - DB_DATABASE=${projectName} + - DB_USERNAME=${projectDBUser} + - DB_PASSWORD=${projectDBPass} + - DB_TIMEZONE=${databaseTimeZone} + - SESSION_SECRET=secret + - SESSION_ADMIN=adminsecret + - NEWS_URI=http://news:3100/news + - NEWS_KEY=${newsKey} + networks: + - app-network + depends_on: + - database + + news: + image: krgamestudios/news-server:v1.0.0 + ports: + - "3100:3100" + labels: + - "traefik.enable=true" + - "traefik.http.routers.newsrouter.rule=Host('${projectWebAddress}')" + - "traefik.http.routers.newsrouter.entrypoints=websecure" + - "traefik.http.routers.newsrouter.tls.certresolver=myresolver" + - "traefik.http.routers.newsrouter.service=newsservice@docker" + - "traefik.http.services.newsservice.loadbalancer.server.port=3100" + environment: + - WEB_PORT=3100 + - DB_HOSTNAME=database + - DB_DATABASE=news + - DB_USERNAME=${newsDBUser} + - DB_PASSWORD=${newsDBPass} + - DB_TIMEZONE=${databaseTimeZone} + - QUERY_LIMIT=10 + - QUERY_KEY=${newsKey} + networks: + - app-network + depends_on: + - database + + #chat: + # image: krgamestudios/chat-server + # ports: + # - "3200:3200" + + database: + image: mariadb + restart: always + environment: + MYSQL_ROOT_PASSWORD: ${databaseRootPassword} + volumes: + - ./mysql:/var/lib/mysql + networks: + - app-network + + traefik: + image: "traefik:v2.4" + container_name: "traefik" + command: > + - "--api.insecure=false" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + - "--certificatesresolvers.myresolver.acme.email=${supportEmail} + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + volumes: + - "./letsencrypt:/letsencrypt" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + networks: + - app-network + +networks: + app-network: + driver: bridge +`; + + fs.writeFileSync('docker-compose.yml', yml); +})() + .then(() => rl.close()) + .catch(e => console.error(e)) +; + +/* Default below + +services: + template: + build: . + ports: + - "3000:3000" + labels: + - "traefik.enable=true" + - "traefik.http.routers.templaterouter.rule=Host('template.com')" + - "traefik.http.routers.templaterouter.entrypoints=websecure" + - "traefik.http.routers.templaterouter.tls.certresolver=myresolver" + - "traefik.http.routers.templaterouter.service=templateservice@docker" + - "traefik.http.services.templateservice.loadbalancer.server.port=3000" + environment: + - WEB_PROTOCOL=https + - WEB_ADDRESS=localhost + - WEB_PORT=3000 + - MAIL_SMTP=smtp.example.com + - MAIL_USERNAME=foobar@example.com + - MAIL_PASSWORD=foobar + - DB_HOSTNAME=database + - DB_DATABASE=template + - DB_USERNAME=template + - DB_PASSWORD=pikachu + - DB_TIMEZONE=Australia/Sydney + - SESSION_SECRET=secret + - SESSION_ADMIN=adminsecret + networks: + - app-network + depends_on: + - database + + news: + image: krgamestudios/news-server:v1.0.0 + ports: + - "3100:3100" + labels: + - "traefik.enable=true" + - "traefik.http.routers.newsrouter.rule=Host('template.com')" + - "traefik.http.routers.newsrouter.entrypoints=websecure" + - "traefik.http.routers.newsrouter.tls.certresolver=myresolver" + - "traefik.http.routers.newsrouter.service=newsservice@docker" + - "traefik.http.services.newsservice.loadbalancer.server.port=3100" + environment: + - WEB_PORT=3100 + - DB_HOSTNAME=database + - DB_DATABASE=news + - DB_USERNAME=news + - DB_PASSWORD=charizard + - DB_TIMEZONE=Australia/Sydney + - QUERY_LIMIT=10 + - QUERY_KEY=key + networks: + - app-network + depends_on: + - database + + #chat: + # image: krgamestudios/chat-server + # ports: + # - "3200:3200" + + database: + image: mariadb + restart: always + environment: + MYSQL_ROOT_PASSWORD: example + volumes: + - ./mysql:/var/lib/mysql + networks: + - app-network + + traefik: + image: "traefik:v2.4" + container_name: "traefik" + command: + - "--api.insecure=false" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + - "--certificatesresolvers.myresolver.acme.email=postmaster@example.com" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + volumes: + - "./letsencrypt:/letsencrypt" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + networks: + - app-network + +networks: + app-network: + driver: bridge + +*/ \ No newline at end of file diff --git a/package.json b/package.json index d73bb02..c66202d 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A website template using the MERN stack.", "main": "server/server.js", "scripts": { + "configure": "node configure-script.js", "start": "npm run build && node server/server.js", "build": "npm run build:server && npm run build:client", "build:server": "exit 0", diff --git a/server/database/index.js b/server/database/index.js index bde6631..7d01b93 100644 --- a/server/database/index.js +++ b/server/database/index.js @@ -1,7 +1,7 @@ const Sequelize = require('sequelize'); const sequelize = new Sequelize(process.env.DB_DATABASE, process.env.DB_USERNAME, process.env.DB_PASSWORD, { - host: process.env.DB_HOSTADDR, + host: process.env.DB_HOSTNAME, dialect: 'mariadb', timezone: process.env.DB_TIMEZONE, logging: false diff --git a/webpack.config.js b/webpack.config.js index 6ea4f2f..e0283b5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -42,8 +42,8 @@ module.exports = ({ production, analyzer }) => { plugins: [ new DefinePlugin({ 'process.env': { - 'NEWS_URI': '"http://dev-news.eggtrainer.com:3100/news"', - 'NEWS_KEY': '"key"', + 'NEWS_URI': production ? `${process.env.NEWS_KEY}` : '"http://dev-news.eggtrainer.com:3100/news"', + 'NEWS_KEY': production ? `${process.env.NEWS_KEY}` : 'key', } }), new CleanWebpackPlugin({