From 0a50ffb17201bd993bfa7079d579df586f169e55 Mon Sep 17 00:00:00 2001 From: Kishan Takoordyal Date: Mon, 11 Aug 2025 22:49:36 +0400 Subject: [PATCH] Initialize Project --- .gitea/workflows/main.yml | 68 ++++++++++++++ .gitignore | 34 +++++++ Dockerfile | 24 +++++ README.md | 21 +++++ bun-env.d.ts | 17 ++++ bun.lock | 38 ++++++++ bunfig.toml | 2 + docker-compose.yml | 16 ++++ package.json | 20 ++++ src/APITester.tsx | 39 ++++++++ src/App.tsx | 25 +++++ src/frontend.tsx | 26 ++++++ src/index.css | 187 ++++++++++++++++++++++++++++++++++++++ src/index.html | 13 +++ src/index.tsx | 41 +++++++++ src/logo.svg | 1 + src/react.svg | 8 ++ tsconfig.json | 36 ++++++++ 18 files changed, 616 insertions(+) create mode 100644 .gitea/workflows/main.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 bun-env.d.ts create mode 100644 bun.lock create mode 100644 bunfig.toml create mode 100644 docker-compose.yml create mode 100644 package.json create mode 100644 src/APITester.tsx create mode 100644 src/App.tsx create mode 100644 src/frontend.tsx create mode 100644 src/index.css create mode 100644 src/index.html create mode 100644 src/index.tsx create mode 100644 src/logo.svg create mode 100644 src/react.svg create mode 100644 tsconfig.json diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml new file mode 100644 index 0000000..2139b67 --- /dev/null +++ b/.gitea/workflows/main.yml @@ -0,0 +1,68 @@ +name: Run tests, build docker images and deploy +run-name: Run tests, build docker images and deploy + +on: + push: + branches: + - master + workflow_dispatch: + +env: + DOCKER_JOB_ID: mscc-react-app + +jobs: + docker-build: + name: Docker build and push + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: sh + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Docker info + run: docker info + - name: Login to Gitea + uses: docker/login-action@v3 + with: + registry: https://${{ vars.DOMAIN_NAME_GITEA }} + username: ${{ secrets.CI_REGISTRY_USER_GITEA }} + password: ${{ secrets.CI_BUILD_TOKEN_GITEA }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + tags: '${{ vars.DOMAIN_NAME_GITEA }}/${{ gitea.repository }}:latest' + deploy: + name: Deploy on server + runs-on: ubuntu-latest + needs: docker-build + steps: + - name: Install openssh-client + run: | + apt update -qy + apt install -y openssh-client + - name: Configure SSH + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + export SSH_PRIVATE_KEY_PATH=~/.ssh/id_rsa + echo "${{ secrets.WORKER_SSH_PRIVATE_KEY }}" > $SSH_PRIVATE_KEY_PATH + chmod 600 $SSH_PRIVATE_KEY_PATH + ssh-keyscan ${{ vars.SERVER_ADDRESS }} >> ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config + eval $(ssh-agent -s) + - name: Checkout + uses: actions/checkout@v4 + - name: Rebuild container + run: | + docker stop ${{ env.DOCKER_JOB_ID }} || true + docker rm ${{ env.DOCKER_JOB_ID }} || true + scp docker-compose.yml ${{ vars.SERVER_USERNAME }}@${{ vars.SERVER_ADDRESS }}:/tmp/docker-compose.yml + ssh -tt -l ${{ vars.SERVER_USERNAME }} ${{ vars.SERVER_ADDRESS }} "cd /tmp/ && docker compose down && docker pull ${{ vars.DOMAIN_NAME_GITEA }}/${{ gitea.repository }}:latest && docker compose up -d && rm -f /tmp/docker-compose.yml" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..50a4b9e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM oven/bun:1.2.20 AS base +WORKDIR /usr/src/app + +FROM base AS install +RUN mkdir -p /temp/prod +COPY package.json bun.lock /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + + +FROM base AS prerelease +COPY --from=install /temp/prod/node_modules node_modules +COPY . . + +ENV NODE_ENV=production +RUN bun run build + +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/dist dist +COPY --from=prerelease /usr/src/app/package.json . + +USER bun +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "start" ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..004b1e0 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# bun-react-template + +To install dependencies: + +```bash +bun install +``` + +To start a development server: + +```bash +bun dev +``` + +To run for production: + +```bash +bun start +``` + +This project was created using `bun init` in bun v1.2.20. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/bun-env.d.ts b/bun-env.d.ts new file mode 100644 index 0000000..72f1c26 --- /dev/null +++ b/bun-env.d.ts @@ -0,0 +1,17 @@ +// Generated by `bun init` + +declare module "*.svg" { + /** + * A path to the SVG file + */ + const path: `${string}.svg`; + export = path; +} + +declare module "*.module.css" { + /** + * A record of class names to their corresponding CSS module classes + */ + const classes: { readonly [key: string]: string }; + export = classes; +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..4bbce1f --- /dev/null +++ b/bun.lock @@ -0,0 +1,38 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "bun-react-template", + "dependencies": { + "react": "^19", + "react-dom": "^19", + }, + "devDependencies": { + "@types/bun": "latest", + "@types/react": "^19", + "@types/react-dom": "^19", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="], + + "@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="], + + "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], + + "@types/react-dom": ["@types/react-dom@19.1.7", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw=="], + + "bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], + + "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], + + "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + + "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + } +} diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..9819bf6 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[serve.static] +env = "BUN_PUBLIC_*" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4b1dabc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + mscc-react-app: + container_name: 'mscc-react-app' + image: 'gitea.mscc.kinesis.world/mscc-react:latest' + network_mode: 'bridge' + restart: 'always' + environment: {} + volumes: [] + labels: + traefik.enable: 'true' + traefik.http.routers.mscc-react-app-service-http.entrypoints: 'http' + traefik.http.routers.mscc-react-app-service-https.entrypoints: 'https' + traefik.http.routers.mscc-react-app-service-http.rule: 'Host(`mscc.kinesis.world`)' + traefik.http.routers.mscc-react-app-service-https.rule: 'Host(`mscc.kinesis.world`)' + traefik.http.routers.mscc-react-app-service-https.tls.certResolver: 'acme-http' + traefik.http.services.mscc-react-app-service-https.loadbalancer.server.port: '3000' diff --git a/package.json b/package.json new file mode 100644 index 0000000..3e7d68f --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "bun-react-template", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "bun --hot src/index.tsx", + "build": "bun build ./src/index.html --outdir=dist --sourcemap --target=browser --minify --define:process.env.NODE_ENV='\"production\"' --env='BUN_PUBLIC_*'", + "start": "NODE_ENV=production bun src/index.tsx" + }, + "dependencies": { + "react": "^19", + "react-dom": "^19" + }, + "devDependencies": { + "@types/react": "^19", + "@types/react-dom": "^19", + "@types/bun": "latest" + } +} diff --git a/src/APITester.tsx b/src/APITester.tsx new file mode 100644 index 0000000..fd2af48 --- /dev/null +++ b/src/APITester.tsx @@ -0,0 +1,39 @@ +import { useRef, type FormEvent } from "react"; + +export function APITester() { + const responseInputRef = useRef(null); + + const testEndpoint = async (e: FormEvent) => { + e.preventDefault(); + + try { + const form = e.currentTarget; + const formData = new FormData(form); + const endpoint = formData.get("endpoint") as string; + const url = new URL(endpoint, location.href); + const method = formData.get("method") as string; + const res = await fetch(url, { method }); + + const data = await res.json(); + responseInputRef.current!.value = JSON.stringify(data, null, 2); + } catch (error) { + responseInputRef.current!.value = String(error); + } + }; + + return ( +
+
+ + + +
+