diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..40a3a6fe --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +{ + "name": "svelte-rich-text-dev", + "dockerComposeFile": ["../docker-compose.dev.yml"], + "service": "svelte-rich-text-dev", + "workspaceFolder": "/app", + "customizations": { + "vscode": { + "extensions": [ + "etmoffat.pip-packages", + "aaron-bond.better-comments", + "formulahendry.auto-rename-tag", + "formulahendry.code-runner", + "github.copilot", + "github.vscode-pull-request-github", + "ms-azuretools.vscode-docker", + "ms-vscode-remote.remote-containers", + "ms-vscode-remote.remote-ssh", + "ms-vscode-remote.vscode-remote-extensionpack", + "ms-toolsai.jupyter", + "ritwickdey.liveserver", + "visualstudioexptteam.vscodeintellicode", + "vscode-icons-team.vscode-icons", + "esbenp.prettier-vscode", + "jpotterm.simple-vim", + "wakatime.vscode-wakatime", + "svelte.svelte-vscode", + "ardenivanov.svelte-intellisense", + "fivethree.vscode-svelte-snippets" + ], + "settings": { + "python.pythonPath": "/usr/local/bin/python", + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, + "python.linting.flake8Args": ["--max-line-length=88"], + "python.formatting.provider": "ms-python.python", + "editor.formatOnSave": true + } + } + }, + "postCreateCommand": "sudo apt-get update && sudo apt-get upgrade -y" +} diff --git a/.env b/.env new file mode 100644 index 00000000..f0a0bf24 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DEPLOY_TARGET=node \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..039cf90d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,14 @@ +# These are supported funding model platforms + +github: [valiantlynx] +patreon: valiantlynx +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..6867cf8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..3e9dad05 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..72718d5a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..ef44393f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,29 @@ +# `dependabot.yml` file with updates +# disabled for Docker and limited for npm + +version: 2 +updates: + # Configuration for Dockerfile + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'weekly' + # Disable all pull requests for Docker dependencies + # open-pull-requests-limit: 0 + + # Configuration for npm + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' + ignore: + # Ignore updates to packages that start with 'aws' + # Wildcards match zero or more arbitrary characters + - dependency-name: 'aws*' + # Ignore some updates to the 'express' package + - dependency-name: 'express' + # Ignore only new versions for 4.x and 5.x + versions: ['4.x', '5.x'] + # For all packages, ignore all patch updates + - dependency-name: '*' + update-types: ['version-update:semver-patch'] diff --git a/.github/extra_workflows/deploy-azure.yml b/.github/extra_workflows/deploy-azure.yml new file mode 100644 index 00000000..1eadfc83 --- /dev/null +++ b/.github/extra_workflows/deploy-azure.yml @@ -0,0 +1,29 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy container app to Azure Web App - svelte-rich-text + +on: + workflow_dispatch: + workflow_run: + workflows: ["svelte-rich-text"] # This triggers the deployment after svelte-rich-text workflow completes + types: + - completed + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: 'production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + + steps: + - name: Deploy to Azure Web App + id: deploy-to-webapp + if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main' + uses: azure/webapps-deploy@v2 + with: + app-name: 'svelte-rich-text' + slot-name: 'production' + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE }} + images: 'index.docker.io/${{ secrets.DOCKER_HUB_USERNAME }}/svelte-rich-text:commit-${{ github.sha }}' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..64dcd699 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,42 @@ +name: Deploy +run-name: ${{ github.actor }} is deploying πŸš€πŸš€πŸš€ + +on: + workflow_dispatch: + workflow_run: + workflows: ["svelte-rich-text"] # This triggers the deployment after ollama-docker workflow completes + types: + - completed + +env: + AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + +jobs: + build-infra: + name: terraform-ci-cd + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main' + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Terraform + uses: hashicorp/setup-terraform@v2 + - name: Terraform Init + id: init + run: terraform init + working-directory: ./terraform + - name: Terraform Validate + id: validate + run: terraform validate + working-directory: ./terraform + - name: Terraform Plan + id: plan + run: terraform plan + working-directory: ./terraform + - name: Terraform Apply + id: apply + run: terraform apply --auto-approve + working-directory: ./terraform diff --git a/.github/workflows/destroy.yml b/.github/workflows/destroy.yml new file mode 100644 index 00000000..98d744cf --- /dev/null +++ b/.github/workflows/destroy.yml @@ -0,0 +1,37 @@ +name: Destroy +run-name: ${{ github.actor }} is destroying production πŸ’₯πŸ’₯πŸ’₯ + +on: + workflow_dispatch: + +env: + AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + +jobs: + build-infra: + name: terraform-ci-cd + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Terraform + uses: hashicorp/setup-terraform@v2 + - name: Terraform Init + id: init + run: terraform init + working-directory: ./terraform + - name: Terraform Validate + id: validate + run: terraform validate + working-directory: ./terraform + - name: Terraform Plan + id: plan + run: terraform plan + working-directory: ./terraform + - name: Terraform destroy + id: destroy + run: terraform destroy --auto-approve + working-directory: ./terraform diff --git a/.github/workflows/svelte-rich-text.yaml b/.github/workflows/svelte-rich-text.yaml new file mode 100644 index 00000000..4fd6de58 --- /dev/null +++ b/.github/workflows/svelte-rich-text.yaml @@ -0,0 +1,96 @@ +name: svelte-rich-text + +run-name: ${{ github.actor }} is building svelte-rich-text image πŸ“· + +on: + push: + branches: + - 'main' + paths: + - '**' + pull_request: + branches: + - 'main' + paths: + - '**' + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + workflow_call: # Added to allow this workflow to be called by other workflows + + +jobs: + build-and-push-images: + name: Build svelte-rich-text + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} + + - name: Login to GitHub Container registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + env: + GITHUB_USER: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + registry: ghcr.io + username: $GITHUB_USER + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: | + ${{ secrets.DOCKER_HUB_USERNAME }}/svelte-rich-text + ghcr.io/${{ secrets.DOCKER_HUB_USERNAME }}/svelte-rich-text + flavor: | + latest=false + tags: | + type=sha,prefix=commit-,format=long + type=raw,value=latest + + + - name: Build and tag Docker image and push to Docker Hub + uses: docker/build-push-action@v2 + id: docker_build + with: + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + builder: ${{ steps.buildx.outputs.name }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + context: . + file: ./Dockerfile.solo + + - name: Show image digest + run: | + echo ${{ steps.docker_build.outputs.digest }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a626a965..29057262 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ node_modules /dist /.svelte-kit /package -.env .env.* !.env.example vite.config.js.timestamp-* diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 00000000..e3cc5d89 --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,50 @@ +# Use Ubuntu as the base image to set up the development environment +FROM ubuntu:20.04 AS dev + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Update and install dependencies, including sudo +RUN apt-get update && apt-get install -y \ + curl \ + git \ + sudo \ + && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Set the working directory in the container +WORKDIR /app + +# Copy the project files into the container +COPY . . + +# No need to add a non-root user or adjust npm configurations for global installs +# The container runs commands as root by default + +# Install Yarn globally +RUN npm install --global yarn + +# Install project dependencies +RUN yarn install + +#install nvim dependecies, will be replaced in dot files +RUN curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz +RUN sudo rm -rf /opt/nvim +RUN sudo tar -C /opt -xzf nvim-linux64.tar.gz +RUN echo 'export PATH="$PATH:/opt/nvim-linux64/bin"' >> ~/.bashrc +RUN chmod +x ~/.bashrc +RUN ~/.bashrc +RUN git clone https://github.com/nvim-lua/kickstart.nvim.git "${XDG_CONFIG_HOME:-$HOME/.config}"/nvim + +# Expose the port the app runs on +EXPOSE 3000 +EXPOSE 5173 +EXPOSE 5174 +EXPOSE 4173 + +# Use bash to handle terminal commands +SHELL ["/bin/bash", "-c"] + +# Start the SvelteKit app in development mode +CMD ["yarn", "dev", "--host"] diff --git a/Dockerfile.solo b/Dockerfile.solo new file mode 100644 index 00000000..8b1fb98d --- /dev/null +++ b/Dockerfile.solo @@ -0,0 +1,51 @@ +FROM node:18-alpine AS base + +# This Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker. +# Make sure you update both files! + +FROM base AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +RUN apk update +# Set working directory +WORKDIR /app +COPY . . + +# Add lockfile and package.json's of isolated subworkspace +FROM base AS installer +RUN apk add --no-cache libc6-compat +RUN apk update +WORKDIR /app + +# First install the dependencies (as they change less often) +COPY .gitignore .gitignore +COPY --from=builder /app/ . +COPY --from=builder /app/yarn.lock ./yarn.lock +RUN yarn install + +# Build the project +COPY --from=builder /app/ . + +# set the deploy target to node +RUN DEPLOY_TARGET=node yarn build + + +# Create a new stage for the runner +FROM base AS runner +WORKDIR /app + +# Don't run production as root - add a non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 svelte +USER svelte + + +# Copy the necessary files from the installer stage +COPY --from=installer /app/package.json . +COPY --from=installer /app/.env . +COPY --from=installer /app/build /app/build + +EXPOSE 3000 + +# Modify this line to start your SvelteKit application, e.g., run your server.js or the appropriate script. +CMD [ "node", "build" ] diff --git a/README_DEV.md b/README_DEV.md new file mode 100644 index 00000000..f28bdc92 --- /dev/null +++ b/README_DEV.md @@ -0,0 +1,185 @@ + ## Introduction + This GitHub project provides a comprehensive guide and a set of resources to create and manage infrastructure using Terraform and automate the deployment process using GitHub Actions. + + Terraform is an open-source infrastructure as code (IaC) tool that allows you to define and provision infrastructure using a declarative configuration language. + + Whereas GitHub Actions is a powerful automation and CI/CD platform provided by GitHub. + + +By combining Terraform and GitHub Actions, you can: + +**Define Infrastructure as Code**: Define your infrastructure components, such as virtual machines, databases, and networks, in a Terraform configuration file. + +**Automate Deployment**: Set up GitHub Actions workflows to automatically deploy your infrastructure whenever there are changes to your Terraform configuration. + +**Version Control**: Keep your infrastructure code version-controlled and easily collaborate with your team. + +**Infrastructure as Code Best Practices**: Follow best practices for infrastructure as code, including versioning, code review, and documentation. + +This project serves as a starting point for your infrastructure automation journey, providing a basic structure and guidelines to build upon. + +## Prerequisites +Before you begin, ensure you have the following prerequisites: + +* GitHub Account +* Terraform installed on your local machine. +* Access to a cloud provider account (e.g., AWS, Azure, Google Cloud) and necessary API credentials. + +# devops Environment Setup Template + +it has docker-compose for development and deploying. it also configured with .devcontainer sΓ₯ you can go into the container and work on it from there. another way is it has python environment using the commands in run.sh or run.bat. + + +# a dev environment for python + +```bash +docker-compose up --build -d +docker-compose down +``` + +# (Optional) everything after this is optional +# deployment +there are two ways. one is simpler using azure container. it just deploys the app to your azure account. +the 2nd option is more heavy duty but comes with more. while the first is just a container. the second one is a full on server. the server is a free tier ec2(if you only have one server). +to choose, its the one in the workflows folder thats active. for azure have the deploy-azure.yml and vision-ai.yml in the folder and the other two in the extra_workflows folder. +for terraform have the deploy-terraform.yml, detroy.yml and vision-ai.yml in the workflow folder and deploy-azure.yml in the extra_workflows folder + +for both you need docker hub account +# docker hub +go to https://hub.docker.com/settings/security and create and copy the token. together with your username go to (your repository)https://github.com/valiantlynx/python-development-environment/settings/secrets/actions and make these repository secrets +- DOCKER_HUB_USERNAME = your username +- DOCKER_HUB_PASSWORD = your docker access token + +# cloudflare +also i use cloudflare as my dns. this is cause they allow me to edit dns records dynamicly for free. so for that you would also need to add these two en variables. if you dont you can comment the relevant parts of the code: +- CLOUDFLARE_EMAIL = your cloudflare email +- CLOUDFLARE_API_TOKEN = your cloudflare api token + +# azure +its just a github action that uses your dockerfile. +got to [azure](https://portal.azure.com/#home) and search for `web app for containers` +choose this:![web app for containers](assets/image.png) + +and then these details: +![basic container](assets/image2.png) + +in the docker section choose what your docker details will be in docker hub. (username/container-name): +![docker details](assets/image3.png) + +leave the rest as they are(yes even deployment can be done later) and review and create. +after the resource is built go to it then deployment center and choose this config(try to relly on azures auto fill to fill this form if possible): +![config stuff](assets/image4.png) +to remove that error so we can save. we need to set the variables in azure +got to configurations and edit the two docker stuff that are empty: +![env in azure](assets/image5.png) +go to general settings tab and turn on SCM basic auth. +save it and go back to deployment center. your might have to redo some of the former step. then save + + +we need some env variables for authentication in git hub action. go back to overview and on the top is the option Download publish profile. copy the whole file and lets go to github + +first the login stuff. go to (your repository)https://github.com/valiantlynx/vision-ai/settings/secrets/actions and make these repository secrets(you might see two env variables azure made delete them) +if your planing to use azure +- AZUREAPPSERVICE_PUBLISHPROFILE = the thing you downloaded and copied + +now the next time you commit to there is a succesfull pull or push to the main branch and the docker image is on docker hub then azure will redeploy + +to see the app azure provides a free url something like https://vision-ai.azurewebsites.net/ go there and you might have to wait like 10 min the first time around. or make a commit to main again a couple times + +# terrafrom +this does alot for and is simpler to set up. +### what you get +simply put its IaC with terraform, config with ansible, monitoring with prometheus-grafana and service-check with uptime-kuma and container management with portainer. +it all automated as well +## setup +you need an aws account. in it make an s3 bucket called Β΄python-development-environmentΒ΄ it can be anything it just has to match with the one in terraform/provider.tf aws. +and lastly access keys, you can get thenm in IAM. just create access key cause we need both the key and the secret +![accesskey](assets/image6.png) + +the login stuff. go to (your repository)https://github.com/valiantlynx/python-development-environment/settings/secrets/actions and make these repository secrets +if your planing to use azure +- AWS_ACCESS_KEY = that you copied +- AWS_SECRET_ACCESS_KEY = the secret you copied + +### this devops sets up everything even ssl. so edit the somain ports and images to your needs +all the files you need to edit are in andible/roles/docker_deploy/files unfortunately for this you need some knowledge(basic is enough) in docker, nginx and certbot. +specifically, +andible/roles/docker_deploy/files/docker/docker-compose.yml - for ports and which domain you want ssl for and containers. +andible/roles/docker_deploy/files/docker/prometheus.yml - together with the one above for your monitoring needs +andible/roles/docker_deploy/files/nginx/http.conf - both this and below need to be edited on each edit. for http edits. and where the containers are accessed. reverse proxy +andible/roles/docker_deploy/files/nginx/https.conf - both this and above need to be edited on each edit. for https edits. and where the containers are accessed. reverse proxy + +you can look at my git is some of my repos logs to get an idea of how i do or contact me for help. + +now the next time you commit to there is a succesfull pull or push to the main branch and the docker image is on docker hub then terraform with deploy the infra and ansible will configure everything all the way to ssl. +this means the first time its building it might fail. that cause the domains that certbot is trying to get ssl for might not be pointing to the newly created ec2. you need to go to your dns and point it to the correct ip. + + + +## some usefule commands +set the aws env variables +```bash +aws configure +``` + +```bash +terraform init +``` + +```bash +terraform validate +``` + +```bash +terraform plan +``` + +```bash +terraform apply --auto-approve +``` + +```bash +terraform destroy --auto-approve +``` + +```bash +ansible-playbook -i 13.60.38.190, -e "ansible_user=ubuntu ansible_ssh_private_key_file=modules/pk/terraform-key.pem" ../ansible/deploy-app.yml +ansible-playbook -i ../ansible/inventory/dynamic_inventory.ini ../ansible/deploy-app.yml +``` + +# error in terraform actions +if you see an error like +```bash +β”‚ Get:22 http://azure.archive.ubuntu.com/ubuntu jammy-security/universe +β”‚ Translation-en [162 kB] +β”‚ Fetched 9514 kB in 2s (5622 kB/s) +β”‚ Reading package lists... +β”‚ Building dependency tree... +β”‚ Reading state information... +β”‚ 34 packages can be upgraded. Run 'apt list --upgradable' to see them. +β”‚ /bin/sh: 2: +: not found +β”‚ /bin/sh: 3: +: not found +β”‚ . +β”‚ .. +β”‚ ansible.cfg +β”‚ deploy-app.yml +β”‚ inventory +β”‚ roles +β”‚ /bin/sh: 4: +: not found +β”‚ deploy-app.yml +β”‚ /bin/sh: 5: +: not found +β”‚ ERROR! the playbook: deploy-app.yml + could not be found +β”‚ +β•΅ +Error: Terraform exited with code 1. +Error: Process completed with exit code 1. +``` + +it cause of the line ending of the os you are using. i was stumped for a while, but to fix it. just remember all line endings og the terraform/ and ansible/ dir's should have linux line endings(LF). this is cause i made the project in linux os and that github action run on linux. this is simple to do in most text editers just google it + +also have the dns in alphabetical order \ No newline at end of file diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 00000000..86cfd2fd --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,2 @@ +[defaults] +host_key_checking = False \ No newline at end of file diff --git a/ansible/deploy-app.yml b/ansible/deploy-app.yml new file mode 100644 index 00000000..87aa2809 --- /dev/null +++ b/ansible/deploy-app.yml @@ -0,0 +1,6 @@ +--- +- hosts: ec2_instances + roles: + - common + - docker + - docker_deploy diff --git a/ansible/inventory/dynamic_inventory.ini b/ansible/inventory/dynamic_inventory.ini new file mode 100644 index 00000000..47420f4d --- /dev/null +++ b/ansible/inventory/dynamic_inventory.ini @@ -0,0 +1,5 @@ +[ec2_instances:children] + svelte-rich-text1 + + [svelte-rich-text1] +16.170.98.91 ansible_user=ubuntu ansible_ssh_private_key_file=/home/valiantlynx/svelte-rich-text/terraform/modules/pk/terraform-key.pem diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml new file mode 100644 index 00000000..a1968188 --- /dev/null +++ b/ansible/roles/common/tasks/main.yml @@ -0,0 +1,5 @@ +- name: Update apt cache + become: true + apt: + update_cache: yes + cache_valid_time: 3600 diff --git a/ansible/roles/docker/tasks/main.yml b/ansible/roles/docker/tasks/main.yml new file mode 100644 index 00000000..f1298dcc --- /dev/null +++ b/ansible/roles/docker/tasks/main.yml @@ -0,0 +1,27 @@ +- name: Install Docker dependencies + become: true + apt: + name: + - docker.io + - python3-pip + state: present + +- name: Uninstall Docker Compose installed via pip (if any) + become: true + pip: + name: docker-compose + state: absent + executable: pip3 + +- name: Download Docker Compose + become: true + get_url: + url: "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-{{ ansible_system }}-{{ ansible_architecture }}" + dest: "/usr/local/bin/docker-compose" + mode: 'u+x,g+x' + +- name: Set executable permission on Docker Compose + become: true + file: + path: "/usr/local/bin/docker-compose" + mode: 'u+x,g+x' diff --git a/ansible/roles/docker_deploy/tasks/main.yml b/ansible/roles/docker_deploy/tasks/main.yml new file mode 100644 index 00000000..965bbf65 --- /dev/null +++ b/ansible/roles/docker_deploy/tasks/main.yml @@ -0,0 +1,106 @@ +--- +- name: Include Cloudflare domain and zone IDs variables + ansible.builtin.include_vars: + file: "vars/cloudflare_vars.yml" + + +- name: Debug cloudflare_zone_ids variable content + ansible.builtin.debug: + var: cloudflare_zone_ids + +- name: Generate Docker Compose file + ansible.builtin.template: + src: "docker/docker-compose.yaml.j2" + dest: "{{ ansible_env.HOME }}/docker-compose.yml" + mode: "0644" + +- name: Generate Docker Compose file for certbot with dynamic domain names + ansible.builtin.template: + src: "docker/docker-compose.certbot.yml.j2" + dest: "{{ ansible_env.HOME }}/docker-compose.certbot.yml" + mode: "0644" + +- name: Ensure nginx directory exists + ansible.builtin.file: + path: "{{ ansible_env.HOME }}/nginx" + state: directory + mode: '0755' + + +- name: Generate http NGINX configuration with dynamic domain names + ansible.builtin.template: + src: "nginx/http.conf.j2" + dest: "{{ ansible_env.HOME }}/nginx/nginx.conf" + mode: "0644" + +- name: Create 'monitoring' network if it does not exist + become: true # Escalate if necessary for Docker commands + ansible.builtin.command: docker network create monitoring + register: network_creation + failed_when: network_creation.rc != 0 and 'already exists' not in network_creation.stderr + ignore_errors: yes + +- name: Pull the latest images with Docker Compose + command: docker-compose pull + become: true # Escalate if necessary for Docker commands + args: + chdir: "{{ ansible_env.HOME }}/" # Ensure this points to where docker-compose.yml is located + +- name: Deploy services using Docker Compose + command: docker-compose up -d nginx + become: true # Escalate if necessary for Docker commands + args: + chdir: "{{ ansible_env.HOME }}/" # Ensure this points to where docker-compose.yml is located + +- name: Determine the primary domain + set_fact: + primary_domain: "{{ cloudflare_zone_ids.keys() | list | first }}" + +- name: Check if SSL certificate needs renewal for the primary domain + ansible.builtin.command: > + openssl x509 -checkend 864000 -noout -in {{ ansible_env.HOME }}/certbot/conf/live/{{ primary_domain }}/fullchain.pem + register: cert_check + failed_when: cert_check.rc > 1 + ignore_errors: yes + +- name: Obtain SSL certificates with Certbot if needed + command: docker-compose -f docker-compose.certbot.yml run --rm certbot + become: true + args: + chdir: "{{ ansible_env.HOME }}/" + when: cert_check.rc == 1 + +- name: Generate https NGINX configuration with dynamic domain names + ansible.builtin.template: + src: "nginx/https.conf.j2" + dest: "{{ ansible_env.HOME }}/nginx/nginx.conf" + mode: "0644" + +- name: Stop existing Docker Compose services + command: docker-compose down --remove-orphans + become: true + args: + chdir: "{{ ansible_env.HOME }}/" + +- name: Deploy services using Docker Compose + command: docker-compose up -d + become: true + args: + chdir: "{{ ansible_env.HOME }}/" + +- name: Delete all hanging stuff + command: docker system prune -a -f --volumes + become: true + args: + chdir: "{{ ansible_env.HOME }}/" + +- name: Schedule automated certificate renewal + ansible.builtin.cron: + name: "Renew Let's Encrypt certificates" + day: "1" + month: "*/2" + hour: "5" + minute: "0" + job: "cd {{ ansible_env.HOME }} && docker-compose run --rm certbot renew && docker-compose exec nginx nginx -s reload" + user: ubuntu + diff --git a/ansible/roles/docker_deploy/templates/docker/docker-compose.certbot.yml.j2 b/ansible/roles/docker_deploy/templates/docker/docker-compose.certbot.yml.j2 new file mode 100644 index 00000000..6cc70e0d --- /dev/null +++ b/ansible/roles/docker_deploy/templates/docker/docker-compose.certbot.yml.j2 @@ -0,0 +1,22 @@ +version: '3.8' + +services: + certbot: + image: certbot/certbot + container_name: certbot + volumes: + - ./certbot/conf:/etc/letsencrypt + - ./certbot/logs:/var/log/letsencrypt + - ./certbot/www:/var/www/certbot + networks: + - monitoring + command: > + certonly --webroot -w /var/www/certbot --force-renewal --email valiantlynxz@gmail.com + {% for domain in cloudflare_zone_ids.keys() %} + -d {{ domain }} -d www.{{ domain }} + {% endfor %} + --agree-tos --non-interactive +networks: + monitoring: + name: monitoring + external: true diff --git a/ansible/roles/docker_deploy/templates/docker/docker-compose.yaml.j2 b/ansible/roles/docker_deploy/templates/docker/docker-compose.yaml.j2 new file mode 100644 index 00000000..6c0b7e6c --- /dev/null +++ b/ansible/roles/docker_deploy/templates/docker/docker-compose.yaml.j2 @@ -0,0 +1,63 @@ +--- +version: '3.8' + +services: + svelte-rich-text: + image: valiantlynx/svelte-rich-text:latest + container_name: svelte-rich-text + restart: always + networks: + - monitoring + + nginx: + image: nginx:latest + container_name: nginx + ports: + - 80:80 + - 443:443 + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./certbot/conf:/etc/letsencrypt + - ./certbot/logs:/var/log/letsencrypt + - ./certbot/www:/var/www/certbot + depends_on: + - svelte-rich-text + restart: unless-stopped + networks: + - monitoring + node-exporter: + image: quay.io/prometheus/node-exporter:latest + container_name: node_exporter + ports: + - 9100:9100 + command: + - '--path.rootfs=/host' + pid: host + restart: unless-stopped + networks: + - monitoring + volumes: + - '/:/host:ro,rslave' + + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: cadvisor + ports: + - 8080:8080 + volumes: + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + restart: unless-stopped + privileged: true + devices: + - /dev/kmsg + networks: + - monitoring + +networks: + monitoring: + name: monitoring + external: true diff --git a/ansible/roles/docker_deploy/templates/nginx/http.conf.j2 b/ansible/roles/docker_deploy/templates/nginx/http.conf.j2 new file mode 100644 index 00000000..8187f86e --- /dev/null +++ b/ansible/roles/docker_deploy/templates/nginx/http.conf.j2 @@ -0,0 +1,27 @@ +events { + worker_connections 1024; +} + +http { + server_tokens off; + charset utf-8; + + # Configuration for astromanga.com + server { + listen 80; + server_name server_name {% for domain in cloudflare_zone_ids.keys() %}{{ domain }} www.{{ domain }} {% endfor %}; + + location / { + proxy_pass http://svelte-rich-text:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ /.well-known/acme-challenge/ { + root /var/www/certbot; + allow all; + } + + } +} \ No newline at end of file diff --git a/ansible/roles/docker_deploy/templates/nginx/https.conf.j2 b/ansible/roles/docker_deploy/templates/nginx/https.conf.j2 new file mode 100644 index 00000000..34c3ccca --- /dev/null +++ b/ansible/roles/docker_deploy/templates/nginx/https.conf.j2 @@ -0,0 +1,40 @@ +events { + worker_connections 1024; +} + +http { + server_tokens off; + charset utf-8; + + # Always redirect HTTP to HTTPS + server { + listen 80; + server_name server_name {% for domain in cloudflare_zone_ids.keys() %}{{ domain }} www.{{ domain }} {% endfor %}; + return 301 https://$host$request_uri; + } + # Configuration for astromanga.com + server { + listen 443 ssl http2; + server_name server_name {% for domain in cloudflare_zone_ids.keys() %}{{ domain }} www.{{ domain }} {% endfor %}; + + # Use the first domain in the list for SSL certificate paths + {% set primary_domain = cloudflare_zone_ids.keys() | list | first %} + ssl_certificate /etc/letsencrypt/live/{{ primary_domain }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ primary_domain }}/privkey.pem; + # include /etc/letsencrypt/options-ssl-nginx.conf; # Recommended SSL settings + # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # DH parameters + + location / { + proxy_pass http://svelte-rich-text:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~ /.well-known/acme-challenge/ { + root /var/www/certbot; + } + } +} + diff --git a/assets/image.png b/assets/image.png new file mode 100644 index 00000000..c3a45343 Binary files /dev/null and b/assets/image.png differ diff --git a/assets/image2.png b/assets/image2.png new file mode 100644 index 00000000..4c14507c Binary files /dev/null and b/assets/image2.png differ diff --git a/assets/image3.png b/assets/image3.png new file mode 100644 index 00000000..a1d2f4f8 Binary files /dev/null and b/assets/image3.png differ diff --git a/assets/image4.png b/assets/image4.png new file mode 100644 index 00000000..9b2efce9 Binary files /dev/null and b/assets/image4.png differ diff --git a/assets/image5.png b/assets/image5.png new file mode 100644 index 00000000..8963ae52 Binary files /dev/null and b/assets/image5.png differ diff --git a/assets/image6.png b/assets/image6.png new file mode 100644 index 00000000..474da07b Binary files /dev/null and b/assets/image6.png differ diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000..2d8e6a57 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,15 @@ +version: "3.8" +services: + svelte-rich-text-dev: + build: + context: . + dockerfile: Dockerfile.dev # Point to the development Dockerfile + ports: + - "3000:3000" # Port for the SvelteKit app + - "5173:5173" # Optional: Include if your dev setup uses this port + - "5174:5174" + - "4173:4173" # Optional: Include if your dev setup uses this port + volumes: + - .:/app # Mount the project directory inside the container for live reloading + - /app/node_modules # Use a volume for node_modules to avoid overwriting the container's copy + restart: always \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..7f26cf66 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3" + +services: + svelte-rich-text: + container_name: svelte-rich-text + build: + context: . + dockerfile: Dockerfile + restart: always + ports: + - 3000:3000 + networks: + - valiantlynx-turborepo + +# Define a network, which allows containers to communicate +# with each other, by using their container name as a hostname +networks: + valiantlynx-turborepo: + external: false diff --git a/run.bat b/run.bat new file mode 100644 index 00000000..b3a00139 --- /dev/null +++ b/run.bat @@ -0,0 +1,11 @@ +@echo off +REM Start the container in detached mode +docker-compose --file docker-compose.dev.yml up -d + +REM Retrieve the container ID of the service named "dev-env" +FOR /f "tokens=*" %%i IN ('docker-compose --file docker-compose.dev.yml ps -q svelte-rich-text-dev') DO SET CONTAINER_ID=%%i + +REM Enter the container +docker exec -it %CONTAINER_ID% /bin/bash + +pause diff --git a/run.sh b/run.sh new file mode 100644 index 00000000..5197ef88 --- /dev/null +++ b/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Bring up the container in detached mode +docker-compose --file docker-compose.dev.yml up -d + +# Get the container ID of the newly started container +CONTAINER_ID=$(docker-compose --file docker-compose.dev.yml ps -q svelte-rich-text-dev) + +# Enter the container +docker exec -it $CONTAINER_ID /bin/bash diff --git a/svelte.config.js b/svelte.config.js index cf464a52..b71947ad 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,17 +1,22 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapterAuto from '@sveltejs/adapter-auto'; +import adapterNode from '@sveltejs/adapter-node'; import { vitePreprocess } from '@sveltejs/kit/vite'; +import dotenv from 'dotenv' +dotenv.config() + +let {DEPLOY_TARGET} = process.env +const deployTarget = DEPLOY_TARGET || 'auto'; // Default to 'auto' /** @type {import('@sveltejs/kit').Config} */ const config = { // Consult https://kit.svelte.dev/docs/integrations#preprocessors // for more information about preprocessors - preprocess: [vitePreprocess({})], - + preprocess: vitePreprocess(), kit: { // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // If your environment is not supported or you settled on a specific environment, switch out the adapter. // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter() + adapter: deployTarget === 'auto' ? adapterAuto() : adapterNode() } }; diff --git a/terraform/.gitignore b/terraform/.gitignore new file mode 100644 index 00000000..1785fcec --- /dev/null +++ b/terraform/.gitignore @@ -0,0 +1,3 @@ +.terraform/ +.terraform.lock.hcl +*.pem \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 00000000..c9e16b69 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,43 @@ +module "vpc" { + source = "./modules/vpc" + vpc_cidr = var.vpc_cidr + subnet_cidr = var.subnet_cidr +} + +module "pk" { + source = "./modules/pk" +} + +module "sg" { + source = "./modules/sg" + vpc_id = module.vpc.vpc_id +} + + +module "ec2" { + source = "./modules/ec2" + sg_id = module.sg.sg_id + subnets = module.vpc.subnet_ids + key_name = module.pk.key_name + private_key_path = module.pk.private_key_path + cloudflare_zone_ids = var.cloudflare_zone_ids +} + +module "cloudflare-ddns" { + source = "./modules/cloudflare-ddns" + public_ip = module.ec2.public_ip + cloudflare_zone_ids = var.cloudflare_zone_ids +} + +# module "eip" { +# source = "./modules/eip" +# instance_id = module.ec2.instances +# } + +# module "alb" { +# source = "./modules/alb" +# sg_id = module.sg.sg_id +# subnets = module.vpc.subnet_ids +# vpc_id = module.vpc.vpc_id +# instances = module.ec2.instances +# } \ No newline at end of file diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf new file mode 100644 index 00000000..14a1da04 --- /dev/null +++ b/terraform/modules/alb/main.tf @@ -0,0 +1,37 @@ +# ALB +resource "aws_lb" "alb" { + name = "svelte-rich-text-load-balancer" + internal = false + load_balancer_type = "application" + security_groups = [var.sg_id] + subnets = var.subnets +} + +# Listener +resource "aws_lb_listener" "listener" { + load_balancer_arn = aws_lb.alb.arn + port = "80" + protocol = "HTTP" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.tg.arn + } +} + +# Target Group +resource "aws_lb_target_group" "tg" { + name = "tg" + port = 80 + protocol = "HTTP" + vpc_id = var.vpc_id +} + +# Target Group Attachment +resource "aws_lb_target_group_attachment" "tga" { + count = length(var.instances) + target_group_arn = aws_lb_target_group.tg.arn + target_id = var.instances[count.index] + port = 80 +} + diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf new file mode 100644 index 00000000..1fc60407 --- /dev/null +++ b/terraform/modules/alb/variables.tf @@ -0,0 +1,19 @@ +variable "sg_id" { + description = "SG ID for Application Load Balancer" + type = string +} + +variable "subnets" { + description = "Subnets for ALB" + type = list(string) +} + +variable "vpc_id" { + description = "VPC ID for ALB" + type = string +} + +variable "instances" { + description = "Instance ID for Target Group Attachment" + type = list(string) +} \ No newline at end of file diff --git a/terraform/modules/cloudflare-ddns/main.tf b/terraform/modules/cloudflare-ddns/main.tf new file mode 100644 index 00000000..c70f8589 --- /dev/null +++ b/terraform/modules/cloudflare-ddns/main.tf @@ -0,0 +1,19 @@ +resource "cloudflare_record" "ec2_dns_a" { + for_each = var.cloudflare_zone_ids + zone_id = each.value + name = each.key + value = var.public_ip[0] + type = "A" + ttl = 1 // Auto + proxied = false +} + +resource "cloudflare_record" "ec2_dns_cname" { + for_each = var.cloudflare_zone_ids + zone_id = each.value + name = "www.${each.key}" + value = each.key + type = "CNAME" + ttl = 1 // Auto + proxied = false +} diff --git a/terraform/modules/cloudflare-ddns/provider.tf b/terraform/modules/cloudflare-ddns/provider.tf new file mode 100644 index 00000000..51149994 --- /dev/null +++ b/terraform/modules/cloudflare-ddns/provider.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 4.0" + } + } +} + +provider "cloudflare" { +} diff --git a/terraform/modules/cloudflare-ddns/variables.tf b/terraform/modules/cloudflare-ddns/variables.tf new file mode 100644 index 00000000..40ce0864 --- /dev/null +++ b/terraform/modules/cloudflare-ddns/variables.tf @@ -0,0 +1,11 @@ +variable "public_ip" { + description = "The ip's of the instance." + type = list(string) +} + +variable "cloudflare_zone_ids" { + type = map(string) + description = "Mapping of domain names to Cloudflare zone IDs" +} + + diff --git a/terraform/modules/ec2/data.tf b/terraform/modules/ec2/data.tf new file mode 100644 index 00000000..35a295c1 --- /dev/null +++ b/terraform/modules/ec2/data.tf @@ -0,0 +1,19 @@ +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] +} + +data "aws_availability_zones" "available" { + state = "available" +} \ No newline at end of file diff --git a/terraform/modules/ec2/main.tf b/terraform/modules/ec2/main.tf new file mode 100644 index 00000000..54d1f3bd --- /dev/null +++ b/terraform/modules/ec2/main.tf @@ -0,0 +1,96 @@ + +resource "aws_instance" "web" { + count = length(var.ec2_names) + ami = data.aws_ami.ubuntu.id + instance_type = "t3.micro" + associate_public_ip_address = true + vpc_security_group_ids = [var.sg_id] + subnet_id = var.subnets[count.index] + availability_zone = data.aws_availability_zones.available.names[count.index] + key_name = var.key_name + + tags = { + Name = var.ec2_names[count.index] + } + + provisioner "remote-exec" { + inline = [ + "echo 'SSH ready!'" + ] + + connection { + type = "ssh" + user = "ubuntu" + private_key = file(abspath(var.private_key_path)) + host = self.public_ip + } + } +} + + +data "template_file" "cloudflare_vars" { + template = <<-EOT + --- + cloudflare_zone_ids: + %{ for domain, zone_id in var.cloudflare_zone_ids ~} + "${domain}": "${zone_id}" + %{ endfor ~} + EOT +} + +resource "local_file" "cloudflare_vars_file" { + filename = "${abspath(path.module)}/../../../ansible/vars/cloudflare_vars.yml" + content = data.template_file.cloudflare_vars.rendered +} + + +data "template_file" "inventory" { + template = <<-EOT + [ec2_instances:children] + %{ for child_node in var.ec2_names ~} + ${child_node} + %{ endfor ~} + + %{ for index in range(length(aws_instance.web.*.public_ip)) ~} + [${var.ec2_names[index]}] + ${aws_instance.web.*.public_ip[index]} ansible_user=ubuntu ansible_ssh_private_key_file=${abspath(var.private_key_path)} + %{ endfor ~} + EOT +} + +resource "local_file" "dynamic_inventory" { + depends_on = [ aws_instance.web ] + + filename = "${abspath(path.module)}/../../../ansible/inventory/dynamic_inventory.ini" + content = data.template_file.inventory.rendered + + provisioner "local-exec" { + command = "chmod 400 ${local_file.dynamic_inventory.filename}" + } +} + + +resource "null_resource" "run_ansible" { + depends_on = [ + local_file.dynamic_inventory, + local_file.cloudflare_vars_file + ] + + provisioner "local-exec" { + command = <