Compare commits
5 Commits
main
...
setups-dra
| Author | SHA1 | Date | |
|---|---|---|---|
| 497bbb6324 | |||
| 385eee64cb | |||
| b1ee30dd80 | |||
| efccada19b | |||
| ad1fefc02a |
@ -8,13 +8,7 @@ This repository is structured into several key directories:
|
||||
|
||||
- **scripts/**: Contains individual scripts for various tasks. Currently, it includes:
|
||||
|
||||
- `scripts/library/`: Libraries used by Python scripts.
|
||||
- `venv_utils.py`: Utility functions for creating, activating, and managing Python virtual environments.
|
||||
- `change_case.py`: A script for renaming files and directories by changing their case.
|
||||
- `video_manage_audio.py`: A script for removing audio from video files.
|
||||
- `video_manage_subtitles.py`: A script for removing subtitles from video files.
|
||||
- `video_autoreduce.py`: A script for automatic resolution reduction of video files.
|
||||
- `video_autoreduce_rename.py`: A script for automated renaming of video files post resolution reduction.
|
||||
- `video_remove_audio.py`: A script for removing audio from video files.
|
||||
|
||||
- **notes/**: A collection of markdown files containing notes on various topics, including:
|
||||
|
||||
@ -28,7 +22,6 @@ This repository is structured into several key directories:
|
||||
- `pdftk.md`: PDF Toolkit usage.
|
||||
- `pip packaging.md`: Packaging Python projects with pip.
|
||||
- `ssh.md`: Secure Shell (SSH) configuration and tips.
|
||||
- `wordpress.md`: WordPress debugging and tips.
|
||||
|
||||
- **pages/other/**: Templates for other pages, such as the homepage of my Debian package repository. These are provided as inspiration and should not be used as-is.
|
||||
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
# WordPress
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [WordPress](#wordpress)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Documentation](#documentation)
|
||||
- [Common WordPress Errors](#common-wordpress-errors)
|
||||
- [Debugging Steps](#debugging-steps)
|
||||
- [Debugging Tools](#debugging-tools)
|
||||
|
||||
## Documentation
|
||||
|
||||
- [wp-cli](https://developer.wordpress.org/cli/commands/)
|
||||
- [Documentation Overview](https://www.wordpress.info/doc/overview/)
|
||||
- [Tutorials](https://wordpress.com/learn/)
|
||||
|
||||
## Common WordPress Errors
|
||||
|
||||
1. **HTTP Errors**
|
||||
|
||||
- **404 Not Found**: Page or resource missing.
|
||||
- **403 Forbidden**: Insufficient permissions to access the resource.
|
||||
- **500 Internal Server Error**: Generic error, often caused by server misconfiguration or PHP errors.
|
||||
- **503 Service Unavailable**: Server is overloaded or in maintenance mode.
|
||||
|
||||
2. **Database Errors**
|
||||
|
||||
- **Error Establishing a Database Connection**: Database credentials incorrect or database server is down.
|
||||
- **Table Prefix Issues**: Wrong `$table_prefix` in `wp-config.php`.
|
||||
- **Corrupt Database**: Can be fixed using `wp db repair`.
|
||||
|
||||
3. **PHP Errors**
|
||||
|
||||
- **Parse Error**: Syntax error in PHP files.
|
||||
- **Fatal Error**: Missing function or class.
|
||||
- **Deprecated Function Warnings**: Old functions being used.
|
||||
|
||||
## Debugging Steps
|
||||
|
||||
1. **Enable Debug Mode**
|
||||
|
||||
Edit `wp-config.php`:
|
||||
|
||||
```php
|
||||
// Enable Debug Mode
|
||||
define('WP_DEBUG', true);
|
||||
// Enable Debug logging to the /wp-content/debug.log file
|
||||
define('WP_DEBUG_LOG', true);
|
||||
// Disable display of WordPress errors and warnings
|
||||
define('WP_DEBUG_DISPLAY', false);
|
||||
// Disable display of PHP errors and warnings
|
||||
@ini_set('display_errors', 0);
|
||||
```
|
||||
|
||||
Logs will be saved in `wp-content/debug.log`.
|
||||
|
||||
2. **Check `.htaccess`**
|
||||
|
||||
Ensure WordPress rules exist:
|
||||
|
||||
```apache
|
||||
# BEGIN WordPress
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
|
||||
# END WordPress
|
||||
```
|
||||
|
||||
3. **Check Apache and VirtualHost config**
|
||||
|
||||
**Redhat and derivatives**
|
||||
|
||||
- `/etc/httpd/conf/httpd.conf`
|
||||
- `/etc/httpd/conf.d/welcome.conf`
|
||||
|
||||
**Debian and derivatives**
|
||||
|
||||
- `/etc/apache2/apache2.conf`
|
||||
- `/etc/apache2/sites-available/000-default.conf`
|
||||
|
||||
```apache
|
||||
<Directory "/var/www">
|
||||
AllowOverride All
|
||||
# Allow open access:
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory "/var/www/html">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
```
|
||||
|
||||
4. **Reset File Permissions**
|
||||
|
||||
```bash
|
||||
find /var/www/html -type d -exec chmod 755 {} \;
|
||||
find /var/www/html -type f -exec chmod 644 {} \;
|
||||
```
|
||||
|
||||
4. **Disable Plugins & Themes**
|
||||
|
||||
Hide the plugin directory:
|
||||
|
||||
```bash
|
||||
mv wp-content/plugins wp-content/.plugins
|
||||
```
|
||||
|
||||
Switch to a default theme:
|
||||
|
||||
```bash
|
||||
wp theme install twentytwentyfive --activate
|
||||
wp theme activate twentytwentyfive
|
||||
```
|
||||
|
||||
5. **Check Server Logs**
|
||||
|
||||
For Apache:
|
||||
|
||||
```bash
|
||||
tail -f /var/log/httpd/error_log
|
||||
```
|
||||
|
||||
For Nginx:
|
||||
|
||||
```bash
|
||||
tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
6. **Verify Database Connection**
|
||||
|
||||
```bash
|
||||
wp db check
|
||||
```
|
||||
|
||||
If corrupt:
|
||||
|
||||
```bash
|
||||
wp db repair
|
||||
```
|
||||
|
||||
7. **Increase Memory Limit**
|
||||
|
||||
Edit `wp-config.php`:
|
||||
|
||||
```php
|
||||
define('WP_MEMORY_LIMIT', '256M');
|
||||
```
|
||||
|
||||
## Debugging Tools
|
||||
|
||||
- **WP-CLI**: Command-line interface for WordPress.
|
||||
- **Query Monitor**: Plugin for analyzing database queries and errors.
|
||||
- **Health Check & Troubleshooting**: Plugin for diagnosing issues.
|
||||
@ -1 +0,0 @@
|
||||
argcomplete
|
||||
@ -1,125 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# === Standard library ===
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
# Allow importing from scripts/library even when run directly
|
||||
project_root = str(Path(__file__).resolve().parent.parent)
|
||||
if project_root not in sys.path:
|
||||
sys.path.append(project_root)
|
||||
|
||||
# === Local import ===
|
||||
from scripts.library import parse_verbose, run_in_venv
|
||||
|
||||
|
||||
def rename_by_case(root_dir: str, case: str, recursive: bool = False) -> None:
|
||||
"""
|
||||
Renames all files and directories in a given directory by changing their case.
|
||||
|
||||
Args:
|
||||
root_dir (str): Root path to start renaming.
|
||||
case (str): One of "lower", "upper", "capitalize".
|
||||
recursive (bool): Whether to apply renaming recursively.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
def apply_case(name: str) -> str:
|
||||
if case == "lower":
|
||||
return name.lower()
|
||||
elif case == "upper":
|
||||
return name.upper()
|
||||
elif case == "capitalize":
|
||||
return name.capitalize()
|
||||
else:
|
||||
raise ValueError(f"Unsupported case transformation: {case}")
|
||||
|
||||
for current_dir, dirs, files in os.walk(root_dir, topdown=False):
|
||||
# Skip descending into subdirectories if not recursive
|
||||
if not recursive and current_dir != root_dir:
|
||||
continue
|
||||
|
||||
# Rename files
|
||||
for name in files:
|
||||
old_path = os.path.join(current_dir, name)
|
||||
new_name = apply_case(name)
|
||||
new_path = os.path.join(current_dir, new_name)
|
||||
if old_path != new_path:
|
||||
if not os.path.exists(new_path):
|
||||
os.rename(old_path, new_path)
|
||||
else:
|
||||
print(
|
||||
f"Cannot rename {old_path} to {new_path}: target already exists."
|
||||
)
|
||||
|
||||
# Rename directories
|
||||
for name in dirs:
|
||||
old_path = os.path.join(current_dir, name)
|
||||
new_name = apply_case(name)
|
||||
new_path = os.path.join(current_dir, new_name)
|
||||
if old_path != new_path:
|
||||
if not os.path.exists(new_path):
|
||||
os.rename(old_path, new_path)
|
||||
else:
|
||||
print(
|
||||
f"Cannot rename {old_path} to {new_path}: target already exists."
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> Namespace:
|
||||
"""
|
||||
Parse and validate command-line arguments.
|
||||
|
||||
Returns:
|
||||
Namespace: Parsed arguments with 'case' and 'recursive' options.
|
||||
"""
|
||||
import argcomplete
|
||||
|
||||
parser = ArgumentParser(
|
||||
description="Rename files and directories by changing case (lower, upper, capitalize)."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--case",
|
||||
"-c",
|
||||
type=str,
|
||||
choices=["lower", "upper", "capitalize"],
|
||||
default="lower",
|
||||
help="Case transformation to apply: 'lower', 'upper', or 'capitalize'. Default is 'lower'.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--recursive",
|
||||
"-r",
|
||||
action="store_true",
|
||||
help="Apply the transformation recursively in all subdirectories.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="Enable verbose output (debug information, warnings)",
|
||||
)
|
||||
|
||||
argcomplete.autocomplete(parser)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Main entry point. Parses arguments and runs case renaming in current working directory.
|
||||
"""
|
||||
args = parse_args()
|
||||
cwd: str = os.getcwd()
|
||||
rename_by_case(cwd, case=args.case, recursive=args.recursive)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_verbose()
|
||||
run_in_venv(main, verbose=args.verbose)
|
||||
@ -1,5 +0,0 @@
|
||||
# scripts/library/__init__.py
|
||||
|
||||
from .venv_utils import parse_verbose, run_in_venv
|
||||
|
||||
__all__ = ["parse_verbose", "run_in_venv"]
|
||||
@ -1,149 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# === Standard library ===
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import venv
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
# === Configuration ===
|
||||
# Define the virtual environment directory in the user's home folder
|
||||
VENV_DIR: Path = Path.home() / ".scripts_fabq_venv"
|
||||
|
||||
# Define the path to the requirements.txt file in the project root
|
||||
REQUIREMENTS_FILE: Path = Path(__file__).resolve().parents[2] / "requirements.txt"
|
||||
|
||||
|
||||
def parse_verbose() -> Namespace:
|
||||
"""
|
||||
Parse the --verbose flag before virtual environment setup.
|
||||
|
||||
This is useful when argument parsing requires packages that may
|
||||
only be available after the virtual environment is initialized.
|
||||
|
||||
Returns:
|
||||
Namespace: Contains a single attribute 'verbose' (bool).
|
||||
"""
|
||||
parser = ArgumentParser(add_help=False)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="count",
|
||||
default=0,
|
||||
help="Increase verbosity: -v = verbose, -vv = debug, -vvv = trace",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
dest="verbose",
|
||||
action="store_const",
|
||||
const=2,
|
||||
help="Enable debug output (equivalent to -vv)",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--trace",
|
||||
dest="verbose",
|
||||
action="store_const",
|
||||
const=3,
|
||||
help="Enable full trace output (equivalent to -vvv)",
|
||||
)
|
||||
|
||||
return parser.parse_known_args()[0]
|
||||
|
||||
|
||||
def is_in_venv() -> bool:
|
||||
"""
|
||||
Determine whether the script is currently running inside a virtual environment.
|
||||
|
||||
Returns:
|
||||
bool: True if running inside a virtual environment, False otherwise.
|
||||
"""
|
||||
return hasattr(sys, "real_prefix") or sys.prefix != getattr(
|
||||
sys, "base_prefix", sys.prefix
|
||||
)
|
||||
|
||||
|
||||
def create_venv(verbose: bool = False) -> None:
|
||||
"""
|
||||
Create a virtual environment in the predefined VENV_DIR location.
|
||||
|
||||
Args:
|
||||
verbose (bool): If True, prints progress messages.
|
||||
"""
|
||||
if verbose >= 2:
|
||||
print(f"Creating virtual environment in {VENV_DIR}...")
|
||||
venv.create(VENV_DIR, with_pip=True)
|
||||
|
||||
|
||||
def install_requirements(verbose: bool = False) -> None:
|
||||
"""
|
||||
Install Python dependencies from requirements.txt into the venv.
|
||||
|
||||
Args:
|
||||
verbose (bool): If True, prints progress and pip output.
|
||||
"""
|
||||
if not REQUIREMENTS_FILE.exists():
|
||||
print(f"Error: requirements.txt not found at {REQUIREMENTS_FILE}")
|
||||
sys.exit(1)
|
||||
|
||||
if verbose >= 2:
|
||||
print(f"Installing/updating requirements from {REQUIREMENTS_FILE}...")
|
||||
|
||||
pip_cmd = [str(VENV_DIR / "bin" / "pip")]
|
||||
|
||||
try:
|
||||
subprocess.check_call(
|
||||
pip_cmd + ["install", "-r", str(REQUIREMENTS_FILE)],
|
||||
stdout=None if verbose >= 2 else subprocess.DEVNULL,
|
||||
stderr=None if verbose >= 2 else subprocess.DEVNULL,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: Failed to install requirements: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
subprocess.check_call(
|
||||
pip_cmd + ["check"],
|
||||
stdout=None if verbose >= 2 else subprocess.DEVNULL,
|
||||
stderr=None if verbose >= 2 else subprocess.DEVNULL,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: Dependency conflict detected — your environment may be broken.")
|
||||
raise
|
||||
|
||||
|
||||
def run_in_venv(main_func: Callable[[], None], verbose: bool = False) -> None:
|
||||
"""
|
||||
Ensure the script is running inside the expected virtual environment.
|
||||
|
||||
If not already in the venv:
|
||||
- Create it if needed
|
||||
- Install requirements
|
||||
- Relaunch the script inside the venv
|
||||
|
||||
If already in the venv:
|
||||
- Ensure requirements are installed
|
||||
- Execute the given main function
|
||||
|
||||
Args:
|
||||
main_func (Callable[[], None]): The function to execute after setup.
|
||||
verbose (bool): If True, enables progress messages and pip output.
|
||||
"""
|
||||
if not is_in_venv():
|
||||
if not VENV_DIR.exists():
|
||||
create_venv(verbose)
|
||||
install_requirements(verbose)
|
||||
|
||||
# Relaunch the script inside the virtual environment
|
||||
os.execv(
|
||||
str(VENV_DIR / "bin" / "python"),
|
||||
[str(VENV_DIR / "bin" / "python")] + sys.argv,
|
||||
)
|
||||
else:
|
||||
install_requirements(verbose)
|
||||
main_func()
|
||||
@ -1,493 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# video_autoreduce.py
|
||||
|
||||
import argparse
|
||||
import argcomplete
|
||||
import colorama
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
|
||||
colorama.init()
|
||||
|
||||
creset = colorama.Fore.RESET
|
||||
ccyan = colorama.Fore.CYAN
|
||||
cyellow = colorama.Fore.YELLOW
|
||||
cgreen = colorama.Fore.GREEN
|
||||
cred = colorama.Fore.RED
|
||||
|
||||
|
||||
def get_ffmpeg_codecs(codec_type="S"):
|
||||
"""
|
||||
Retrieve a list of supported codecs for a specific type from ffmpeg.
|
||||
|
||||
This function runs the `ffmpeg -codecs` command and parses its output to extract
|
||||
the list of supported codecs based on the provided `codec_type` (e.g., 'S' for subtitles,
|
||||
'V' for video, 'A' for audio). The list of codecs is returned as a set for efficient lookup.
|
||||
|
||||
Args:
|
||||
codec_type (str): The type of codecs to retrieve.
|
||||
- "S" for subtitles (default)
|
||||
- "V" for video
|
||||
- "A" for audio
|
||||
- "D" for data
|
||||
- "T" for attachment
|
||||
|
||||
Returns:
|
||||
set: A set of codec names of the specified type.
|
||||
If an error occurs or no codecs are found, an empty set is returned.
|
||||
"""
|
||||
try:
|
||||
# Run ffmpeg -codecs
|
||||
result = subprocess.run(["ffmpeg", "-codecs"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True)
|
||||
output = result.stdout
|
||||
|
||||
# Extract codecs using regex
|
||||
codec_pattern = re.compile(rf"^[ D.E]+{codec_type}[ A-Z.]+ (\S+)",
|
||||
re.MULTILINE)
|
||||
codecs = codec_pattern.findall(output)
|
||||
|
||||
return set(codecs) # Return as a set for easy lookup
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("Error running ffmpeg:", e)
|
||||
return set()
|
||||
|
||||
|
||||
def findfreename(filepath, attempt=0):
|
||||
'''
|
||||
Given a filepath, it will try to find a free filename by appending a number to the name.
|
||||
First, it tries using the filepath passed in the argument, then adds a number to the end. If all fail, it appends (#).
|
||||
|
||||
Args:
|
||||
filepath (str): A string containing the full filepath.
|
||||
attempt (int): The number of attempts already made to find a free filename.
|
||||
|
||||
Returns:
|
||||
str: The first free filepath found.
|
||||
'''
|
||||
attempt += 1
|
||||
filename = str(filepath)[:str(filepath).rindex(".")]
|
||||
extension = str(filepath)[str(filepath).rindex("."):]
|
||||
copynumpath = filename + f"({attempt})" + extension
|
||||
|
||||
if not os.path.exists(filepath) and attempt <= 2:
|
||||
return filepath
|
||||
elif not os.path.exists(copynumpath):
|
||||
return copynumpath
|
||||
return findfreename(filepath, attempt)
|
||||
|
||||
|
||||
def deletefile(filepath):
|
||||
'''Delete a file, Returns a boolean
|
||||
|
||||
Args:
|
||||
filepath : A string containing the full filepath
|
||||
Returns:
|
||||
Bool : The success of the operation
|
||||
'''
|
||||
try:
|
||||
os.remove(filepath)
|
||||
except OSError:
|
||||
print(f"{cred}Error deleting {filepath}{creset}")
|
||||
return False
|
||||
|
||||
print(f"{cgreen}Successfully deleted {filepath}{creset}")
|
||||
return True
|
||||
|
||||
|
||||
def ensure_output_path(output_path):
|
||||
'''
|
||||
Ensure that the output directory exists. If it doesn't, attempt to create it.
|
||||
|
||||
Args:
|
||||
output_path (str): The path of the output directory to be ensured.
|
||||
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
try:
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
# Attempts to create the output directory if it doesn't exist.
|
||||
# exist_ok=True ensures that an OSError is not raised if the directory already exists.
|
||||
except OSError as e:
|
||||
# If an OSError occurs during directory creation, print an error message and exit the program.
|
||||
print(f"{cred}Failed to create output directory:{creset} {e}",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def has_supported_subs(video_file, supported_subtitle_codecs, debug=False):
|
||||
"""
|
||||
Check if the given video file contains subtitles in a supported codec.
|
||||
|
||||
This function uses `ffprobe` to extract subtitle stream information from the
|
||||
video file and checks whether any subtitle stream has a codec that matches
|
||||
one of the supported codecs provided in the `supported_subtitle_codecs` list.
|
||||
|
||||
Args:
|
||||
video_file (str): Path to the video file to be checked.
|
||||
supported_subtitle_codecs (set): Set of subtitle codec names that are considered supported.
|
||||
debug (bool): If True, prints debug information for errors. Defaults to False.
|
||||
|
||||
Returns:
|
||||
bool: True if at least one subtitle stream is found with a supported codec,
|
||||
False otherwise or in case of an error.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run([
|
||||
'ffprobe', '-v', 'error', '-select_streams', 's', '-show_entries',
|
||||
'stream=index,codec_name', '-of', 'json', video_file
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
if debug:
|
||||
print(f"ffprobe error: {result.stderr.strip()}")
|
||||
return False # Assume no supported subtitles on failure
|
||||
|
||||
try:
|
||||
streams = json.loads(result.stdout).get("streams", [])
|
||||
except json.JSONDecodeError as e:
|
||||
if debug:
|
||||
print(f"JSON parsing error: {e}")
|
||||
return False # Assume no supported subtitles if JSON is malformed
|
||||
|
||||
return any(
|
||||
stream.get("codec_name") in supported_subtitle_codecs
|
||||
for stream in streams)
|
||||
|
||||
except Exception as e:
|
||||
if debug:
|
||||
print(f"Unexpected error: {e}")
|
||||
return False # Assume no supported subtitles
|
||||
|
||||
|
||||
def find_videos_to_convert(input_path, max_height=720, debug=False):
|
||||
'''
|
||||
Find video files in the specified directory tree that meet conversion criteria.
|
||||
|
||||
Args:
|
||||
input_path (str): The path of the directory to search for video files.
|
||||
max_height (int, optional): The maximum height (in pixels) of the video to consider for conversion. Default is 720.
|
||||
debug (bool, optional): If True, print debug messages. Default is False.
|
||||
|
||||
Returns:
|
||||
list: A list of paths to video files that meet the conversion criteria.
|
||||
'''
|
||||
# Print a message indicating the start of the scanning process
|
||||
print(
|
||||
f"{cgreen}Scanning {input_path} for video files to convert.{creset}\n")
|
||||
|
||||
# Initialize lists to store all video files and valid video files
|
||||
video_files = []
|
||||
valid_videos_files = []
|
||||
|
||||
# Find all video files in the directory tree
|
||||
for root, dirs, files in os.walk(input_path):
|
||||
for file in files:
|
||||
fullpath = os.path.join(root, file)
|
||||
# Check if the file is a video file with one of the specified extensions
|
||||
if (file.lower().endswith(
|
||||
('.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.divx'))
|
||||
and os.path.isfile(fullpath)):
|
||||
video_files.append(fullpath)
|
||||
|
||||
# List of encodings to try for decoding output from ffprobe
|
||||
ENCODINGS_TO_TRY = ['utf-8', 'latin-1', 'iso-8859-1']
|
||||
|
||||
# Iterate through each video file
|
||||
for video_file in video_files:
|
||||
try:
|
||||
# Run ffprobe command to get video resolution
|
||||
resolution_output = subprocess.check_output(
|
||||
[
|
||||
'ffprobe', '-v', 'error', '-select_streams', 'v:0',
|
||||
'-show_entries', 'stream=width,height', '-of', 'csv=p=0',
|
||||
video_file
|
||||
],
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Attempt to decode the output using different encodings
|
||||
dimensions_str = None
|
||||
for encoding in ENCODINGS_TO_TRY:
|
||||
try:
|
||||
dimensions_str = resolution_output.decode(
|
||||
encoding).strip().rstrip(',')
|
||||
|
||||
dimensions = [
|
||||
int(d) for d in dimensions_str.split(',')
|
||||
if d.strip().isdigit()
|
||||
]
|
||||
if len(dimensions) >= 2:
|
||||
width, height = dimensions[:2]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unexpected dimensions format: {dimensions_str}")
|
||||
break # Stop trying encodings once successful decoding and conversion
|
||||
except (UnicodeDecodeError, ValueError):
|
||||
continue # Try the next encoding
|
||||
|
||||
# If decoding was unsuccessful with all encodings, skip this video
|
||||
if dimensions_str is None:
|
||||
if debug:
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: Unable to decode output from ffprobe using any of the specified encodings.{creset}",
|
||||
file=sys.stderr)
|
||||
continue # Skip this video and continue with the next one
|
||||
|
||||
# Ignore vertical videos
|
||||
if height > width:
|
||||
continue # Skip this video and continue with the next one
|
||||
|
||||
# Check if the video height exceeds the maximum allowed height
|
||||
if height > max_height:
|
||||
valid_videos_files.append(video_file)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
if debug:
|
||||
# Print error message if subprocess call fails
|
||||
if e.output is not None:
|
||||
decoded_error = e.output.decode("utf-8")
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: {decoded_error}{creset}\n",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: No output from subprocess{creset}\n",
|
||||
file=sys.stderr)
|
||||
except ValueError as e:
|
||||
if debug:
|
||||
# Print error message if value error occurs during decoding
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: ValueError decoding the file{creset}\n",
|
||||
file=sys.stderr)
|
||||
|
||||
valid_videos_files.sort()
|
||||
|
||||
return valid_videos_files
|
||||
|
||||
|
||||
def convert_videos(input_path, output_path, max_height=720, debug=False):
|
||||
'''
|
||||
Convert videos taller than a specified height to 720p resolution with x265 encoding and MKV container.
|
||||
|
||||
Args:
|
||||
input_path (str): The path of the directory containing input video files.
|
||||
output_path (str): The path of the directory where converted video files will be saved.
|
||||
max_height (int, optional): The maximum height (in pixels) of the video to consider for conversion. Default is 720.
|
||||
debug (bool, optional): If True, print debug messages. Default is False.
|
||||
|
||||
Returns:
|
||||
bool: True if conversion succeeds, False otherwise.
|
||||
'''
|
||||
|
||||
# Get supported subtitle codecs
|
||||
supported_subtitle_codecs = get_ffmpeg_codecs("S")
|
||||
|
||||
# Find video files to convert in the input directory
|
||||
video_files = find_videos_to_convert(input_path, max_height, debug)
|
||||
counter = 0
|
||||
|
||||
# If no video files found, print a message and return False
|
||||
if len(video_files) == 0:
|
||||
print(f"No video files to convert found.\n")
|
||||
return False
|
||||
|
||||
# Ensure the output directory exists
|
||||
ensure_output_path(output_path)
|
||||
|
||||
# Print a message indicating the start of the conversion process
|
||||
print(
|
||||
f"Converting {len(video_files)} videos taller than {max_height} pixels to 720p resolution with x265 encoding and MKV container...\n"
|
||||
)
|
||||
|
||||
# Variable to keep a track of the current_file in case of failure
|
||||
current_file = None
|
||||
|
||||
# Iterate through each video file for conversion
|
||||
for video_file in video_files:
|
||||
counter += 1
|
||||
|
||||
# Print conversion progress information
|
||||
print(
|
||||
f"{cgreen}****** Starting conversion {counter} of {len(video_files)}: '{os.path.basename(video_file)}'...{creset}"
|
||||
)
|
||||
print(f"{ccyan}Original file:{creset}")
|
||||
print(video_file)
|
||||
|
||||
# Generate output file path and name
|
||||
output_file = findfreename(
|
||||
os.path.join(
|
||||
output_path,
|
||||
os.path.splitext(os.path.basename(video_file))[0] + '.mkv'))
|
||||
|
||||
try:
|
||||
# Get original video width and height
|
||||
video_info = subprocess.run([
|
||||
'ffprobe', '-v', 'error', '-select_streams', 'v:0',
|
||||
'-show_entries', 'stream=width,height', '-of', 'csv=p=0',
|
||||
video_file
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
width, height = map(
|
||||
int,
|
||||
video_info.stdout.strip().rstrip(',').split(','))
|
||||
|
||||
# Calculate the scaled width maintaining aspect ratio
|
||||
scaled_width = int(max_height * (width / height))
|
||||
# Ensure the width is even
|
||||
if scaled_width % 2 != 0:
|
||||
scaled_width += 1
|
||||
|
||||
# Keep a track of the current_file in case of failure
|
||||
current_file = output_file
|
||||
|
||||
# Check if the video has supported subtitles
|
||||
include_subs = has_supported_subs(video_file,
|
||||
supported_subtitle_codecs, debug)
|
||||
|
||||
# Construct the FFmpeg command dynamically
|
||||
ffmpeg_command = [
|
||||
'ffmpeg', '-n', '-i', video_file, '-map', '0:v', '-c:v',
|
||||
'libx265', '-vf', f'scale={scaled_width}:{max_height}',
|
||||
'-preset', 'medium', '-crf', '28', '-map', '0:a?', '-c:a',
|
||||
'copy'
|
||||
]
|
||||
|
||||
# Only include subtitles if they are supported
|
||||
if include_subs:
|
||||
ffmpeg_command.extend(['-map', '0:s?', '-c:s', 'copy'])
|
||||
|
||||
ffmpeg_command.append(output_file)
|
||||
|
||||
# Print the FFmpeg command as a single string
|
||||
if (debug):
|
||||
print("FFmpeg command: " + ' '.join(ffmpeg_command))
|
||||
|
||||
# Run the FFmpeg command
|
||||
process = subprocess.run(ffmpeg_command,
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Check if ffmpeg process returned successfully
|
||||
if process.returncode != 0:
|
||||
raise subprocess.CalledProcessError(process.returncode,
|
||||
process.args,
|
||||
output=process.stderr)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
# If we have a partial video converted, delete it due to the failure
|
||||
if current_file:
|
||||
deletefile(current_file)
|
||||
|
||||
# Handle subprocess errors
|
||||
if debug:
|
||||
if e.output is not None:
|
||||
decoded_error = e.output.decode("utf-8")
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: {decoded_error}{creset}\n",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: No output from subprocess{creset}\n",
|
||||
file=sys.stderr)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"{cyellow}Conversions cancelled, cleaning up...{creset}")
|
||||
|
||||
# If we have a partial video converted, delete it due to the failure
|
||||
if current_file:
|
||||
deletefile(current_file)
|
||||
current_file = None
|
||||
|
||||
exit()
|
||||
|
||||
except Exception as e:
|
||||
# If we have a partial video converted, delete it due to the failure
|
||||
if current_file:
|
||||
deletefile(current_file)
|
||||
|
||||
# Handle other exceptions
|
||||
if debug:
|
||||
print(
|
||||
f"{cred}Error processing {video_file}: {str(e)}{creset}\n",
|
||||
file=sys.stderr)
|
||||
|
||||
else:
|
||||
|
||||
# Print success message if conversion is successful
|
||||
print(
|
||||
f"{ccyan}Successfully converted video to output file:{creset}")
|
||||
print(f"{output_file}\n")
|
||||
|
||||
try:
|
||||
os.chmod(output_file, 0o777)
|
||||
except PermissionError:
|
||||
print(f"{cred}PermissionError on: '{output_file}'{creset}")
|
||||
|
||||
# Remove the now succefully converted filepath
|
||||
current_file = None
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
'''
|
||||
Main function to parse command line arguments and initiate video conversion.
|
||||
'''
|
||||
|
||||
# Create argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert videos taller than a certain height to 720p resolution with x265 encoding and MKV container.'
|
||||
)
|
||||
|
||||
# Define command line arguments
|
||||
parser.add_argument(
|
||||
'input_path',
|
||||
nargs='?',
|
||||
default=os.getcwd(),
|
||||
help=
|
||||
'directory path to search for video files (default: current directory)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-o',
|
||||
'--output-path',
|
||||
default=os.path.join(os.getcwd(), 'converted'),
|
||||
help='directory path to save converted videos (default: ./converted)')
|
||||
parser.add_argument(
|
||||
'-mh',
|
||||
'--max-height',
|
||||
type=int,
|
||||
default=720,
|
||||
help='maximum height of videos to be converted (default: 720)')
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
action='store_true',
|
||||
help='enable debug mode for printing additional error messages')
|
||||
|
||||
# Enable autocomplete for argparse
|
||||
argcomplete.autocomplete(parser)
|
||||
|
||||
# Parse command line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Convert videos
|
||||
convert_videos(args.input_path, args.output_path, args.max_height,
|
||||
args.debug)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Execute main function when the script is run directly
|
||||
main()
|
||||
@ -1,133 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# video_autoreduce_rename.py
|
||||
|
||||
import argparse
|
||||
import argcomplete
|
||||
import colorama
|
||||
import os
|
||||
import re
|
||||
|
||||
colorama.init()
|
||||
|
||||
creset = colorama.Fore.RESET
|
||||
ccyan = colorama.Fore.CYAN
|
||||
cyellow = colorama.Fore.YELLOW
|
||||
cgreen = colorama.Fore.GREEN
|
||||
cred = colorama.Fore.RED
|
||||
|
||||
|
||||
def autorename(input_path, max_height=720, debug=False):
|
||||
'''
|
||||
Rename video files and folders in the specified directory tree that contain a resolution
|
||||
in their name to the specified max_height resolution.
|
||||
|
||||
Args:
|
||||
input_path (str): The path of the directory to search for video files and folders.
|
||||
max_height (int, optional): The maximum height (in pixels) of the video to consider for conversion. Default is 720.
|
||||
debug (bool, optional): If True, print debug messages. Default is False.
|
||||
'''
|
||||
# Define patterns to search for various resolutions
|
||||
resolution_patterns = {
|
||||
2160: r'(4096x2160|3840x2160|2880p|2160p|2160|1440p|4k)',
|
||||
1080: r'(1920x1080|1080p|1080)',
|
||||
720: r'(1280x720|720p|720)',
|
||||
}
|
||||
|
||||
files_to_rename = []
|
||||
dirs_to_rename = []
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(input_path, topdown=True):
|
||||
# Collect files to rename
|
||||
for filename in filenames:
|
||||
for resolution, pattern in resolution_patterns.items():
|
||||
# Only process resolutions greater than max_height
|
||||
if resolution > max_height and re.search(
|
||||
pattern, filename, re.IGNORECASE):
|
||||
old_file = os.path.join(dirpath, filename)
|
||||
new_filename = re.sub(pattern,
|
||||
f'{max_height}p',
|
||||
filename,
|
||||
flags=re.IGNORECASE)
|
||||
new_file = os.path.join(dirpath, new_filename)
|
||||
if old_file != new_file:
|
||||
files_to_rename.append((old_file, new_file))
|
||||
break
|
||||
|
||||
# Collect directories to rename
|
||||
for dirname in dirnames:
|
||||
for resolution, pattern in resolution_patterns.items():
|
||||
# Only process resolutions greater than max_height
|
||||
if resolution > max_height and re.search(
|
||||
pattern, dirname, re.IGNORECASE):
|
||||
old_dir = os.path.join(dirpath, dirname)
|
||||
new_dirname = re.sub(pattern,
|
||||
f'{max_height}p',
|
||||
dirname,
|
||||
flags=re.IGNORECASE)
|
||||
new_dir = os.path.join(dirpath, new_dirname)
|
||||
if old_dir != new_dir:
|
||||
dirs_to_rename.append((old_dir, new_dir))
|
||||
break
|
||||
|
||||
# Rename files first
|
||||
for old_file, new_file in files_to_rename:
|
||||
if os.path.exists(new_file):
|
||||
print(f"Error: Target filename {new_file} already exists.")
|
||||
continue
|
||||
os.rename(old_file, new_file)
|
||||
if debug:
|
||||
print(f'Renamed file: {old_file} -> {new_file}')
|
||||
|
||||
# Rename directories after
|
||||
for old_dir, new_dir in sorted(dirs_to_rename, key=lambda x: -len(x[0])):
|
||||
if os.path.exists(new_dir):
|
||||
print(f"Error: Target directory name {new_dir} already exists.")
|
||||
continue
|
||||
os.rename(old_dir, new_dir)
|
||||
if debug:
|
||||
print(f'Renamed directory: {old_dir} -> {new_dir}')
|
||||
|
||||
|
||||
def main():
|
||||
'''
|
||||
Main function to parse command line arguments and initiate file renamings.
|
||||
'''
|
||||
|
||||
# Create argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Rename video files containing resolutions in their filenames to a specified max_height resolution.'
|
||||
)
|
||||
|
||||
# Define command line arguments
|
||||
parser.add_argument(
|
||||
'input_path',
|
||||
nargs='?',
|
||||
default=os.getcwd(),
|
||||
help=
|
||||
'directory path to search for video files (default: current directory)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-mh',
|
||||
'--max-height',
|
||||
type=int,
|
||||
default=720,
|
||||
help='maximum height of videos to be converted (default: 720)')
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
action='store_true',
|
||||
help='enable debug mode for printing additional messages')
|
||||
|
||||
# Enable autocomplete for argparse
|
||||
argcomplete.autocomplete(parser)
|
||||
|
||||
# Parse command line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Rename videos
|
||||
autorename(args.input_path, args.max_height, args.debug)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Execute main function when the script is run directly
|
||||
main()
|
||||
@ -1,132 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# video_manage_audio.py
|
||||
|
||||
import argparse
|
||||
import argcomplete
|
||||
import colorama
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# Initialize colorama for colored output in the terminal
|
||||
colorama.init()
|
||||
|
||||
creset = colorama.Fore.RESET
|
||||
ccyan = colorama.Fore.CYAN
|
||||
cyellow = colorama.Fore.YELLOW
|
||||
cgreen = colorama.Fore.GREEN
|
||||
cred = colorama.Fore.RED
|
||||
|
||||
|
||||
def process_audio(file_path, track, command):
|
||||
"""
|
||||
Modify audio tracks of a video file based on the given command.
|
||||
- 'remove': Remove the specified audio track while keeping everything else.
|
||||
- 'keep': Keep only the specified audio track and remove all others.
|
||||
The function preserves video, subtitles, and metadata.
|
||||
"""
|
||||
print(f"{cgreen}Processing file: {file_path}{creset}")
|
||||
output_file = f"{os.path.splitext(file_path)[0]}_{command}_audio{os.path.splitext(file_path)[1]}"
|
||||
|
||||
# Construct ffmpeg command based on user choice
|
||||
if command == "remove":
|
||||
# Remove only the specified audio track while keeping all other streams
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0", "-map", f"-0:a:{track}",
|
||||
"-c", "copy", output_file
|
||||
]
|
||||
elif command == "keep":
|
||||
# Keep only the specified audio track while preserving video, subtitles, and metadata
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0:v", "-map", f"0:a:{track}",
|
||||
"-map", "0:s?", "-map", "0:t?", "-c", "copy", output_file
|
||||
]
|
||||
else:
|
||||
print(f"{cred}Invalid command: {command}{creset}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Execute the ffmpeg command and capture output
|
||||
result = subprocess.run(ffmpeg_command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(
|
||||
f"{ccyan}Audio processing complete. Output saved to {output_file}{creset}\n"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"{cred}Command failed with return code {result.returncode}{creset}\n"
|
||||
)
|
||||
print(f"{cred}Error output:\n{result.stderr}{creset}\n")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"{cred}Error processing audio: {e}{creset}\n")
|
||||
|
||||
|
||||
def process_directory(dir_path, track, command):
|
||||
"""
|
||||
Recursively processes all video files in the specified directory.
|
||||
Applies the chosen audio modification (remove or keep) to each file.
|
||||
"""
|
||||
for root, _, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if file.endswith((".mp4", ".mkv", ".avi", ".mov")):
|
||||
file_path = os.path.join(root, file)
|
||||
process_audio(file_path, track, command)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to parse command-line arguments and initiate the audio processing.
|
||||
"""
|
||||
|
||||
# Create argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Manage audio tracks in video files.")
|
||||
|
||||
# Define command line arguments
|
||||
# Add a positional argument for the command
|
||||
parser.add_argument('command',
|
||||
choices=['remove', 'keep'],
|
||||
help='Command to run')
|
||||
|
||||
# Add other arguments with both short and long options, including defaults
|
||||
parser.add_argument("-t",
|
||||
"--track",
|
||||
type=int,
|
||||
default=0,
|
||||
help="Audio track index (default is 0).")
|
||||
parser.add_argument("-f",
|
||||
"--file",
|
||||
type=str,
|
||||
help="Path to a specific video file.")
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dir",
|
||||
type=str,
|
||||
default=os.getcwd(),
|
||||
help="Directory to process (default is current directory).")
|
||||
|
||||
# Enable autocomplete for command-line arguments
|
||||
argcomplete.autocomplete(parser)
|
||||
|
||||
# Parse command line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Process a single file if provided, otherwise process a directory
|
||||
if args.file:
|
||||
if os.path.isfile(args.file):
|
||||
process_audio(args.file, args.track, args.command)
|
||||
else:
|
||||
print(f"{cred}File {args.file} does not exist.{creset}")
|
||||
else:
|
||||
if os.path.isdir(args.dir):
|
||||
process_directory(args.dir, args.track, args.command)
|
||||
else:
|
||||
print(f"{cred}Directory {args.dir} does not exist.{creset}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,142 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# video_manage_subtitles.py
|
||||
|
||||
import argparse
|
||||
import argcomplete
|
||||
import colorama
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# Initialize colorama for colored output in the terminal
|
||||
colorama.init()
|
||||
|
||||
creset = colorama.Fore.RESET
|
||||
ccyan = colorama.Fore.CYAN
|
||||
cyellow = colorama.Fore.YELLOW
|
||||
cgreen = colorama.Fore.GREEN
|
||||
cred = colorama.Fore.RED
|
||||
|
||||
|
||||
def process_subtitles(file_path, track, command):
|
||||
"""
|
||||
Modify subtitles tracks of a video file based on the given command.
|
||||
- 'remove': Remove the specified subtitle track while keeping everything else.
|
||||
- 'keep': Keep only the specified subtitle track and remove all others.
|
||||
- 'none': Remove all subtitle tracks.
|
||||
The function preserves video, audio, and metadata.
|
||||
"""
|
||||
print(f"{cgreen}Processing file: {file_path}{creset}")
|
||||
output_file = f"{os.path.splitext(file_path)[0]}_{command}_subtitles{os.path.splitext(file_path)[1]}"
|
||||
|
||||
# Construct ffmpeg command based on user choice
|
||||
if command == "remove":
|
||||
# Remove only the specified subtitle track while keeping all other streams
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0", "-map", f"-0:s:{track}",
|
||||
"-c", "copy", output_file
|
||||
]
|
||||
elif command == "keep":
|
||||
# Keep only the specified subtitle track while preserving video, audio, and metadata
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0:v", "-map", "0:a", "-map",
|
||||
f"0:s:{track}", "-map", "0:t?", "-c", "copy", output_file
|
||||
]
|
||||
elif command == "none":
|
||||
# Remove all subtitle tracks
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0", "-map", "-0:s", "-c",
|
||||
"copy", output_file
|
||||
]
|
||||
else:
|
||||
print(f"{cred}Invalid command: {command}{creset}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Execute the ffmpeg command and capture output
|
||||
result = subprocess.run(ffmpeg_command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(
|
||||
f"{ccyan}Subtitles processing complete. Output saved to {output_file}{creset}\n"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"{cred}Command failed with return code {result.returncode}{creset}\n"
|
||||
)
|
||||
print(f"{cred}Error output:\n{result.stderr}{creset}\n")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"{cred}Error processing subtitles: {e}{creset}\n")
|
||||
|
||||
|
||||
def process_directory(dir_path, track, command):
|
||||
"""
|
||||
Recursively processes all video files in the specified directory.
|
||||
Applies the chosen subtitle modification (remove, keep, none) to each file.
|
||||
"""
|
||||
for root, _, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if file.endswith((".mp4", ".mkv", ".avi", ".mov")):
|
||||
file_path = os.path.join(root, file)
|
||||
process_subtitles(file_path, track, command)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to parse command-line arguments and initiate the subtitle processing.
|
||||
"""
|
||||
|
||||
# Create argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Manage subtitle tracks in video files.")
|
||||
|
||||
# Define command line arguments
|
||||
# Add a positional argument for the command
|
||||
parser.add_argument('command',
|
||||
choices=['remove', 'keep', 'none'],
|
||||
help='Command to run (remove, keep, or none)')
|
||||
|
||||
# Add other arguments with both short and long options, including defaults
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--track",
|
||||
type=int,
|
||||
default=0,
|
||||
help=
|
||||
"Subtitle track index (default is 0). Use 'none' to remove all subtitles."
|
||||
)
|
||||
parser.add_argument("-f",
|
||||
"--file",
|
||||
type=str,
|
||||
help="Path to a specific video file.")
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dir",
|
||||
type=str,
|
||||
default=os.getcwd(),
|
||||
help="Directory to process (default is current directory).")
|
||||
|
||||
# Enable autocomplete for command-line arguments
|
||||
argcomplete.autocomplete(parser)
|
||||
|
||||
# Parse command line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Process a single file if provided, otherwise process a directory
|
||||
if args.file:
|
||||
if os.path.isfile(args.file):
|
||||
process_subtitles(args.file, args.track, args.command)
|
||||
else:
|
||||
print(f"{cred}File {args.file} does not exist.{creset}")
|
||||
else:
|
||||
if os.path.isdir(args.dir):
|
||||
process_directory(args.dir, args.track, args.command)
|
||||
else:
|
||||
print(f"{cred}Directory {args.dir} does not exist.{creset}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
95
scripts/video_remove_audio.py
Executable file
95
scripts/video_remove_audio.py
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import argcomplete
|
||||
import colorama
|
||||
import subprocess
|
||||
|
||||
colorama.init()
|
||||
|
||||
creset = colorama.Fore.RESET
|
||||
ccyan = colorama.Fore.CYAN
|
||||
cyellow = colorama.Fore.YELLOW
|
||||
cgreen = colorama.Fore.GREEN
|
||||
cred = colorama.Fore.RED
|
||||
|
||||
|
||||
# Function to remove audio track from a video using ffmpeg
|
||||
def remove_audio_track(file_path, track):
|
||||
print(f"{cgreen}Processing file: {file_path}{creset}")
|
||||
output_file = f"{os.path.splitext(file_path)[0]}_no_audio{os.path.splitext(file_path)[1]}"
|
||||
# ffmpeg command to remove the specified audio track and keep other streams
|
||||
command = [
|
||||
"ffmpeg", "-i", file_path, "-map", "0", "-map", f"-0:a:{track}", "-c",
|
||||
"copy", output_file
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(
|
||||
f"{ccyan}Audio track {track} removed. Output saved to {output_file}{creset}\n"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"{cred}Command failed with return code {result.returncode}{creset}\n"
|
||||
)
|
||||
print(f"{cred}Error output:", result.stderr, f"{creset}\n")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"{cred}Error removing audio track: {e}{creset}\n")
|
||||
|
||||
|
||||
# Function to recursively process videos in a directory
|
||||
def process_directory(dir_path, track):
|
||||
for root, _, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if file.endswith((".mp4", ".mkv", ".avi",
|
||||
".mov")): # Add more formats as needed
|
||||
file_path = os.path.join(root, file)
|
||||
remove_audio_track(file_path, track)
|
||||
|
||||
|
||||
def main():
|
||||
# Set up argument parser
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Remove audio track from video files.")
|
||||
parser.add_argument("--track",
|
||||
type=int,
|
||||
default=0,
|
||||
help="Audio track index to remove (default is 0).")
|
||||
parser.add_argument("--file",
|
||||
type=str,
|
||||
help="Path to a specific video file.")
|
||||
parser.add_argument(
|
||||
"--dir",
|
||||
type=str,
|
||||
default=os.getcwd(),
|
||||
help="Directory to process (default is current directory).")
|
||||
|
||||
# Enable autocomplete for argparse
|
||||
argcomplete.autocomplete(parser)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Process single file if provided
|
||||
if args.file:
|
||||
if os.path.isfile(args.file):
|
||||
remove_audio_track(args.file, args.track)
|
||||
else:
|
||||
print(f"File {args.file} does not exist.")
|
||||
# Otherwise, process all files in the specified directory
|
||||
else:
|
||||
if os.path.isdir(args.dir):
|
||||
process_directory(args.dir, args.track)
|
||||
else:
|
||||
print(f"Directory {args.dir} does not exist.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
520
setups-drafts/almalinux_setup_wordpress.md
Normal file
520
setups-drafts/almalinux_setup_wordpress.md
Normal file
@ -0,0 +1,520 @@
|
||||
# Installing Wordpress on AlmaLinux
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installing Wordpress on AlmaLinux](#installing-wordpress-on-almalinux)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Disclaimer: Incomplete Guide](#disclaimer-incomplete-guide)
|
||||
- [Introduction](#introduction)
|
||||
- [Why WordPress?](#why-wordpress)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [What This Guide Covers](#what-this-guide-covers)
|
||||
- [Placeholders](#placeholders)
|
||||
- [Important Warnings and Security Practices](#important-warnings-and-security-practices)
|
||||
- [Useful Commands and Information](#useful-commands-and-information)
|
||||
- [Documentation](#documentation)
|
||||
- [Links](#links)
|
||||
- [Software on the Machine](#software-on-the-machine)
|
||||
- [Paths](#paths)
|
||||
- [Proxmox Commands](#proxmox-commands)
|
||||
- [SSH Connection](#ssh-connection)
|
||||
- [Installation Procedure](#installation-procedure)
|
||||
|
||||
## Disclaimer: Incomplete Guide
|
||||
|
||||
This document is a draft and may contain incomplete, untested, or outdated information. It is a work in progress and has not been verified for accuracy or usability. Use this guide at your own discretion, and consider it as a reference for further development or exploration. Updates may follow in the future, but no guarantees are made.
|
||||
|
||||
## Introduction
|
||||
|
||||
Welcome to the installation guide for WordPress on AlmaLinux in a Proxmox LXC container. WordPress is a powerful content management system (CMS) that allows you to create and manage websites efficiently.
|
||||
|
||||
## Why WordPress?
|
||||
|
||||
WordPress is one of the most popular content management systems (CMS) in the world, powering over 40% of websites on the internet. It offers a flexible and user-friendly platform for building anything from simple blogs to complex e-commerce sites.
|
||||
|
||||
**Key Benefits:**
|
||||
|
||||
- **Open-Source & Free** – No licensing fees, with a large community contributing to its continuous development.
|
||||
- **Extensive Plugin Ecosystem** – Thousands of plugins to add features like SEO, security, performance optimization, and more.
|
||||
- **Customizable Themes** – A wide variety of free and premium themes allow you to tailor your website's design.
|
||||
- **SEO-Friendly** – Built-in SEO features and plugins like Yoast SEO help improve search engine rankings.
|
||||
- **Scalability** – Suitable for small personal blogs to large enterprise websites with high traffic.
|
||||
- **Active Community & Support** – Large developer and user communities provide extensive documentation, forums, and professional support options.
|
||||
|
||||
Whether you're launching a blog, a portfolio, or a business website, WordPress provides the flexibility and power to meet your needs.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin the installation process, ensure that your AlmaLinux system meets the following requirements:
|
||||
|
||||
- AlmaLinux GNU/Linux 9 or later
|
||||
- Access to a terminal with sudo privileges
|
||||
- Basic familiarity with the command line interface
|
||||
- Stable internet connection to download necessary packages
|
||||
|
||||
## What This Guide Covers
|
||||
|
||||
This guide covers the installation and configuration of Wordpress on an AlmaLinux server, along with additional setup tasks such as SSH connection management and Proxmox commands. It covers:
|
||||
|
||||
1. **Installation**: Installing Wordpress from the official site.
|
||||
2. **Configuration**: Configuring Wordpress to suit your environment and preferences.
|
||||
3. **Setup**: Setting up Wordpress as a service and accessing it.
|
||||
|
||||
## Placeholders
|
||||
|
||||
Replace the placeholders below with the appropriate values for your setup:
|
||||
|
||||
- **User Details**
|
||||
|
||||
- Username: `<username>` (e.g., admin)
|
||||
- Username - Hypervisor: `<username-hypervisor>` (e.g., admin)
|
||||
|
||||
- **Server Configuration**
|
||||
|
||||
- Server IP address: `<server-ip-address>` (e.g., 192.168.1.100)
|
||||
- Hostname - Intranet: `<hostname-intranet>` (e.g., wordpress-server.domain.com)
|
||||
- Hostname - Internet: `<hostname-internet>` (e.g., wordpress.domain.com)
|
||||
- Hostname - Hypervisor: `<hostname-hypervisor>` (e.g., proxmox-hypervisor.domain.com)
|
||||
- Hostname - Hypervisor NAS: `<hostname-hypervisor-nas>` (e.g., nas-server.domain.com)
|
||||
- Name - Hypervisor NAS: `<name-hypervisor-nas>` (e.g., nas-server)
|
||||
- Container ID: `<container-id>` (e.g., 100)
|
||||
|
||||
- **SSH Keys**
|
||||
|
||||
- SSH key - Proxmox: `<ssh-key-proxmox>` (e.g., /home/user/.ssh/id_rsa.pub)
|
||||
- SSH key - Client: `<ssh-key-client>` (e.g., /home/user/.ssh/client_id_rsa.pub)
|
||||
|
||||
- **Networking**
|
||||
|
||||
- Wireguard port: `<wireguard-port>` (e.g., 51820)
|
||||
|
||||
- **Database**
|
||||
|
||||
- Database password : `<database-password>` (e.g., 15GbGnOn3Vjy9RQ4G9TfUF95wPcoKAy5)
|
||||
|
||||
- **Paths**
|
||||
|
||||
- Path index: `<path-index>` (e.g., /var/www/html)
|
||||
|
||||
## Important Warnings and Security Practices
|
||||
|
||||
Before executing any commands in this documentation, please adhere to the following guidelines to ensure the security and integrity of the system:
|
||||
|
||||
1. **Execute Commands with Caution**: Always review and understand a command before executing it. Misuse of commands can lead to data loss or system instability.
|
||||
2. **Backup Command Execution**: The backup command must be executed only by authorized users. Ensure that proper permissions are set to prevent unauthorized access to backup files.
|
||||
3. **Regular Backups**: Maintain regular backups of all critical data. It is advisable to use automated backup solutions and verify backup integrity periodically.
|
||||
4. **System Updates**: Regularly update the system and all installed packages to protect against vulnerabilities. Use the package manager responsibly to avoid potential conflicts.
|
||||
5. **Monitor System Logs**: Continuously monitor system logs for any unusual activity. Use logging tools to help identify potential security breaches or system failures.
|
||||
6. **User Permissions**: Ensure that user permissions are strictly managed. Limit access to sensitive commands and data to only those who need it to perform their job functions.
|
||||
7. **Network Security**: Implement proper network security measures, such as firewalls and intrusion detection systems, to protect against external threats.
|
||||
8. **Data Encryption**: Encrypt sensitive data at rest and in transit to prevent unauthorized access.
|
||||
|
||||
By following these practices, you will help maintain the security and stability of the system while minimizing the risk of data loss or compromise.
|
||||
|
||||
## Useful Commands and Information
|
||||
|
||||
### Documentation
|
||||
|
||||
- [AlmaLinux Wiki](https://wiki.almalinux.org/)
|
||||
- [Documentation Overview](https://www.wordpress.info/doc/overview/)
|
||||
- [Tutorials](https://wordpress.com/learn/)
|
||||
|
||||
### Links
|
||||
|
||||
- [Wordpress appliance](https://<hostname-internet>/)
|
||||
|
||||
### Software on the Machine
|
||||
|
||||
- **Operating System**: AlmaLinux
|
||||
- **Web Server**: Apache
|
||||
- **Database**: Mariadb
|
||||
- **Security**: GnuPG, WireGuard, firewalld
|
||||
- **Other**: Git, sudo
|
||||
|
||||
### Paths
|
||||
|
||||
- **Apache AlmaLinux Default Configuration**: `/etc/httpd/conf.d/welcome.conf`
|
||||
- **Wordpress Configuration**: `/var/www/html/wp-config.php`
|
||||
- **Wordpress Work Path**: `/var/www/html`
|
||||
|
||||
### Proxmox Commands
|
||||
|
||||
**List available Proxmox templates**
|
||||
|
||||
```bash
|
||||
ssh <username>@<hostname-hypervisor-nas> "ls /mnt/proxmox/template/cache/"
|
||||
```
|
||||
|
||||
**Create the container**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create <container-id> <name-hypervisor-nas>:vztmpl/almalinux-9-default_20240911_amd64.tar.xz --hostname <hostname-intranet> --cores 2 --memory 4096 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:100 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox>"
|
||||
```
|
||||
|
||||
**Backup**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
**Set the state of the Proxmox HA Manager for Container <container-id>**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:<container-id>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:<container-id>"
|
||||
```
|
||||
|
||||
**Set the state and limits of the Proxmox Container <container-id> in the HA Manager**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct start <container-id>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct stop <container-id>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot <container-id>"
|
||||
```
|
||||
|
||||
**Destroy the Proxmox Container <container-id> forcefully**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy <container-id> --force --purge"
|
||||
```
|
||||
|
||||
**Move the Proxmox Container <container-id> to another host**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate <container-id> hv2"
|
||||
```
|
||||
|
||||
### SSH Connection
|
||||
|
||||
**Connection with specific keys**
|
||||
|
||||
```bash
|
||||
ssh -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> <username>@<server-ip-address>
|
||||
```
|
||||
|
||||
**Remove offending keys from known_hosts**
|
||||
|
||||
```bash
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<hostname-intranet>"
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<server-ip-address>"
|
||||
```
|
||||
|
||||
**Copy SSH public key to remote host**
|
||||
|
||||
```bash
|
||||
ssh-copy-id -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
```
|
||||
|
||||
## Installation Procedure
|
||||
|
||||
1. **Fresh AlmaLinux Installation**
|
||||
|
||||
- Install a fresh AlmaLinux operating system on your new server following the standard installation procedure.
|
||||
|
||||
2. **Backup before starting**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
3. **Install Required Dependencies**
|
||||
|
||||
**Upgrade the base system**
|
||||
|
||||
```bash
|
||||
dnf update
|
||||
```
|
||||
|
||||
**Enable EPEL repository**
|
||||
Extra Package for Enterprise Linux repository has packages like Apache and Nginx
|
||||
|
||||
```bash
|
||||
dnf install -y epel-release
|
||||
dnf makecache
|
||||
```
|
||||
|
||||
4. **Install LAMP Stack**
|
||||
|
||||
**Install dependencies**
|
||||
|
||||
```bash
|
||||
dnf install -y sudo nano firewalld firewall-config tar wget curl unzip
|
||||
dnf install -y gnupg nginx git wireguard-tools
|
||||
|
||||
dnf install -y httpd httpd-tools mariadb-server mariadb
|
||||
|
||||
# Basic command
|
||||
dnf install -y php php-mysqlnd php-fpm php-json php-mbstring php-xml php-curl php-zip php-gd php-intl php-bcmath php-soap php-exif
|
||||
# More tooling and security
|
||||
dnf install -y httpd mod_ssl php php-cli php-common php-fpm php-gd php-intl php-json php-mbstring php-mysqlnd php-opcache php-pdo php-pecl-imagick php-xml php-zip policycoreutils-python-utils
|
||||
# Other xml php modules
|
||||
dnf install -y php-dom php-simplexml php-xmlreader php-iconv php-posix php-sockets php-tokenizer
|
||||
|
||||
# Necessary for wp-cli
|
||||
dnf install -y php-cli php-mbstring unzip curl
|
||||
```
|
||||
|
||||
**WordPress Dependencies on AlmaLinux (MariaDB)**
|
||||
|
||||
**Web Server**
|
||||
|
||||
- `httpd` (Apache)
|
||||
- `mod_ssl` (For HTTPS support)
|
||||
- OR `nginx` (If using Nginx instead of Apache)
|
||||
|
||||
**PHP**
|
||||
|
||||
- `php` (Main PHP package)
|
||||
- `php-cli` (Command-line interface for PHP)
|
||||
- `php-common` (Common PHP files)
|
||||
- `php-fpm` (FastCGI Process Manager for PHP, required for Nginx)
|
||||
- `php-gd` (Image processing)
|
||||
- `php-intl` (Internationalization)
|
||||
- `php-json` (JSON support)
|
||||
- `php-mbstring` (Multibyte string functions)
|
||||
- `php-mysqlnd` (MySQL/MariaDB support)
|
||||
- `php-opcache` (Performance optimization)
|
||||
- `php-pdo` (PHP Data Objects)
|
||||
- `php-pecl-imagick` (ImageMagick extension, recommended for media handling)
|
||||
- `php-xml` (XML parsing)
|
||||
- `php-zip` (ZIP file support)
|
||||
|
||||
**Database (MariaDB)**
|
||||
|
||||
- `mariadb-server` (MariaDB database server)
|
||||
- `mariadb` (MariaDB client)
|
||||
|
||||
**Additional System Packages**
|
||||
|
||||
- `tar` (Required for extracting WordPress archives)
|
||||
- `wget` (To fetch files from the web)
|
||||
- `curl` (For network requests)
|
||||
- `unzip` (Extracting ZIP files)
|
||||
- `policycoreutils-python-utils` (SELinux tools, required if SELinux is enabled)
|
||||
- `firewalld` (For firewall management, if needed)
|
||||
|
||||
**Optional Debugging & Performance Tools**
|
||||
|
||||
- `php-pecl-apcu` (APC User Cache for PHP)
|
||||
- `php-pecl-memcached` (Memcached support)
|
||||
- `php-pecl-redis` (Redis support)
|
||||
|
||||
5. **Ensure Hostname**
|
||||
|
||||
```bash
|
||||
nano /etc/hosts
|
||||
# Add line: 127.0.1.1 <hostname-intranet>
|
||||
nano /etc/hostname
|
||||
# Set to: <hostname-intranet>
|
||||
hostnamectl set-hostname <hostname-intranet>
|
||||
```
|
||||
|
||||
6. **Add Users and set Credentials**
|
||||
|
||||
```bash
|
||||
passwd root
|
||||
```
|
||||
|
||||
```bash
|
||||
adduser <username>
|
||||
passwd <username>
|
||||
|
||||
groupadd sudo
|
||||
usermod -aG sudo <username>
|
||||
nano /etc/sudoers
|
||||
```
|
||||
|
||||
```
|
||||
## Allows people in group sudo to run all commands
|
||||
%sudo ALL=(ALL) ALL
|
||||
```
|
||||
|
||||
7. **Setup SSH Connectors**
|
||||
|
||||
- Configure SSH connectors as per your setup script to establish secure connections to the server.
|
||||
|
||||
8. **Test users, SSH, and sudo**
|
||||
|
||||
1. **Transfer SSH keys for User**
|
||||
2. **Connect as User with SSH key**
|
||||
3. **Test sudo**
|
||||
|
||||
```bash
|
||||
sudo su -
|
||||
```
|
||||
|
||||
4. **Disconnect as root**
|
||||
|
||||
9. **Secure SSH**
|
||||
|
||||
```bash
|
||||
nano /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
```ini
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
```
|
||||
|
||||
**Restart SSH**
|
||||
|
||||
```bash
|
||||
systemctl restart sshd
|
||||
```
|
||||
|
||||
10. **Configure Firewall**
|
||||
|
||||
**Open ports**
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --zone=public --add-service=ssh
|
||||
firewall-cmd --permanent --zone=public --add-service=http
|
||||
firewall-cmd --permanent --zone=public --add-service=https
|
||||
firewall-cmd --permanent --zone=public --add-port=<wireguard-port>/udp
|
||||
```
|
||||
|
||||
**Reload firewall to apply changes**
|
||||
|
||||
```bash
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
**Enable and start firewall**
|
||||
|
||||
```bash
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
```
|
||||
|
||||
11. **Start Apache And MariaDB**
|
||||
|
||||
```bash
|
||||
systemctl enable httpd --now
|
||||
systemctl enable mariadb --now
|
||||
```
|
||||
|
||||
Now, your web server is available at `http://<hostname-intranet>`.
|
||||
|
||||
12. **Create PHP test page**
|
||||
|
||||
```bash
|
||||
echo "<?php phpinfo() ?>" > /var/www/html/info.php
|
||||
```
|
||||
|
||||
Now, your web server php information is available at `http://<hostname-intranet>/info.php`.
|
||||
|
||||
```bash
|
||||
rm /var/www/html/info.php
|
||||
```
|
||||
|
||||
13. **Secure MariaDB Installation**
|
||||
|
||||
```bash
|
||||
mysql_secure_installation
|
||||
```
|
||||
|
||||
14. **Creating the new Database**
|
||||
|
||||
```bash
|
||||
mariadb
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE DATABASE wordpress;
|
||||
CREATE USER `admin`@`localhost` IDENTIFIED BY '<database-password>';
|
||||
GRANT ALL ON wordpress.* TO `admin`@`localhost`;
|
||||
FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
15. **Download and Extract WordPress**
|
||||
|
||||
```bash
|
||||
curl https://wordpress.org/latest.tar.gz --output wordpress.tar.gz
|
||||
tar xf wordpress.tar.gz
|
||||
cp -r wordpress/* /var/www/html/
|
||||
```
|
||||
|
||||
16. **Download and Install WordPress CLI**
|
||||
|
||||
As root
|
||||
|
||||
```bash
|
||||
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
php wp-cli.phar --info
|
||||
chmod +x wp-cli.phar
|
||||
mv wp-cli.phar /usr/local/bin/wp
|
||||
wp --info
|
||||
```
|
||||
|
||||
As user **Enable Tab Completion**
|
||||
|
||||
```bash
|
||||
su -s /bin/bash -l apache
|
||||
mkdir -p ~/.wp-cli
|
||||
wp cli completions --shell=bash > ~/.wp-cli/wp-completion.bash
|
||||
echo 'source ~/.wp-cli/wp-completion.bash' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
17. **Modify Permissions**
|
||||
|
||||
Set appropriate ownership and adjust the SELinux security context for WordPress files:
|
||||
|
||||
```bash
|
||||
chown -R apache:apache /var/www/html
|
||||
chmod -R 755 /var/www/html
|
||||
```
|
||||
|
||||
Enable Apache's ability to establish network connections, allowing WordPress to download updates and plugins:
|
||||
|
||||
```bash
|
||||
setsebool -P httpd_can_network_connect true
|
||||
```
|
||||
|
||||
18. **Allow Override**
|
||||
|
||||
```bash
|
||||
nano /etc/httpd/conf/httpd.conf
|
||||
```
|
||||
|
||||
Allow Overrides on `/var/www` and `/var/www/html`:
|
||||
|
||||
```apache
|
||||
AllowOverride All
|
||||
```
|
||||
|
||||
19. **Configure Wordpress**
|
||||
|
||||
Now, visit `http://<hostname-intranet>` to follow the wordpress configuration.
|
||||
|
||||
20. **Configure WireGuard**
|
||||
|
||||
```bash
|
||||
nano /etc/wireguard/proxy-lan.conf
|
||||
systemctl enable wg-quick@proxy-lan --now
|
||||
wg show
|
||||
```
|
||||
|
||||
21. **Back-up post installation**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
```
|
||||
|
||||
22. **Start the server**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct start <container-id>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id> --state started --max_relocate 3 --max_restart 3"
|
||||
```
|
||||
481
setups-drafts/oracle_setup_oro.md
Normal file
481
setups-drafts/oracle_setup_oro.md
Normal file
@ -0,0 +1,481 @@
|
||||
# Installing Oro on Oracle Linux
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installing Oro on Oracle Linux](#installing-oro-on-oracle-linux)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Disclaimer: Incomplete Guide](#disclaimer-incomplete-guide)
|
||||
- [Introduction](#introduction)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [What This Guide Covers](#what-this-guide-covers)
|
||||
- [Placeholders](#placeholders)
|
||||
- [Important Warnings and Security Practices](#important-warnings-and-security-practices)
|
||||
- [Useful Commands and Information](#useful-commands-and-information)
|
||||
- [Documentation](#documentation)
|
||||
- [Links](#links)
|
||||
- [Software on the Machine](#software-on-the-machine)
|
||||
- [Paths](#paths)
|
||||
- [Proxmox Commands](#proxmox-commands)
|
||||
- [SSH Connection](#ssh-connection)
|
||||
- [Installation Procedure](#installation-procedure)
|
||||
|
||||
## Disclaimer: Incomplete Guide
|
||||
|
||||
This document is a draft and may contain incomplete, untested, or outdated information. It is a work in progress and has not been verified for accuracy or usability. Use this guide at your own discretion, and consider it as a reference for further development or exploration. Updates may follow in the future, but no guarantees are made.
|
||||
|
||||
## Introduction
|
||||
|
||||
This guide provides step-by-step instructions for installing and configuring Oro on a Oracle Linux server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin the installation process, ensure that your Oracle Linux system meets the following requirements:
|
||||
|
||||
- Oracle Linux GNU/Linux 9 or later
|
||||
- Access to a terminal with sudo privileges
|
||||
- Basic familiarity with the command line interface
|
||||
- Stable internet connection to download necessary packages
|
||||
|
||||
## What This Guide Covers
|
||||
|
||||
This guide covers the installation and configuration of Oro on a Oracle Linux server, along with additional setup tasks such as SSH connection management and Proxmox commands. It covers:
|
||||
|
||||
1. **Installation**: Installing Oro from the official repository.
|
||||
2. **Configuration**: Configuring Oro to suit your environment and preferences.
|
||||
3. **Setup**: Setting up Oro as a service and accessing it.
|
||||
|
||||
## Placeholders
|
||||
|
||||
Replace the placeholders below with the appropriate values for your setup:
|
||||
|
||||
- **User Details**
|
||||
|
||||
- Username: `<username>` (e.g., admin)
|
||||
- Username - Hypervisor: `<username-hypervisor>` (e.g., admin)
|
||||
|
||||
- **Server Configuration**
|
||||
|
||||
- Server IP address: `<server-ip-address>` (e.g., 192.168.1.100)
|
||||
- Hostname - Intranet: `<hostname-intranet>` (e.g., oro-server.domain.com)
|
||||
- Hostname - Internet: `<hostname-internet>` (e.g., oro.domain.com)
|
||||
- Hostname - Hypervisor: `<hostname-hypervisor>` (e.g., proxmox-hypervisor.domain.com)
|
||||
- Hostname - Hypervisor NAS: `<hostname-hypervisor-nas>` (e.g., nas-server.domain.com)
|
||||
- Name - Hypervisor NAS: `<name-hypervisor-nas>` (e.g., nas-server)
|
||||
|
||||
- **SSH Keys**
|
||||
|
||||
- SSH key - Proxmox: `<ssh-key-proxmox>` (e.g., /home/user/.ssh/id_rsa.pub)
|
||||
- SSH key - Client: `<ssh-key-client>` (e.g., /home/user/.ssh/client_id_rsa.pub)
|
||||
|
||||
- **Networking**
|
||||
|
||||
- Wireguard port: `<wireguard-port>` (e.g., 51820)
|
||||
|
||||
- **Paths**
|
||||
|
||||
## Important Warnings and Security Practices
|
||||
|
||||
Before executing any commands in this documentation, please adhere to the following guidelines to ensure the security and integrity of the system:
|
||||
|
||||
1. **Execute Commands with Caution**: Always review and understand a command before executing it. Misuse of commands can lead to data loss or system instability.
|
||||
2. **Backup Command Execution**: The backup command must be executed only by authorized users. Ensure that proper permissions are set to prevent unauthorized access to backup files.
|
||||
3. **Regular Backups**: Maintain regular backups of all critical data. It is advisable to use automated backup solutions and verify backup integrity periodically.
|
||||
4. **System Updates**: Regularly update the system and all installed packages to protect against vulnerabilities. Use the package manager responsibly to avoid potential conflicts.
|
||||
5. **Monitor System Logs**: Continuously monitor system logs for any unusual activity. Use logging tools to help identify potential security breaches or system failures.
|
||||
6. **User Permissions**: Ensure that user permissions are strictly managed. Limit access to sensitive commands and data to only those who need it to perform their job functions.
|
||||
7. **Network Security**: Implement proper network security measures, such as firewalls and intrusion detection systems, to protect against external threats.
|
||||
8. **Data Encryption**: Encrypt sensitive data at rest and in transit to prevent unauthorized access.
|
||||
|
||||
By following these practices, you will help maintain the security and stability of the system while minimizing the risk of data loss or compromise.
|
||||
|
||||
## Useful Commands and Information
|
||||
|
||||
### Documentation
|
||||
|
||||
- [Documentation Overview](https://doc.oroinc.com/)
|
||||
- [Documentation community edition](https://doc.oroinc.com/backend/setup/dev-environment/community-edition/)
|
||||
- [Installation](https://doc.oroinc.com/backend/setup/installation/)
|
||||
|
||||
### Links
|
||||
|
||||
- [Oro appliance](https://<hostname-internet>/)
|
||||
|
||||
### Software on the Machine
|
||||
|
||||
- **Operating System**: Oracle Linux
|
||||
- **Web Server**:
|
||||
- **Security**: GnuPG, WireGuard, firewalld
|
||||
- **Other**: Git, sudo
|
||||
|
||||
### Paths
|
||||
|
||||
- **Oro Configuration**:
|
||||
- **Oro Work Path**:
|
||||
|
||||
### Proxmox Commands
|
||||
|
||||
**List available Proxmox templates**
|
||||
|
||||
```bash
|
||||
ssh <username>@<hostname-hypervisor-nas> "ls /mnt/proxmox/template/cache/"
|
||||
```
|
||||
|
||||
**Create the container**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create 100 <name-hypervisor-nas>:vztmpl/oracle-9-sshnano_20240603_amd64.tar.zst --hostname <hostname-intranet> --cores 2 --memory 4096 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:100 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox>"
|
||||
```
|
||||
|
||||
**Backup**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
**Set the state of the Proxmox HA Manager for Container 100**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:100"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:100"
|
||||
```
|
||||
|
||||
**Set the state and limits of the Proxmox Container 100 in the HA Manager**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct start 100"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct stop 100"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot 100"
|
||||
```
|
||||
|
||||
**Destroy the Proxmox Container 100 forcefully**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy 100 --force --purge"
|
||||
```
|
||||
|
||||
**Move the Proxmox Container 100 to another host**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate 100 hv2"
|
||||
```
|
||||
|
||||
### SSH Connection
|
||||
|
||||
**Connection with specific keys**
|
||||
|
||||
```bash
|
||||
ssh -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> <username>@<server-ip-address>
|
||||
```
|
||||
|
||||
**Remove offending keys from known_hosts**
|
||||
|
||||
```bash
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<hostname-intranet>"
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<server-ip-address>"
|
||||
```
|
||||
|
||||
**Copy SSH public key to remote host**
|
||||
|
||||
```bash
|
||||
ssh-copy-id -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
```
|
||||
|
||||
**Transfer SSH keys and files**
|
||||
|
||||
## Installation Procedure
|
||||
|
||||
1. **Fresh Oracle Linux Installation**
|
||||
|
||||
- Install a fresh Oracle Linux operating system on your new server following the standard installation procedure.
|
||||
|
||||
2. **Backup before starting**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
3. **Install Required Dependencies**
|
||||
|
||||
**Upgrade the base system**
|
||||
|
||||
```bash
|
||||
dnf update
|
||||
```
|
||||
|
||||
**Enable EPEL repository**
|
||||
Extra Package for Enterprise Linux repository has packages like Apache and Nginx
|
||||
|
||||
```bash
|
||||
dnf install epel-release
|
||||
```
|
||||
|
||||
**Enable Postgres repository**
|
||||
|
||||
Get instructions and urls in their [documentation](https://www.postgresql.org/download/linux/redhat/)
|
||||
|
||||
```bash
|
||||
# Install the repository RPM:
|
||||
dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
|
||||
|
||||
# Disable the built-in PostgreSQL module:
|
||||
dnf -qy module disable postgresql
|
||||
|
||||
# Install PostgreSQL:
|
||||
dnf install -y postgresql15-server
|
||||
|
||||
# Optionally initialize the database and enable automatic start:
|
||||
/usr/pgsql-15/bin/postgresql-15-setup initdb
|
||||
systemctl enable postgresql-15
|
||||
systemctl start postgresql-15
|
||||
```
|
||||
|
||||
**Enable Remi repository**
|
||||
|
||||
```bash
|
||||
cat >"/etc/yum.repos.d/oropublic.repo" <<__EOF__
|
||||
[oropublic]
|
||||
name=OroPublic
|
||||
baseurl=https://nexus.oro.cloud/repository/oropublic/8/x86_64/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
module_hotfixes=1
|
||||
__EOF__
|
||||
```
|
||||
|
||||
```bash
|
||||
dnf config-manager --set-enabled remi
|
||||
```
|
||||
|
||||
**Enable oro repository**
|
||||
|
||||
```bash
|
||||
dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
|
||||
```
|
||||
|
||||
**Enable DNF streams**
|
||||
|
||||
```bash
|
||||
dnf module list nginx
|
||||
dnf module list nodejs
|
||||
dnf module list php
|
||||
|
||||
dnf -y module enable nginx:1.24 nodejs:20 php:remi-8.3
|
||||
dnf -y upgrade
|
||||
```
|
||||
|
||||
**Install dependencies**
|
||||
|
||||
```bash
|
||||
dnf install -y sudo nano findutils rsync psmisc wget glibc-langpack-en bzip2 unzip p7zip p7zip-plugins parallel patch nodejs npm git-core jq bc postgresql postgresql-server postgresql-contrib
|
||||
dnf install -y gnupg wireguard-tools firewalld firewall-config
|
||||
```
|
||||
|
||||
**Install Apache or Nginx**
|
||||
Uncomment one of the following lines depending on the web server you prefer to use
|
||||
|
||||
**Apache**
|
||||
|
||||
```bash
|
||||
dnf install -y httpd
|
||||
```
|
||||
|
||||
**Nginx Configuration**
|
||||
|
||||
```bash
|
||||
dnf install -y nginx
|
||||
```
|
||||
|
||||
**PHP and modules**
|
||||
|
||||
```bash
|
||||
dnf install -y php-common php-cli php-fpm php-opcache php-mbstring php-mysqlnd php-pgsql php-pdo php-json php-process php-ldap php-gd php-ctype php-curl php-fileinfo php-intl php-bcmath php-xml php-soap php-sodium php-openssl php-pcre php-simplexml php-tokenizer php-zip php-tidy php-imap php-pecl-zip php-pecl-mongodb
|
||||
```
|
||||
|
||||
**nodejs**
|
||||
|
||||
```bash
|
||||
dnf install -y nodejs
|
||||
```
|
||||
|
||||
Verify Node.js and NPM versions
|
||||
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
|
||||
**Supervisor for process control**
|
||||
|
||||
```bash
|
||||
dnf install -y supervisor
|
||||
```
|
||||
|
||||
Enable and start Supervisor service
|
||||
|
||||
```bash
|
||||
systemctl enable supervisord
|
||||
systemctl start supervisord
|
||||
```
|
||||
|
||||
**Redis**
|
||||
|
||||
```bash
|
||||
dnf install -y redis
|
||||
```
|
||||
|
||||
Enable and start Redis service
|
||||
|
||||
```bash
|
||||
systemctl enable redis
|
||||
systemctl start redis
|
||||
```
|
||||
|
||||
**pngquant and jpegoptim**
|
||||
dnf install -y pngquant jpegoptim
|
||||
|
||||
4. **Ensure Hostname**
|
||||
|
||||
```bash
|
||||
nano /etc/hosts
|
||||
# Add line: 127.0.1.1 <hostname-intranet>
|
||||
nano /etc/hostname
|
||||
# Set to: <hostname-intranet>
|
||||
hostnamectl set-hostname <hostname-intranet>
|
||||
```
|
||||
|
||||
5. **Add Users and set Credentials**
|
||||
|
||||
```bash
|
||||
passwd -f root
|
||||
```
|
||||
|
||||
```bash
|
||||
adduser <username>
|
||||
passwd -f <username>
|
||||
groupadd sudo
|
||||
usermod -aG sudo <username>
|
||||
nano /etc/sudoers
|
||||
```
|
||||
|
||||
```
|
||||
## Allows people in group sudo to run all commands
|
||||
%sudo ALL=(ALL) ALL
|
||||
```
|
||||
|
||||
6. **Setup SSH Connectors**
|
||||
|
||||
- Configure SSH connectors as per your setup script to establish secure connections to the server.
|
||||
|
||||
7. **Test users, SSH, and sudo**
|
||||
|
||||
1. **Transfer SSH keys for User**
|
||||
2. **Connect as User with SSH key**
|
||||
3. **Test sudo**
|
||||
|
||||
```bash
|
||||
sudo su -
|
||||
```
|
||||
|
||||
4. **Disconnect as root**
|
||||
|
||||
8. **Secure SSH**
|
||||
|
||||
```bash
|
||||
nano /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
```ini
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
```
|
||||
|
||||
**Restart SSH**
|
||||
|
||||
```bash
|
||||
systemctl restart sshd
|
||||
```
|
||||
|
||||
9. **Configure Firewall**
|
||||
|
||||
**Open ports**
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --add-service=ssh
|
||||
firewall-cmd --permanent --add-service=http
|
||||
firewall-cmd --permanent --add-service=https
|
||||
firewall-cmd --permanent --add-port=<wireguard-port>/udp
|
||||
```
|
||||
|
||||
**Reload firewall to apply changes**
|
||||
|
||||
```bash
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
**Enable and start firewall**
|
||||
|
||||
```bash
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
```
|
||||
|
||||
10. **Configure PHP**
|
||||
|
||||
```bash
|
||||
php --ini
|
||||
nano /etc/php.ini
|
||||
```
|
||||
|
||||
**Add or update the following settings**
|
||||
|
||||
````ini
|
||||
date.timezone = America/Toronto
|
||||
detect_unicode = Off
|
||||
memory_limit = 1G
|
||||
max_execution_time = <appropriate-time>
|
||||
|
||||
If xdebug is installed, update or add these settings
|
||||
```ini
|
||||
xdebug.scream = Off
|
||||
xdebug.show_exception_trace = Off
|
||||
xdebug.max_nesting_level = 100
|
||||
````
|
||||
|
||||
11. **Install Oro**
|
||||
|
||||
```bash
|
||||
dnf -y --setopt=install_weak_deps=False --best --nogpgcheck install oro-nginx oro-nginx-mod-http-cache_purge oro-nginx-mod-http-cookie_flag oro-nginx-mod-http-geoip oro-nginx-mod-http-gridfs oro-nginx-mod-http-headers_more oro-nginx-mod-http-naxsi oro-nginx-mod-http-njs oro-nginx-mod-http-pagespeed oro-nginx-mod-http-sorted_querystring oro-nginx-mod-http-testcookie_access oro-nginx-mod-http-xslt-filter
|
||||
```
|
||||
|
||||
12. **Enable Oro service**
|
||||
|
||||
13. **Setup nginx proxy**
|
||||
|
||||
14. **Configure SSL**
|
||||
|
||||
15. **Correct permissions**
|
||||
|
||||
16. **Run the installer**
|
||||
|
||||
17. **Verify installation**
|
||||
|
||||
18. **Back-up post installation**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
```
|
||||
|
||||
19. **Start the server**
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
```
|
||||
@ -68,7 +68,6 @@ Replace the placeholders below with the appropriate values for your setup:
|
||||
- Hostname - Hypervisor: `<hostname-hypervisor>` (e.g., proxmox-hypervisor.domain.com)
|
||||
- Hostname - Hypervisor NAS: `<hostname-hypervisor-nas>` (e.g., nas-server.domain.com)
|
||||
- Name - Hypervisor NAS: `<name-hypervisor-nas>` (e.g., nas-server)
|
||||
- Container ID - Hypervisor: `<container-id-hypervisor>` (e.g., 100)
|
||||
|
||||
- **SSH Keys**
|
||||
|
||||
@ -141,40 +140,40 @@ ssh <username>@<hostname-hypervisor-nas> "ls /mnt/proxmox/template/cache/"
|
||||
**Create the container**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create <container-id-hypervisor> <name-hypervisor-nas>:vztmpl/debian-12-upgraded_12.5_amd64.tar.zst --hostname <hostname-intranet> --cores 2 --memory 2048 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:250 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox> --start 1"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create 100 <name-hypervisor-nas>:vztmpl/debian-12-upgraded_12.5_amd64.tar.zst --hostname <hostname-intranet> --cores 2 --memory 2048 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:250 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox> --start 1"
|
||||
```
|
||||
|
||||
**Backup**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
**Set the state of the Proxmox HA Manager for Container <container-id-hypervisor>**
|
||||
**Set the state of the Proxmox HA Manager for Container 100**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:<container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:<container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:100"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:100"
|
||||
```
|
||||
|
||||
**Set the state and limits of the Proxmox Container <container-id-hypervisor> in the HA Manager**
|
||||
**Set the state and limits of the Proxmox Container 100 in the HA Manager**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot <container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot 100"
|
||||
```
|
||||
|
||||
**Destroy the Proxmox Container <container-id-hypervisor> forcefully**
|
||||
**Destroy the Proxmox Container 100 forcefully**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy <container-id-hypervisor> --force --purge"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy 100 --force --purge"
|
||||
```
|
||||
|
||||
**Move the Proxmox Container <container-id-hypervisor> to another host**
|
||||
**Move the Proxmox Container 100 to another host**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate <container-id-hypervisor> hv2"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate 100 hv2"
|
||||
```
|
||||
|
||||
### SSH Connection
|
||||
@ -221,7 +220,7 @@ chown -R aptly:aptly /home/aptly/.ssh/
|
||||
2. **Backup before starting**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
3. **Install Required Dependencies**
|
||||
@ -624,11 +623,11 @@ chown -R aptly:aptly /home/aptly/.ssh/
|
||||
14. **Back-up post installation**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
```
|
||||
|
||||
15. **Start the server**
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
```
|
||||
|
||||
@ -1,626 +0,0 @@
|
||||
# Installing Dolibarr on Debian
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installing Dolibarr on Debian](#installing-dolibarr-on-debian)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Why Dolibarr?](#why-dolibarr)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [What This Guide Covers](#what-this-guide-covers)
|
||||
- [Placeholders](#placeholders)
|
||||
- [Important Warnings and Security Practices](#important-warnings-and-security-practices)
|
||||
- [Useful Commands and Information](#useful-commands-and-information)
|
||||
- [Documentation](#documentation)
|
||||
- [Links](#links)
|
||||
- [Software on the Machine](#software-on-the-machine)
|
||||
- [Paths](#paths)
|
||||
- [Proxmox Commands](#proxmox-commands)
|
||||
- [SSH Connection](#ssh-connection)
|
||||
- [Installation Procedure](#installation-procedure)
|
||||
|
||||
## Introduction
|
||||
|
||||
Welcome to the installation guide for Dolibarr on Debian. Dolibarr is an open-source ERP and CRM platform designed for small and medium-sized businesses, offering features such as invoicing, accounting, inventory management, and customer relationship management through a clean, web-based interface. By following this guide, you’ll be able to install and configure Dolibarr on your Debian system efficiently.
|
||||
|
||||
## Why Dolibarr?
|
||||
|
||||
Dolibarr is a flexible and open-source ERP/CRM solution that allows you to keep full control over your business data and infrastructure. It covers a wide range of needs for small and medium-sized organizations, including invoicing, accounting, customer management, inventory, and project tracking, all from a single web interface. Being self-hosted, Dolibarr gives you data ownership, transparency, and the ability to customize or extend the platform to fit your workflows without relying on third-party SaaS providers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin the installation process, ensure that your Debian system meets the following requirements:
|
||||
|
||||
- Debian GNU/Linux 9 (Stretch) or later
|
||||
- Access to a terminal with sudo privileges
|
||||
- Basic familiarity with the command line interface
|
||||
- Stable internet connection to download necessary packages
|
||||
|
||||
## What This Guide Covers
|
||||
|
||||
This guide will walk you through the installation of Dolibarr on Debian step by step, covering:
|
||||
|
||||
1. **Installation**: Installing Dolibarr from the official Git repository.
|
||||
2. **Configuration**: Adjusting Dolibarr and system settings to match your environment.
|
||||
3. **Web Setup**: Configuring Dolibarr with Apache and accessing the web installer.
|
||||
4. **Security**: Hardening Debian, PHP, and Dolibarr for a production setup.
|
||||
|
||||
## Placeholders
|
||||
|
||||
Replace the placeholders below with the appropriate values for your setup:
|
||||
|
||||
- **User Details**
|
||||
|
||||
- Username: `<username>` (e.g., admin)
|
||||
- Username - Hypervisor: `<username-hypervisor>` (e.g., admin)
|
||||
|
||||
- **Server Configuration**
|
||||
|
||||
- Server IP address: `<server-ip-address>` (e.g., 192.168.1.100)
|
||||
- Hostname - Intranet: `<hostname-intranet>` (e.g., dolibarr-server.domain.com)
|
||||
- Hostname - Internet: `<hostname-internet>` (e.g., dolibarr.domain.com)
|
||||
- Hostname - Hypervisor: `<hostname-hypervisor>` (e.g., proxmox-hypervisor.domain.com)
|
||||
- Hostname - Hypervisor NAS: `<hostname-hypervisor-nas>` (e.g., nas-server.domain.com)
|
||||
- Name - Hypervisor NAS: `<name-hypervisor-nas>` (e.g., nas-server)
|
||||
- Container ID - Hypervisor: `<container-id-hypervisor>` (e.g., 100)
|
||||
|
||||
- **SSH Keys**
|
||||
|
||||
- SSH key - Proxmox: `<ssh-key-proxmox>` (e.g., /home/user/.ssh/id_rsa.pub)
|
||||
- SSH key - Client: `<ssh-key-client>` (e.g., /home/user/.ssh/client_id_rsa.pub)
|
||||
|
||||
- **Networking**
|
||||
|
||||
- Wireguard port: `<wireguard-port>` (e.g., 51820)
|
||||
|
||||
- **Dolibarr specifics post-install**
|
||||
|
||||
- Contract paths: `<dolibarr-contract-paths-source>` (e.g., /home/username/contracts/)
|
||||
- Fonts paths: `<dolibarr-fonts-paths-source>` (e.g., /home/username/fonts/)
|
||||
|
||||
## Important Warnings and Security Practices
|
||||
|
||||
Before executing any commands in this documentation, please adhere to the following guidelines to ensure the security and integrity of the system:
|
||||
|
||||
1. **Execute Commands with Caution**: Always review and understand a command before executing it. Misuse of commands can lead to data loss or system instability.
|
||||
2. **Backup Command Execution**: The backup command must be executed only by authorized users. Ensure that proper permissions are set to prevent unauthorized access to backup files.
|
||||
3. **Regular Backups**: Maintain regular backups of all critical data. It is advisable to use automated backup solutions and verify backup integrity periodically.
|
||||
4. **System Updates**: Regularly update the system and all installed packages to protect against vulnerabilities. Use the package manager responsibly to avoid potential conflicts.
|
||||
5. **Monitor System Logs**: Continuously monitor system logs for any unusual activity. Use logging tools to help identify potential security breaches or system failures.
|
||||
6. **User Permissions**: Ensure that user permissions are strictly managed. Limit access to sensitive commands and data to only those who need it to perform their job functions.
|
||||
7. **Network Security**: Implement proper network security measures, such as firewalls and intrusion detection systems, to protect against external threats.
|
||||
8. **Data Encryption**: Encrypt sensitive data at rest and in transit to prevent unauthorized access.
|
||||
|
||||
By following these practices, you will help maintain the security and stability of the system while minimizing the risk of data loss or compromise.
|
||||
|
||||
## Useful Commands and Information
|
||||
|
||||
### Documentation
|
||||
|
||||
- [Setup other](https://wiki.dolibarr.org/index.php?title=Setup_Other)
|
||||
- [Create an ODT or ODS document template](https://wiki.dolibarr.org/index.php?title=Create_an_ODT_or_ODS_document_template)
|
||||
- [Setup Other](https://wiki.dolibarr.org/index.php?title=Setup_Other)
|
||||
- [List of releases, change log and compatibilities](https://wiki.dolibarr.org/index.php?title=List_of_releases,_change_log_and_compatibilities)
|
||||
- [GitHub](https://github.com/Dolibarr/dolibarr)
|
||||
|
||||
### Links
|
||||
|
||||
- [https](https://<hostname-intranet>/)
|
||||
- [http](http://<hostname-intranet>/)
|
||||
- [local https](https://<hostname-intranet>/)
|
||||
- [local http](http://<hostname-intranet>/)
|
||||
- [<server-ip-address>](http://<server-ip-address>/)
|
||||
|
||||
### Software on the Machine
|
||||
|
||||
- **Operating System**: Debian
|
||||
- **Database**: MariaDB (configured as MySQL)
|
||||
- **Web Server**: Apache
|
||||
- **Security**: GnuPG, WireGuard, UFW
|
||||
- **Other**: Git, sudo, certbot (for SSL certificate management)
|
||||
|
||||
### Paths
|
||||
|
||||
- **Dolibarr Configuration**: `/etc/dolibarr/app.ini`
|
||||
- **Dolibarr Work Path**: `/var/lib/dolibarr`
|
||||
|
||||
### Proxmox Commands
|
||||
|
||||
**List available Proxmox templates**
|
||||
|
||||
```bash
|
||||
ssh <username>@<hostname-hypervisor-nas> "ls /mnt/proxmox/template/cache/"
|
||||
```
|
||||
|
||||
**Create the container**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create <container-id-hypervisor> <name-hypervisor-nas>:vztmpl/debian-12-upgraded_12.5_amd64.tar.zst --hostname <hostname-intranet> --cores 4 --memory 4096 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:250 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox>"
|
||||
```
|
||||
|
||||
**Backup**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
**Set the state of the Proxmox HA Manager for Container <container-id-hypervisor>**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:<container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:<container-id-hypervisor>"
|
||||
```
|
||||
|
||||
**Set the state and limits of the Proxmox Container <container-id-hypervisor> in the HA Manager**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct start <container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct stop <container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot <container-id-hypervisor>"
|
||||
```
|
||||
|
||||
**Destroy the Proxmox Container <container-id-hypervisor> forcefully**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy <container-id-hypervisor> --force --purge"
|
||||
```
|
||||
|
||||
**Move the Proxmox Container <container-id-hypervisor> to another host**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate <container-id-hypervisor> hv2"
|
||||
```
|
||||
|
||||
### SSH Connection
|
||||
|
||||
**Connection with specific keys**
|
||||
|
||||
```bash
|
||||
ssh -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
ssh -i <ssh-key-client> <username>@<server-ip-address>
|
||||
```
|
||||
|
||||
**Remove offending keys from known_hosts**
|
||||
|
||||
```bash
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<hostname-intranet>"
|
||||
ssh-keygen -f "/home/<username>/.ssh/known_hosts" -R "<server-ip-address>"
|
||||
```
|
||||
|
||||
**Copy SSH public key to remote host**
|
||||
|
||||
```bash
|
||||
ssh-copy-id -i <ssh-key-client> root@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> root@<hostname-intranet>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<server-ip-address>
|
||||
ssh-copy-id -i <ssh-key-client> <username>@<hostname-intranet>
|
||||
```
|
||||
|
||||
**Transfer SSH keys and files**
|
||||
|
||||
```bash
|
||||
scp /home/<username>/.ssh/<username>* <username>@<hostname-intranet>:/home/<username>/.ssh/
|
||||
mkdir -p /home/<username>/.ssh/
|
||||
mv /home/<username>/.ssh/<username>* /home/<username>/.ssh/
|
||||
chown -R <username>:<username> /home/<username>/.ssh/
|
||||
cat /home/<username>/.ssh/<username>.pub >> /home/<username>/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
## Installation Procedure
|
||||
|
||||
1. **Fresh Debian Installation**
|
||||
|
||||
- Install a fresh Debian operating system on your new server following the standard installation procedure.
|
||||
|
||||
2. **Backup before starting**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
3. **Install Required Dependencies**
|
||||
|
||||
**Upgrade the base system**
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt upgrade -y
|
||||
```
|
||||
|
||||
**Install dependencies**
|
||||
|
||||
- [Prerequisites](https://wiki.dolibarr.org/index.php?title=Prerequisites)
|
||||
- [Dolibarr downloads](https://www.dolibarr.org/downloads.php)
|
||||
- [Dolibarr Sourceforge Debian packages](https://sourceforge.net/projects/dolibarr/files/Dolibarr%20installer%20for%20Debian-Ubuntu%20%28DoliDeb%29/20.0.3/)
|
||||
|
||||
```bash
|
||||
apt install -y git apache2 mariadb-server php php-mysql php-mbstring php-gd php-curl php-xml php-intl php-imap php-zip libapache2-mod-php sudo
|
||||
apt install -y libreoffice-common libreoffice-writer --no-install-recommends
|
||||
apt install -y fonts-dejavu fonts-liberation ttf-mscorefonts-installer
|
||||
|
||||
apt install -y gpg
|
||||
```
|
||||
|
||||
4. **Ensure Hostname**
|
||||
|
||||
```bash
|
||||
nano /etc/hosts
|
||||
# Add line: 127.0.1.1 <hostname-intranet>
|
||||
nano /etc/hostname
|
||||
# Set to: <hostname-intranet>
|
||||
hostnamectl set-hostname <hostname-intranet>
|
||||
```
|
||||
|
||||
5. **Add Users**
|
||||
|
||||
```bash
|
||||
adduser <username>
|
||||
usermod -aG sudo <username>
|
||||
```
|
||||
|
||||
```bash
|
||||
mkdir -p /home/www-data
|
||||
chown www-data:www-data /home/www-data
|
||||
usermod -d /home/www-data www-data
|
||||
usermod -s /bin/bash www-data
|
||||
```
|
||||
|
||||
```bash
|
||||
adduser --system --shell /bin/bash --gecos 'Dolibarr CRM' --group --disabled-password --home /home/www-data www-data
|
||||
```
|
||||
|
||||
6. **Setup SSH Connectors**
|
||||
|
||||
- Configure SSH connectors as per your setup script to establish secure connections to the server.
|
||||
|
||||
7. **Test users, SSH, and sudo**
|
||||
|
||||
1. **Transfer SSH keys for User**
|
||||
2. **Connect as User with SSH key**
|
||||
3. **Test sudo**
|
||||
|
||||
```bash
|
||||
sudo su -
|
||||
```
|
||||
|
||||
4. **Disconnect as root**
|
||||
|
||||
8. **Secure SSH**
|
||||
|
||||
```bash
|
||||
nano /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
```ini
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
```
|
||||
|
||||
**Restart SSH**
|
||||
|
||||
```bash
|
||||
systemctl restart ssh
|
||||
```
|
||||
|
||||
9. **Configure Firewall**
|
||||
|
||||
**Open ports**
|
||||
|
||||
```bash
|
||||
ufw allow OpenSSH
|
||||
ufw allow <wireguard-port>/udp
|
||||
```
|
||||
|
||||
**Enable firewall**
|
||||
|
||||
```bash
|
||||
ufw enable
|
||||
```
|
||||
|
||||
10. **Configure Mariadb**
|
||||
|
||||
1. **Security**
|
||||
|
||||
```bash
|
||||
mariadb-secure-installation
|
||||
mariadb
|
||||
```
|
||||
|
||||
2. **Mariadb Users**
|
||||
|
||||
```sql
|
||||
SET old_passwords=0;
|
||||
CREATE USER 'dolibarr' IDENTIFIED BY '';
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE USER IF NOT EXISTS 'dolibarr'@'192.168.1.%' IDENTIFIED BY '';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'dolibarr'@'192.168.1.%' IDENTIFIED BY '' WITH GRANT OPTION;
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'dolibarr'@'localhost' IDENTIFIED BY '' WITH GRANT OPTION;
|
||||
SET PASSWORD FOR 'dolibarr'@'localhost' = PASSWORD('');
|
||||
SET PASSWORD FOR 'dolibarr'@'192.168.1.%' = PASSWORD('');
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
3. **Create Dolibarr Database**
|
||||
|
||||
```bash
|
||||
mariadb -u root -p
|
||||
```
|
||||
|
||||
Inside the MariaDB shell:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE dolibarr;
|
||||
CREATE USER 'dolibarruser'@'localhost' IDENTIFIED BY 'strongpassword';
|
||||
GRANT ALL PRIVILEGES ON dolibarr.* TO 'dolibarruser'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
11. **Install Dolibarr**
|
||||
|
||||
[Versions](https://wiki.dolibarr.org/index.php?title=List_of_releases,_change_log_and_compatibilities)
|
||||
|
||||
```bash
|
||||
sudo su -
|
||||
mv /var/www/html /var/www/.html
|
||||
|
||||
cd /var/www
|
||||
git clone --depth 1 -b 20.0.3 https://github.com/Dolibarr/dolibarr.git dolibarr
|
||||
mv dolibarr-20.0.3 dolibarr
|
||||
```
|
||||
|
||||
12. **Create required directory structure**
|
||||
|
||||
```bash
|
||||
chmod -R 755 /var/www/dolibarr
|
||||
chown -R www-data:www-data /var/www/dolibarr
|
||||
cd dolibarr ; touch htdocs/conf/conf.php ; chown www-data htdocs/conf/conf.php
|
||||
mkdir -p /var/lib/dolibarr/sessions ; chown www-data /var/lib/dolibarr/sessions
|
||||
mkdir -p /var/lib/dolibarr/documents ; chown www-data /var/lib/dolibarr/documents
|
||||
```
|
||||
|
||||
13. **Apache Configuration**
|
||||
|
||||
1. **Allow http https**
|
||||
|
||||
```bash
|
||||
ufw allow https
|
||||
ufw allow http
|
||||
```
|
||||
|
||||
2. **Backup Default Files**
|
||||
Backup the default index.html and configuration files.
|
||||
|
||||
```bash
|
||||
mv /var/www/html /var/www/.html
|
||||
mv /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/.000-default.conf
|
||||
rm /etc/apache2/sites-enabled/000-default.conf
|
||||
```
|
||||
|
||||
3. **Create Default Server Block for Unauthorized Access**
|
||||
|
||||
4. **Site configuration**
|
||||
|
||||
Edit the Apache configuration file to proxy to the Dolibarr CRM.
|
||||
|
||||
```bash
|
||||
nano /etc/apache2/sites-available/<hostname-intranet>.conf
|
||||
```
|
||||
|
||||
Add the following configuration:
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
LogLevel info
|
||||
ServerName <hostname-intranet>
|
||||
ServerAdmin admin@fabq.ca
|
||||
DocumentRoot /var/www/dolibarr
|
||||
|
||||
# Alias for Dolibarr
|
||||
Alias / /var/www/dolibarr/htdocs/
|
||||
|
||||
<Directory /var/www/dolibarr/htdocs>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.dolibarr.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.dolibarr.log combined
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
5. **Enabling the new VirtualHost**
|
||||
|
||||
```bash
|
||||
a2ensite <hostname-intranet>.conf
|
||||
a2enmod rewrite
|
||||
```
|
||||
|
||||
6. **Managing Apache**
|
||||
|
||||
**Enable Apache**
|
||||
|
||||
```bash
|
||||
systemctl enable apache2
|
||||
```
|
||||
|
||||
**Apache reload**
|
||||
|
||||
```bash
|
||||
systemctl reload apache2
|
||||
```
|
||||
|
||||
**`Optional` Apache restart**
|
||||
|
||||
```bash
|
||||
systemctl restart apache2
|
||||
```
|
||||
|
||||
14. **Create folders**
|
||||
|
||||
```bash
|
||||
mkdir -p /var/tmp
|
||||
mkdir -p /var/upload_tmp
|
||||
```
|
||||
|
||||
15. **Set temporary file paths**
|
||||
|
||||
```bash
|
||||
nano /etc/php/8.2/apache2/php.ini
|
||||
```
|
||||
|
||||
```ini
|
||||
sys_temp_dir = "/var/tmp"
|
||||
upload_tmp_dir = "/var/upload_tmp"
|
||||
open_basedir = "/var/www/dolibarr:/var/lib/dolibarr/documents:/var/lib/dolibarr/sessions:/var/tmp:/var/upload_tmp"
|
||||
```
|
||||
|
||||
16. **Fix permissions**
|
||||
|
||||
```bash
|
||||
chown -R www-data:www-data /var/lib/php/sessions
|
||||
|
||||
chown -R www-data:www-data /var/www/dolibarr/htdocs/conf/conf.php
|
||||
chmod 640 /var/www/dolibarr/htdocs/conf/conf.php
|
||||
|
||||
find /var/www/dolibarr -type d -exec chmod 755 {} \;
|
||||
find /var/www/dolibarr -type f -exec chmod 644 {} \;
|
||||
|
||||
chmod go-w /var/lib/dolibarr/documents;
|
||||
chmod go-w /var/lib/dolibarr/sessions;
|
||||
|
||||
chown -R www-data:www-data /var/tmp
|
||||
chown -R www-data:www-data /var/upload_tmp
|
||||
chmod -R 750 /var/tmp
|
||||
chmod -R 750 /var/upload_tmp
|
||||
```
|
||||
|
||||
17. **Secure PHP**
|
||||
|
||||
```bash
|
||||
nano /etc/php/8.2/apache2/php.ini
|
||||
```
|
||||
|
||||
```ini
|
||||
session.use_strict_mode = 1
|
||||
|
||||
allow_url_fopen = No
|
||||
|
||||
disable_functions = dl, apache_note, apache_setenv, pcntl_alarm, pcntl_fork, pcntl_waitpid, pcntl_wait, pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wifcontinued, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig, pcntl_signal, pcntl_signal_get_handler, pcntl_signal_dispatch, pcntl_get_last_error, pcntl_strerror, pcntl_sigprocmask, pcntl_sigwaitinfo, pcntl_sigtimedwait, pcntl_exec, pcntl_getpriority, pcntl_setpriority, pcntl_async_signals, show_source, virtual, passthru, shell_exec, system, proc_open, popen
|
||||
|
||||
session.gc_maxlifetime = 604800
|
||||
```
|
||||
|
||||
18. **Complete the Installation through Web Interface**
|
||||
|
||||
- Open your web browser and navigate to `http://<hostname-intranet>/install/`
|
||||
- Follow the on-screen instructions to complete the installation.
|
||||
|
||||
19. **Lock installation**
|
||||
|
||||
```bash
|
||||
chmod -w /var/www/dolibarr/htdocs/conf/conf.php
|
||||
touch /var/lib/dolibarr/documents/install.lock;
|
||||
chmod go-w /var/lib/dolibarr/documents;
|
||||
```
|
||||
|
||||
20. **Secure installation**
|
||||
|
||||
```bash
|
||||
chmod -R -w /var/www/dolibarr/htdocs
|
||||
cp /etc/php/8.4/apache2/php.ini /etc/php/8.4/apache2/php.ini.back
|
||||
nano /etc/php/8.4/apache2/php.ini
|
||||
```
|
||||
|
||||
```ini
|
||||
session.save_path = /var/lib/dolibarr/sessions
|
||||
session.use_strict_mode = 1
|
||||
session.use_only_cookies = 1
|
||||
session.cookie_httponly = 1
|
||||
session.cookie_samesite = Lax
|
||||
session.gc_maxlifetime = 604800
|
||||
open_basedir = "/var/www/dolibarr:/var/lib/dolibarr/documents:/var/lib/dolibarr/sessions:/var/tmp:/var/upload_tmp"
|
||||
short_open_tag = Off
|
||||
allow_url_fopen = Off
|
||||
allow_url_include = Off
|
||||
disable_functions = dl, apache_note, apache_setenv, pcntl_alarm, pcntl_fork, pcntl_waitpid, pcntl_wait, pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wifcontinued, pcntl_wexitstatus, pcntl_wtermsig, pcntl_wstopsig, pcntl_signal, pcntl_signal_get_handler, pcntl_signal_dispatch, pcntl_get_last_error, pcntl_strerror, pcntl_sigprocmask, pcntl_sigwaitinfo, pcntl_sigtimedwait, pcntl_exec, pcntl_getpriority, pcntl_setpriority, pcntl_async_signals, show_source, virtual, passthru, shell_exec, system, proc_open, popen
|
||||
```
|
||||
|
||||
```bash
|
||||
nano /var/www/dolibarr/htdocs/conf/conf.php
|
||||
chmod o-r /var/www/dolibarr/htdocs/conf/conf.php
|
||||
```
|
||||
|
||||
```php
|
||||
$dolibarr_main_prod='1';
|
||||
```
|
||||
|
||||
21. **Configure SSL with Certbot (Optional, If directly accessed)**
|
||||
|
||||
```bash
|
||||
certbot --apache -d <hostname-internet>
|
||||
systemctl status certbot.timer
|
||||
certbot renew --dry-run
|
||||
```
|
||||
|
||||
22. **Verify installation**
|
||||
|
||||
- Verify that the Dolibarr instance is installed restored by accessing it through a web browser. Ensure that all repositories, users, and configurations are intact.
|
||||
|
||||
```bash
|
||||
systemctl status apache2
|
||||
```
|
||||
|
||||
Visit the server at [http://<hostname-internet>](http://<hostname-internet>) or [https://<hostname-internet>](https://<hostname-internet>).
|
||||
|
||||
23. **Backup post installation**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
```
|
||||
|
||||
24. **Start the server**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
```
|
||||
|
||||
25. **Move contracts**
|
||||
|
||||
```bash
|
||||
scp '<dolibarr-contract-paths-source>/contract.odt' <username>@<hostname-intranet>:~/
|
||||
|
||||
sudo su -
|
||||
mv /home/<username>/*.odt /var/lib/dolibarr/documents/doctemplates/contracts/
|
||||
chown -R www-data:www-data /var/lib/dolibarr/documents/doctemplates/contracts/
|
||||
```
|
||||
|
||||
26. **ODT to PDF**
|
||||
|
||||
[See forum 1](https://www.dolibarr.org/forum/t/solved-odt-to-pdf/16931)
|
||||
[See forum 2](https://www.dolibarr.org/forum/t/setup-dolibarr-to-generate-odt-to-pdf/22112/9)
|
||||
|
||||
```bash
|
||||
MAIN_ODT_AS_PDF
|
||||
```
|
||||
|
||||
27. **Install fonts**
|
||||
|
||||
```bash
|
||||
scp -r <dolibarr-fonts-paths-source>/Lato <username>@<hostname-intranet>:~/
|
||||
```
|
||||
|
||||
```bash
|
||||
mv /home/<username>/Lato /usr/local/share/fonts/
|
||||
chmod -R 755 /usr/local/share/fonts/
|
||||
chown -R root:root /usr/local/share/fonts/
|
||||
```
|
||||
@ -63,7 +63,6 @@ Replace the placeholders below with the appropriate values for your setup:
|
||||
- Hostname - Hypervisor: `<hostname-hypervisor>` (e.g., proxmox-hypervisor.domain.com)
|
||||
- Hostname - Hypervisor NAS: `<hostname-hypervisor-nas>` (e.g., nas-server.domain.com)
|
||||
- Name - Hypervisor NAS: `<name-hypervisor-nas>` (e.g., nas-server)
|
||||
- Container ID - Hypervisor: `<container-id-hypervisor>` (e.g., 100)
|
||||
|
||||
- **SSH Keys**
|
||||
|
||||
@ -127,40 +126,40 @@ ssh <username>@<hostname-hypervisor-nas> "ls /mnt/proxmox/template/cache/"
|
||||
**Create the container**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create <container-id-hypervisor> <name-hypervisor-nas>:vztmpl/debian-12-upgraded_12.5_amd64.tar.zst --hostname <hostname-intranet> --cores 2 --memory 2048 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:250 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox> --start 1"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct create 100 <name-hypervisor-nas>:vztmpl/debian-12-upgraded_12.5_amd64.tar.zst --hostname <hostname-intranet> --cores 2 --memory 2048 --swap 2048 --net0 name=net0,bridge=vmbr0,ip=dhcp,firewall=1 --rootfs <name-hypervisor-nas>:250 --unprivileged 1 --features nesting=1 --ssh-public-keys <ssh-key-proxmox> --start 1"
|
||||
```
|
||||
|
||||
**Backup**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
**Set the state of the Proxmox HA Manager for Container <container-id-hypervisor>**
|
||||
**Set the state of the Proxmox HA Manager for Container 100**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:<container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:<container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager add ct:100"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager remove ct:100"
|
||||
```
|
||||
|
||||
**Set the state and limits of the Proxmox Container <container-id-hypervisor> in the HA Manager**
|
||||
**Set the state and limits of the Proxmox Container 100 in the HA Manager**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot <container-id-hypervisor>"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct reboot 100"
|
||||
```
|
||||
|
||||
**Destroy the Proxmox Container <container-id-hypervisor> forcefully**
|
||||
**Destroy the Proxmox Container 100 forcefully**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy <container-id-hypervisor> --force --purge"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct destroy 100 --force --purge"
|
||||
```
|
||||
|
||||
**Move the Proxmox Container <container-id-hypervisor> to another host**
|
||||
**Move the Proxmox Container 100 to another host**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate <container-id-hypervisor> hv2"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "pct migrate 100 hv2"
|
||||
```
|
||||
|
||||
### SSH Connection
|
||||
@ -209,7 +208,7 @@ cat /home/<username>/.ssh/<username>.pub >> /home/<username>/.ssh/authorized_key
|
||||
2. **Backup before starting**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup fresh install\""
|
||||
```
|
||||
|
||||
3. **Install Required Dependencies**
|
||||
@ -504,11 +503,11 @@ cat /home/<username>/.ssh/<username>.pub >> /home/<username>/.ssh/authorized_key
|
||||
19. **Backup post installation**
|
||||
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump <container-id-hypervisor> --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state stopped"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "vzdump 100 --compress zstd --mode stop --storage <name-hypervisor-nas> --note \"$(date +'%Y-%m-%d %H:%M') Backup post installation\""
|
||||
```
|
||||
|
||||
20. **Start the server**
|
||||
```bash
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:<container-id-hypervisor> --state started --max_relocate 3 --max_restart 3"
|
||||
ssh <username-hypervisor>@<hostname-hypervisor> "ha-manager set ct:100 --state started --max_relocate 3 --max_restart 3"
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue
Block a user