diff --git a/.gitignore b/.gitignore index 91c18132f..bdc55c860 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ yarn-error.log* apps/tasks/tasks.cjs apps/websocket/wssServer.cjs apps/nextjs/.million/ +packages/cli/cli.cjs #personal backgrounds diff --git a/.nvmrc b/.nvmrc index 3516580bb..2a393af59 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.17.0 +20.18.0 diff --git a/Dockerfile b/Dockerfile index 4257ae086..7ae43e72f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.17.0-alpine AS base +FROM node:20.18.0-alpine AS base FROM base AS builder RUN apk add --no-cache libc6-compat @@ -46,6 +46,7 @@ COPY --from=builder /app/cli-out/full/ . # Copy static data as it is not part of the build COPY static-data ./static-data ARG SKIP_ENV_VALIDATION='true' +ARG CI='true' ARG DISABLE_REDIS_LOGS='true' RUN corepack enable pnpm && pnpm build @@ -58,6 +59,8 @@ RUN mkdir /appdata RUN mkdir /appdata/db RUN mkdir /appdata/redis VOLUME /appdata +RUN mkdir /secrets +VOLUME /secrets @@ -71,6 +74,7 @@ RUN chmod +x /usr/bin/homarr # Don't run production as root RUN chown -R nextjs:nodejs /appdata +RUN chown -R nextjs:nodejs /secrets RUN mkdir -p /var/cache/nginx && chown -R nextjs:nodejs /var/cache/nginx && \ mkdir -p /var/log/nginx && chown -R nextjs:nodejs /var/log/nginx && \ mkdir -p /var/lib/nginx && chown -R nextjs:nodejs /var/lib/nginx && \ @@ -93,6 +97,7 @@ COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/standalone ./ COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/static ./apps/nextjs/.next/static COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/public ./apps/nextjs/public COPY --chown=nextjs:nodejs scripts/run.sh ./run.sh +COPY --chown=nextjs:nodejs scripts/generateEncryptionKey.js ./generateEncryptionKey.js COPY --chown=nextjs:nodejs packages/redis/redis.conf /app/redis.conf COPY --chown=nextjs:nodejs nginx.conf /etc/nginx/templates/nginx.conf diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 52981dbe6..9a8c7ec6c 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -30,23 +30,24 @@ "@homarr/modals-collection": "workspace:^0.1.0", "@homarr/notifications": "workspace:^0.1.0", "@homarr/old-schema": "workspace:^0.1.0", + "@homarr/redis": "workspace:^0.1.0", "@homarr/server-settings": "workspace:^0.1.0", "@homarr/spotlight": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", "@homarr/widgets": "workspace:^0.1.0", - "@mantine/colors-generator": "^7.13.0", - "@mantine/core": "^7.13.0", - "@mantine/hooks": "^7.13.0", - "@mantine/modals": "^7.13.0", - "@mantine/tiptap": "^7.13.0", - "@million/lint": "1.0.0-rc.84", + "@mantine/colors-generator": "^7.13.2", + "@mantine/core": "^7.13.2", + "@mantine/hooks": "^7.13.2", + "@mantine/modals": "^7.13.2", + "@mantine/tiptap": "^7.13.2", + "@million/lint": "1.0.8", "@t3-oss/env-nextjs": "^0.11.1", - "@tabler/icons-react": "^3.18.0", - "@tanstack/react-query": "^5.56.2", - "@tanstack/react-query-devtools": "^5.58.0", - "@tanstack/react-query-next-experimental": "5.56.2", + "@tabler/icons-react": "^3.19.0", + "@tanstack/react-query": "^5.59.0", + "@tanstack/react-query-devtools": "^5.59.0", + "@tanstack/react-query-next-experimental": "5.59.0", "@trpc/client": "next", "@trpc/next": "next", "@trpc/react-query": "next", @@ -62,14 +63,14 @@ "glob": "^11.0.0", "jotai": "^2.10.0", "mantine-react-table": "2.0.0-beta.6", - "next": "^14.2.13", + "next": "^14.2.14", "postcss-preset-mantine": "^1.17.0", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.0.13", "react-simple-code-editor": "^0.14.1", - "sass": "^1.79.3", + "sass": "^1.79.4", "superjson": "2.2.1", "swagger-ui-react": "^5.17.14", "use-deep-compare-effect": "^1.8.1" @@ -81,7 +82,7 @@ "@types/chroma-js": "2.4.4", "@types/node": "^20.16.10", "@types/prismjs": "^1.26.4", - "@types/react": "^18.3.10", + "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@types/swagger-ui-react": "^4.18.3", "concurrently": "^9.0.1", diff --git a/apps/nextjs/src/app/[locale]/manage/apps/page.tsx b/apps/nextjs/src/app/[locale]/manage/apps/page.tsx index 7ddf2816a..11aa2e8eb 100644 --- a/apps/nextjs/src/app/[locale]/manage/apps/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/apps/page.tsx @@ -105,7 +105,7 @@ const AppNoResults = async () => { {t("app.page.list.noResults.title")} - {t("app.page.list.noResults.description")} + {t("app.page.list.noResults.action")} ); diff --git a/apps/nextjs/src/app/[locale]/manage/layout.tsx b/apps/nextjs/src/app/[locale]/manage/layout.tsx index 58314d65a..b4d1564c4 100644 --- a/apps/nextjs/src/app/[locale]/manage/layout.tsx +++ b/apps/nextjs/src/app/[locale]/manage/layout.tsx @@ -15,6 +15,7 @@ import { IconPlug, IconQuestionMark, IconReport, + IconSearch, IconSettings, IconTool, IconUser, @@ -53,6 +54,11 @@ export default async function ManageLayout({ children }: PropsWithChildren) { href: "/manage/integrations", label: t("items.integrations"), }, + { + icon: IconSearch, + href: "/manage/search-engines", + label: t("items.searchEngies"), + }, { icon: IconUser, label: t("items.users.label"), diff --git a/apps/nextjs/src/app/[locale]/manage/search-engines/_form.tsx b/apps/nextjs/src/app/[locale]/manage/search-engines/_form.tsx new file mode 100644 index 000000000..e937cf503 --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/search-engines/_form.tsx @@ -0,0 +1,73 @@ +"use client"; + +import Link from "next/link"; +import { Button, Grid, Group, Stack, Textarea, TextInput } from "@mantine/core"; + +import { useZodForm } from "@homarr/form"; +import type { TranslationFunction } from "@homarr/translation"; +import { useI18n } from "@homarr/translation/client"; +import type { z } from "@homarr/validation"; +import { validation } from "@homarr/validation"; + +import { IconPicker } from "~/components/icons/picker/icon-picker"; + +type FormType = z.infer; + +interface SearchEngineFormProps { + submitButtonTranslation: (t: TranslationFunction) => string; + initialValues?: FormType; + handleSubmit: (values: FormType) => void; + isPending: boolean; + disableShort?: boolean; +} + +export const SearchEngineForm = (props: SearchEngineFormProps) => { + const { submitButtonTranslation, handleSubmit, initialValues, isPending, disableShort } = props; + const t = useI18n(); + + const form = useZodForm(validation.searchEngine.manage, { + initialValues: initialValues ?? { + name: "", + short: "", + iconUrl: "", + urlTemplate: "", + description: "", + }, + }); + + return ( +
+ + + + + + + + + + + +