|
@@ -58,16 +58,8 @@ export class SystemExecutors {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
- private ensureFilePermissions = async (rootFolderHost: string, logSudoRequest = true) => {
|
|
|
|
|
|
+ private ensureFilePermissions = async (rootFolderHost: string) => {
|
|
const logger = new TerminalSpinner('');
|
|
const logger = new TerminalSpinner('');
|
|
- // if we are running as root, we don't need to change permissions
|
|
|
|
- if (process.getuid && process.getuid() === 0) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (logSudoRequest) {
|
|
|
|
- logger.log('Tipi needs to change permissions on some files and folders and will ask for your password.');
|
|
|
|
- }
|
|
|
|
|
|
|
|
// Create group tipi if it does not exist
|
|
// Create group tipi if it does not exist
|
|
try {
|
|
try {
|
|
@@ -106,11 +98,25 @@ export class SystemExecutors {
|
|
path.join(rootFolderHost, 'VERSION'),
|
|
path.join(rootFolderHost, 'VERSION'),
|
|
];
|
|
];
|
|
|
|
|
|
|
|
+ const files600 = [path.join(rootFolderHost, 'traefik', 'acme.json')];
|
|
|
|
+
|
|
// Give permission to read and write to all files and folders for the current user
|
|
// Give permission to read and write to all files and folders for the current user
|
|
await Promise.all(
|
|
await Promise.all(
|
|
filesAndFolders.map(async (fileOrFolder) => {
|
|
filesAndFolders.map(async (fileOrFolder) => {
|
|
if (await pathExists(fileOrFolder)) {
|
|
if (await pathExists(fileOrFolder)) {
|
|
- await execAsync(`sudo chmod -R a+rwx ${fileOrFolder}`);
|
|
|
|
|
|
+ await execAsync(`sudo chmod -R a+rwx ${fileOrFolder}`).catch(() => {
|
|
|
|
+ logger.fail(`Failed to set permissions on ${fileOrFolder}`);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }),
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ await Promise.all(
|
|
|
|
+ files600.map(async (fileOrFolder) => {
|
|
|
|
+ if (await pathExists(fileOrFolder)) {
|
|
|
|
+ await execAsync(`sudo chmod 600 ${fileOrFolder}`).catch(() => {
|
|
|
|
+ logger.fail(`Failed to set permissions on ${fileOrFolder}`);
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}),
|
|
}),
|
|
);
|
|
);
|
|
@@ -149,15 +155,6 @@ export class SystemExecutors {
|
|
await appExecutor.stopApp(app, {}, true);
|
|
await appExecutor.stopApp(app, {}, true);
|
|
spinner.done(`${app} stopped`);
|
|
spinner.done(`${app} stopped`);
|
|
}
|
|
}
|
|
-
|
|
|
|
- await Promise.all(
|
|
|
|
- apps.map(async (app) => {
|
|
|
|
- const appSpinner = new TerminalSpinner(`Stopping ${app}...`);
|
|
|
|
- appSpinner.start();
|
|
|
|
- await appExecutor.stopApp(app, {}, true);
|
|
|
|
- appSpinner.done(`${app} stopped`);
|
|
|
|
- }),
|
|
|
|
- );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
spinner.setMessage('Stopping containers...');
|
|
spinner.setMessage('Stopping containers...');
|
|
@@ -177,17 +174,37 @@ export class SystemExecutors {
|
|
* This method will start Tipi.
|
|
* This method will start Tipi.
|
|
* It will copy the system files, generate the system env file, pull the images and start the containers.
|
|
* It will copy the system files, generate the system env file, pull the images and start the containers.
|
|
*/
|
|
*/
|
|
- public start = async () => {
|
|
|
|
|
|
+ public start = async (sudo = true) => {
|
|
const spinner = new TerminalSpinner('Starting Tipi...');
|
|
const spinner = new TerminalSpinner('Starting Tipi...');
|
|
try {
|
|
try {
|
|
const { isSudo } = getUserIds();
|
|
const { isSudo } = getUserIds();
|
|
|
|
|
|
- if (!isSudo) {
|
|
|
|
|
|
+ if (!sudo) {
|
|
|
|
+ console.log(
|
|
|
|
+ boxen(
|
|
|
|
+ "You are running in sudoless mode. While Tipi should work as expected, you'll probably run into permission issues and will have to manually fix them. We recommend running Tipi with sudo for beginners.",
|
|
|
|
+ {
|
|
|
|
+ title: '⛔️Sudoless mode',
|
|
|
|
+ titleAlignment: 'center',
|
|
|
|
+ textAlignment: 'center',
|
|
|
|
+ padding: 1,
|
|
|
|
+ borderStyle: 'double',
|
|
|
|
+ borderColor: 'red',
|
|
|
|
+ margin: { top: 1, bottom: 1 },
|
|
|
|
+ width: 80,
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!isSudo && sudo) {
|
|
console.log(chalk.red('Tipi needs to run as root to start. Use sudo ./runtipi-cli start'));
|
|
console.log(chalk.red('Tipi needs to run as root to start. Use sudo ./runtipi-cli start'));
|
|
throw new Error('Tipi needs to run as root to start. Use sudo ./runtipi-cli start');
|
|
throw new Error('Tipi needs to run as root to start. Use sudo ./runtipi-cli start');
|
|
}
|
|
}
|
|
|
|
|
|
- await this.ensureFilePermissions(this.rootFolder);
|
|
|
|
|
|
+ if (sudo) {
|
|
|
|
+ await this.ensureFilePermissions(this.rootFolder);
|
|
|
|
+ }
|
|
|
|
|
|
spinner.start();
|
|
spinner.start();
|
|
spinner.setMessage('Copying system files...');
|
|
spinner.setMessage('Copying system files...');
|
|
@@ -195,7 +212,9 @@ export class SystemExecutors {
|
|
|
|
|
|
spinner.done('System files copied');
|
|
spinner.done('System files copied');
|
|
|
|
|
|
- await this.ensureFilePermissions(this.rootFolder, false);
|
|
|
|
|
|
+ if (sudo) {
|
|
|
|
+ await this.ensureFilePermissions(this.rootFolder);
|
|
|
|
+ }
|
|
|
|
|
|
spinner.setMessage('Generating system env file...');
|
|
spinner.setMessage('Generating system env file...');
|
|
spinner.start();
|
|
spinner.start();
|
|
@@ -270,13 +289,19 @@ export class SystemExecutors {
|
|
|
|
|
|
spinner.done('Database migrations complete');
|
|
spinner.done('Database migrations complete');
|
|
|
|
|
|
|
|
+ // Start all apps
|
|
|
|
+ const appExecutor = new AppExecutors();
|
|
|
|
+ await appExecutor.startAllApps();
|
|
|
|
+
|
|
console.log(
|
|
console.log(
|
|
boxen(`Visit: http://${envMap.get('INTERNAL_IP')}:${envMap.get('NGINX_PORT')} to access the dashboard\n\nFind documentation and guides at: https://runtipi.io`, {
|
|
boxen(`Visit: http://${envMap.get('INTERNAL_IP')}:${envMap.get('NGINX_PORT')} to access the dashboard\n\nFind documentation and guides at: https://runtipi.io`, {
|
|
title: 'Tipi successfully started 🎉',
|
|
title: 'Tipi successfully started 🎉',
|
|
titleAlignment: 'center',
|
|
titleAlignment: 'center',
|
|
|
|
+ textAlignment: 'center',
|
|
padding: 1,
|
|
padding: 1,
|
|
borderStyle: 'double',
|
|
borderStyle: 'double',
|
|
borderColor: 'green',
|
|
borderColor: 'green',
|
|
|
|
+ width: 80,
|
|
margin: { top: 1 },
|
|
margin: { top: 1 },
|
|
}),
|
|
}),
|
|
);
|
|
);
|
|
@@ -327,7 +352,7 @@ export class SystemExecutors {
|
|
|
|
|
|
if (!targetVersion || targetVersion === 'latest') {
|
|
if (!targetVersion || targetVersion === 'latest') {
|
|
spinner.setMessage('Fetching latest version...');
|
|
spinner.setMessage('Fetching latest version...');
|
|
- const { data } = await axios.get<{ tag_name: string }>('https://api.github.com/repos/meienberger/runtipi/releases');
|
|
|
|
|
|
+ const { data } = await axios.get<{ tag_name: string }>('https://api.github.com/repos/meienberger/runtipi/releases/latest');
|
|
targetVersion = data.tag_name;
|
|
targetVersion = data.tag_name;
|
|
}
|
|
}
|
|
|
|
|