diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index fa8b0f412..ff40e86e6 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -50,7 +50,8 @@ jobs: git push origin --delete "release-${VERSION}" || true # Delete local branch if it exists git branch -D "release-${VERSION}" || true - # Create new branch from master + # For testing purpose + # git checkout beta-workflow git checkout -b "release-${VERSION}" - name: Install dependencies diff --git a/.github/workflows/build-docker-base.yml b/.github/workflows/build-docker-base.yml new file mode 100644 index 000000000..a4d98977c --- /dev/null +++ b/.github/workflows/build-docker-base.yml @@ -0,0 +1,48 @@ +name: Build Docker Base Images + +on: + workflow_dispatch: # Allow manual trigger + +permissions: {} + +jobs: + build-docker-base: + runs-on: ubuntu-latest + timeout-minutes: 120 + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: { persist-credentials: false } + + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: Login to Docker Hub + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ghcr.io + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Use Node.js 20 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + node-version: 20 + + - name: Build and push base2-slim image + run: npm run build-docker-base-slim + + - name: Build and push base2 image + run: npm run build-docker-base diff --git a/.github/workflows/conflict_labeler.yml b/.github/workflows/conflict-labeler.yml similarity index 100% rename from .github/workflows/conflict_labeler.yml rename to .github/workflows/conflict-labeler.yml diff --git a/.github/workflows/mark-as-draft-on-requesting-changes.yml b/.github/workflows/mark-as-draft-on-requesting-changes.yml new file mode 100644 index 000000000..61407184c --- /dev/null +++ b/.github/workflows/mark-as-draft-on-requesting-changes.yml @@ -0,0 +1,65 @@ +name: Mark PR as draft when changes are requested + +# pull_request_target is safe here because: +# 1. Does not use any external actions; only uses the GitHub CLI via run commands +# 2. Has minimal permissions +# 3. Doesn't checkout or execute any untrusted code from PRs +# 4. Only adds/removes labels or changes the draft status +on: # zizmor: ignore[dangerous-triggers] + pull_request_target: + types: + - review_submitted + - labeled + - ready_for_review + +permissions: {} + +jobs: + mark-draft: + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: | + ( + github.event.action == 'review_submitted' && + github.event.review.state == 'changes_requested' + ) || ( + github.event.action == 'labeled' && + github.event.label.name == 'pr:please address review comments' + ) + steps: + - name: Add label on requested changes + if: github.event.review.state == 'changes_requested' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh issue edit "${{ github.event.pull_request.number }}" \ + --repo "${{ github.repository }}" \ + --add-label "pr:please address review comments" + + - name: Mark PR as draft + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr ready "${{ github.event.pull_request.number }}" \ + --repo "${{ github.repository }}" \ + --undo || true + # || true to ignore the case where the pr is already a draft + + ready-for-review: + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: github.event.action == 'ready_for_review' + steps: + - name: Update labels for review + env: + GH_TOKEN: ${{ github.token }} + run: | + gh issue edit "${{ github.event.pull_request.number }}" \ + --repo "${{ github.repository }}" \ + --remove-label "pr:please address review comments" || true + + gh issue edit "${{ github.event.pull_request.number }}" \ + --repo "${{ github.repository }}" \ + --add-label "pr:needs review" diff --git a/.github/workflows/new_contributor_pr.yml b/.github/workflows/new-contributor-pr.yml similarity index 100% rename from .github/workflows/new_contributor_pr.yml rename to .github/workflows/new-contributor-pr.yml diff --git a/config/vite.config.js b/config/vite.config.js index eaba9b342..91aa3cbcd 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -36,6 +36,9 @@ export default defineConfig({ srcDir: "src", filename: "serviceWorker.ts", strategies: "injectManifest", + injectManifest: { + maximumFileSizeToCacheInBytes: 3 * 1024 * 1024, // 3 MiB + }, }), ], css: { diff --git a/extra/beta/update-version.js b/extra/beta/update-version.mjs similarity index 83% rename from extra/beta/update-version.js rename to extra/beta/update-version.mjs index 5465763df..116824dd7 100644 --- a/extra/beta/update-version.js +++ b/extra/beta/update-version.mjs @@ -1,3 +1,6 @@ +import { createRequire } from "module"; +const require = createRequire(import.meta.url); + const pkg = require("../../package.json"); const fs = require("fs"); const childProcess = require("child_process"); @@ -58,8 +61,13 @@ function commit(version) { throw new Error("commit error"); } - // Note: Push is handled by gh pr create in the release script - // No need to push here as we're on a release branch, not master + // Get the current branch name + res = childProcess.spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]); + let branchName = res.stdout.toString().trim(); + console.log("Current branch:", branchName); + + // Git push the branch + childProcess.spawnSync("git", ["push", "origin", branchName, "--force"], { stdio: "inherit" }); } /** diff --git a/extra/generate-changelog.mjs b/extra/generate-changelog.mjs index 18ec20983..756ea7273 100644 --- a/extra/generate-changelog.mjs +++ b/extra/generate-changelog.mjs @@ -4,7 +4,7 @@ import * as childProcess from "child_process"; -const ignoreList = ["louislam", "CommanderStorm", "UptimeKumaBot", "weblate", "Copilot"]; +const ignoreList = ["louislam", "CommanderStorm", "UptimeKumaBot", "weblate", "Copilot", "@autofix-ci[bot]"]; const mergeList = ["Translations Update from Weblate", "Update dependencies"]; diff --git a/extra/release/beta.mjs b/extra/release/beta.mjs index d951bcba8..9e3d86848 100644 --- a/extra/release/beta.mjs +++ b/extra/release/beta.mjs @@ -48,7 +48,7 @@ checkDocker(); await checkTagExists(repoNames, version); // node extra/beta/update-version.js -execSync("node ./extra/beta/update-version.js"); +await import("../beta/update-version.mjs"); // Create Pull Request (gh pr create will handle pushing the branch) await createReleasePR(version, previousVersion, dryRun, branchName, githubRunId); diff --git a/package-lock.json b/package-lock.json index 490b563e4..db0a67846 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "2.1.0-beta.1", + "version": "2.1.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "2.1.0-beta.1", + "version": "2.1.0-beta.2", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.8.22", @@ -97,7 +97,7 @@ "@fortawesome/fontawesome-svg-core": "~1.2.36", "@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4", - "@fortawesome/vue-fontawesome": "~3.0.0-5", + "@fortawesome/vue-fontawesome": "~3.1.3", "@playwright/test": "~1.39.0", "@popperjs/core": "~2.10.2", "@testcontainers/hivemq": "^10.13.1", @@ -132,8 +132,8 @@ "favico.js": "~0.3.10", "get-port-please": "^3.1.1", "node-ssh": "~13.1.0", - "postcss-html": "~1.5.0", - "postcss-rtlcss": "~3.7.2", + "postcss-html": "~1.8.1", + "postcss-rtlcss": "~5.7.1", "postcss-scss": "~4.0.4", "prettier": "^3.7.4", "prismjs": "~1.30.0", @@ -151,11 +151,11 @@ "vite": "~5.4.15", "vite-plugin-compression": "^0.5.1", "vite-plugin-pwa": "^1.1.0", - "vue": "~3.4.2", + "vue": "~3.5.26", "vue-chartjs": "~5.2.0", "vue-confirm-dialog": "~1.0.2", "vue-contenteditable": "~3.0.4", - "vue-i18n": "~9.14.3", + "vue-i18n": "~11.2.8", "vue-image-crop-upload": "~3.0.3", "vue-multiselect": "~3.0.0-alpha.2", "vue-prism-editor": "~2.0.0-alpha.2", @@ -3491,13 +3491,13 @@ } }, "node_modules/@fortawesome/vue-fontawesome": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.8.tgz", - "integrity": "sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.3.tgz", + "integrity": "sha512-OHHUTLPEzdwP8kcYIzhioUdUOjZ4zzmi+midwa4bqscza4OJCOvTKJEHkXNz8PgZ23kWci1HkKVX0bm8f9t9gQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", "vue": ">= 3.0.0 < 4" } }, @@ -3605,14 +3605,14 @@ "license": "BSD-3-Clause" }, "node_modules/@intlify/core-base": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz", - "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz", + "integrity": "sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==", "dev": true, "license": "MIT", "dependencies": { - "@intlify/message-compiler": "9.14.5", - "@intlify/shared": "9.14.5" + "@intlify/message-compiler": "11.2.8", + "@intlify/shared": "11.2.8" }, "engines": { "node": ">= 16" @@ -3622,13 +3622,13 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", - "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.2.8.tgz", + "integrity": "sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==", "dev": true, "license": "MIT", "dependencies": { - "@intlify/shared": "9.14.5", + "@intlify/shared": "11.2.8", "source-map-js": "^1.0.2" }, "engines": { @@ -3639,9 +3639,9 @@ } }, "node_modules/@intlify/shared": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz", - "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.2.8.tgz", + "integrity": "sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==", "dev": true, "license": "MIT", "engines": { @@ -6473,51 +6473,128 @@ "license": "MIT" }, "node_modules/@vue/reactivity": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.38.tgz", - "integrity": "sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz", + "integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/shared": "3.4.38" + "@vue/shared": "3.5.26" } }, + "node_modules/@vue/reactivity/node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/runtime-core": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.38.tgz", - "integrity": "sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz", + "integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/reactivity": "3.5.26", + "@vue/shared": "3.5.26" } }, + "node_modules/@vue/runtime-core/node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/runtime-dom": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.38.tgz", - "integrity": "sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz", + "integrity": "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.4.38", - "@vue/runtime-core": "3.4.38", - "@vue/shared": "3.4.38", - "csstype": "^3.1.3" + "@vue/reactivity": "3.5.26", + "@vue/runtime-core": "3.5.26", + "@vue/shared": "3.5.26", + "csstype": "^3.2.3" } }, + "node_modules/@vue/runtime-dom/node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/server-renderer": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.38.tgz", - "integrity": "sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz", + "integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-ssr": "3.5.26", + "@vue/shared": "3.5.26" }, "peerDependencies": { - "vue": "3.4.38" + "vue": "3.5.26" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", + "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.26", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz", + "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", + "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/server-renderer/node_modules/entities": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", + "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/@vue/shared": { @@ -12584,9 +12661,9 @@ } }, "node_modules/js-tokens": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", - "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true, "license": "MIT" }, @@ -15089,15 +15166,15 @@ } }, "node_modules/postcss-html": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.5.0.tgz", - "integrity": "sha512-kCMRWJRHKicpA166kc2lAVUGxDZL324bkj/pVOb6RhjB0Z5Krl7mN0AsVkBhVIRZZirY0lyQXG38HCVaoKVNoA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.1.tgz", + "integrity": "sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==", "dev": true, "license": "MIT", "dependencies": { "htmlparser2": "^8.0.0", - "js-tokens": "^8.0.0", - "postcss": "^8.4.0", + "js-tokens": "^9.0.0", + "postcss": "^8.5.0", "postcss-safe-parser": "^6.0.0" }, "engines": { @@ -15132,19 +15209,19 @@ "license": "MIT" }, "node_modules/postcss-rtlcss": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-3.7.2.tgz", - "integrity": "sha512-GurrGedCKvOTe1QrifI+XpDKXA3bJky1v8KiOa/TYYHs1bfJOxI53GIRvVSqLJLly7e1WcNMz8KMESTN01vbZQ==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-5.7.1.tgz", + "integrity": "sha512-zE68CuARv5StOG/UQLa0W1Y/raUTzgJlfjtas43yh3/G1BFmoPEaHxPRHgeowXRFFhW33FehrNgsljxRLmPVWw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "rtlcss": "^3.5.0" + "rtlcss": "4.3.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" }, "peerDependencies": { - "postcss": "^8.0.0" + "postcss": "^8.4.21" } }, "node_modules/postcss-safe-parser": { @@ -16431,19 +16508,22 @@ } }, "node_modules/rtlcss": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", - "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^5.0.0", + "escalade": "^3.1.1", "picocolors": "^1.0.0", - "postcss": "^8.3.11", + "postcss": "^8.4.21", "strip-json-comments": "^3.1.1" }, "bin": { "rtlcss": "bin/rtlcss.js" + }, + "engines": { + "node": ">=12.0.0" } }, "node_modules/run-applescript": { @@ -18943,17 +19023,17 @@ } }, "node_modules/vue": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz", - "integrity": "sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz", + "integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.4.38", - "@vue/compiler-sfc": "3.4.38", - "@vue/runtime-dom": "3.4.38", - "@vue/server-renderer": "3.4.38", - "@vue/shared": "3.4.38" + "@vue/compiler-dom": "3.5.26", + "@vue/compiler-sfc": "3.5.26", + "@vue/runtime-dom": "3.5.26", + "@vue/server-renderer": "3.5.26", + "@vue/shared": "3.5.26" }, "peerDependencies": { "typescript": "*" @@ -19048,15 +19128,14 @@ } }, "node_modules/vue-i18n": { - "version": "9.14.5", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz", - "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", - "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html", + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.8.tgz", + "integrity": "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==", "dev": true, "license": "MIT", "dependencies": { - "@intlify/core-base": "9.14.5", - "@intlify/shared": "9.14.5", + "@intlify/core-base": "11.2.8", + "@intlify/shared": "11.2.8", "@vue/devtools-api": "^6.5.0" }, "engines": { @@ -19150,6 +19229,80 @@ "vue": "^3.0.2" } }, + "node_modules/vue/node_modules/@vue/compiler-core": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", + "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.26", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-dom": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz", + "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/vue/node_modules/@vue/compiler-sfc": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz", + "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.26", + "@vue/compiler-dom": "3.5.26", + "@vue/compiler-ssr": "3.5.26", + "@vue/shared": "3.5.26", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-ssr": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", + "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.26", + "@vue/shared": "3.5.26" + } + }, + "node_modules/vue/node_modules/@vue/shared": { + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue/node_modules/entities": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", + "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/vuedraggable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", diff --git a/package.json b/package.json index 1b6a10bfc..5574a67b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "2.1.0-beta.1", + "version": "2.1.0-beta.2", "license": "MIT", "repository": { "type": "git", @@ -159,7 +159,7 @@ "@fortawesome/fontawesome-svg-core": "~1.2.36", "@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4", - "@fortawesome/vue-fontawesome": "~3.0.0-5", + "@fortawesome/vue-fontawesome": "~3.1.3", "@playwright/test": "~1.39.0", "@popperjs/core": "~2.10.2", "@testcontainers/hivemq": "^10.13.1", @@ -194,8 +194,8 @@ "favico.js": "~0.3.10", "get-port-please": "^3.1.1", "node-ssh": "~13.1.0", - "postcss-html": "~1.5.0", - "postcss-rtlcss": "~3.7.2", + "postcss-html": "~1.8.1", + "postcss-rtlcss": "~5.7.1", "postcss-scss": "~4.0.4", "prettier": "^3.7.4", "prismjs": "~1.30.0", @@ -213,11 +213,11 @@ "vite": "~5.4.15", "vite-plugin-compression": "^0.5.1", "vite-plugin-pwa": "^1.1.0", - "vue": "~3.4.2", + "vue": "~3.5.26", "vue-chartjs": "~5.2.0", "vue-confirm-dialog": "~1.0.2", "vue-contenteditable": "~3.0.4", - "vue-i18n": "~9.14.3", + "vue-i18n": "~11.2.8", "vue-image-crop-upload": "~3.0.3", "vue-multiselect": "~3.0.0-alpha.2", "vue-prism-editor": "~2.0.0-alpha.2", diff --git a/server/model/domain_expiry.js b/server/model/domain_expiry.js index 91d5b54e3..3502a4b08 100644 --- a/server/model/domain_expiry.js +++ b/server/model/domain_expiry.js @@ -159,31 +159,22 @@ class DomainExpiry extends BeanModel { const tld = parseTld(target); // Avoid logging for incomplete/invalid input while editing monitors. - if (!tld.domain) { - throw new TranslatableError("domain_expiry_unsupported_invalid_domain", { hostname: tld.hostname }); - } - if (!tld.publicSuffix) { - throw new TranslatableError("domain_expiry_unsupported_public_suffix", { publicSuffix: tld.publicSuffix }); - } if (tld.isIp) { throw new TranslatableError("domain_expiry_unsupported_is_ip", { hostname: tld.hostname }); } - // No one-letter public suffix exists; treat this as an incomplete/invalid input while typing. if (tld.publicSuffix.length < 2) { throw new TranslatableError("domain_expiry_public_suffix_too_short", { publicSuffix: tld.publicSuffix }); } + if (!tld.isIcann) { + throw new TranslatableError("domain_expiry_unsupported_is_icann", { + domain: tld.domain, + publicSuffix: tld.publicSuffix, + }); + } const rdap = await getRdapServer(tld.publicSuffix); if (!rdap) { - // Only warn when the monitor actually has domain expiry notifications enabled. - // The edit monitor page calls this method frequently while the user is typing. - if (Boolean(monitor.domainExpiryNotification)) { - log.warn( - "domain_expiry", - `Domain expiry unsupported for '.${tld.publicSuffix}' because its RDAP endpoint is not listed in the IANA database.` - ); - } throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", { publicSuffix: tld.publicSuffix, }); diff --git a/server/model/monitor.js b/server/model/monitor.js index 01d655f23..e01977133 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1059,7 +1059,15 @@ class Monitor extends BeanModel { log.debug("monitor", `Failed getting expiration date for domain ${supportInfo.domain}`); } } catch (error) { - // purposely not logged due to noise. Is accessible via checkMointor + if ( + error.message === "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint" && + Boolean(this.domainExpiryNotification) + ) { + log.warn( + "domain_expiry", + `Domain expiry unsupported for '.${error.meta.publicSuffix}' because its RDAP endpoint is not listed in the IANA database.` + ); + } } } diff --git a/server/monitor-types/postgres.js b/server/monitor-types/postgres.js index fb6cc9b0d..c9daf65f0 100644 --- a/server/monitor-types/postgres.js +++ b/server/monitor-types/postgres.js @@ -3,26 +3,61 @@ const { log, UP } = require("../../src/util"); const dayjs = require("dayjs"); const postgresConParse = require("pg-connection-string").parse; const { Client } = require("pg"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); class PostgresMonitorType extends MonitorType { name = "postgres"; + supportsConditions = true; + conditionVariables = [new ConditionVariable("result", defaultStringOperators)]; + /** * @inheritdoc */ async check(monitor, heartbeat, _server) { - let startTime = dayjs().valueOf(); - let query = monitor.databaseQuery; // No query provided by user, use SELECT 1 if (!query || (typeof query === "string" && query.trim() === "")) { query = "SELECT 1"; } - await this.postgresQuery(monitor.databaseConnectionString, query); - heartbeat.msg = ""; - heartbeat.status = UP; - heartbeat.ping = dayjs().valueOf() - startTime; + const conditions = monitor.conditions ? ConditionExpressionGroup.fromMonitor(monitor) : null; + const hasConditions = conditions && conditions.children && conditions.children.length > 0; + + const startTime = dayjs().valueOf(); + + try { + if (hasConditions) { + // When conditions are enabled, expect a single value result + const result = await this.postgresQuerySingleValue(monitor.databaseConnectionString, query); + heartbeat.ping = dayjs().valueOf() - startTime; + + const conditionsResult = evaluateExpressionGroup(conditions, { result: String(result) }); + + if (!conditionsResult) { + throw new Error(`Query result did not meet the specified conditions (${result})`); + } + + heartbeat.status = UP; + heartbeat.msg = "Query did meet specified conditions"; + } else { + // Backwards compatible: just check connection and return row count + const result = await this.postgresQuery(monitor.databaseConnectionString, query); + heartbeat.ping = dayjs().valueOf() - startTime; + heartbeat.status = UP; + heartbeat.msg = result; + } + } catch (error) { + heartbeat.ping = dayjs().valueOf() - startTime; + // Re-throw condition errors as-is, wrap database errors + if (error.message.includes("did not meet the specified conditions")) { + throw error; + } + throw new Error(`Database connection/query failed: ${error.message}`); + } } /** @@ -76,6 +111,75 @@ class PostgresMonitorType extends MonitorType { }); }); } + + /** + * Run a query on Postgres + * @param {string} connectionString The database connection string + * @param {string} query The query to validate the database with + * @returns {Promise<(string[] | object[] | object)>} Response from + * server + */ + async postgresQuerySingleValue(connectionString, query) { + return new Promise((resolve, reject) => { + const config = postgresConParse(connectionString); + + // Fix #3868, which true/false is not parsed to boolean + if (typeof config.ssl === "string") { + config.ssl = config.ssl === "true"; + } + + if (config.password === "") { + // See https://github.com/brianc/node-postgres/issues/1927 + reject(new Error("Password is undefined.")); + return; + } + const client = new Client(config); + + client.on("error", (error) => { + log.debug(this.name, "Error caught in the error event handler."); + reject(error); + }); + + client.connect((err) => { + if (err) { + reject(err); + client.end(); + } else { + // Connected here + try { + client.query(query, (err, res) => { + if (err) { + reject(err); + } else { + // Check if we have results + if (!res.rows || res.rows.length === 0) { + reject(new Error("Query returned no results")); + return; + } + // Check if we have multiple rows + if (res.rows.length > 1) { + reject(new Error("Multiple values were found, expected only one value")); + return; + } + const firstRow = res.rows[0]; + const columnNames = Object.keys(firstRow); + // Check if we have multiple columns + if (columnNames.length > 1) { + reject(new Error("Multiple columns were found, expected only one value")); + return; + } + resolve(firstRow[columnNames[0]]); + } + client.end(); + }); + } catch (e) { + reject(e); + client.end(); + } + } + }); + }); + } } module.exports = { diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 79768e3df..3ed509cea 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -11,6 +11,11 @@ class Discord extends NotificationProvider { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { const okMsg = "Sent Successfully."; + // Discord Message Flags + // @see https://discord.com/developers/docs/resources/message#message-object-message-flags + // This message will not trigger push and desktop notifications + const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12; + try { let config = this.getAxiosConfigWithProxy({}); const discordDisplayName = notification.discordUsername || "Uptime Kuma"; @@ -41,6 +46,9 @@ class Discord extends NotificationProvider { if (notification.discordChannelType === "createNewForumPost") { discordtestdata.thread_name = notification.postName; } + if (notification.discordSuppressNotifications) { + discordtestdata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } await axios.post(webhookUrl.toString(), discordtestdata, config); return okMsg; } @@ -89,6 +97,9 @@ class Discord extends NotificationProvider { if (notification.discordPrefixMessage) { discorddowndata.content = notification.discordPrefixMessage; } + if (notification.discordSuppressNotifications) { + discorddowndata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } await axios.post(webhookUrl.toString(), discorddowndata, config); return okMsg; @@ -140,6 +151,9 @@ class Discord extends NotificationProvider { if (notification.discordPrefixMessage) { discordupdata.content = notification.discordPrefixMessage; } + if (notification.discordSuppressNotifications) { + discordupdata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } await axios.post(webhookUrl.toString(), discordupdata, config); return okMsg; diff --git a/src/components/CertificateInfo.vue b/src/components/CertificateInfo.vue index d81b5beb9..8c9645692 100644 --- a/src/components/CertificateInfo.vue +++ b/src/components/CertificateInfo.vue @@ -1,7 +1,7 @@ - - - - diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue index caf6f09d4..ffcbda104 100644 --- a/src/components/notifications/Octopush.vue +++ b/src/components/notifications/Octopush.vue @@ -2,8 +2,8 @@
{{ $t("octopushLegacyHint") }} diff --git a/src/components/settings/APIKeys.vue b/src/components/settings/APIKeys.vue index d57bc2371..68484cce6 100644 --- a/src/components/settings/APIKeys.vue +++ b/src/components/settings/APIKeys.vue @@ -27,7 +27,7 @@
{{ $t("apiKey-" + item.status) }}
-
{{ $t("Created") }}: {{ item.createdDate }}
+
{{ $t("createdAt", { date: item.createdDate }) }}
{{ $t("Expires") }}: {{ item.expires || $t("Never") }} diff --git a/src/components/settings/About.vue b/src/components/settings/About.vue index 012f60ecb..0ae4b22fd 100644 --- a/src/components/settings/About.vue +++ b/src/components/settings/About.vue @@ -3,8 +3,8 @@