🐳 Fix issues with dockerfile (#1611)

* Add `wait $PID` to be able to send SIG_ABORT

* Move to docker `entrypoint`

* Change default NEXTAUTH_URL

* Add `VOLUME` instruction

* corrected a typo

* 🐳 Fix docker TCP not working

Fixes Lost docker connection via TCP with 0.14.0 update #1577

* 🚧 Improve dockerfile and start script and fix permission issue by adding new user with permission to read / write to /data folder

* 🐛 Cleanup changes, Local db:migrate script not working, CI failed

*  Image properties customization (#1590)

* 🌐 New Crowdin updates (#1572)

*  Add notice page for readonly db

* Misc docker changes

* 🐳 Add `homarr` as `USER`

* 🐛 Unable to use user homarr because db.sqlite file is already owned by root

---------

Co-authored-by: Lumilias <10852161+Lumilias@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
Co-authored-by: Manuel <manuel.ruwe@bluewin.ch>
This commit is contained in:
Thomas Camlong
2023-11-13 20:04:44 +01:00
committed by GitHub
parent a3ca74ab46
commit 811d940f2b
7 changed files with 657 additions and 42 deletions

View File

@@ -0,0 +1,42 @@
import { Center, Code, List, Stack, Text, Title } from '@mantine/core';
import Head from 'next/head';
export const DatabaseNotWriteable = ({ error, errorMessage }: { error: any | unknown, errorMessage: string | undefined }) => {
return (
<>
<Head>
<title>Onboard - Error Homarr</title>
</Head>
<Center h="100%">
<Stack align="center" p="lg">
<Title order={1} weight={800} size="3rem" opacity={0.8}>
Critical error while starting Homarr
</Title>
<Text size="lg" mb={40}>
We detected that Homarr is unable to write to the database. Please troubleshoot using
the following steps:
</Text>
<List>
<List.Item>
Ensure that you mounted the path <code>/data</code> to a writeable location with
enough disk space. For this, you must add the following mounting point to your docker
compose: <Code block>{' - <your-path>/data:/data'}</Code>
</List.Item>
<List.Item>
Ensure that you followed the installation instructions at{' '}
<a href="https://homarr.dev/docs/introduction/installation">
https://homarr.dev/docs/introduction/installation
</a>
</List.Item>
</List>
<Code block>{error && JSON.stringify(error)}</Code>
{errorMessage && (
<Code block>{errorMessage}</Code>
)}
</Stack>
</Center>
</>
);
};

View File

@@ -47,11 +47,5 @@ const shouldRedirectToOnboard = async (): Promise<boolean> => {
return cachedUserCount === 0;
};
if (!process.env.DATABASE_URL?.startsWith('file:')) {
return await cacheAndGetUserCount();
}
const fileUri = process.env.DATABASE_URL.substring(4);
return await cacheAndGetUserCount();
// TODO: Show an error page if the database file is read-only
};

View File

@@ -1,19 +1,28 @@
import { Box, Button, Center, Image, Stack, Text, Title, useMantineTheme } from '@mantine/core';
import { Button, Center, Image, Stack, Text, Title, useMantineTheme } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconArrowRight } from '@tabler/icons-react';
import Consola from 'consola';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import Head from 'next/head';
import { DatabaseNotWriteable } from '~/components/Onboarding/database-not-writeable';
import { OnboardingSteps } from '~/components/Onboarding/onboarding-steps';
import { ThemeSchemeToggle } from '~/components/ThemeSchemeToggle/ThemeSchemeToggle';
import { FloatingBackground } from '~/components/layout/Background/FloatingBackground';
import { db } from '~/server/db';
import { env } from '~/env';
import { getTotalUserCountAsync } from '~/server/db/queries/user';
import { getConfig } from '~/tools/config/getConfig';
import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations';
const util = require('util');
const exec = util.promisify(require('child_process').exec);
export default function OnboardPage({
configSchemaVersions,
databaseNotWriteable,
error,
errorMessage
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
const { fn, colors, colorScheme } = useMantineTheme();
const background = colorScheme === 'dark' ? 'dark.6' : 'gray.1';
@@ -39,29 +48,35 @@ export default function OnboardPage({
</Center>
</Center>
{onboardingSteps ? (
<OnboardingSteps isUpdate={isUpgradeFromSchemaOne} />
{databaseNotWriteable == true ? (
<DatabaseNotWriteable error={error} errorMessage={errorMessage} />
) : (
<Center h="100%">
<Stack align="center" p="lg">
<Title order={1} weight={800} size="3rem" opacity={0.8}>
Welcome to Homarr!
</Title>
<Text size="lg" mb={40}>
Your favorite dashboard has received a big upgrade.
<br />
We'll help you update within the next few steps
</Text>
<>
{onboardingSteps ? (
<OnboardingSteps isUpdate={isUpgradeFromSchemaOne} />
) : (
<Center h="100%">
<Stack align="center" p="lg">
<Title order={1} weight={800} size="3rem" opacity={0.8}>
Welcome to Homarr!
</Title>
<Text size="lg" mb={40}>
Your favorite dashboard has received a big upgrade.
<br />
We'll help you update within the next few steps
</Text>
<Button
onClick={showOnboardingSteps}
rightIcon={<IconArrowRight size="1rem" />}
variant="default"
>
Start update process
</Button>
</Stack>
</Center>
<Button
onClick={showOnboardingSteps}
rightIcon={<IconArrowRight size="1rem" />}
variant="default"
>
Start update process
</Button>
</Stack>
</Center>
)}
</>
)}
</Stack>
</>
@@ -87,10 +102,65 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
ctx.res
);
if (env.DATABASE_URL.startsWith('file:')) {
const rawDatabaseUrl = env.DATABASE_URL.substring('file:'.length);
Consola.info(
`Instance is using a database on the file system. Checking if file '${rawDatabaseUrl}' is writable...`
);
try {
await fsPromises.access(rawDatabaseUrl, fs.constants.W_OK);
} catch (error) {
// this usually occurs when the database path is not mounted in Docker
Consola.error(`Database '${rawDatabaseUrl}' is not writable.`, error);
return {
props: {
...translations,
configSchemaVersions: configSchemaVersions,
databaseNotWriteable: true,
error: error,
},
};
}
Consola.info('Database is writeable');
if (process.platform !== 'win32') {
try {
const { stdout, stderr } = await exec("mount | grep '/data'");
if (stderr.split('\n').length > 1 || stdout.split('\n').length <= 1) {
Consola.error(`Database at '${rawDatabaseUrl}' has not been mounted: ${stdout.replace('\n', '\\n')} ${stderr.replace('\n', '\\n')}`);
return {
props: {
...translations,
configSchemaVersions: configSchemaVersions,
databaseNotWriteable: true,
error: `Database at '${rawDatabaseUrl}' is not mounted:\n${stdout}`,
},
};
}
} catch (error) {
const errorMessage = `Database at '${rawDatabaseUrl}' has not been mounted: ${error}`;
Consola.error(errorMessage);
return {
props: {
...translations,
configSchemaVersions: configSchemaVersions,
databaseNotWriteable: true,
error: error,
errorMessage: errorMessage
},
};
}
}
Consola.info(`Database at '${rawDatabaseUrl}' is writeable and mounted`);
}
return {
props: {
...translations,
configSchemaVersions: configSchemaVersions,
databaseNotWriteable: false
},
};
};

View File

@@ -1,6 +1,6 @@
import { ConfigType } from '~/types/config';
import defaultConfig from '../../../data/configs/default.json';
import defaultConfig from '../../../data/default.json';
export const getFallbackConfig = (name?: string) => ({
...defaultConfig,