mirror of
https://github.com/meienberger/runtipi.git
synced 2024-10-26 20:19:56 +03:00
chore(sentry): avoid sending errors related to custom user configs (#1579)
This commit is contained in:
parent
eef0485436
commit
f31daec736
@ -6,10 +6,13 @@ const IgnoreErrors = [
|
||||
// Innocuous browser errors
|
||||
/ResizeObserver loop limit exceeded/,
|
||||
/ResizeObserver loop completed with undelivered notifications/,
|
||||
// Error on user's side
|
||||
/no space left on device/,
|
||||
// Dark reader extension
|
||||
/WeakMap key undefined must be an object or an unregistered symbol/,
|
||||
// Docker-compose error
|
||||
/no space left on device/,
|
||||
/port is already allocated/,
|
||||
/address already in use/,
|
||||
/Error with your custom app/,
|
||||
];
|
||||
|
||||
const cleanseUrl = (url: string) => {
|
||||
|
@ -1,17 +1,21 @@
|
||||
import path from 'path';
|
||||
import path from 'node:path';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { execAsync, pathExists } from '@runtipi/shared/node';
|
||||
import { SocketEvent, sanitizePath, socketEventSchema } from '@runtipi/shared';
|
||||
import { type SocketEvent, sanitizePath, socketEventSchema } from '@runtipi/shared';
|
||||
import { logger } from '@/lib/logger';
|
||||
import { getEnv } from '@/lib/environment';
|
||||
import { APP_DATA_DIR, DATA_DIR } from '@/config/constants';
|
||||
import { Socket } from 'socket.io';
|
||||
import type { Socket } from 'socket.io';
|
||||
import { getRepoHash } from 'src/services/repo/repo.helpers';
|
||||
import { DEFAULT_REPO_URL } from '../system/system.helpers';
|
||||
|
||||
const getBaseComposeArgsApp = async (appId: string) => {
|
||||
const { arch, appsRepoId } = getEnv();
|
||||
const appDataDirPath = path.join(APP_DATA_DIR, sanitizePath(appId));
|
||||
const appDirPath = path.join(DATA_DIR, 'apps', sanitizePath(appId));
|
||||
|
||||
let isCustomConfig = appsRepoId !== getRepoHash(DEFAULT_REPO_URL);
|
||||
|
||||
const args: string[] = [`--env-file ${path.join(appDataDirPath, 'app.env')}`];
|
||||
|
||||
// User custom env file
|
||||
@ -28,33 +32,23 @@ const getBaseComposeArgsApp = async (appId: string) => {
|
||||
}
|
||||
args.push(`-f ${composeFile}`);
|
||||
|
||||
const commonComposeFile = path.join(
|
||||
DATA_DIR,
|
||||
'repos',
|
||||
sanitizePath(appsRepoId),
|
||||
'apps',
|
||||
'docker-compose.common.yml',
|
||||
);
|
||||
const commonComposeFile = path.join(DATA_DIR, 'repos', sanitizePath(appsRepoId), 'apps', 'docker-compose.common.yml');
|
||||
args.push(`-f ${commonComposeFile}`);
|
||||
|
||||
// User defined overrides
|
||||
const userComposeFile = path.join(
|
||||
DATA_DIR,
|
||||
'user-config',
|
||||
sanitizePath(appId),
|
||||
'docker-compose.yml',
|
||||
);
|
||||
const userComposeFile = path.join(DATA_DIR, 'user-config', sanitizePath(appId), 'docker-compose.yml');
|
||||
if (await pathExists(userComposeFile)) {
|
||||
isCustomConfig = true;
|
||||
args.push(`--file ${userComposeFile}`);
|
||||
}
|
||||
|
||||
return args;
|
||||
return { args, isCustomConfig };
|
||||
};
|
||||
|
||||
const getBaseComposeArgsTipi = async () => {
|
||||
const args: string[] = [`--env-file ${path.join(DATA_DIR, '.env')}`];
|
||||
|
||||
args.push(`--project-name runtipi`);
|
||||
args.push('--project-name runtipi');
|
||||
|
||||
const composeFile = path.join(DATA_DIR, 'docker-compose.yml');
|
||||
args.push(`-f ${composeFile}`);
|
||||
@ -74,24 +68,25 @@ const getBaseComposeArgsTipi = async () => {
|
||||
* @param {string} command - Command to execute
|
||||
*/
|
||||
export const compose = async (appId: string, command: string) => {
|
||||
const args = await getBaseComposeArgsApp(appId);
|
||||
const { args, isCustomConfig } = await getBaseComposeArgsApp(appId);
|
||||
args.push(command);
|
||||
|
||||
logger.info(`Running docker compose with args ${args.join(' ')}`);
|
||||
const { stdout, stderr } = await execAsync(`docker-compose ${args.join(' ')}`);
|
||||
|
||||
if (stderr && stderr.includes('Command failed:')) {
|
||||
if (stderr?.includes('Command failed:')) {
|
||||
if (isCustomConfig) {
|
||||
throw new Error(
|
||||
`Error with your custom app: ${stderr}. Before opening an issue try to remove any user-config files or any custom app-store repo and try again.`,
|
||||
);
|
||||
}
|
||||
throw new Error(stderr);
|
||||
}
|
||||
|
||||
return { stdout, stderr };
|
||||
};
|
||||
|
||||
export const handleViewRuntipiLogsEvent = async (
|
||||
socket: Socket,
|
||||
event: SocketEvent,
|
||||
emit: (event: SocketEvent) => Promise<void>,
|
||||
) => {
|
||||
export const handleViewRuntipiLogsEvent = async (socket: Socket, event: SocketEvent, emit: (event: SocketEvent) => Promise<void>) => {
|
||||
const { success, data } = socketEventSchema.safeParse(event);
|
||||
|
||||
if (!success) {
|
||||
@ -142,11 +137,7 @@ export const handleViewRuntipiLogsEvent = async (
|
||||
});
|
||||
};
|
||||
|
||||
export const handleViewAppLogsEvent = async (
|
||||
socket: Socket,
|
||||
event: SocketEvent,
|
||||
emit: (event: SocketEvent) => Promise<void>,
|
||||
) => {
|
||||
export const handleViewAppLogsEvent = async (socket: Socket, event: SocketEvent, emit: (event: SocketEvent) => Promise<void>) => {
|
||||
const parsedEvent = socketEventSchema.safeParse(event);
|
||||
|
||||
if (!parsedEvent.success) {
|
||||
@ -160,7 +151,7 @@ export const handleViewAppLogsEvent = async (
|
||||
|
||||
const { appId, maxLines } = parsedEvent.data.data;
|
||||
|
||||
const args = await getBaseComposeArgsApp(appId);
|
||||
const { args } = await getBaseComposeArgsApp(appId);
|
||||
args.push(`logs --follow -n ${maxLines || 25}`);
|
||||
|
||||
const logsCommand = `docker-compose ${args.join(' ')}`;
|
||||
|
@ -43,7 +43,7 @@ type EnvKeys =
|
||||
| (string & {});
|
||||
|
||||
const OLD_DEFAULT_REPO_URL = 'https://github.com/meienberger/runtipi-appstore';
|
||||
const DEFAULT_REPO_URL = 'https://github.com/runtipi/runtipi-appstore';
|
||||
export const DEFAULT_REPO_URL = 'https://github.com/runtipi/runtipi-appstore';
|
||||
|
||||
/**
|
||||
* Reads and returns the generated seed
|
||||
@ -155,43 +155,26 @@ export const generateSystemEnvFile = async () => {
|
||||
envMap.set('ARCHITECTURE', getArchitecture());
|
||||
envMap.set('JWT_SECRET', jwtSecret);
|
||||
envMap.set('DOMAIN', data.domain || envMap.get('DOMAIN') || 'example.com');
|
||||
envMap.set(
|
||||
'RUNTIPI_APP_DATA_PATH',
|
||||
data.appDataPath || envMap.get('RUNTIPI_APP_DATA_PATH') || rootFolderHost,
|
||||
);
|
||||
envMap.set('RUNTIPI_APP_DATA_PATH', data.appDataPath || envMap.get('RUNTIPI_APP_DATA_PATH') || rootFolderHost);
|
||||
envMap.set('POSTGRES_HOST', 'runtipi-db');
|
||||
envMap.set('POSTGRES_DBNAME', 'tipi');
|
||||
envMap.set('POSTGRES_USERNAME', 'tipi');
|
||||
envMap.set('POSTGRES_PORT', String(5432));
|
||||
envMap.set('REDIS_HOST', 'runtipi-redis');
|
||||
envMap.set(
|
||||
'DEMO_MODE',
|
||||
typeof data.demoMode === 'boolean' ? String(data.demoMode) : envMap.get('DEMO_MODE') || 'false',
|
||||
);
|
||||
envMap.set(
|
||||
'GUEST_DASHBOARD',
|
||||
typeof data.guestDashboard === 'boolean'
|
||||
? String(data.guestDashboard)
|
||||
: envMap.get('GUEST_DASHBOARD') || 'false',
|
||||
);
|
||||
envMap.set('DEMO_MODE', typeof data.demoMode === 'boolean' ? String(data.demoMode) : envMap.get('DEMO_MODE') || 'false');
|
||||
envMap.set('GUEST_DASHBOARD', typeof data.guestDashboard === 'boolean' ? String(data.guestDashboard) : envMap.get('GUEST_DASHBOARD') || 'false');
|
||||
envMap.set('LOCAL_DOMAIN', data.localDomain || envMap.get('LOCAL_DOMAIN') || 'tipi.lan');
|
||||
envMap.set(
|
||||
'ALLOW_AUTO_THEMES',
|
||||
typeof data.allowAutoThemes === 'boolean'
|
||||
? String(data.allowAutoThemes)
|
||||
: envMap.get('ALLOW_AUTO_THEMES') || 'true',
|
||||
typeof data.allowAutoThemes === 'boolean' ? String(data.allowAutoThemes) : envMap.get('ALLOW_AUTO_THEMES') || 'true',
|
||||
);
|
||||
envMap.set(
|
||||
'ALLOW_ERROR_MONITORING',
|
||||
typeof data.allowErrorMonitoring === 'boolean'
|
||||
? String(data.allowErrorMonitoring)
|
||||
: envMap.get('ALLOW_ERROR_MONITORING') || 'false',
|
||||
typeof data.allowErrorMonitoring === 'boolean' ? String(data.allowErrorMonitoring) : envMap.get('ALLOW_ERROR_MONITORING') || 'false',
|
||||
);
|
||||
envMap.set(
|
||||
'PERSIST_TRAEFIK_CONFIG',
|
||||
typeof data.persistTraefikConfig === 'boolean'
|
||||
? String(data.persistTraefikConfig)
|
||||
: envMap.get('PERSIST_TRAEFIK_CONFIG') || 'false',
|
||||
typeof data.persistTraefikConfig === 'boolean' ? String(data.persistTraefikConfig) : envMap.get('PERSIST_TRAEFIK_CONFIG') || 'false',
|
||||
);
|
||||
|
||||
await fs.promises.writeFile(envFilePath, envMapToString(envMap));
|
||||
@ -221,10 +204,7 @@ export const copySystemFiles = async (envMap: Map<EnvKeys, string>) => {
|
||||
logger.warn('Skipping the copy of traefik files because persistTraefikConfig is set to true');
|
||||
} else {
|
||||
logger.info('Copying traefik files');
|
||||
await fs.promises.copyFile(
|
||||
path.join(assetsFolder, 'traefik', 'traefik.yml'),
|
||||
path.join(DATA_DIR, 'traefik', 'traefik.yml'),
|
||||
);
|
||||
await fs.promises.copyFile(path.join(assetsFolder, 'traefik', 'traefik.yml'), path.join(DATA_DIR, 'traefik', 'traefik.yml'));
|
||||
await fs.promises.copyFile(
|
||||
path.join(assetsFolder, 'traefik', 'dynamic', 'dynamic.yml'),
|
||||
path.join(DATA_DIR, 'traefik', 'dynamic', 'dynamic.yml'),
|
||||
@ -325,10 +305,7 @@ export const generateTlsCertificates = async (data: { domain?: string }) => {
|
||||
const { stderr } = await execAsync(
|
||||
`openssl req -x509 -newkey rsa:4096 -keyout ${DATA_DIR}/traefik/tls/key.pem -out ${DATA_DIR}/traefik/tls/cert.pem -days 365 -subj "${subject}" -addext "subjectAltName = ${subjectAltName}" -nodes`,
|
||||
);
|
||||
if (
|
||||
!(await pathExists(path.join(tlsFolder, 'cert.pem'))) ||
|
||||
!(await pathExists(path.join(tlsFolder, 'key.pem')))
|
||||
) {
|
||||
if (!(await pathExists(path.join(tlsFolder, 'cert.pem'))) || !(await pathExists(path.join(tlsFolder, 'key.pem')))) {
|
||||
logger.error(`Failed to generate TLS certificate for ${data.domain}`);
|
||||
logger.error(stderr);
|
||||
} else {
|
||||
|
@ -25,6 +25,5 @@ if (allowErrorMonitoring && process.env.NODE_ENV === 'production' && process.env
|
||||
dsn: 'https://7a73d72f886948478b55621e7b92c3c7@o4504242900238336.ingest.sentry.io/4504826587971584',
|
||||
beforeSend: cleanseErrorData,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ export async function register() {
|
||||
dsn: 'https://7a73d72f886948478b55621e7b92c3c7@o4504242900238336.ingest.sentry.io/4504826587971584',
|
||||
beforeSend: cleanseErrorData,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
@ -19,7 +18,6 @@ export async function register() {
|
||||
dsn: 'https://7a73d72f886948478b55621e7b92c3c7@o4504242900238336.ingest.sentry.io/4504826587971584',
|
||||
beforeSend: cleanseErrorData,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user