mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
e3b166803a
- 14 API endpoints: accounts, packages, domains, dns, email, databases, ftp, ssl, cron, php, files, stats, webmail, server_setup - 8 lib managers: AccountManager, VhostManager, DNSManager, EmailManager, DatabaseManager, PHPManager, FTPManager, SSLManager - Roundcube webmail on dedicated port 8883 (sequenced after 8880/8881/8882) - Custom NovaCPX SVG icon sprite (30+ unique icons), logo, mark, favicon - PORT_WEBMAIL=8883 wired into Core.php, install.sh, UFW, Fail2Ban, credentials file Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.6 KiB
PHP
67 lines
2.6 KiB
PHP
<?php
|
|
/**
|
|
* FTPManager — ProFTPD virtual user management via MySQL
|
|
*/
|
|
class FTPManager {
|
|
|
|
public static function createAccount(int $accountId, string $username, string $password, string $homeDir, int $quotaMb = 0): int {
|
|
$db = DB::getInstance();
|
|
$hashed = password_hash($password, PASSWORD_BCRYPT);
|
|
|
|
// Create system user for FTP (no shell, no home dir creation)
|
|
$acct = $db->fetchOne("SELECT username as owner FROM accounts WHERE id = ?", [$accountId]);
|
|
$ftpUser = strtolower(preg_replace('/[^a-z0-9_]/', '', $username));
|
|
|
|
$id = (int)$db->insert(
|
|
"INSERT INTO ftp_accounts (account_id, username, password, home_dir, quota_mb) VALUES (?,?,?,?,?)",
|
|
[$accountId, $ftpUser, $hashed, $homeDir, $quotaMb]
|
|
);
|
|
|
|
self::syncProftpd();
|
|
novacpx_log('info', "FTP account created: $ftpUser");
|
|
return $id;
|
|
}
|
|
|
|
public static function deleteAccount(int $id): void {
|
|
DB::getInstance()->execute("DELETE FROM ftp_accounts WHERE id = ?", [$id]);
|
|
self::syncProftpd();
|
|
}
|
|
|
|
public static function changePassword(int $id, string $newPassword): void {
|
|
DB::getInstance()->execute(
|
|
"UPDATE ftp_accounts SET password = ? WHERE id = ?",
|
|
[password_hash($newPassword, PASSWORD_BCRYPT), $id]
|
|
);
|
|
self::syncProftpd();
|
|
}
|
|
|
|
public static function suspend(int $id): void {
|
|
DB::getInstance()->execute("UPDATE ftp_accounts SET status = 'suspended' WHERE id = ?", [$id]);
|
|
self::syncProftpd();
|
|
}
|
|
|
|
private static function syncProftpd(): void {
|
|
// Write ProFTPD virtual users file (passwd format)
|
|
$db = DB::getInstance();
|
|
$accounts = $db->fetchAll("SELECT f.*, a.username as owner FROM ftp_accounts f JOIN accounts a ON a.id = f.account_id WHERE f.status = 'active'");
|
|
$passwd = '';
|
|
foreach ($accounts as $a) {
|
|
$uid = self::getUid($a['owner']);
|
|
$gid = self::getGid('www-data');
|
|
$passwd .= "{$a['username']}:{$a['password']}:{$uid}:{$gid}:NovaCPX FTP:{$a['home_dir']}:/sbin/nologin\n";
|
|
}
|
|
file_put_contents('/etc/proftpd/novacpx-users.passwd', $passwd);
|
|
shell_exec('systemctl reload proftpd 2>/dev/null || true');
|
|
}
|
|
|
|
private static function getUid(string $username): int {
|
|
$out = trim(shell_exec("id -u " . escapeshellarg($username) . " 2>/dev/null") ?: '33');
|
|
return (int)$out;
|
|
}
|
|
|
|
private static function getGid(string $group): int {
|
|
$out = trim(shell_exec("getent group " . escapeshellarg($group) . " | cut -d: -f3 2>/dev/null") ?: '33');
|
|
return (int)$out;
|
|
}
|
|
}
|