From f80e8b377f11a587350162f0c8f2fa0c12735336 Mon Sep 17 00:00:00 2001 From: Thada Wangthammang Date: Sat, 24 Aug 2024 23:37:12 +0700 Subject: [PATCH 1/5] feat(budget-app): add scale up button --- budget-app/src/app/tabs/SettingTab.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/budget-app/src/app/tabs/SettingTab.tsx b/budget-app/src/app/tabs/SettingTab.tsx index 55f6d95..9295dc2 100644 --- a/budget-app/src/app/tabs/SettingTab.tsx +++ b/budget-app/src/app/tabs/SettingTab.tsx @@ -54,8 +54,17 @@ export function SettingTab() { return ( -
+

Scale Settings

+ +
+
+

Cache Settings

+ + From 9a1231dcfe663a8cdf18306c9813416bd406a370 Mon Sep 17 00:00:00 2001 From: Thada Wangthammang Date: Sun, 25 Aug 2024 00:02:15 +0700 Subject: [PATCH 2/5] feat(budget-app): add scale up handler and api to control github actions --- budget-app/.env.example | 4 +- budget-app/package-lock.json | 179 +++++++++++++++++++++++++ budget-app/package.json | 1 + budget-app/src/app/api/scale/route.ts | 22 +++ budget-app/src/app/tabs/SettingTab.tsx | 25 +++- budget-app/src/bootstrap.ts | 8 ++ budget-app/src/env.ts | 4 + budget-app/src/libs/github.ts | 27 ++++ 8 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 budget-app/src/app/api/scale/route.ts create mode 100644 budget-app/src/libs/github.ts diff --git a/budget-app/.env.example b/budget-app/.env.example index 5a1ea58..335b6b2 100644 --- a/budget-app/.env.example +++ b/budget-app/.env.example @@ -10,4 +10,6 @@ AZURE_STORAGE_CONNECTION_STRING=UseDevelopmentStorage=true AZURE_STORAGE_QUEUE_NAME=budgetqueue AZURE_FUNCTION_RESET_CACHE_PATH=/api/resetcache -NEXT_PUBLIC_AZURE_FUNCTION_URL=http://localhost:7072 \ No newline at end of file +NEXT_PUBLIC_AZURE_FUNCTION_URL=http://localhost:7072 + +GITHUB_TOKEN= \ No newline at end of file diff --git a/budget-app/package-lock.json b/budget-app/package-lock.json index f2dee44..de60184 100644 --- a/budget-app/package-lock.json +++ b/budget-app/package-lock.json @@ -20,6 +20,7 @@ "@mui/material": "^5.15.15", "@mui/material-nextjs": "^5.15.11", "@mui/x-date-pickers": "^7.1.1", + "@octokit/core": "^6.1.2", "@tanstack/query-sync-storage-persister": "^5.29.0", "@tanstack/react-query": "^5.29.2", "@tanstack/react-query-persist-client": "^5.29.2", @@ -3421,6 +3422,94 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "dependencies": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "dependencies": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "node_modules/@octokit/request": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", + "dependencies": { + "@octokit/types": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, "node_modules/@opentelemetry/api": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", @@ -5226,6 +5315,11 @@ } ] }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, "node_modules/bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", @@ -10882,6 +10976,11 @@ "node": ">=8" } }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -14092,6 +14191,76 @@ "fastq": "^1.6.0" } }, + "@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==" + }, + "@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "requires": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + } + }, + "@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "requires": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + } + }, + "@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "requires": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + } + }, + "@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "@octokit/request": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "requires": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + } + }, + "@octokit/request-error": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", + "requires": { + "@octokit/types": "^13.0.0" + } + }, + "@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "requires": { + "@octokit/openapi-types": "^22.2.0" + } + }, "@opentelemetry/api": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", @@ -15342,6 +15511,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, "bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", @@ -19333,6 +19507,11 @@ "crypto-random-string": "^2.0.0" } }, + "universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + }, "universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/budget-app/package.json b/budget-app/package.json index 2017968..661e46e 100644 --- a/budget-app/package.json +++ b/budget-app/package.json @@ -23,6 +23,7 @@ "@mui/material": "^5.15.15", "@mui/material-nextjs": "^5.15.11", "@mui/x-date-pickers": "^7.1.1", + "@octokit/core": "^6.1.2", "@tanstack/query-sync-storage-persister": "^5.29.0", "@tanstack/react-query": "^5.29.2", "@tanstack/react-query-persist-client": "^5.29.2", diff --git a/budget-app/src/app/api/scale/route.ts b/budget-app/src/app/api/scale/route.ts new file mode 100644 index 0000000..e978f11 --- /dev/null +++ b/budget-app/src/app/api/scale/route.ts @@ -0,0 +1,22 @@ +import { octokit } from "@/bootstrap"; +import { scaleContainerApp } from "@/libs/github"; +import { NextResponse } from "next/server"; + +/** + * Scale Up Azure Container App (Set min_replicas to 1) + * @param req + * @returns + */ +export async function GET(req: Request) { + await scaleContainerApp(octokit, { + owner: "mildronize", + repo: "bunsheet", + ref: "main", + inputs: { + min_replicas: 1, + }, + }) + return NextResponse.json({ + message: "Sent request to scale up Azure Container App, check the action logs in the repository.", + }); +} diff --git a/budget-app/src/app/tabs/SettingTab.tsx b/budget-app/src/app/tabs/SettingTab.tsx index 9295dc2..5d9c702 100644 --- a/budget-app/src/app/tabs/SettingTab.tsx +++ b/budget-app/src/app/tabs/SettingTab.tsx @@ -10,13 +10,12 @@ import axios from "axios"; import { LocalStorage } from "@/libs/local-storage"; import { useState } from "react"; import { useGlobalLoading } from "@/hooks/useGlobalLoading"; -import { useSignalR } from "@/hooks/useSignalR"; import { useVersion } from "@/hooks/useVersion"; export type TransactionGetResponse = InferRouteResponse; export function SettingTab() { - const [isResettingCache, setIsResettingCache] = useState(false); + const [isLoading, setIsLoading] = useState(false); const clearAppCache = () => { const caches = [ @@ -28,7 +27,7 @@ export function SettingTab() { const resetCache = async () => { try { - setIsResettingCache(true); + setIsLoading(true); clearAppCache(); await axios.get("/api/cache/reset"); /** @@ -41,10 +40,22 @@ export function SettingTab() { toast.error("Failed to reset cache"); return; } - setIsResettingCache(false); + setIsLoading(false); }; - useGlobalLoading(isResettingCache); + const scaleUp = async () => { + setIsLoading(true); + try { + await axios.get("/api/scale"); + toast.success("Sent request to scale up Azure Container App"); + } catch (error) { + toast.error("Failed to scale up Azure Container App"); + return; + } + setIsLoading(false); + } + + useGlobalLoading(isLoading); const version = useVersion(); const reloadPage = () => { @@ -58,6 +69,8 @@ export function SettingTab() {

Scale Settings