chore(release): automatic release v1.46.0

This commit is contained in:
homarr-releases[bot]
2025-12-12 19:16:55 +00:00
committed by GitHub
41 changed files with 1248 additions and 1115 deletions

View File

@@ -0,0 +1,43 @@
name: Extract Build Artifact
description: Extracts artifacts from an existing Docker image to be used for from source installation
inputs:
digest:
description: Digest of Docker image to use
required: true
architecture:
description: Name of architecture, will be used to create directories (e.g. amd64, arm64)
required: true
release-tag:
description: Tag of the release to which the artifact will be attached
required: true
repository:
description: Repository to which the release belongs, e.g. owner/repo
required: true
token:
description: GitHub token with permissions to upload release assets
required: true
runs:
using: "composite"
steps:
- name: Start docker container for ${{ inputs.architecture }}
run: |
docker run --name homarr \
-e "SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000" \
--detach --rm ${{ inputs.digest }}
shell: bash
- name: Extract build from ${{ inputs.architecture }} container
run: |
docker exec homarr cp /etc/nginx/templates/nginx.conf /app && \
docker exec homarr tar -czf extraction.tar.gz -C /app . && \
mkdir -p ${{ runner.temp }}/extraction/${{ inputs.architecture }} && \
docker cp homarr:/app/extraction.tar.gz ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-${{ inputs.architecture }}.tar.gz
shell: bash
- name: Stop ${{ inputs.architecture }} container
if: always()
run: docker container remove --force --volumes homarr
shell: bash
- name: Add build archive to release
env:
GH_TOKEN: ${{ inputs.token }}
run: gh release upload --repo ${{ inputs.repository }} ${{ inputs.release-tag }} ${{ runner.temp }}/extraction/${{ inputs.architecture }}/build-${{ inputs.architecture }}.tar.gz --clobber
shell: bash

View File

@@ -37,7 +37,7 @@ jobs:
outputs:
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
skipped: ${{ env.SKIP_RELEASE }}
steps:
- run: echo "Skipping release for workflow_dispatch event"
if: env.SKIP_RELEASE == 'true'
@@ -146,7 +146,6 @@ jobs:
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
env:
SKIP_ENV_VALIDATION: true
build-arm64:
name: Build docker image for arm64
needs: release
@@ -185,10 +184,38 @@ jobs:
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
env:
SKIP_ENV_VALIDATION: true
extract-asset-amd64:
name: Extract amd64 asset from docker image
needs: [release, build-amd64]
runs-on: ubuntu-latest
steps:
- name: Extract amd64
if: needs.release.outputs.skipped == 'false'
uses: homarr-labs/homarr/.github/actions/extract-build-artifact@dev
with:
digest: "${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }}"
architecture: amd64
release-tag: ${{ needs.release.outputs.version }}
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
extract-asset-arm64:
name: Extract arm64 asset from docker image
needs: [release, build-arm64]
runs-on: ubuntu-24.04-arm
steps:
- name: Extract arm64
if: needs.release.outputs.skipped == 'false'
uses: homarr-labs/homarr/.github/actions/extract-build-artifact@dev
with:
digest: "${{ env.GHCR_REPO }}@${{ needs.build-arm64.outputs.digest }}"
architecture: arm64
release-tag: ${{ needs.release.outputs.version }}
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
publish:
name: Complete deployment and notify
needs: [release, build-amd64, build-arm64]
needs: [release, build-amd64, build-arm64, extract-asset-amd64, extract-asset-arm64]
runs-on: ubuntu-latest
env:
NEXT_VERSION: ${{ needs.release.outputs.version }}
@@ -222,6 +249,13 @@ jobs:
${{ env.GHCR_REPO }}@${{ needs.build-amd64.outputs.digest }} \
${{ env.GHCR_REPO }}@${{ needs.build-arm64.outputs.digest }}
- name: Publish release
if: needs.release.outputs.skipped == 'false'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release edit --repo ${{ github.repository }} ${{ needs.release.outputs.version }} --draft=false
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}

View File

@@ -44,6 +44,7 @@
{
"successComment": false,
"failComment": false,
"draftRelease": true,
"releaseBodyTemplate": "<%= _.truncate(nextRelease.notes, { 'length': 124000, 'omission': '' }) %>"
}
]

View File

@@ -58,7 +58,7 @@
"@mantine/tiptap": "^8.3.9",
"@million/lint": "1.0.14",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"@tanstack/react-query-next-experimental": "^5.91.0",
"@trpc/client": "^11.7.2",
@@ -78,11 +78,11 @@
"isomorphic-dompurify": "^2.33.0",
"jotai": "^2.15.2",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"next": "16.0.10",
"postcss-preset-mantine": "^1.18.0",
"prismjs": "^1.30.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"react-error-boundary": "^6.0.0",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.94.2",

View File

@@ -64,16 +64,23 @@ export const RegistrationForm = ({ invite }: RegistrationFormProps) => {
<Stack gap="xl">
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} autoComplete="off" {...form.getInputProps("username")} />
<TextInput
label={t("field.username.label")}
id="username"
autoComplete="username"
{...form.getInputProps("username")}
/>
<CustomPasswordInput
withPasswordRequirements
label={t("field.password.label")}
id="password"
autoComplete="new-password"
{...form.getInputProps("password")}
/>
<PasswordInput
label={t("field.passwordConfirm.label")}
id="password-confirm"
autoComplete="new-password"
{...form.getInputProps("confirmPassword")}
/>

View File

@@ -116,8 +116,18 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
<>
<form onSubmit={form.onSubmit((credentials) => void signInAsync(credentials.provider, credentials))}>
<Stack gap="lg">
<TextInput label={t("field.username.label")} {...form.getInputProps("name")} />
<PasswordInput label={t("field.password.label")} {...form.getInputProps("password")} />
<TextInput
label={t("field.username.label")}
id="username"
autoComplete="username"
{...form.getInputProps("name")}
/>
<PasswordInput
label={t("field.password.label")}
id="password"
autoComplete="current-password"
{...form.getInputProps("password")}
/>
{providers.includes("credentials") && (
<Stack gap="sm">

View File

@@ -49,7 +49,7 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^24.10.1",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.7.4",
"tsx": "4.20.4",

View File

@@ -34,7 +34,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.18.1",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.7.4",
"typescript": "^5.9.3"

View File

@@ -43,7 +43,7 @@
"@semantic-release/npm": "^13.1.2",
"@semantic-release/release-notes-generator": "^14.1.0",
"@testcontainers/redis": "^11.9.0",
"@turbo/gen": "^2.6.1",
"@turbo/gen": "^2.6.3",
"@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.0.15",
"@vitest/ui": "^4.0.15",
@@ -54,7 +54,7 @@
"prettier": "^3.7.4",
"semantic-release": "^25.0.2",
"testcontainers": "^11.9.0",
"turbo": "^2.6.1",
"turbo": "^2.6.3",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.0.15"
@@ -82,14 +82,14 @@
"axios@>=1.0.0 <1.8.2": ">=1.13.2",
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
"esbuild@<=0.24.2": ">=0.27.0",
"esbuild@<=0.24.2": ">=0.27.1",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.10.7",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.16.0",
"react-is": "^19.2.0",
"react-is": "^19.2.1",
"rollup@>=4.0.0 <4.22.4": ">=4.53.3",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",

View File

@@ -43,15 +43,15 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@kubernetes/client-node": "^1.4.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.7.2",
"@trpc/react-query": "^11.7.2",
"@trpc/server": "^11.7.2",
"@trpc/tanstack-react-query": "^11.7.2",
"lodash.clonedeep": "^4.5.0",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"superjson": "2.2.6",
"trpc-to-openapi": "^3.1.0",
"zod": "^4.1.13"

View File

@@ -34,11 +34,11 @@
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.0.9",
"next": "16.0.7",
"ldapts": "8.0.14",
"next": "16.0.10",
"next-auth": "5.0.0-beta.30",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {

View File

@@ -25,8 +25,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"react": "19.2.0",
"react-dom": "19.2.0"
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",

View File

@@ -34,7 +34,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"typescript": "^5.9.3"
}

View File

@@ -32,10 +32,10 @@
"@paralleldrive/cuid2": "^3.1.0",
"dayjs": "^1.11.19",
"dns-caching": "^0.2.7",
"next": "16.0.7",
"next": "16.0.10",
"octokit": "^5.0.5",
"react": "19.2.0",
"react-dom": "19.2.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"undici": "7.16.0",
"zod": "^4.1.13",
"zod-validation-error": "^5.0.0"

View File

@@ -29,12 +29,12 @@
"@homarr/core": "workspace:^0.1.0",
"@homarr/cron-jobs": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0",
"@tanstack/react-query": "^5.90.11",
"@tanstack/react-query": "^5.90.12",
"@trpc/client": "^11.7.2",
"@trpc/server": "^11.7.2",
"@trpc/tanstack-react-query": "^11.7.2",
"node-cron": "^4.2.1",
"react": "19.2.0",
"react": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {

View File

@@ -53,10 +53,10 @@
"@paralleldrive/cuid2": "^3.1.0",
"@testcontainers/mysql": "^11.9.0",
"@testcontainers/postgresql": "^11.9.0",
"better-sqlite3": "^12.4.1",
"better-sqlite3": "^12.5.0",
"dotenv": "^17.2.3",
"drizzle-kit": "^0.31.7",
"drizzle-orm": "^0.44.7",
"drizzle-kit": "^0.31.8",
"drizzle-orm": "^0.45.0",
"drizzle-zod": "^0.8.3",
"mysql2": "3.15.3",
"pg": "^8.16.3",
@@ -69,7 +69,7 @@
"@types/better-sqlite3": "7.6.13",
"@types/pg": "^8.15.6",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.0",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"prettier": "^3.7.4",
"tsx": "4.20.4",

View File

@@ -31,7 +31,7 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.9",
"react": "19.2.0",
"react": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {

View File

@@ -46,6 +46,7 @@ export class TransmissionIntegration extends Integration implements IDownloadCli
name: torrent.name,
size: torrent.totalSize,
sent: torrent.uploadedEver,
received: torrent.downloadedEver,
downSpeed: torrent.percentDone !== 1 ? torrent.rateDownload : undefined,
upSpeed: torrent.rateUpload,
time:

View File

@@ -26,6 +26,8 @@ export const downloadClientItemSchema = z.object({
size: z.number(),
/** Total uploaded in Bytes, only required for Torrent items */
sent: z.number().optional(),
/** Total downloaded in Bytes, only required for Torrent items */
received: z.number().optional(),
/** Download speed in Bytes/s, only required if not complete
* (Says 0 only if it should be downloading but isn't) */
downSpeed: z.number().optional(),

View File

@@ -169,17 +169,15 @@ export class PlexIntegration extends Integration implements IMediaServerIntegrat
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
const token = super.getSecretValue("apiKey");
const response = await input.fetchAsync(this.url("/"), {
const response = await input.fetchAsync(super.url("/prefs"), {
headers: {
"X-Plex-Token": token,
Accept: "application/json",
},
});
if (!response.ok) return TestConnectionError.StatusResult(response);
const result = await response.text();
await PlexIntegration.parseXmlAsync<PlexResponse>(result);
return { success: true };
}

View File

@@ -26,7 +26,7 @@
"dependencies": {
"@homarr/core": "workspace:^0.1.0",
"superjson": "2.2.6",
"winston": "3.18.3",
"winston": "3.19.0",
"zod": "^4.1.13"
},
"devDependencies": {

View File

@@ -36,9 +36,9 @@
"@mantine/core": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"dayjs": "^1.11.19",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"zod": "^4.1.13"
},
"devDependencies": {

View File

@@ -26,7 +26,7 @@
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"react": "19.2.0"
"react": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",

View File

@@ -40,9 +40,9 @@
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"adm-zip": "0.5.16",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"superjson": "2.2.6",
"zod": "^4.1.13",
"zod-form-data": "^3.0.1"

View File

@@ -27,9 +27,9 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.3.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0"
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",

View File

@@ -38,9 +38,9 @@
"@mantine/spotlight": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"jotai": "^2.15.2",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"use-deep-compare-effect": "^1.8.1"
},
"devDependencies": {

View File

@@ -32,10 +32,10 @@
"dayjs": "^1.11.19",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"next": "16.0.10",
"next-intl": "4.5.8",
"react": "19.2.0",
"react-dom": "19.2.0"
"react": "19.2.1",
"react-dom": "19.2.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",

View File

@@ -1939,7 +1939,23 @@
"dockerContainers": {
"name": "Docker stats",
"description": "Stats of your containers (This widget can only be added with administrator privileges)",
"option": {},
"option": {
"enableRowSorting": {
"label": "Enable items sorting"
},
"defaultSort": {
"label": "Column used for sorting by default",
"option": {
"name": "Name",
"state": "State",
"cpuUsage": "CPU usage",
"memoryUsage": "Memory usage"
}
},
"descendingDefaultSort": {
"label": "Invert sorting"
}
},
"error": {
"internalServerError": "Failed to fetch containers stats"
}

View File

@@ -341,7 +341,7 @@
"description": ""
},
"full-all": {
"label": "",
"label": "Acesso completo a aplicativos",
"description": ""
}
}

View File

@@ -35,9 +35,9 @@
"@mantine/hooks": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"svgson": "^5.3.1"
},
"devDependencies": {

View File

@@ -52,29 +52,29 @@
"@mantine/core": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"@tabler/icons-react": "^3.35.0",
"@tiptap/extension-color": "2.27.1",
"@tiptap/extension-highlight": "2.27.1",
"@tiptap/extension-image": "2.27.1",
"@tiptap/extension-link": "^2.27.1",
"@tiptap/extension-placeholder": "^2.27.1",
"@tiptap/extension-table": "2.27.1",
"@tiptap/extension-table-cell": "2.27.1",
"@tiptap/extension-table-header": "2.27.1",
"@tiptap/extension-table-row": "2.27.1",
"@tiptap/extension-task-item": "2.27.1",
"@tiptap/extension-task-list": "2.27.1",
"@tiptap/extension-text-align": "2.27.1",
"@tiptap/extension-text-style": "2.27.1",
"@tiptap/extension-underline": "2.27.1",
"@tiptap/react": "^2.27.1",
"@tiptap/starter-kit": "^2.27.1",
"@tiptap/extension-color": "3.13.0",
"@tiptap/extension-highlight": "3.13.0",
"@tiptap/extension-image": "3.13.0",
"@tiptap/extension-link": "^3.13.0",
"@tiptap/extension-placeholder": "^3.13.0",
"@tiptap/extension-table": "3.13.0",
"@tiptap/extension-table-cell": "3.13.0",
"@tiptap/extension-table-header": "3.13.0",
"@tiptap/extension-table-row": "3.13.0",
"@tiptap/extension-task-item": "3.13.0",
"@tiptap/extension-task-list": "3.13.0",
"@tiptap/extension-text-align": "3.13.0",
"@tiptap/extension-text-style": "3.13.0",
"@tiptap/extension-underline": "3.13.0",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"mantine-form-zod-resolver": "^1.3.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"next": "16.0.10",
"react": "19.2.1",
"react-dom": "19.2.1",
"react-markdown": "^10.1.0",
"recharts": "^2.15.4",
"video.js": "^8.23.4",

View File

@@ -56,6 +56,7 @@ const createColumns = (
t: ReturnType<typeof useScopedI18n<"docker">>,
): MRT_ColumnDef<RouterOutputs["docker"]["getContainers"]["containers"][number]>[] => [
{
id: "name",
accessorKey: "name",
header: t("field.name.label"),
Cell({ renderedCellValue, row }) {
@@ -70,6 +71,7 @@ const createColumns = (
},
},
{
id: "state",
accessorKey: "state",
size: 100,
header: t("field.state.label"),
@@ -78,6 +80,13 @@ const createColumns = (
},
},
{
id: "cpuUsage",
sortingFn: (rowA, rowB) => {
const cpuUsageA = safeValue(rowA.original.cpuUsage);
const cpuUsageB = safeValue(rowB.original.cpuUsage);
return cpuUsageA - cpuUsageB;
},
accessorKey: "cpuUsage",
size: 80,
header: t("field.stats.cpu.label"),
@@ -92,6 +101,13 @@ const createColumns = (
},
},
{
id: "memoryUsage",
sortingFn: (rowA, rowB) => {
const memoryUsageA = safeValue(rowA.original.memoryUsage);
const memoryUsageB = safeValue(rowB.original.memoryUsage);
return memoryUsageA - memoryUsageB;
},
accessorKey: "memoryUsage",
size: 80,
header: t("field.stats.memory.label"),
@@ -106,9 +122,11 @@ const createColumns = (
},
},
{
id: "actions",
accessorKey: "actions",
size: 80,
header: t("action.title"),
enableSorting: false,
Cell({ row }) {
const utils = clientApi.useUtils();
// eslint-disable-next-line no-restricted-syntax
@@ -168,7 +186,7 @@ const createColumns = (
},
];
export default function DockerWidget({ width }: WidgetComponentProps<"dockerContainers">) {
export default function DockerWidget({ options, width, isEditMode }: WidgetComponentProps<"dockerContainers">) {
const t = useScopedI18n("docker");
const isTiny = width <= 256;
@@ -192,8 +210,8 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont
enablePagination: false,
enableTopToolbar: false,
enableBottomToolbar: false,
enableSorting: false,
enableColumnActions: false,
enableSorting: options.enableRowSorting && !isEditMode,
enableStickyHeader: false,
enableColumnOrdering: false,
enableRowSelection: false,
@@ -203,6 +221,7 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont
enableFilters: false,
enableHiding: false,
initialState: {
sorting: [{ id: options.defaultSort, desc: options.descendingDefaultSort }],
density: "xs",
},
mantinePaperProps: {

View File

@@ -1,12 +1,35 @@
import { IconBrandDocker, IconServerOff } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";
const columnsList = [
"name",
"state",
"cpuUsage",
"memoryUsage",
] as const satisfies (keyof RouterOutputs["docker"]["getContainers"]["containers"][number])[];
export const { definition, componentLoader } = createWidgetDefinition("dockerContainers", {
icon: IconBrandDocker,
createOptions() {
return optionsBuilder.from(() => ({}));
return optionsBuilder.from((factory) => ({
enableRowSorting: factory.switch({
defaultValue: false,
}),
defaultSort: factory.select({
defaultValue: "name",
options: columnsList.map((value) => ({
value,
label: (t) => t(`widget.dockerContainers.option.defaultSort.option.${value}`),
})),
}),
descendingDefaultSort: factory.switch({
defaultValue: false,
}),
}));
},
errors: {
INTERNAL_SERVER_ERROR: {

View File

@@ -36,6 +36,7 @@ import {
IconX,
} from "@tabler/icons-react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type { MRT_ColumnDef, MRT_VisibilityState } from "mantine-react-table";
import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
@@ -48,6 +49,8 @@ import { useScopedI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition";
dayjs.extend(relativeTime);
interface QuickFilter {
integrationKinds: string[];
statuses: ExtendedDownloadClientItem["state"][];
@@ -188,14 +191,14 @@ export default function DownloadClientsWidget({
)
//Add extrapolated data and actions if user is allowed interaction
.map((item): ExtendedDownloadClientItem => {
const received = Math.floor(item.size * item.progress);
const received = item.received ?? Math.floor(item.size * item.progress);
const integrationIds = [pair.integration.id];
return {
integration: pair.integration,
...item,
category: item.category !== undefined && item.category.length > 0 ? item.category : undefined,
received,
ratio: item.sent !== undefined ? item.sent / (received || 1) : undefined,
ratio: item.sent !== undefined ? item.sent / item.size : undefined,
//Only add if permission to use mutations
actions: integrationsWithInteractions.includes(pair.integration.id)
? {
@@ -714,7 +717,10 @@ const ItemInfoModal = ({ items, currentIndex, opened, onClose }: ItemInfoModalPr
/>
{item.type !== "miscellaneous" && <NormalizedLine itemKey="ratio" values={item.ratio} />}
<NormalizedLine itemKey="added" values={item.added === undefined ? "unknown" : dayjs(item.added).format()} />
<NormalizedLine itemKey="time" values={item.time !== 0 ? dayjs().add(item.time).format() : "∞"} />
<NormalizedLine
itemKey="time"
values={item.time !== 0 ? dayjs().add(item.time, "milliseconds").fromNow() : "∞"}
/>
<NormalizedLine itemKey="category" values={item.category} />
</Stack>
)}

View File

@@ -41,21 +41,21 @@ import {
IconX,
} from "@tabler/icons-react";
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import Image from "@tiptap/extension-image";
import Placeholder from "@tiptap/extension-placeholder";
import Table from "@tiptap/extension-table";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import TableRow from "@tiptap/extension-table-row";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextAlign from "@tiptap/extension-text-align";
import TextStyle from "@tiptap/extension-text-style";
import Underline from "@tiptap/extension-underline";
import { Highlight } from "@tiptap/extension-highlight";
import { Image } from "@tiptap/extension-image";
import { Placeholder } from "@tiptap/extension-placeholder";
import { Table } from "@tiptap/extension-table";
import { TableCell } from "@tiptap/extension-table-cell";
import { TableHeader } from "@tiptap/extension-table-header";
import { TableRow } from "@tiptap/extension-table-row";
import { TaskItem } from "@tiptap/extension-task-item";
import { TaskList } from "@tiptap/extension-task-list";
import { TextAlign } from "@tiptap/extension-text-align";
import { TextStyle } from "@tiptap/extension-text-style";
import type { Editor } from "@tiptap/react";
import { BubbleMenu, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEditor } from "@tiptap/react";
import { BubbleMenu } from "@tiptap/react/menus";
import { StarterKit } from "@tiptap/starter-kit";
import type { Node } from "prosemirror-model";
import { clientApi } from "@homarr/api/client";
@@ -133,7 +133,8 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
};
},
}),
StarterKit,
// we use a custom link implementation from mantine
StarterKit.configure({ link: false }),
Table.configure({
resizable: true,
lastColumnResizable: false,
@@ -170,8 +171,9 @@ export function Notebook({ options, setOptions, isEditMode, boardId, itemId }: W
TaskList.configure({ itemTypeName: "taskItem" }),
TextAlign.configure({ types: ["heading", "paragraph"] }),
TextStyle,
Underline,
],
shouldRerenderOnTransaction: true,
immediatelyRender: false,
content,
onUpdate: ({ editor }) => {
setContent(editor.getHTML());

1965
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,4 +5,4 @@ packages:
minimumReleaseAge: 4320 # Only download deps if they are at least 3 days old (in minutes)
minimumReleaseAgeExclude:
- next
- "@next/eslint-plugin-next"
- "@next/*"

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash
# Create sub directories in volume
mkdir -p /appdata/db
mkdir -p /appdata/redis
mkdir -p /appdata/trusted-certificates
# Run migrations
if [ $DB_MIGRATIONS_DISABLED = "true" ]; then
if [ "$DB_MIGRATIONS_DISABLED" = "true" ]; then
echo "DB migrations are disabled, skipping"
else
echo "Running DB migrations"
@@ -20,13 +22,15 @@ export CRON_JOB_API_KEY=$(openssl rand -base64 32)
# 1. Replace the HOSTNAME in the nginx template file
# 2. Create the nginx configuration file from the template
# 3. Start the nginx server
export HOSTNAME
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
# Start services in the background and store their PIDs
nginx -g 'daemon off;' &
NGINX_PID=$!
if [ $REDIS_IS_EXTERNAL = "true" ]; then
if [ "$REDIS_IS_EXTERNAL" = "true" ]; then
echo "Using external Redis server at redis://$REDIS_HOST:$REDIS_PORT"
REDIS_PID=""
else
echo "Starting internal Redis server"
redis-server /app/redis.conf &
@@ -49,9 +53,11 @@ terminate() {
echo "Received SIGTERM. Shutting down..."
kill -TERM $NGINX_PID $TASKS_PID $WSS_PID $NEXTJS_PID 2>/dev/null
wait
# kill redis-server last because of logging of other services
kill -TERM $REDIS_PID 2>/dev/null
wait
# kill redis-server last because of logging of other services and only if $REDIS_PID is set
if [ -n "$REDIS_PID" ]; then
kill -TERM $REDIS_PID 2>/dev/null
wait
fi
echo "Shutdown complete."
exit 0
}
@@ -61,4 +67,4 @@ trap terminate TERM INT
# Wait for all processes
wait $NEXTJS_PID
terminate
terminate

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,9 +17,9 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@next/eslint-plugin-next": "16.0.7",
"@next/eslint-plugin-next": "16.0.10",
"eslint-config-prettier": "^10.1.8",
"eslint-config-turbo": "^2.6.1",
"eslint-config-turbo": "^2.6.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",