diff --git a/.github/workflows/restart-runners.yml b/.github/workflows/restart-runners.yml new file mode 100644 index 00000000..14bbcefb --- /dev/null +++ b/.github/workflows/restart-runners.yml @@ -0,0 +1,39 @@ +name: Restart Runners + +on: + schedule: + - cron: '0 0 */7 * *' + +permissions: + id-token: write + contents: read + +jobs: + ec2-maintenance-prod: + runs-on: ubuntu-latest + permissions: write-all + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }} + role-duration-seconds: 3600 + aws-region: ${{ secrets.AWS_REGION }} + + - name: Verify AWS identity + run: aws sts get-caller-identity + + - name: Start EC2 instance + run: | + aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }} + aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }} + echo "EC2 instances started" + + - name: Wait + run: sleep 600 + + - name: Stop EC2 instance + run: | + aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }} + aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }} + echo "EC2 instances stopped" diff --git a/package-lock.json b/package-lock.json index 083b2491..c6e60cef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "lucide-react": "0.559.0", "maxmind": "5.0.1", "moment": "2.30.1", - "next": "15.5.7", + "next": "15.5.9", "next-intl": "4.5.8", "next-themes": "0.4.6", "nextjs-toploader": "3.9.17", @@ -88,7 +88,7 @@ "pg": "8.16.3", "posthog-node": "5.17.2", "qrcode.react": "4.2.0", - "react": "19.2.1", + "react": "19.2.3", "react-day-picker": "9.12.0", "react-dom": "19.2.1", "react-easy-sort": "1.8.0", @@ -3835,9 +3835,9 @@ } }, "node_modules/@next/env": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz", - "integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==", + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz", + "integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -16291,13 +16291,13 @@ } }, "node_modules/next": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz", - "integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==", + "version": "15.5.9", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz", + "integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==", "license": "MIT", "peer": true, "dependencies": { - "@next/env": "15.5.7", + "@next/env": "15.5.9", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -19737,9 +19737,9 @@ } }, "node_modules/react": { - "version": "19.2.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", - "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "peer": true, "engines": { diff --git a/package.json b/package.json index 78a9ffbc..5609b688 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "lucide-react": "0.559.0", "maxmind": "5.0.1", "moment": "2.30.1", - "next": "15.5.7", + "next": "15.5.9", "next-intl": "4.5.8", "next-themes": "0.4.6", "nextjs-toploader": "3.9.17", @@ -112,7 +112,7 @@ "pg": "8.16.3", "posthog-node": "5.17.2", "qrcode.react": "4.2.0", - "react": "19.2.1", + "react": "19.2.3", "react-day-picker": "9.12.0", "react-dom": "19.2.1", "react-easy-sort": "1.8.0", @@ -178,4 +178,4 @@ "typescript": "5.9.3", "typescript-eslint": "8.49.0" } -} \ No newline at end of file +} diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 57149bf6..d93cf224 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,6 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -// export const APP_VERSION = "1.13.0-rc.0"; export const APP_VERSION = "1.13.0"; export const __FILENAME = fileURLToPath(import.meta.url); diff --git a/server/lib/ip.ts b/server/lib/ip.ts index 36065df3..02683edc 100644 --- a/server/lib/ip.ts +++ b/server/lib/ip.ts @@ -120,11 +120,13 @@ function bigIntToIp(num: bigint, version: IPVersion): string { * Parses an endpoint string (ip:port) handling both IPv4 and IPv6 addresses. * IPv6 addresses may be bracketed like [::1]:8080 or unbracketed like ::1:8080. * For unbracketed IPv6, the last colon-separated segment is treated as the port. - * + * * @param endpoint The endpoint string to parse (e.g., "192.168.1.1:8080" or "[::1]:8080" or "2607:fea8::1:8080") * @returns An object with ip and port, or null if parsing fails */ -export function parseEndpoint(endpoint: string): { ip: string; port: number } | null { +export function parseEndpoint( + endpoint: string +): { ip: string; port: number } | null { if (!endpoint) return null; // Check for bracketed IPv6 format: [ip]:port @@ -138,7 +140,7 @@ export function parseEndpoint(endpoint: string): { ip: string; port: number } | // Check if this looks like IPv6 (contains multiple colons) const colonCount = (endpoint.match(/:/g) || []).length; - + if (colonCount > 1) { // This is IPv6 - the port is after the last colon const lastColonIndex = endpoint.lastIndexOf(":"); @@ -163,7 +165,7 @@ export function parseEndpoint(endpoint: string): { ip: string; port: number } | /** * Formats an IP and port into a consistent endpoint string. * IPv6 addresses are wrapped in brackets for proper parsing. - * + * * @param ip The IP address (IPv4 or IPv6) * @param port The port number * @returns Formatted endpoint string diff --git a/server/private/license/license.ts b/server/private/license/license.ts index db3db509..f8f774c6 100644 --- a/server/private/license/license.ts +++ b/server/private/license/license.ts @@ -84,14 +84,11 @@ LQIDAQAB -----END PUBLIC KEY-----`; constructor(private hostMeta: HostMeta) { - setInterval( - async () => { - this.doRecheck = true; - await this.check(); - this.doRecheck = false; - }, - 1000 * this.phoneHomeInterval - ); + setInterval(async () => { + this.doRecheck = true; + await this.check(); + this.doRecheck = false; + }, 1000 * this.phoneHomeInterval); } public listKeys(): LicenseKeyCache[] { @@ -242,7 +239,9 @@ LQIDAQAB // First failure: fail silently logger.error("Error communicating with license server:"); logger.error(e); - logger.error(`Allowing failure. Will retry one more time at next run interval.`); + logger.error( + `Allowing failure. Will retry one more time at next run interval.` + ); // return last known good status return this.statusCache.get( this.statusKey diff --git a/src/components/Credenza.tsx b/src/components/Credenza.tsx index 9d468e60..6a48fc54 100644 --- a/src/components/Credenza.tsx +++ b/src/components/Credenza.tsx @@ -177,7 +177,13 @@ const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => { const CredenzaFooter = isDesktop ? DialogFooter : SheetFooter; return ( - + {children} );