|
@@ -15,6 +15,7 @@ import { TerminalSpinner } from '@/utils/logger/terminal-spinner';
|
|
import { pathExists } from '@/utils/fs-helpers';
|
|
import { pathExists } from '@/utils/fs-helpers';
|
|
import { getEnv } from '@/utils/environment/environment';
|
|
import { getEnv } from '@/utils/environment/environment';
|
|
import { fileLogger } from '@/utils/logger/file-logger';
|
|
import { fileLogger } from '@/utils/logger/file-logger';
|
|
|
|
+import { getUserIds } from '@/utils/environment/user';
|
|
|
|
|
|
const execAsync = promisify(exec);
|
|
const execAsync = promisify(exec);
|
|
|
|
|
|
@@ -74,13 +75,13 @@ export class SystemExecutors {
|
|
path.join(rootFolderHost, 'VERSION'),
|
|
path.join(rootFolderHost, 'VERSION'),
|
|
];
|
|
];
|
|
|
|
|
|
|
|
+ const { uid, gid } = getUserIds();
|
|
|
|
+
|
|
// 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)) {
|
|
- if (process.getgid && process.getuid) {
|
|
|
|
- await execAsync(`sudo chown -R ${process.getuid()}:${process.getgid()} ${fileOrFolder}`);
|
|
|
|
- }
|
|
|
|
|
|
+ await execAsync(`sudo chown -R ${uid}:${gid} ${fileOrFolder}`);
|
|
|
|
|
|
await execAsync(`sudo chmod -R 750 ${fileOrFolder}`);
|
|
await execAsync(`sudo chmod -R 750 ${fileOrFolder}`);
|
|
}
|
|
}
|
|
@@ -143,11 +144,22 @@ 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 (permissions = true) => {
|
|
|
|
|
|
+ public start = async (sudo = true) => {
|
|
const spinner = new TerminalSpinner('Starting Tipi...');
|
|
const spinner = new TerminalSpinner('Starting Tipi...');
|
|
try {
|
|
try {
|
|
- if (permissions) {
|
|
|
|
|
|
+ if (sudo) {
|
|
await this.ensureFilePermissions(this.rootFolder);
|
|
await this.ensureFilePermissions(this.rootFolder);
|
|
|
|
+ } else {
|
|
|
|
+ console.log(
|
|
|
|
+ boxen("You are running in sudoless mode. While tipi should work as expected, you'll probably face folder permission issues with the apps you install and you'll need to manually fix them.", {
|
|
|
|
+ title: '⛔️ Sudoless mode',
|
|
|
|
+ titleAlignment: 'center',
|
|
|
|
+ padding: 1,
|
|
|
|
+ borderStyle: 'double',
|
|
|
|
+ borderColor: 'red',
|
|
|
|
+ margin: { top: 1 },
|
|
|
|
+ }),
|
|
|
|
+ );
|
|
}
|
|
}
|
|
|
|
|
|
spinner.start();
|
|
spinner.start();
|
|
@@ -156,7 +168,7 @@ export class SystemExecutors {
|
|
|
|
|
|
spinner.done('System files copied');
|
|
spinner.done('System files copied');
|
|
|
|
|
|
- if (permissions) {
|
|
|
|
|
|
+ if (sudo) {
|
|
await this.ensureFilePermissions(this.rootFolder, false);
|
|
await this.ensureFilePermissions(this.rootFolder, false);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -200,8 +212,16 @@ export class SystemExecutors {
|
|
const out = fs.openSync('./logs/watcher.log', 'a');
|
|
const out = fs.openSync('./logs/watcher.log', 'a');
|
|
const err = fs.openSync('./logs/watcher.log', 'a');
|
|
const err = fs.openSync('./logs/watcher.log', 'a');
|
|
|
|
|
|
- const subprocess = spawn('./runtipi-cli', [process.argv[1] as string, 'watch'], { cwd: this.rootFolder, detached: true, stdio: ['ignore', out, err] });
|
|
|
|
- subprocess.unref();
|
|
|
|
|
|
+ if (sudo) {
|
|
|
|
+ // Dummy sudo call to ask for password
|
|
|
|
+ await execAsync('sudo echo "Dummy sudo call"');
|
|
|
|
+ const subprocess = spawn('sudo', ['./runtipi-cli', 'watch'], { cwd: this.rootFolder, stdio: ['inherit', out, err] });
|
|
|
|
+
|
|
|
|
+ subprocess.unref();
|
|
|
|
+ } else {
|
|
|
|
+ const subprocess = spawn('./runtipi-cli', [process.argv[1] as string, 'watch'], { cwd: this.rootFolder, detached: true, stdio: ['ignore', out, err] });
|
|
|
|
+ subprocess.unref();
|
|
|
|
+ }
|
|
|
|
|
|
spinner.done('Watcher started');
|
|
spinner.done('Watcher started');
|
|
|
|
|
|
@@ -254,7 +274,7 @@ export class SystemExecutors {
|
|
* runtipi-cli binary with the new one.
|
|
* runtipi-cli binary with the new one.
|
|
* @param {string} target
|
|
* @param {string} target
|
|
*/
|
|
*/
|
|
- public update = async (target: string) => {
|
|
|
|
|
|
+ public update = async (target: string, sudo = true) => {
|
|
const spinner = new TerminalSpinner('Evaluating target version...');
|
|
const spinner = new TerminalSpinner('Evaluating target version...');
|
|
try {
|
|
try {
|
|
spinner.start();
|
|
spinner.start();
|
|
@@ -344,7 +364,15 @@ export class SystemExecutors {
|
|
// eslint-disable-next-line no-promise-executor-return
|
|
// eslint-disable-next-line no-promise-executor-return
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
|
|
|
|
- const childProcess = spawn('./runtipi-cli', [process.argv[1] as string, 'start', '--no-permissions']);
|
|
|
|
|
|
+ const args = [process.argv[1] as string, 'start'];
|
|
|
|
+
|
|
|
|
+ const { isSudo } = getUserIds();
|
|
|
|
+
|
|
|
|
+ if (!sudo && !isSudo) {
|
|
|
|
+ args.push('--no-sudo');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const childProcess = spawn('./runtipi-cli', args);
|
|
|
|
|
|
childProcess.stdout.on('data', (data) => {
|
|
childProcess.stdout.on('data', (data) => {
|
|
process.stdout.write(data);
|
|
process.stdout.write(data);
|