diff --git a/.dev.env b/.env.example similarity index 62% rename from .dev.env rename to .env.example index 8c175b09..95f9c4d1 100644 --- a/.dev.env +++ b/.env.example @@ -6,25 +6,28 @@ # Template files must be committed to the VCS, but must not contain # any secret values. -EXPRESS_PORT=3000 +EXPRESS_PORT= +EXPRESS_PROTOCOL= +EXPRESS_SECRET= -EMAIL_HOST=localhost -EMAIL_PORT=3003 -EMAIL_SECURE=false -EMAIL_FROM_NAME=Droplet (Dev) -EMAIL_FROM_ADDRESS=noreply@vylpes.com -EMAIL_AUTH_ENABLE=false +EMAIL_HOST= +EMAIL_PORT= +EMAIL_SECURE= +EMAIL_FROM_NAME= +EMAIL_FROM_ADDRESS= +EMAIL_AUTH_ENABLE= EMAIL_AUTH_USER= EMAIL_AUTH_PASS= -EMAIL_TLS_REJECT_UNAUTHORISED=true +EMAIL_TLS_REJECT_UNAUTHORISED= EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK=http://localhost:3000/auth/password-reset/reset?token={token} EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK=http://localhost:3000/auth/verify?token={token} -DB_HOST=127.0.0.1 -DB_PORT=3001 -DB_NAME=droplet -DB_AUTH_USER=dev -DB_AUTH_PASS=dev -DB_SYNC=true -DB_LOGGING=true +DB_HOST= +DB_PORT= +DB_NAME= +DB_AUTH_USER= +DB_AUTH_PASS= +DB_SYNC= +DB_LOGGING= +DB_DATA_LOCATION= \ No newline at end of file diff --git a/.github/workflows/stage.yml b/.github/workflows/stage.yml new file mode 100644 index 00000000..de293aa4 --- /dev/null +++ b/.github/workflows/stage.yml @@ -0,0 +1,78 @@ +name: Deploy To Stage + +on: + push: + branches: + - develop + +jobs: + build: + environment: stage + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 18.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test + + - name: RSync to Stage Server + uses: D3rHase/rsync-deploy-action@v0.2 + with: + HOST: ${{ secrets.SSH_HOST }} + PORT: ${{ secrets.SSH_PORT }} + USER: ${{ secrets.SSH_USER }} + PRIVATE_SSH_KEY: ${{ secrets.SSH_KEY }} + REPOSITORY_PATH: ${{ secrets.SSH_REPO_PATH }} + SERVER_PATH: ${{ secrets.SSH_SERVER_PATH }} + + deploy: + environment: stage + needs: build + runs-on: ubuntu-latest + steps: + - uses: appleboy/ssh-action@v1.0.0 + env: + DB_NAME: ${{ secrets.DB_NAME }} + DB_AUTH_USER: ${{ secrets.DB_AUTH_USER }} + DB_AUTH_PASS: ${{ secrets.DB_AUTH_PASS }} + DB_HOST: ${{ secrets.DB_HOST }} + DB_PORT: ${{ secrets.DB_PORT }} + DB_ROOT_HOST: ${{ secrets.DB_ROOT_HOST }} + DB_SYNC: ${{ secrets.DB_SYNC }} + DB_LOGGING: ${{ secrets.DB_LOGGING }} + DB_DATA_LOCATION: ${{ secrets.DB_DATA_LOCATION }} + EXPRESS_PORT: ${{ secrets.EXPRESS_PORT }} + EXPRESS_PROTOCOL: ${{ secrets.EXPRESS_PROTOCOL }} + EXPRESS_SECRET: ${{ secrets.EXPRESS_SECRET }} + EMAIL_HOST: ${{ secrets.EMAIL_HOST }} + EMAIL_PORT: ${{ secrets.EMAIL_PORT }} + EMAIL_SECURE: ${{ secrets.EMAIL_SECURE }} + EMAIL_FROM_NAME: ${{ vars.EMAIL_FROM_NAME }} + EMAIL_FROM_ADDRESS: ${{ vars.EMAIL_FROM_ADDRESS }} + EMAIL_AUTH_ENABLE: ${{ secrets.EMAIL_AUTH_ENABLE }} + EMAIL_AUTH_USER: ${{ secrets.EMAIL_AUTH_USER }} + EMAIL_AUTH_PASS: ${{ secrets.EMAIL_AUTH_PASS }} + EMAIL_TLS_REJECT_UNAUTHORISED: ${{ secrets.EMAIL_TLS_REJECT_UNAUTHORISED }} + EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK: ${{ vars.EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK }} + EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK: ${{ vars.EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK }} + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} + port: ${{ secrets.SSH_PORT }} + envs: DB_NAME,DB_AUTH_USER,DB_AUTH_PASS,DB_HOST,DB_PORT,DB_ROOT_HOST,DB_SYNC,DB_LOGGING,DB_DATA_LOCATION,EXPRESS_PORT,EXPRESS_PROTOCOL,EXPRESS_SECRET,EMAIL_HOST,EMAIL_PORT,EMAIL_SECURE,EMAIL_FROM_NAME,EMAIL_FROM_ADDRESS,EMAIL_AUTH_ENABLE,EMAIL_AUTH_USER,EMAIL_AUTH_PASS,EMAIL_TLS_REJECT_UNAUTHORISED,EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK,EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK + script: | + cd ~/app \ + && docker compose down \ + && (pm2 stop droplet || true) \ + && (pm2 delete droplet || true) \ + && docker compose up -d \ + && sleep 10 \ + && yarn run db:up \ + && pm2 start --name droplet dist/index.js \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 00000000..be0a8a96 --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,23 @@ +name: Testing + +on: + push: + branches: + - feature/* + - hotfix/* + - renovate/* + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 18.x + - run: yarn install + - run: yarn build + - run: yarn test \ No newline at end of file diff --git a/.gitignore b/.gitignore index bf354267..21aa49d8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,8 @@ coverage/ ormconfig.json yarn-error.log .DS_Store -secret.txt \ No newline at end of file +secret.txt + +.terraform/ +*.tfvars +*.tfstate \ No newline at end of file diff --git a/.prod.env b/.prod.env deleted file mode 100644 index dc9d35de..00000000 --- a/.prod.env +++ /dev/null @@ -1,30 +0,0 @@ -# Security Warning! Do not commit this file to any VCS! -# This is a local file to speed up development process, -# so you don't have to change your environment variables. -# -# This is not applied to `.env.template`! -# Template files must be committed to the VCS, but must not contain -# any secret values. - -EXPRESS_PORT=3020 - -EMAIL_HOST=localhost -EMAIL_PORT=3023 -EMAIL_SECURE=false -EMAIL_FROM_NAME=Droplet -EMAIL_FROM_ADDRESS=noreply@vylpes.com -EMAIL_AUTH_ENABLE=false -EMAIL_AUTH_USER= -EMAIL_AUTH_PASS= -EMAIL_TLS_REJECT_UNAUTHORISED=true - -EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK=http://droplet.vylpes.xyz/auth/password-reset/reset?token={token} -EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK=http://droplet.vylpes.xyz/auth/verify?token={token} - -DB_HOST=127.0.0.1 -DB_PORT=3021 -DB_NAME=droplet -DB_AUTH_USER=prod -DB_AUTH_PASS=prod -DB_SYNC=false -DB_LOGGING=false diff --git a/.stage.env b/.stage.env deleted file mode 100644 index 608a2017..00000000 --- a/.stage.env +++ /dev/null @@ -1,30 +0,0 @@ -# Security Warning! Do not commit this file to any VCS! -# This is a local file to speed up development process, -# so you don't have to change your environment variables. -# -# This is not applied to `.env.template`! -# Template files must be committed to the VCS, but must not contain -# any secret values. - -EXPRESS_PORT=3010 - -EMAIL_HOST=localhost -EMAIL_PORT=3013 -EMAIL_SECURE=false -EMAIL_FROM_NAME=Droplet (Stage) -EMAIL_FROM_ADDRESS=noreply@vylpes.com -EMAIL_AUTH_ENABLE=false -EMAIL_AUTH_USER= -EMAIL_AUTH_PASS= -EMAIL_TLS_REJECT_UNAUTHORISED=true - -EMAIL_TEMPLATE_PASSWORDRESET_RESETLINK=http://droplet-stage.vylpes.xyz/auth/password-reset/reset?token={token} -EMAIL_TEMPLATE_VERIFYUSER_VERIFYLINK=http://droplet-stage.vylpes.xyz/auth/verify?token={token} - -DB_HOST=127.0.0.1 -DB_PORT=3011 -DB_NAME=droplet -DB_AUTH_USER=stage -DB_AUTH_PASS=stage -DB_SYNC=false -DB_LOGGING=false diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 65e6acb7..00000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: "3" - -volumes: - prod_database_data: - -services: - database: - image: mysql/mysql-server - command: --default-authentication-plugin=mysql_native_password - restart: always - environment: - - MYSQL_DATABASE=droplet - - MYSQL_USER=prod - - MYSQL_PASSWORD=prod - - MYSQL_ROOT_PASSWORD=root - - MYSQL_ROOT_HOST=0.0.0.0 - ports: - - "3021:3306" - volumes: - - stage_database_data:/var/lib/mysql - - phpmyadmin: - image: phpmyadmin - restart: always - ports: - - "3022:80" - environment: - - PMA_ARBITRARY=1 - - mail: - image: namshi/smtp - ports: - - "3023:25" \ No newline at end of file diff --git a/docker-compose.stage.yml b/docker-compose.stage.yml deleted file mode 100644 index 56465b27..00000000 --- a/docker-compose.stage.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: "3" - -volumes: - stage_database_data: - -services: - database: - image: mysql/mysql-server - command: --default-authentication-plugin=mysql_native_password - restart: always - environment: - - MYSQL_DATABASE=droplet - - MYSQL_USER=stage - - MYSQL_PASSWORD=stage - - MYSQL_ROOT_PASSWORD=root - - MYSQL_ROOT_HOST=0.0.0.0 - ports: - - "3011:3306" - volumes: - - stage_database_data:/var/lib/mysql - - phpmyadmin: - image: phpmyadmin - restart: always - ports: - - "3012:80" - environment: - - PMA_ARBITRARY=1 - - mail: - image: namshi/smtp - ports: - - "3013:25" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index cb1d32cc..7bb8eb12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,34 +1,22 @@ version: "3" -volumes: - dev_database_data: - services: database: image: mysql/mysql-server command: --default-authentication-plugin=mysql_native_password restart: always environment: - - MYSQL_DATABASE=droplet - - MYSQL_USER=dev - - MYSQL_PASSWORD=dev - - MYSQL_ROOT_PASSWORD=root - - MYSQL_ROOT_HOST=0.0.0.0 + - MYSQL_DATABASE=$DB_NAME + - MYSQL_USER=$DB_AUTH_USER + - MYSQL_PASSWORD=$DB_AUTH_PASS + - MYSQL_ROOT_PASSWORD=$DB_AUTH_PASS + - MYSQL_ROOT_HOST=$DB_ROOT_HOST ports: - - "3001:3306" + - "3306:3306" volumes: - - dev_database_data:/var/lib/mysql - - phpmyadmin: - image: phpmyadmin - restart: always - ports: - - "3002:80" - environment: - - PMA_ARBITRARY=1 - - mail: + - $DB_DATA_LOCATION:/var/lib/mysql + mailhog: image: mailhog/mailhog ports: - - "3003:1025" - - "3004:8025" \ No newline at end of file + - 1025:1025 # smtp server + - 8025:8025 # web ui \ No newline at end of file diff --git a/infrastructure/.terraform.lock.hcl b/infrastructure/.terraform.lock.hcl new file mode 100644 index 00000000..cef51961 --- /dev/null +++ b/infrastructure/.terraform.lock.hcl @@ -0,0 +1,26 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/vultr/vultr" { + version = "2.16.1" + constraints = "2.16.1" + hashes = [ + "h1:qd1tyNxmbcaju1/hqqYcc2RabUYNmI0zt9uL0RD/prg=", + "zh:12bc50f3ffbec94bbd8b29de5ef0d3b6e5407aefa5dac4f96c460a0930e5c052", + "zh:15a8a7825ad5934811a04c4864b0059cdf5fcb24f7f1424ab50f47b192769857", + "zh:508c8580f6844fbd3629a712f080298636c32d94645d8c115a8afac37c09b74a", + "zh:70e2d5ffbaf680a79e2808bec7cbfce8cc648d867309959bdfe7cccfac3b11a0", + "zh:7c559bc1c3b8566a0d49929720a94b57c54e58e6fe6a37afbb61eb846b7f366f", + "zh:810562af419bb89dafc556e50c162656c495654496ffb99d5816e2f975ada2a3", + "zh:997ba870f92870daec5fc632ed706d86e3df01d02d70b713e983cd8833d8700c", + "zh:9f189d81f1dbd584498ecfc6ae847fc7bf2267630688d24d6a7314689297acaf", + "zh:9fb0dda2f0ad392cd8354978eafb5b77b577634054717b69a6f0fee447f00801", + "zh:a9efbce3fe4435fad5fda98aeab1bf024327965ffaa74bffbddced5be91bf50b", + "zh:c099f640a1c4c2cf588ef7e3c85b61853424ccafff8e035c82af1d0f9130aad7", + "zh:c2b76ea52cc72325c115985573f68f57dccf382ba532ed12645fa8c958049d70", + "zh:c8171de3b5ebae7d51975cd6a9fbc4d6d3ed3e591719d11d2d83a14edbd0d56e", + "zh:d4636f27dbe3df3e4db3998063a9b5eb064911b1950e6874d28887d35e02700f", + "zh:e09e2ae4232a69d9a1edabc6d4e600107563c2909e009e5e6d6dde2acdd786c5", + "zh:e670e2b5060ec3ac67f234be52d8c85149556ab6601899564a8db45c4c4e02ef", + ] +} diff --git a/infrastructure/cloud-config.yml b/infrastructure/cloud-config.yml new file mode 100644 index 00000000..4bf4ad28 --- /dev/null +++ b/infrastructure/cloud-config.yml @@ -0,0 +1,62 @@ +#cloud-config + +users: + - default + - name: vylpes + primary-group: vylpes + shell: /bin/bash + groups: users, docker, sudo + ssh-authorized-keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/EDwCD5t3/WpyqDwpGAJMHTfbpPT9W5ffQfVqlMFl4TA8eVzPS+H/czy3PQOMUAgVKtO1Xup8GdFovN6dMVEk4IPNNS8w8OheSUKOCsRq1A1r2vDHZ/h393l4A9jWGq2HRBdUdQhy7SDXC/VBY2Yo5IQuAhI6gaXz/AENQ0Qf5PpMevfGs2jVu+0Ic5J1ccjX+8FWXZtRi0VF84WIixUfdyNgsi0RkywlRKyas7bDAWRtfB556qrxaQd0iTdyL0sCR+XAIx5cGbf5knFUXXeQvsASczgzn6X4Jzzgnv/MB0K/nALZeETxI9IXki4yQuGSomiR9WRYSIRLZsjrWxUTwk5RPMuAofW8hr20HL5QqBLVRzFVf7RipmCug8JAx8EE1uk8SLFOJCQBwzYTTbp1KAsJtVkUL+0YEIsgFuhPcXXIN1DbHCeKu5WHKnPxx9kwW/bx9q+Id1crYFWMm/a+MJPBNhIGmv9+HAWOeSZDeROCYd9Nx3yEdT15+hn5L/GZaMyk5AbCwjFWVwUVIAt5Pcn/AkBngRc5DuA+JE7TbbeWsYyN379gwswl/IYBP6fO13V80iDwwkQdizvBCtnmD3Q200aI1unpydR4lZlpPyP0ug0t7jJSEAn9nzQmsMSw7sI9BJ3uf49Y8Qf7LPd9llQeJl+qFXpvDBcji/qHYQ== vylpes@Ethans-MacBook-Pro.local + sudo: ['ALL=(ALL) NOPASSWD:ALL'] + +apt: + sources: + docker.list: + source: deb [arch=amd64] https://download.docker.com/linux/debian $RELEASE stable + keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 + nodejs.list: + source: deb [signed-by=$KEY_FILE] https://deb.nodesource.com/node_18.x $RELEASE main + keyid: 9FD3B784BC1C6FC31A8A0A1C1655A0AB68576280 + +write_files: + - path: /etc/nginx/sites-enabled/app + owner: root:root + permissions: '0775' + content: | + server { + server_name droplet-stage.vylpes.xyz; + listen 80; + + location / { + proxy_pass http://127.0.0.1:3000; + } + } + - path: /opt/droplet/certbot.sh + owner: root:root + permissions: '0775' + content: | + sudo certbot --nginx -d droplet-stage.vylpes.xyz --non-interactive --agree-tos -m ethan@vylpes.com + +packages: + - git + - rsync + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + - nodejs + - npm + - nginx + - snapd + +runcmd: + - snap install core + - snap install --classic certbot + - ln -sf /snap/bin/cerbot /usr/bin/cerbot + - npm install -g yarn pm2 + - ufw limit ssh + - ufw allow 80 + - ufw allow 443 + - ufw enable \ No newline at end of file diff --git a/infrastructure/main.tf b/infrastructure/main.tf new file mode 100644 index 00000000..d17d354e --- /dev/null +++ b/infrastructure/main.tf @@ -0,0 +1,56 @@ +# Variables +variable "VULTR_API_KEY" { + description = "The Vultr API Key" +} + +variable "INSTANCE_NAME" { + description = "The name of the project this instance is for" +} + +variable "INSTANCE_ENV" { + description = "The environment this project will be running" + default = "prod" +} + +variable "INSTANCE_LOCATION" { + description = "The location all instances will be generated in" + default = "lhr" +} + +# Providers +terraform { + required_providers { + vultr = { + source = "vultr/vultr" + version = "2.16.1" + } + } +} + +provider "vultr" { + api_key = var.VULTR_API_KEY + rate_limit = 100 + retry_limit = 3 +} + +# Resources +resource "vultr_instance" "vps-app" { + label = "vps-${var.INSTANCE_NAME}-${var.INSTANCE_ENV}-${var.INSTANCE_LOCATION}-app" + hostname = "vps-${var.INSTANCE_NAME}-${var.INSTANCE_ENV}-${var.INSTANCE_LOCATION}-app" + plan = "vc2-1c-1gb" + region = var.INSTANCE_LOCATION + os_id = "2136" + enable_ipv6 = false + user_data = file("./cloud-config.yml") +} + +resource "vultr_reserved_ip" "ip-app" { + region = var.INSTANCE_LOCATION + ip_type = "v4" + instance_id = "${vultr_instance.vps-app.id}" +} + +# Outputs +output "instance_ip" { + value = vultr_instance.vps-app.main_ip +} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index bd86842b..5b92b447 100644 --- a/src/app.ts +++ b/src/app.ts @@ -73,7 +73,7 @@ export class App { private async SetupApp() { dotenv.config(); - const expressSessionSecret = readFileSync(`${process.cwd()}/secret.txt`).toString(); + const expressSessionSecret = process.env.EXPRESS_SECRET || readFileSync(`${process.cwd()}/secret.txt`).toString(); this._app.set('views', path.join(process.cwd(), 'views')); this._app.set('view engine', 'pug');