chore(release): automatic release v1.23.0
This commit is contained in:
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -31,6 +31,7 @@ body:
|
|||||||
label: Version
|
label: Version
|
||||||
description: What version of Homarr are you running?
|
description: What version of Homarr are you running?
|
||||||
options:
|
options:
|
||||||
|
- 1.22.0
|
||||||
- 1.21.0
|
- 1.21.0
|
||||||
- 1.20.0
|
- 1.20.0
|
||||||
- 1.19.1
|
- 1.19.1
|
||||||
|
|||||||
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
|
**Thank you for your contribution. Please ensure that your pull request meets the following pull request:**
|
||||||
|
|
||||||
- [ ] Builds without warnings or errors (``pnpm build``, autofix with ``pnpm format:fix``)
|
- [ ] Builds without warnings or errors (`pnpm build`, autofix with `pnpm format:fix`)
|
||||||
- [ ] Pull request targets ``dev`` branch
|
- [ ] Pull request targets `dev` branch
|
||||||
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
|
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||||
- [ ] No shorthand variable names are used (eg. ``x``, ``y``, ``i`` or any abbrevation)
|
- [ ] No shorthand variable names are used (eg. `x`, `y`, `i` or any abbrevation)
|
||||||
|
- [ ] Documentation is up to date. Create a pull request [here](https://github.com/homarr-labs/documentation/).
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const nextConfig: NextConfig = {
|
|||||||
script-src * 'unsafe-inline' 'unsafe-eval';
|
script-src * 'unsafe-inline' 'unsafe-eval';
|
||||||
base-uri 'self';
|
base-uri 'self';
|
||||||
connect-src *;
|
connect-src *;
|
||||||
style-src 'self' 'unsafe-inline';
|
style-src * 'unsafe-inline';
|
||||||
frame-ancestors *;
|
frame-ancestors *;
|
||||||
frame-src *;
|
frame-src *;
|
||||||
form-action 'self';
|
form-action 'self';
|
||||||
|
|||||||
@@ -55,14 +55,14 @@
|
|||||||
"@mantine/modals": "^8.0.2",
|
"@mantine/modals": "^8.0.2",
|
||||||
"@mantine/tiptap": "^8.0.2",
|
"@mantine/tiptap": "^8.0.2",
|
||||||
"@million/lint": "1.0.14",
|
"@million/lint": "1.0.14",
|
||||||
"@tabler/icons-react": "^3.33.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"@tanstack/react-query": "^5.79.0",
|
"@tanstack/react-query": "^5.80.6",
|
||||||
"@tanstack/react-query-devtools": "^5.79.0",
|
"@tanstack/react-query-devtools": "^5.80.6",
|
||||||
"@tanstack/react-query-next-experimental": "^5.79.0",
|
"@tanstack/react-query-next-experimental": "^5.80.6",
|
||||||
"@trpc/client": "^11.1.4",
|
"@trpc/client": "^11.3.1",
|
||||||
"@trpc/next": "^11.1.4",
|
"@trpc/next": "^11.3.1",
|
||||||
"@trpc/react-query": "^11.1.4",
|
"@trpc/react-query": "^11.3.1",
|
||||||
"@trpc/server": "^11.1.4",
|
"@trpc/server": "^11.3.1",
|
||||||
"@xterm/addon-canvas": "^0.7.0",
|
"@xterm/addon-canvas": "^0.7.0",
|
||||||
"@xterm/addon-fit": "0.10.0",
|
"@xterm/addon-fit": "0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
@@ -81,24 +81,24 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-simple-code-editor": "^0.14.1",
|
"react-simple-code-editor": "^0.14.1",
|
||||||
"sass": "^1.89.0",
|
"sass": "^1.89.1",
|
||||||
"superjson": "2.2.2",
|
"superjson": "2.2.2",
|
||||||
"swagger-ui-react": "^5.22.0",
|
"swagger-ui-react": "^5.24.0",
|
||||||
"use-deep-compare-effect": "^1.8.1",
|
"use-deep-compare-effect": "^1.8.1",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/chroma-js": "3.1.1",
|
"@types/chroma-js": "3.1.1",
|
||||||
"@types/node": "^22.15.28",
|
"@types/node": "^22.15.30",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
"@types/react": "19.1.6",
|
"@types/react": "19.1.6",
|
||||||
"@types/react-dom": "19.1.5",
|
"@types/react-dom": "19.1.6",
|
||||||
"@types/swagger-ui-react": "^5.18.0",
|
"@types/swagger-ui-react": "^5.18.0",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"node-loader": "^2.1.0",
|
"node-loader": "^2.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
|||||||
@@ -44,10 +44,10 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/node": "^22.15.28",
|
"@types/node": "^22.15.30",
|
||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tsx": "4.19.4",
|
"tsx": "4.19.4",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -39,21 +39,21 @@
|
|||||||
"@semantic-release/npm": "^12.0.1",
|
"@semantic-release/npm": "^12.0.1",
|
||||||
"@semantic-release/release-notes-generator": "^14.0.3",
|
"@semantic-release/release-notes-generator": "^14.0.3",
|
||||||
"@turbo/gen": "^2.5.4",
|
"@turbo/gen": "^2.5.4",
|
||||||
"@vitejs/plugin-react": "^4.5.0",
|
"@vitejs/plugin-react": "^4.5.1",
|
||||||
"@vitest/coverage-v8": "^3.1.4",
|
"@vitest/coverage-v8": "^3.2.2",
|
||||||
"@vitest/ui": "^3.1.4",
|
"@vitest/ui": "^3.2.2",
|
||||||
"conventional-changelog-conventionalcommits": "^9.0.0",
|
"conventional-changelog-conventionalcommits": "^9.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"semantic-release": "^24.2.5",
|
"semantic-release": "^24.2.5",
|
||||||
"testcontainers": "^10.28.0",
|
"testcontainers": "^11.0.0",
|
||||||
"turbo": "^2.5.4",
|
"turbo": "^2.5.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^3.1.4"
|
"vitest": "^3.2.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.11.0",
|
"packageManager": "pnpm@10.11.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.16.0"
|
"node": ">=22.16.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,24 +41,24 @@
|
|||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@kubernetes/client-node": "^1.3.0",
|
"@kubernetes/client-node": "^1.3.0",
|
||||||
"@tanstack/react-query": "^5.79.0",
|
"@tanstack/react-query": "^5.80.6",
|
||||||
"@trpc/client": "^11.1.4",
|
"@trpc/client": "^11.3.1",
|
||||||
"@trpc/react-query": "^11.1.4",
|
"@trpc/react-query": "^11.3.1",
|
||||||
"@trpc/server": "^11.1.4",
|
"@trpc/server": "^11.3.1",
|
||||||
"@trpc/tanstack-react-query": "^11.1.4",
|
"@trpc/tanstack-react-query": "^11.3.1",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"next": "15.3.3",
|
"next": "15.3.3",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"superjson": "2.2.2",
|
"superjson": "2.2.2",
|
||||||
"trpc-to-openapi": "^2.3.1",
|
"trpc-to-openapi": "^2.3.1",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"next-auth": "5.0.0-beta.28",
|
"next-auth": "5.0.0-beta.28",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/cookies": "0.9.0",
|
"@types/cookies": "0.9.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,14 +34,14 @@
|
|||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"undici": "7.10.0",
|
"undici": "7.10.0",
|
||||||
"zod": "^3.25.42",
|
"zod": "^3.25.55",
|
||||||
"zod-validation-error": "^3.4.1"
|
"zod-validation-error": "^3.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,14 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"node-cron": "^4.0.7"
|
"node-cron": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,11 @@
|
|||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
"@testcontainers/mysql": "^10.28.0",
|
"@testcontainers/mysql": "^11.0.0",
|
||||||
"better-sqlite3": "^11.10.0",
|
"better-sqlite3": "^11.10.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-kit": "^0.31.1",
|
"drizzle-kit": "^0.31.1",
|
||||||
"drizzle-orm": "^0.44.0",
|
"drizzle-orm": "^0.44.2",
|
||||||
"drizzle-zod": "^0.7.1",
|
"drizzle-zod": "^0.7.1",
|
||||||
"mysql2": "3.14.1"
|
"mysql2": "3.14.1"
|
||||||
},
|
},
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"@types/better-sqlite3": "7.6.13",
|
"@types/better-sqlite3": "7.6.13",
|
||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"esbuild": "^0.25.5",
|
"esbuild": "^0.25.5",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tsx": "4.19.4",
|
"tsx": "4.19.4",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as mysqlSchema from "../schema/mysql";
|
|||||||
|
|
||||||
describe("Mysql Migration", () => {
|
describe("Mysql Migration", () => {
|
||||||
test("should add all tables and keys specified in migration files", async () => {
|
test("should add all tables and keys specified in migration files", async () => {
|
||||||
const mysqlContainer = await new MySqlContainer().start();
|
const mysqlContainer = await new MySqlContainer("mysql:latest").start();
|
||||||
|
|
||||||
const connection = mysql.createConnection({
|
const connection = mysql.createConnection({
|
||||||
host: mysqlContainer.getHost(),
|
host: mysqlContainer.getHost(),
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"fast-xml-parser": "^5.2.3",
|
"fast-xml-parser": "^5.2.4",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"tsx": "4.19.4",
|
"tsx": "4.19.4",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,14 +26,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/env": "workspace:^0.1.0",
|
"@homarr/env": "workspace:^0.1.0",
|
||||||
"dockerode": "^4.0.6"
|
"dockerode": "^4.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/dockerode": "^3.3.39",
|
"@types/dockerode": "^3.3.39",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
packages/env/package.json
vendored
4
packages/env/package.json
vendored
@@ -24,13 +24,13 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@t3-oss/env-nextjs": "^0.13.6",
|
"@t3-oss/env-nextjs": "^0.13.6",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/form": "^8.0.2",
|
"@mantine/form": "^8.0.2",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,13 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"tsdav": "^2.1.4",
|
"tsdav": "^2.1.4",
|
||||||
"undici": "7.10.0",
|
"undici": "7.10.0",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/node-unifi": "^2.5.1",
|
"@types/node-unifi": "^2.5.1",
|
||||||
"@types/xml2js": "^0.4.14",
|
"@types/xml2js": "^0.4.14",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import dayjs from "dayjs";
|
|
||||||
import objectSupport from "dayjs/plugin/objectSupport";
|
|
||||||
import utc from "dayjs/plugin/utc";
|
|
||||||
import * as ical from "node-ical";
|
import * as ical from "node-ical";
|
||||||
import { DAVClient } from "tsdav";
|
import { DAVClient } from "tsdav";
|
||||||
import type { Dispatcher, RequestInit as UndiciFetchRequestInit } from "undici";
|
import type { Dispatcher, RequestInit as UndiciFetchRequestInit } from "undici";
|
||||||
@@ -15,9 +12,6 @@ import { Integration } from "../base/integration";
|
|||||||
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||||
import type { CalendarEvent } from "../calendar-types";
|
import type { CalendarEvent } from "../calendar-types";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(objectSupport);
|
|
||||||
|
|
||||||
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
|
||||||
export class NextcloudIntegration extends Integration {
|
export class NextcloudIntegration extends Integration {
|
||||||
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
@@ -27,7 +21,7 @@ export class NextcloudIntegration extends Integration {
|
|||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getCalendarEventsAsync(start: Date, end: Date): Promise<CalendarEvent[]> {
|
public async getCalendarEventsAsync(start: Date, end: Date, _showUnmonitored?: boolean): Promise<CalendarEvent[]> {
|
||||||
const client = await this.createCalendarClientAsync();
|
const client = await this.createCalendarClientAsync();
|
||||||
await client.login();
|
await client.login();
|
||||||
|
|
||||||
@@ -57,14 +51,7 @@ export class NextcloudIntegration extends Integration {
|
|||||||
|
|
||||||
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
||||||
|
|
||||||
const date = dayjs.utc({
|
const date = veventObject.start;
|
||||||
days: veventObject.start.getDay(),
|
|
||||||
month: veventObject.start.getMonth(),
|
|
||||||
year: veventObject.start.getFullYear(),
|
|
||||||
hours: veventObject.start.getHours(),
|
|
||||||
minutes: veventObject.start.getMinutes(),
|
|
||||||
seconds: veventObject.start.getSeconds(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const eventUrlWithoutHost = new URL(event.url).pathname;
|
const eventUrlWithoutHost = new URL(event.url).pathname;
|
||||||
const dateInMillis = veventObject.start.valueOf();
|
const dateInMillis = veventObject.start.valueOf();
|
||||||
@@ -75,7 +62,7 @@ export class NextcloudIntegration extends Integration {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: veventObject.summary,
|
name: veventObject.summary,
|
||||||
date: date.toDate(),
|
date,
|
||||||
subName: "",
|
subName: "",
|
||||||
description: veventObject.description,
|
description: veventObject.description,
|
||||||
links: [
|
links: [
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export class OpenMediaVaultIntegration extends Integration {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
version: systemResult.data.response.version,
|
version: systemResult.data.response.version,
|
||||||
cpuModelName: systemResult.data.response.cpuModelName,
|
cpuModelName: systemResult.data.response.cpuModelName ?? "Unknown CPU",
|
||||||
cpuUtilization: systemResult.data.response.cpuUtilization,
|
cpuUtilization: systemResult.data.response.cpuUtilization,
|
||||||
memUsed: systemResult.data.response.memUsed,
|
memUsed: systemResult.data.response.memUsed,
|
||||||
memAvailable: systemResult.data.response.memAvailable,
|
memAvailable: systemResult.data.response.memAvailable,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { z } from "zod";
|
|||||||
export const systemInformationSchema = z.object({
|
export const systemInformationSchema = z.object({
|
||||||
response: z.object({
|
response: z.object({
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
cpuModelName: z.string(),
|
cpuModelName: z.string().nullable(),
|
||||||
cpuUtilization: z.number(),
|
cpuUtilization: z.number(),
|
||||||
memUsed: z.string(),
|
memUsed: z.string(),
|
||||||
memAvailable: z.string(),
|
memAvailable: z.string(),
|
||||||
|
|||||||
@@ -64,14 +64,14 @@ describe("Home Assistant integration", () => {
|
|||||||
const prepareHomeAssistantContainerAsync = async () => {
|
const prepareHomeAssistantContainerAsync = async () => {
|
||||||
const homeAssistantContainer = createHomeAssistantContainer();
|
const homeAssistantContainer = createHomeAssistantContainer();
|
||||||
const startedContainer = await homeAssistantContainer.start();
|
const startedContainer = await homeAssistantContainer.start();
|
||||||
|
|
||||||
await startedContainer.exec(["unzip", "-o", "/tmp/config.zip", "-d", "/config"]);
|
await startedContainer.exec(["unzip", "-o", "/tmp/config.zip", "-d", "/config"]);
|
||||||
await startedContainer.restart();
|
await startedContainer.restart();
|
||||||
return startedContainer;
|
return startedContainer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createHomeAssistantContainer = () => {
|
const createHomeAssistantContainer = () => {
|
||||||
return new GenericContainer(IMAGE_NAME)
|
return (
|
||||||
|
new GenericContainer(IMAGE_NAME)
|
||||||
.withCopyFilesToContainer([
|
.withCopyFilesToContainer([
|
||||||
{
|
{
|
||||||
source: join(__dirname, "/volumes/home-assistant-config.zip"),
|
source: join(__dirname, "/volumes/home-assistant-config.zip"),
|
||||||
@@ -80,10 +80,13 @@ const createHomeAssistantContainer = () => {
|
|||||||
])
|
])
|
||||||
.withPrivilegedMode()
|
.withPrivilegedMode()
|
||||||
.withExposedPorts(8123)
|
.withExposedPorts(8123)
|
||||||
.withWaitStrategy(Wait.forHttp("/", 8123));
|
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
|
||||||
|
.withWaitStrategy(Wait.forHttp("/onboarding.html", 8123))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createHomeAssistantIntegration = (container: StartedTestContainer, apiKeyOverride?: string) => {
|
const createHomeAssistantIntegration = (container: StartedTestContainer, apiKeyOverride?: string) => {
|
||||||
|
console.log("Creating Home Assistant integration...");
|
||||||
return new HomeAssistantIntegration({
|
return new HomeAssistantIntegration({
|
||||||
id: "1",
|
id: "1",
|
||||||
decryptedSecrets: [
|
decryptedSecrets: [
|
||||||
|
|||||||
@@ -145,8 +145,8 @@ describe("Nzbget integration", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await expect(actAsync()).resolves.not.toThrow();
|
await expect(actAsync()).resolves.not.toThrow();
|
||||||
// NzbGet is slow and we wait for a second before querying the items. Test was flaky without this.
|
// NzbGet is slow and we wait for a few seconds before querying the items. Test was flaky without this.
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
const result = await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
|
const result = await nzbGetIntegration.getClientJobsAndStatusAsync({ limit: 99 });
|
||||||
expect(result.items).toHaveLength(0);
|
expect(result.items).toHaveLength(0);
|
||||||
|
|
||||||
|
|||||||
@@ -211,12 +211,15 @@ const createPiHoleIntegrationV5 = (container: StartedTestContainer, apiKey: stri
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createPiHoleV6Container = (password: string) => {
|
const createPiHoleV6Container = (password: string) => {
|
||||||
return new GenericContainer("pihole/pihole:latest")
|
return (
|
||||||
|
new GenericContainer("pihole/pihole:latest")
|
||||||
.withEnvironment({
|
.withEnvironment({
|
||||||
FTLCONF_webserver_api_password: password,
|
FTLCONF_webserver_api_password: password,
|
||||||
})
|
})
|
||||||
.withExposedPorts(80)
|
.withExposedPorts(80)
|
||||||
.withWaitStrategy(Wait.forHttp("/admin", 80));
|
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
|
||||||
|
.withWaitStrategy(Wait.forHttp("/admin/login", 80))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPiHoleIntegrationV6 = (container: StartedTestContainer, apiKey: string) => {
|
const createPiHoleIntegrationV6 = (container: StartedTestContainer, apiKey: string) => {
|
||||||
|
|||||||
@@ -192,7 +192,8 @@ describe("Sabnzbd integration", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const createSabnzbdContainer = () => {
|
const createSabnzbdContainer = () => {
|
||||||
return new GenericContainer(IMAGE_NAME)
|
return (
|
||||||
|
new GenericContainer(IMAGE_NAME)
|
||||||
.withCopyFilesToContainer([
|
.withCopyFilesToContainer([
|
||||||
{
|
{
|
||||||
source: join(__dirname, "/volumes/usenet/sabnzbd.ini"),
|
source: join(__dirname, "/volumes/usenet/sabnzbd.ini"),
|
||||||
@@ -201,7 +202,9 @@ const createSabnzbdContainer = () => {
|
|||||||
])
|
])
|
||||||
.withExposedPorts(1212)
|
.withExposedPorts(1212)
|
||||||
.withEnvironment({ PUID: "0", PGID: "0" })
|
.withEnvironment({ PUID: "0", PGID: "0" })
|
||||||
.withWaitStrategy(Wait.forHttp("/", 1212));
|
// This has to be a page that is not redirected (or a status code has to be defined withStatusCode(statusCode))
|
||||||
|
.withWaitStrategy(Wait.forHttp("/sabnzbd/wizard/", 1212))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSabnzbdIntegration = (container: StartedTestContainer, apiKey: string) => {
|
const createSabnzbdIntegration = (container: StartedTestContainer, apiKey: string) => {
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
"ioredis": "5.6.1",
|
"ioredis": "5.6.1",
|
||||||
"superjson": "2.2.2",
|
"superjson": "2.2.2",
|
||||||
"winston": "3.17.0",
|
"winston": "3.17.0",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,18 +34,18 @@
|
|||||||
"@homarr/ui": "workspace:^0.1.0",
|
"@homarr/ui": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"@tabler/icons-react": "^3.33.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"next": "15.3.3",
|
"next": "15.3.3",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,13 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/ui": "workspace:^0.1.0",
|
"@homarr/ui": "workspace:^0.1.0",
|
||||||
"@mantine/notifications": "^8.0.2",
|
"@mantine/notifications": "^8.0.2",
|
||||||
"@tabler/icons-react": "^3.33.0"
|
"@tabler/icons-react": "^3.34.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"superjson": "2.2.2",
|
"superjson": "2.2.2",
|
||||||
"zod": "^3.25.42",
|
"zod": "^3.25.55",
|
||||||
"zod-form-data": "^2.0.7"
|
"zod-form-data": "^2.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/adm-zip": "0.5.7",
|
"@types/adm-zip": "0.5.7",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,13 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"@mantine/hooks": "^8.0.2",
|
"@mantine/hooks": "^8.0.2",
|
||||||
"@mantine/spotlight": "^8.0.2",
|
"@mantine/spotlight": "^8.0.2",
|
||||||
"@tabler/icons-react": "^3.33.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"jotai": "^2.12.5",
|
"jotai": "^2.12.5",
|
||||||
"next": "15.3.3",
|
"next": "15.3.3",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -612,17 +612,17 @@
|
|||||||
"select": {
|
"select": {
|
||||||
"label": "选择应用",
|
"label": "选择应用",
|
||||||
"notFound": "未找到应用",
|
"notFound": "未找到应用",
|
||||||
"search": "",
|
"search": "搜索应用",
|
||||||
"noResults": "",
|
"noResults": "没有结果",
|
||||||
"action": "",
|
"action": "选择 {app}",
|
||||||
"title": ""
|
"title": "选择一个应用程序添加到此面板"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "",
|
"title": "创建应用",
|
||||||
"description": "",
|
"description": "创建应用 ",
|
||||||
"action": ""
|
"action": "打开应用创建"
|
||||||
},
|
},
|
||||||
"add": ""
|
"add": "添加应用"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
@@ -705,59 +705,59 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"common": {
|
"common": {
|
||||||
"cause": {
|
"cause": {
|
||||||
"title": ""
|
"title": "更多详情"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unknown": {
|
"unknown": {
|
||||||
"title": "",
|
"title": "未知错误",
|
||||||
"description": ""
|
"description": "发生未知错误,打开下面的原因以查看更多详情"
|
||||||
},
|
},
|
||||||
"parse": {
|
"parse": {
|
||||||
"title": "",
|
"title": "解析错误",
|
||||||
"description": ""
|
"description": "无法解析该响应。请确认URL指向服务的基本URL。"
|
||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
"title": "",
|
"title": "授权错误",
|
||||||
"description": ""
|
"description": "请求未被授权。请验证凭据是否正确,并且您拥有足够的权限。"
|
||||||
},
|
},
|
||||||
"statusCode": {
|
"statusCode": {
|
||||||
"title": "",
|
"title": "响应错误",
|
||||||
"description": "",
|
"description": "",
|
||||||
"otherDescription": "",
|
"otherDescription": "",
|
||||||
"reason": {
|
"reason": {
|
||||||
"badRequest": "",
|
"badRequest": "错误请求",
|
||||||
"notFound": "",
|
"notFound": "未找到",
|
||||||
"tooManyRequests": "",
|
"tooManyRequests": "请求过于频繁",
|
||||||
"internalServerError": "",
|
"internalServerError": "服务器内部错误",
|
||||||
"serviceUnavailable": "",
|
"serviceUnavailable": "服务暂时不可用",
|
||||||
"gatewayTimeout": ""
|
"gatewayTimeout": "网关超时"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"certificate": {
|
"certificate": {
|
||||||
"title": "",
|
"title": "证书错误",
|
||||||
"description": {
|
"description": {
|
||||||
"expired": "",
|
"expired": "此证书已经过期。",
|
||||||
"notYetValid": "",
|
"notYetValid": "此证书尚未生效。",
|
||||||
"untrusted": "",
|
"untrusted": "此证书不受信任。",
|
||||||
"hostnameMismatch": ""
|
"hostnameMismatch": "证书主机名与 URL 不匹配。"
|
||||||
},
|
},
|
||||||
"alert": {
|
"alert": {
|
||||||
"permission": {
|
"permission": {
|
||||||
"title": "",
|
"title": "权限不足",
|
||||||
"message": ""
|
"message": "您无权信任或上传证书。请与您的管理员联系上传必要的根证书。"
|
||||||
},
|
},
|
||||||
"hostnameMismatch": {
|
"hostnameMismatch": {
|
||||||
"title": "",
|
"title": "主机名不匹配",
|
||||||
"message": ""
|
"message": "证书中的主机名与您连接的主机名不匹配。 这可能会显示安全风险,但您仍然可以选择信任此证书。"
|
||||||
},
|
},
|
||||||
"extract": {
|
"extract": {
|
||||||
"title": "",
|
"title": "CA证书提取失败",
|
||||||
"message": ""
|
"message": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"retry": {
|
"retry": {
|
||||||
"label": ""
|
"label": "重试创建"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"label": ""
|
"label": ""
|
||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "Repositories",
|
"label": "Repositories",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "Tilføj repository"
|
"label": "Tilføj repository"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "Repositories",
|
"label": "Repositories",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "Repository hinzufügen"
|
"label": "Repository hinzufügen"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,9 +2223,18 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "Repositories",
|
"label": "Repositories",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "Add repository"
|
"label": "Add repository"
|
||||||
},
|
},
|
||||||
|
"importRepositories": {
|
||||||
|
"label": "Import from docker",
|
||||||
|
"loading": "Loading docker images",
|
||||||
|
"noImagesFound": "No docker images found",
|
||||||
|
"listFoundImages": "List of found images",
|
||||||
|
"listAlreadyImportedImages": "List of already imported images",
|
||||||
|
"allImagesAlreadyImported": "All images already imported",
|
||||||
|
"onlyAdminCanImport": "Only administrators can import from docker"
|
||||||
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
"label": "Provider"
|
"label": "Provider"
|
||||||
},
|
},
|
||||||
@@ -2266,6 +2275,9 @@
|
|||||||
"label": "Confirm"
|
"label": "Confirm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"importForm": {
|
||||||
|
"title": "Import from Docker"
|
||||||
|
},
|
||||||
"example": {
|
"example": {
|
||||||
"label": "Example"
|
"label": "Example"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -747,7 +747,7 @@
|
|||||||
"message": ""
|
"message": ""
|
||||||
},
|
},
|
||||||
"hostnameMismatch": {
|
"hostnameMismatch": {
|
||||||
"title": "",
|
"title": "Nom d'hôte incohérent",
|
||||||
"message": ""
|
"message": ""
|
||||||
},
|
},
|
||||||
"extract": {
|
"extract": {
|
||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -1758,7 +1758,7 @@
|
|||||||
"label": "הצג מידע זיכרון"
|
"label": "הצג מידע זיכרון"
|
||||||
},
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": "הצג זמן פעולה"
|
||||||
},
|
},
|
||||||
"fileSystem": {
|
"fileSystem": {
|
||||||
"label": "הצג מידע על מערכת הקבצים"
|
"label": "הצג מידע על מערכת הקבצים"
|
||||||
@@ -1767,7 +1767,7 @@
|
|||||||
"label": "כרטיסיית ברירת מחדל"
|
"label": "כרטיסיית ברירת מחדל"
|
||||||
},
|
},
|
||||||
"visibleClusterSections": {
|
"visibleClusterSections": {
|
||||||
"label": ""
|
"label": "מקטעי אשכול גלויים"
|
||||||
},
|
},
|
||||||
"sectionIndicatorRequirement": {
|
"sectionIndicatorRequirement": {
|
||||||
"label": "דרישת מציין מקטע"
|
"label": "דרישת מציין מקטע"
|
||||||
@@ -1961,8 +1961,8 @@
|
|||||||
"label": "השתמש במסנן כדי לחשב יחס"
|
"label": "השתמש במסנן כדי לחשב יחס"
|
||||||
},
|
},
|
||||||
"limitPerIntegration": {
|
"limitPerIntegration": {
|
||||||
"label": "",
|
"label": "הגבלת פריטים לכל אינטגרציה",
|
||||||
"description": ""
|
"description": "פעולה זו תגביל את מספר הפריטים המוצגים בכל אינטגרציה, לא באופן גלובלי"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "מאגרים",
|
"label": "מאגרים",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "הוסף מאגר"
|
"label": "הוסף מאגר"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "Depolar",
|
"label": "Depolar",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "Depo Ekle"
|
"label": "Depo Ekle"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -2223,7 +2223,7 @@
|
|||||||
},
|
},
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"label": "儲存庫",
|
"label": "儲存庫",
|
||||||
"addRRepository": {
|
"addRepository": {
|
||||||
"label": "新增儲存庫"
|
"label": "新增儲存庫"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"@mantine/dates": "^8.0.2",
|
"@mantine/dates": "^8.0.2",
|
||||||
"@mantine/hooks": "^8.0.2",
|
"@mantine/hooks": "^8.0.2",
|
||||||
"@tabler/icons-react": "^3.33.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "15.3.3",
|
"next": "15.3.3",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/css-modules": "^1.0.5",
|
"@types/css-modules": "^1.0.5",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"zod": "^3.25.42",
|
"zod": "^3.25.55",
|
||||||
"zod-form-data": "^2.0.7"
|
"zod-form-data": "^2.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,22 +51,22 @@
|
|||||||
"@mantine/charts": "^8.0.2",
|
"@mantine/charts": "^8.0.2",
|
||||||
"@mantine/core": "^8.0.2",
|
"@mantine/core": "^8.0.2",
|
||||||
"@mantine/hooks": "^8.0.2",
|
"@mantine/hooks": "^8.0.2",
|
||||||
"@tabler/icons-react": "^3.33.0",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"@tiptap/extension-color": "2.12.0",
|
"@tiptap/extension-color": "2.14.0",
|
||||||
"@tiptap/extension-highlight": "2.12.0",
|
"@tiptap/extension-highlight": "2.14.0",
|
||||||
"@tiptap/extension-image": "2.12.0",
|
"@tiptap/extension-image": "2.14.0",
|
||||||
"@tiptap/extension-link": "^2.12.0",
|
"@tiptap/extension-link": "^2.14.0",
|
||||||
"@tiptap/extension-table": "2.12.0",
|
"@tiptap/extension-table": "2.14.0",
|
||||||
"@tiptap/extension-table-cell": "2.12.0",
|
"@tiptap/extension-table-cell": "2.14.0",
|
||||||
"@tiptap/extension-table-header": "2.12.0",
|
"@tiptap/extension-table-header": "2.14.0",
|
||||||
"@tiptap/extension-table-row": "2.12.0",
|
"@tiptap/extension-table-row": "2.14.0",
|
||||||
"@tiptap/extension-task-item": "2.12.0",
|
"@tiptap/extension-task-item": "2.14.0",
|
||||||
"@tiptap/extension-task-list": "2.12.0",
|
"@tiptap/extension-task-list": "2.14.0",
|
||||||
"@tiptap/extension-text-align": "2.12.0",
|
"@tiptap/extension-text-align": "2.14.0",
|
||||||
"@tiptap/extension-text-style": "2.12.0",
|
"@tiptap/extension-text-style": "2.14.0",
|
||||||
"@tiptap/extension-underline": "2.12.0",
|
"@tiptap/extension-underline": "2.14.0",
|
||||||
"@tiptap/react": "^2.12.0",
|
"@tiptap/react": "^2.14.0",
|
||||||
"@tiptap/starter-kit": "^2.12.0",
|
"@tiptap/starter-kit": "^2.14.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
@@ -75,15 +75,15 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.3",
|
||||||
"video.js": "^8.22.0",
|
"video.js": "^8.23.3",
|
||||||
"zod": "^3.25.42"
|
"zod": "^3.25.55"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/video.js": "^7.3.58",
|
"@types/video.js": "^7.3.58",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,46 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ActionIcon, Button, Divider, Fieldset, Group, Select, Stack, Text, TextInput } from "@mantine/core";
|
import {
|
||||||
|
Accordion,
|
||||||
|
ActionIcon,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Code,
|
||||||
|
Divider,
|
||||||
|
Fieldset,
|
||||||
|
Group,
|
||||||
|
Image,
|
||||||
|
Loader,
|
||||||
|
Select,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import type { CheckboxProps } from "@mantine/core";
|
||||||
import type { FormErrors } from "@mantine/form";
|
import type { FormErrors } from "@mantine/form";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import { IconEdit, IconTrash, IconTriangleFilled } from "@tabler/icons-react";
|
import {
|
||||||
|
IconBrandDocker,
|
||||||
|
IconEdit,
|
||||||
|
IconPlus,
|
||||||
|
IconSquare,
|
||||||
|
IconSquareCheck,
|
||||||
|
IconTrash,
|
||||||
|
IconTriangleFilled,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
import { escapeForRegEx } from "@tiptap/react";
|
import { escapeForRegEx } from "@tiptap/react";
|
||||||
|
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
import { useSession } from "@homarr/auth/client";
|
||||||
import { findBestIconMatch, IconPicker } from "@homarr/forms-collection";
|
import { findBestIconMatch, IconPicker } from "@homarr/forms-collection";
|
||||||
import { createModal, useModalAction } from "@homarr/modals";
|
import { createModal, useModalAction } from "@homarr/modals";
|
||||||
import { useScopedI18n } from "@homarr/translation/client";
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
import { MaskedOrNormalImage } from "@homarr/ui";
|
import { MaskedImage } from "@homarr/ui";
|
||||||
|
|
||||||
import { Providers } from "../releases/releases-providers";
|
import { isProviderKey, Providers } from "../releases/releases-providers";
|
||||||
import type { ReleasesRepository, ReleasesVersionFilter } from "../releases/releases-repository";
|
import type { ReleasesRepository, ReleasesVersionFilter } from "../releases/releases-repository";
|
||||||
import type { CommonWidgetInputProps } from "./common";
|
import type { CommonWidgetInputProps } from "./common";
|
||||||
import { useWidgetInputTranslation } from "./common";
|
import { useWidgetInputTranslation } from "./common";
|
||||||
@@ -32,11 +59,14 @@ export const WidgetMultiReleasesRepositoriesInput = ({
|
|||||||
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const repositories = form.values.options[property] as ReleasesRepository[];
|
const repositories = form.values.options[property] as ReleasesRepository[];
|
||||||
const { openModal } = useModalAction(ReleaseEditModal);
|
const { openModal: openEditModal } = useModalAction(RepositoryEditModal);
|
||||||
|
const { openModal: openImportModal } = useModalAction(RepositoryImportModal);
|
||||||
const versionFilterPrecisionOptions = useMemo(
|
const versionFilterPrecisionOptions = useMemo(
|
||||||
() => [tRepository("versionFilter.precision.options.none"), "#", "#.#", "#.#.#", "#.#.#.#", "#.#.#.#.#"],
|
() => [tRepository("versionFilter.precision.options.none"), "#", "#.#", "#.#.#", "#.#.#.#", "#.#.#.#.#"],
|
||||||
[tRepository],
|
[tRepository],
|
||||||
);
|
);
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const isAdmin = session?.user.permissions.includes("admin") ?? false;
|
||||||
|
|
||||||
const onRepositorySave = useCallback(
|
const onRepositorySave = useCallback(
|
||||||
(repository: ReleasesRepository, index: number): FormValidation => {
|
(repository: ReleasesRepository, index: number): FormValidation => {
|
||||||
@@ -62,8 +92,8 @@ export const WidgetMultiReleasesRepositoriesInput = ({
|
|||||||
[form, property],
|
[form, property],
|
||||||
);
|
);
|
||||||
|
|
||||||
const addNewItem = () => {
|
const addNewRepository = () => {
|
||||||
const item = {
|
const repository: ReleasesRepository = {
|
||||||
providerKey: "DockerHub",
|
providerKey: "DockerHub",
|
||||||
identifier: "",
|
identifier: "",
|
||||||
};
|
};
|
||||||
@@ -74,16 +104,16 @@ export const WidgetMultiReleasesRepositoriesInput = ({
|
|||||||
...previous,
|
...previous,
|
||||||
options: {
|
options: {
|
||||||
...previous.options,
|
...previous.options,
|
||||||
[property]: [...previousValues, item],
|
[property]: [...previousValues, repository],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const index = repositories.length;
|
const index = repositories.length;
|
||||||
|
|
||||||
openModal({
|
openEditModal({
|
||||||
fieldPath: `options.${property}.${index}`,
|
fieldPath: `options.${property}.${index}`,
|
||||||
repository: item,
|
repository,
|
||||||
onRepositorySave: (saved) => onRepositorySave(saved, index),
|
onRepositorySave: (saved) => onRepositorySave(saved, index),
|
||||||
onRepositoryCancel: () => onRepositoryRemove(index),
|
onRepositoryCancel: () => onRepositoryRemove(index),
|
||||||
versionFilterPrecisionOptions,
|
versionFilterPrecisionOptions,
|
||||||
@@ -106,24 +136,56 @@ export const WidgetMultiReleasesRepositoriesInput = ({
|
|||||||
return (
|
return (
|
||||||
<Fieldset legend={t("label")}>
|
<Fieldset legend={t("label")}>
|
||||||
<Stack gap="5">
|
<Stack gap="5">
|
||||||
<Button onClick={addNewItem}>{tRepository("addRRepository.label")}</Button>
|
<Group grow>
|
||||||
|
<Button leftSection={<IconPlus />} onClick={addNewRepository}>
|
||||||
|
{tRepository("addRepository.label")}
|
||||||
|
</Button>
|
||||||
|
<Tooltip label={tRepository("importRepositories.onlyAdminCanImport")} disabled={isAdmin} withArrow>
|
||||||
|
<Button
|
||||||
|
disabled={!isAdmin}
|
||||||
|
leftSection={<IconBrandDocker stroke={1.25} />}
|
||||||
|
onClick={() =>
|
||||||
|
openImportModal({
|
||||||
|
repositories,
|
||||||
|
versionFilterPrecisionOptions,
|
||||||
|
onConfirm: (selectedRepositories) => {
|
||||||
|
if (!selectedRepositories.length) return;
|
||||||
|
|
||||||
|
form.setValues((previous) => {
|
||||||
|
const previousValues = previous.options?.[property] as ReleasesRepository[];
|
||||||
|
return {
|
||||||
|
...previous,
|
||||||
|
options: {
|
||||||
|
...previous.options,
|
||||||
|
[property]: [...previousValues, ...selectedRepositories],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isAdmin,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{tRepository("importRepositories.label")}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
<Divider my="sm" />
|
<Divider my="sm" />
|
||||||
|
|
||||||
{repositories.map((repository, index) => {
|
{repositories.map((repository, index) => {
|
||||||
return (
|
return (
|
||||||
<Stack key={`${repository.providerKey}.${repository.identifier}`} gap={5}>
|
<Stack key={`${repository.providerKey}.${repository.identifier}`} gap={5}>
|
||||||
<Group align="center" gap="xs">
|
<Group align="center" gap="xs">
|
||||||
<MaskedOrNormalImage
|
<Image
|
||||||
hasColor={false}
|
src={repository.iconUrl ?? Providers[repository.providerKey].iconUrl}
|
||||||
imageUrl={repository.iconUrl ?? Providers[repository.providerKey]?.iconUrl}
|
|
||||||
style={{
|
style={{
|
||||||
height: "1em",
|
height: "1.2em",
|
||||||
width: "1em",
|
width: "1.2em",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text c="dimmed" fw={100} size="xs">
|
<Text c="dimmed" fw={100} size="xs">
|
||||||
{Providers[repository.providerKey]?.name}
|
{Providers[repository.providerKey].name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Group justify="space-between" align="center" style={{ flex: 1 }} gap={5}>
|
<Group justify="space-between" align="center" style={{ flex: 1 }} gap={5}>
|
||||||
@@ -135,7 +197,7 @@ export const WidgetMultiReleasesRepositoriesInput = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openModal({
|
openEditModal({
|
||||||
fieldPath: `options.${property}.${index}`,
|
fieldPath: `options.${property}.${index}`,
|
||||||
repository,
|
repository,
|
||||||
onRepositorySave: (saved) => onRepositorySave(saved, index),
|
onRepositorySave: (saved) => onRepositorySave(saved, index),
|
||||||
@@ -185,7 +247,7 @@ const formatIdentifierName = (identifier: string) => {
|
|||||||
return unformattedName?.replace(/[-_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (char) => char.toUpperCase()) ?? "";
|
return unformattedName?.replace(/[-_]/g, " ").replace(/(?:^\w|[A-Z]|\b\w)/g, (char) => char.toUpperCase()) ?? "";
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReleaseEditProps {
|
interface RepositoryEditProps {
|
||||||
fieldPath: string;
|
fieldPath: string;
|
||||||
repository: ReleasesRepository;
|
repository: ReleasesRepository;
|
||||||
onRepositorySave: (repository: ReleasesRepository) => FormValidation;
|
onRepositorySave: (repository: ReleasesRepository) => FormValidation;
|
||||||
@@ -193,7 +255,7 @@ interface ReleaseEditProps {
|
|||||||
versionFilterPrecisionOptions: string[];
|
versionFilterPrecisionOptions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions }) => {
|
const RepositoryEditModal = createModal<RepositoryEditProps>(({ innerProps, actions }) => {
|
||||||
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [tempRepository, setTempRepository] = useState(() => ({ ...innerProps.repository }));
|
const [tempRepository, setTempRepository] = useState(() => ({ ...innerProps.repository }));
|
||||||
@@ -262,7 +324,7 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
value={tempRepository.providerKey}
|
value={tempRepository.providerKey}
|
||||||
error={formErrors[`${innerProps.fieldPath}.providerKey`]}
|
error={formErrors[`${innerProps.fieldPath}.providerKey`]}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (value && Providers[value]) {
|
if (value && isProviderKey(value)) {
|
||||||
handleChange({ providerKey: value });
|
handleChange({ providerKey: value });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -386,7 +448,7 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
{tRepository("editForm.cancel.label")}
|
{tRepository("editForm.cancel.label")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button data-autofocus onClick={handleConfirm} color="red.9" loading={loading}>
|
<Button data-autofocus onClick={handleConfirm} loading={loading}>
|
||||||
{tRepository("editForm.confirm.label")}
|
{tRepository("editForm.confirm.label")}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -398,3 +460,247 @@ const ReleaseEditModal = createModal<ReleaseEditProps>(({ innerProps, actions })
|
|||||||
},
|
},
|
||||||
size: "xl",
|
size: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface ReleasesRepositoryImport extends ReleasesRepository {
|
||||||
|
alreadyImported: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ContainerImageSelectorProps {
|
||||||
|
containerImage: ReleasesRepositoryImport;
|
||||||
|
versionFilterPrecisionOptions: string[];
|
||||||
|
onImageSelectionChanged?: (isSelected: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContainerImageSelector = ({
|
||||||
|
containerImage,
|
||||||
|
versionFilterPrecisionOptions,
|
||||||
|
onImageSelectionChanged,
|
||||||
|
}: ContainerImageSelectorProps) => {
|
||||||
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
|
const checkBoxProps: CheckboxProps = !onImageSelectionChanged
|
||||||
|
? {
|
||||||
|
disabled: true,
|
||||||
|
checked: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
onChange: (event) => onImageSelectionChanged(event.currentTarget.checked),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
key={`${Providers[containerImage.providerKey].name}/${containerImage.identifier}`}
|
||||||
|
gap="xl"
|
||||||
|
justify="space-between"
|
||||||
|
>
|
||||||
|
<Group gap="md">
|
||||||
|
<Checkbox
|
||||||
|
label={
|
||||||
|
<Group>
|
||||||
|
<Image
|
||||||
|
src={containerImage.iconUrl}
|
||||||
|
style={{
|
||||||
|
height: "1.2em",
|
||||||
|
width: "1.2em",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text>{containerImage.identifier}</Text>
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
{...checkBoxProps}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{containerImage.versionFilter && (
|
||||||
|
<Group gap={5}>
|
||||||
|
<Text c="dimmed" size="xs">
|
||||||
|
{tRepository("versionFilter.label")}:
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Code>{containerImage.versionFilter.prefix && containerImage.versionFilter.prefix}</Code>
|
||||||
|
<Code color="var(--mantine-primary-color-light)" fw={700}>
|
||||||
|
{versionFilterPrecisionOptions[containerImage.versionFilter.precision]}
|
||||||
|
</Code>
|
||||||
|
<Code>{containerImage.versionFilter.suffix && containerImage.versionFilter.suffix}</Code>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<MaskedImage
|
||||||
|
color="dimmed"
|
||||||
|
imageUrl={Providers[containerImage.providerKey].iconUrl}
|
||||||
|
style={{
|
||||||
|
height: "1em",
|
||||||
|
width: "1em",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text ff="monospace" c="dimmed" size="sm">
|
||||||
|
{Providers[containerImage.providerKey].name}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RepositoryImportProps {
|
||||||
|
repositories: ReleasesRepository[];
|
||||||
|
versionFilterPrecisionOptions: string[];
|
||||||
|
onConfirm: (selectedRepositories: ReleasesRepositoryImport[]) => void;
|
||||||
|
isAdmin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps, actions }) => {
|
||||||
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [selectedImages, setSelectedImages] = useState([] as ReleasesRepositoryImport[]);
|
||||||
|
|
||||||
|
const docker = clientApi.docker.getContainers.useQuery(undefined, {
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
enabled: innerProps.isAdmin,
|
||||||
|
});
|
||||||
|
|
||||||
|
const containersImages: ReleasesRepositoryImport[] = useMemo(
|
||||||
|
() =>
|
||||||
|
docker.data?.containers.reduce<ReleasesRepositoryImport[]>((acc, containerImage) => {
|
||||||
|
const providerKey = containerImage.image.startsWith("ghcr.io/") ? "Github" : "DockerHub";
|
||||||
|
const [identifier, version] = containerImage.image.replace(/^(ghcr\.io\/|docker\.io\/)/, "").split(":");
|
||||||
|
|
||||||
|
if (!identifier) return acc;
|
||||||
|
|
||||||
|
if (acc.some((item) => item.providerKey === providerKey && item.identifier === identifier)) return acc;
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
providerKey,
|
||||||
|
identifier,
|
||||||
|
iconUrl: containerImage.iconUrl ?? undefined,
|
||||||
|
name: formatIdentifierName(identifier),
|
||||||
|
versionFilter: version ? parseImageVersionToVersionFilter(version) : undefined,
|
||||||
|
alreadyImported: innerProps.repositories.some(
|
||||||
|
(item) => item.providerKey === providerKey && item.identifier === identifier,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, []) ?? [],
|
||||||
|
[docker.data, innerProps.repositories],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleConfirm = useCallback(() => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
innerProps.onConfirm(selectedImages);
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
actions.closeModal();
|
||||||
|
}, [innerProps, selectedImages, actions]);
|
||||||
|
|
||||||
|
const allImagesImported = useMemo(
|
||||||
|
() => containersImages.every((containerImage) => containerImage.alreadyImported),
|
||||||
|
[containersImages],
|
||||||
|
);
|
||||||
|
|
||||||
|
const anyImagesImported = useMemo(
|
||||||
|
() => containersImages.some((containerImage) => containerImage.alreadyImported),
|
||||||
|
[containersImages],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
{docker.isPending ? (
|
||||||
|
<Stack justify="center" align="center">
|
||||||
|
<Loader size="xl" />
|
||||||
|
<Title order={3}>{tRepository("importRepositories.loading")}</Title>
|
||||||
|
</Stack>
|
||||||
|
) : containersImages.length === 0 ? (
|
||||||
|
<Stack justify="center" align="center">
|
||||||
|
<IconBrandDocker stroke={1} size={128} />
|
||||||
|
<Title order={3}>{tRepository("importRepositories.noImagesFound")}</Title>
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<Stack>
|
||||||
|
<Accordion defaultValue={!allImagesImported ? "foundImages" : anyImagesImported ? "alreadyImported" : ""}>
|
||||||
|
<Accordion.Item value="foundImages">
|
||||||
|
<Accordion.Control disabled={allImagesImported} icon={<IconSquare stroke={1.25} />}>
|
||||||
|
<Group>
|
||||||
|
{tRepository("importRepositories.listFoundImages")}
|
||||||
|
{allImagesImported && (
|
||||||
|
<Text c="dimmed" size="sm">
|
||||||
|
{tRepository("importRepositories.allImagesAlreadyImported")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
{!allImagesImported &&
|
||||||
|
containersImages
|
||||||
|
.filter((containerImage) => !containerImage.alreadyImported)
|
||||||
|
.map((containerImage) => {
|
||||||
|
return (
|
||||||
|
<ContainerImageSelector
|
||||||
|
key={`${containerImage.providerKey}/${containerImage.identifier}`}
|
||||||
|
containerImage={containerImage}
|
||||||
|
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
||||||
|
onImageSelectionChanged={(isSelected) =>
|
||||||
|
isSelected
|
||||||
|
? setSelectedImages([...selectedImages, containerImage])
|
||||||
|
: setSelectedImages(selectedImages.filter((img) => img !== containerImage))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
<Accordion.Item value="alreadyImported">
|
||||||
|
<Accordion.Control disabled={!anyImagesImported} icon={<IconSquareCheck stroke={1.25} />}>
|
||||||
|
{tRepository("importRepositories.listAlreadyImportedImages")}
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
{anyImagesImported &&
|
||||||
|
containersImages
|
||||||
|
.filter((containerImage) => containerImage.alreadyImported)
|
||||||
|
.map((containerImage) => {
|
||||||
|
return (
|
||||||
|
<ContainerImageSelector
|
||||||
|
key={`${containerImage.providerKey}/${containerImage.identifier}`}
|
||||||
|
containerImage={containerImage}
|
||||||
|
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
</Accordion>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Group justify="flex-end">
|
||||||
|
<Button variant="default" onClick={actions.closeModal} color="gray.5">
|
||||||
|
{tRepository("editForm.cancel.label")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button onClick={handleConfirm} loading={loading} disabled={selectedImages.length === 0}>
|
||||||
|
{tRepository("editForm.confirm.label")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}).withOptions({
|
||||||
|
defaultTitle(t) {
|
||||||
|
return t("widget.releases.option.repositories.importForm.title");
|
||||||
|
},
|
||||||
|
size: "xl",
|
||||||
|
});
|
||||||
|
|
||||||
|
const parseImageVersionToVersionFilter = (imageVersion: string): ReleasesVersionFilter | undefined => {
|
||||||
|
const version = /(?<=\D|^)\d+(?:\.\d+)*(?![\d.])/.exec(imageVersion)?.[0];
|
||||||
|
|
||||||
|
if (!version) return undefined;
|
||||||
|
|
||||||
|
const [prefix, suffix] = imageVersion.split(version);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefix,
|
||||||
|
precision: version.split(".").length,
|
||||||
|
suffix,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export default function ReleasesWidget({ options }: WidgetComponentProps<"releas
|
|||||||
>
|
>
|
||||||
<MaskedOrNormalImage
|
<MaskedOrNormalImage
|
||||||
className="releases-repository-header-icon"
|
className="releases-repository-header-icon"
|
||||||
imageUrl={repository.iconUrl ?? Providers[repository.providerKey]?.iconUrl}
|
imageUrl={repository.iconUrl ?? Providers[repository.providerKey].iconUrl}
|
||||||
hasColor={hasIconColor}
|
hasColor={hasIconColor}
|
||||||
style={{
|
style={{
|
||||||
width: "1em",
|
width: "1em",
|
||||||
@@ -474,7 +474,7 @@ const ExpandedDisplay = ({ repository, hasIconColor }: ExtendedDisplayProps) =>
|
|||||||
<Group className="releases-repository-expanded-header-provider-wrapper" gap={5} align="center">
|
<Group className="releases-repository-expanded-header-provider-wrapper" gap={5} align="center">
|
||||||
<MaskedOrNormalImage
|
<MaskedOrNormalImage
|
||||||
className="releases-repository-expanded-header-provider-icon"
|
className="releases-repository-expanded-header-provider-icon"
|
||||||
imageUrl={Providers[repository.providerKey]?.iconUrl}
|
imageUrl={Providers[repository.providerKey].iconUrl}
|
||||||
hasColor={hasIconColor}
|
hasColor={hasIconColor}
|
||||||
style={{
|
style={{
|
||||||
width: "1em",
|
width: "1em",
|
||||||
@@ -482,7 +482,7 @@ const ExpandedDisplay = ({ repository, hasIconColor }: ExtendedDisplayProps) =>
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text className="releases-repository-expanded-header-provider-name" size="xs" c="iconColor" ff="monospace">
|
<Text className="releases-repository-expanded-header-provider-name" size="xs" c="iconColor" ff="monospace">
|
||||||
{Providers[repository.providerKey]?.name}
|
{Providers[repository.providerKey].name}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -3,16 +3,7 @@ export interface ReleasesProvider {
|
|||||||
iconUrl: string;
|
iconUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProvidersProps {
|
export const Providers = {
|
||||||
[key: string]: ReleasesProvider;
|
|
||||||
DockerHub: ReleasesProvider;
|
|
||||||
Github: ReleasesProvider;
|
|
||||||
Gitlab: ReleasesProvider;
|
|
||||||
Npm: ReleasesProvider;
|
|
||||||
Codeberg: ReleasesProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Providers: ProvidersProps = {
|
|
||||||
DockerHub: {
|
DockerHub: {
|
||||||
name: "Docker Hub",
|
name: "Docker Hub",
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/docker.svg",
|
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/docker.svg",
|
||||||
@@ -33,4 +24,10 @@ export const Providers: ProvidersProps = {
|
|||||||
name: "Codeberg",
|
name: "Codeberg",
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/codeberg.svg",
|
iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/codeberg.svg",
|
||||||
},
|
},
|
||||||
|
} as const satisfies Record<string, ReleasesProvider>;
|
||||||
|
|
||||||
|
export type ProviderKey = keyof typeof Providers;
|
||||||
|
|
||||||
|
export const isProviderKey = (key: string): key is ProviderKey => {
|
||||||
|
return key in Providers;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ProviderKey } from "./releases-providers";
|
||||||
|
|
||||||
export interface ReleasesVersionFilter {
|
export interface ReleasesVersionFilter {
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
precision: number;
|
precision: number;
|
||||||
@@ -5,7 +7,7 @@ export interface ReleasesVersionFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ReleasesRepository {
|
export interface ReleasesRepository {
|
||||||
providerKey: string;
|
providerKey: ProviderKey;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
versionFilter?: ReleasesVersionFilter;
|
versionFilter?: ReleasesVersionFilter;
|
||||||
|
|||||||
2300
pnpm-lock.yaml
generated
2300
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -24,12 +24,12 @@
|
|||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"typescript-eslint": "^8.33.0"
|
"typescript-eslint": "^8.33.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"prettier-plugin-packagejson": "^2.5.14",
|
"prettier-plugin-packagejson": "^2.5.15",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user