diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 9635864f..7efe9d6a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -7,6 +7,13 @@ on: - 'website/**' workflow_dispatch: +env: + SRV_HOST: ${{secrets.SRV_HOST}} + SRV_USER: ${{secrets.SRV_USER}} + SRV_PORT: ${{secrets.SRV_PORT}} + SRV_PASS: ${{secrets.SRV_PASS}} + APP_PORT: ${{secrets.APP_PORT}} + jobs: deploy: runs-on: ubuntu-latest @@ -43,3 +50,6 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./website/build + + - name: Deploy + run: cd website && npm run deploy diff --git a/.gitignore b/.gitignore index 63492704..db583d27 100755 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .DS_Store /lib /ci +.env diff --git a/website/.env.example b/website/.env.example new file mode 100644 index 00000000..19f98163 --- /dev/null +++ b/website/.env.example @@ -0,0 +1,5 @@ +SRV_HOST="" +SRV_USER="" +SRV_PASS="" +SRV_PORT= +APP_PORT= diff --git a/website/.eslintignore b/website/.eslintignore index c3e8b07c..4541721b 100644 --- a/website/.eslintignore +++ b/website/.eslintignore @@ -1,3 +1,4 @@ /build /.docusaurus /.cache-loader +/.svps diff --git a/website/.gitignore b/website/.gitignore index 5b642efb..82bf858a 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -3,6 +3,8 @@ # Production /build +/.svps +.env # Generated files .docusaurus diff --git a/website/.prettierignore b/website/.prettierignore index 9ef0805a..4541721b 100644 --- a/website/.prettierignore +++ b/website/.prettierignore @@ -1,4 +1,4 @@ /build /.docusaurus /.cache-loader -# /**/*.mdx +/.svps diff --git a/website/Dockerfile b/website/Dockerfile new file mode 100644 index 00000000..ebb1b107 --- /dev/null +++ b/website/Dockerfile @@ -0,0 +1,8 @@ +FROM oven/bun:alpine + +WORKDIR /usr/app + +COPY ./build ./build +COPY ./server.ts ./server.ts + +CMD ["bun", "server.ts"] diff --git a/website/deploy/access.ts b/website/deploy/access.ts new file mode 100644 index 00000000..5f293861 --- /dev/null +++ b/website/deploy/access.ts @@ -0,0 +1,13 @@ +import 'dotenv/config'; +import { SVPS } from 'svps'; + +const { SRV_HOST, SRV_PORT, SRV_USER, SRV_PASS } = process.env; + +export const svps = new SVPS({ + access: { + host: String(SRV_HOST), + port: Number(SRV_PORT), + username: String(SRV_USER), + password: String(SRV_PASS), + }, +}); diff --git a/website/deploy/mount-server.ts b/website/deploy/mount-server.ts new file mode 100644 index 00000000..8fd48df5 --- /dev/null +++ b/website/deploy/mount-server.ts @@ -0,0 +1,27 @@ +import 'dotenv/config'; +import { exit } from 'poku'; +import { svps } from './access.js'; + +(async () => { + const mounted = await svps.mount({ + repair: true, + apt: true, + apache: true, + docker: true, + firewall: true, + }); + + const createdVH = + mounted && + (await svps.createVirtualHosts([ + { + domain: 'poku.dev', + port: Number(process.env.APP_PORT), + www: true, + }, + ])); + + await svps.end(); + + exit(createdVH ? 0 : 1); +})(); diff --git a/website/deploy/upload.ts b/website/deploy/upload.ts new file mode 100644 index 00000000..2efc3585 --- /dev/null +++ b/website/deploy/upload.ts @@ -0,0 +1,34 @@ +import 'dotenv/config'; +import { svps } from './access.js'; +import { exit } from 'poku'; + +(async () => { + const port = process.env.APP_PORT; + + const uploaded = await svps.upload([ + { + local: './server.ts', + remote: 'server.ts', + }, + { + local: './build', + remote: './', + }, + { + local: './Dockerfile', + remote: 'Dockerfile', + }, + { + local: './docker-compose.yml', + remote: 'docker-compose.yml', + }, + ]); + + const composed = + uploaded && + (await svps.commands([`APP_PORT=${port} docker compose up --build -d`])); + + await svps.end(); + + exit(composed ? 0 : 1); +})(); diff --git a/website/docker-compose.yml b/website/docker-compose.yml new file mode 100644 index 00000000..7646d11f --- /dev/null +++ b/website/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.9' +services: + server: + build: . + container_name: poku-website + restart: always + ports: + - '127.0.0.1:${APP_PORT}:${APP_PORT}' + environment: + APP_PORT: ${APP_PORT} diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 324a89cb..f0656723 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -32,7 +32,7 @@ const config: Config = { containerId: 'GTM-K554VSWG', }, gtag: { - trackingID: 'G-GKVD8FM0B0', + trackingID: 'G-3EXQWTDQSK', anonymizeIP: true, }, } satisfies Preset.Options, diff --git a/website/package-lock.json b/website/package-lock.json index 03801aff..ddcd3e3e 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -16,6 +16,7 @@ "@mdx-js/react": "^3.0.1", "clsx": "^2.1.0", "docusaurus-plugin-sass": "^0.2.5", + "dotenv": "^16.4.5", "lucide-react": "^0.336.0", "prism-react-renderer": "^2.3.1", "react": "^18.2.0", @@ -28,9 +29,11 @@ "@docusaurus/module-type-aliases": "^3.1.1", "@docusaurus/tsconfig": "^3.1.1", "@docusaurus/types": "^3.1.1", + "@types/bun": "^1.0.7", "@types/node": "^20.11.20", "@typescript-eslint/eslint-plugin": "^7.0.2", "@typescript-eslint/parser": "^7.0.2", + "bun": "^1.0.29", "eslint": "^8.56.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -40,6 +43,7 @@ "packages-update": "^1.2.1", "poku": "^1.4.0", "prettier": "^3.2.5", + "svps": "^2.2.1", "tsx": "^4.7.1", "typescript": "^5.3.3" }, @@ -3839,6 +3843,84 @@ "node": ">= 8" } }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.0.29.tgz", + "integrity": "sha512-OhlPQO0zE7rgZ2LpkLr3A3cOoS6V+V3QvkPtlwB5TLlnrbcs78rnRBse/ibl46GAA1HnEn1ugxbsjAJkZ31WVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.0.29.tgz", + "integrity": "sha512-y7zjOWdUl7rtzt900XTYPwaSL0qBZpkGmHJNqEMmhVuqUclMwmQmqUnBDMoKwgW84nLQXkzcKyBZlOBNohpQZw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64-baseline": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.0.29.tgz", + "integrity": "sha512-/4RLSFIO+a0wVBR6/OZqEZJX3lGjPKK30V1Nce+ziceuk2Hmm+MsmzK/704UDi9MWWtXdRhe9Ayd4yZ+C0kZww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.0.29.tgz", + "integrity": "sha512-X8czxV2WBKSO4tctlmgImhYeWIuD4Abd2gvwUqwDliMsMhkNL/AiLcbgS3M7TCG+YFsqyIkOj2uf7nixs3hIPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.0.29.tgz", + "integrity": "sha512-i9YbwQ2sh+IiruNq3nhKvZx0piZkMf/b58jkxbjxW+WfgNFpo0EMuj+ZwNDjF9VC6SMbsI9CoC6lmP4+CCM3nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-baseline": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.0.29.tgz", + "integrity": "sha512-mZTBwBi94dU9229M2z24euFIMHbeHbMrrlzl3f+CKPzZxKPe5FG6EOudr8xRCoiuvZGgbvbkqZeRFJQjpVdOfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -4234,6 +4316,15 @@ "@types/node": "*" } }, + "node_modules/@types/bun": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.0.7.tgz", + "integrity": "sha512-zaPoQi+uBaqy7BwAh6HQ5dSt6H95XeejCSGEukXHYO32xIPdzPXJjNzmCJ64TWCpM4+R7WyPMdCnkZyETAZfuw==", + "dev": true, + "dependencies": { + "bun-types": "1.0.28" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -4547,6 +4638,24 @@ "@types/node": "*" } }, + "node_modules/@types/ssh2": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.19.tgz", + "integrity": "sha512-ydbQAqEcdNVy2t1w7dMh6eWMr+iOgtEkqM/3K9RMijMaok/ER7L8GW6PwsOypHCN++M+c8S/UR9SgMqNIFstbA==", + "dev": true, + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.18.tgz", + "integrity": "sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/unist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", @@ -5449,6 +5558,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/astring": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", @@ -5612,6 +5730,15 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -5763,6 +5890,53 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bun": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.0.29.tgz", + "integrity": "sha512-OQfMWb+UxgBA5Fnne/6GPmk3kh9HmECOIGG2P/m97QYmCthKMJlFBF8Q5TC+ayL5TvfVEXZxw7V4yHr1cqGK6w==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "hasInstallScript": true, + "os": [ + "darwin", + "linux" + ], + "bin": { + "bun": "bin/bun", + "bunx": "bin/bun" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.0.29", + "@oven/bun-darwin-x64": "1.0.29", + "@oven/bun-darwin-x64-baseline": "1.0.29", + "@oven/bun-linux-aarch64": "1.0.29", + "@oven/bun-linux-x64": "1.0.29", + "@oven/bun-linux-x64-baseline": "1.0.29" + } + }, + "node_modules/bun-types": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.28.tgz", + "integrity": "sha512-wQqbLYRM0YnsXZMFujbCr/9YxlEl51jshMXcJ2Y9wEuU7k6TKcX2KDh032k9oHfB1wH8/SleXboIsULMtFaAaA==", + "dev": true, + "dependencies": { + "@types/node": "~20.11.3", + "@types/ws": "~8.5.10" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -6458,6 +6632,21 @@ "node": ">=10" } }, + "node_modules/cpu-features": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", + "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.17.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7137,6 +7326,17 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -12952,6 +13152,13 @@ "multicast-dns": "cli.js" } }, + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "dev": true, + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -16105,6 +16312,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ssh2": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", + "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.9", + "nan": "^2.18.0" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -16452,6 +16677,23 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/svps": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/svps/-/svps-2.2.1.tgz", + "integrity": "sha512-dXQFo24G9uyi+ny7bkNpzJOBr41hFIjxBT/e1UQNvydigwQN7V+CGibns6c/x62flkmuS0ToVHkm29KCjeRTaQ==", + "dev": true, + "dependencies": { + "@types/ssh2": "^1.11.14", + "ssh2": "^1.14.0" + }, + "bin": { + "svps": "bin/index.js" + }, + "engines": { + "node": ">=14.5.0", + "npm": ">=7.0.2" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -16713,6 +16955,12 @@ "fsevents": "~2.3.3" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/website/package.json b/website/package.json index e5604d12..6f5c41c6 100644 --- a/website/package.json +++ b/website/package.json @@ -16,7 +16,8 @@ "lint": "prettier --write . && eslint . --fix", "test:unit": "npx poku --parallel test/unit", "test": "npm run typecheck && npm run lintcheck && npm run test:unit && npm run clear && npm run build", - "update": "npx npu; npm i; npm run lint" + "update": "npx npu; npm i; npm run lint", + "deploy": "npx tsx deploy/upload.ts" }, "dependencies": { "@docusaurus/core": "^3.1.1", @@ -27,6 +28,7 @@ "@mdx-js/react": "^3.0.1", "clsx": "^2.1.0", "docusaurus-plugin-sass": "^0.2.5", + "dotenv": "^16.4.5", "lucide-react": "^0.336.0", "prism-react-renderer": "^2.3.1", "react": "^18.2.0", @@ -39,9 +41,11 @@ "@docusaurus/module-type-aliases": "^3.1.1", "@docusaurus/tsconfig": "^3.1.1", "@docusaurus/types": "^3.1.1", + "@types/bun": "^1.0.7", "@types/node": "^20.11.20", "@typescript-eslint/eslint-plugin": "^7.0.2", "@typescript-eslint/parser": "^7.0.2", + "bun": "^1.0.29", "eslint": "^8.56.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -51,6 +55,7 @@ "packages-update": "^1.2.1", "poku": "^1.4.0", "prettier": "^3.2.5", + "svps": "^2.2.1", "tsx": "^4.7.1", "typescript": "^5.3.3" }, diff --git a/website/server.ts b/website/server.ts new file mode 100644 index 00000000..9b426c3a --- /dev/null +++ b/website/server.ts @@ -0,0 +1,35 @@ +import { serve, file } from 'bun'; + +const publicDir = './build'; +const index = `${publicDir}/index.html`; +const docs = `${publicDir}/docs.html`; +const port = process.env.APP_PORT; + +serve({ + port, + fetch: async (req: Request) => { + try { + const { pathname } = new URL(req.url); + const filePath = `${publicDir}${pathname}`; + + const asset = file(filePath); + const isAsset = await asset.exists(); + + if (isAsset) { + return new Response(asset.stream(), { + headers: { 'Content-Type': asset.type }, + }); + } + + const page = pathname.includes('/docs') ? docs : index; + + return new Response(file(page).stream(), { + headers: { 'Content-Type': 'text/html' }, + }); + } catch (error) { + return new Response('Not Found', { status: 404 }); + } + }, +}); + +console.log(`Serving at port ${port}`);