diff --git a/backend/app/services/scheduler/schedule_account.py b/backend/app/services/scheduler/schedule_account.py index 98a765b..445bb09 100644 --- a/backend/app/services/scheduler/schedule_account.py +++ b/backend/app/services/scheduler/schedule_account.py @@ -77,103 +77,106 @@ def run_account_check(schedule_log_id: int, db: Session): result.mailbox_done_at = now_tw() fail_reasons.append(f"mailbox: {e}") - # [3] NC user check - try: - from app.core.config import settings as _cfg - nc = NextcloudClient(tenant.domain, _cfg.NC_ADMIN_USER, _cfg.NC_ADMIN_PASSWORD) - nc_exists = nc.user_exists(account.sso_account) - if nc_exists: - result.nc_result = True - else: - created = nc.create_user(account.sso_account, account.default_password, account.quota_limit) - result.nc_result = created - # 確保 quota 設定正確(無論新建或已存在) - if result.nc_result and account.quota_limit: - nc.set_user_quota(account.sso_account, account.quota_limit) + # [3] NC user + Mail(manager 租戶無 NC container,跳過) + if tenant.is_manager: + result.nc_result = True result.nc_done_at = now_tw() - except Exception as e: - result.nc_result = False - result.nc_done_at = now_tw() - fail_reasons.append(f"nc: {e}") - - # [3.5] NC 台灣國定假日行事曆訂閱 - # 前置條件:NC 帳號已存在;MKCALENDAR 為 idempotent(已存在回傳 405 視為成功) - TW_HOLIDAYS_ICS_URL = "https://www.officeholidays.com/ics-clean/taiwan" - if result.nc_result: + result.nc_mail_result = True + result.nc_mail_done_at = now_tw() + else: + # [3] NC user check try: - cal_ok = nc.subscribe_calendar( - account.sso_account, - "taiwan-public-holidays", - "台灣國定假日", - TW_HOLIDAYS_ICS_URL, - "#e9322d", - ) - if cal_ok: - logger.info(f"NC calendar subscribed [{account.sso_account}]: taiwan-public-holidays") - else: - logger.warning(f"NC calendar subscribe failed [{account.sso_account}]") - except Exception as e: - logger.warning(f"NC calendar subscribe error [{account.sso_account}]: {e}") - - # [4] NC Mail 帳號設定 - # 前置條件:NC 帳號已建立 + mailbox 已建立 - try: - if result.nc_result and result.mailbox_result: - import paramiko from app.core.config import settings as _cfg - is_active = tenant.status == "active" - nc_container = f"nc-{tenant.code}" if is_active else f"nc-{tenant.code}-test" - email = account.email or f"{account.sso_account}@{tenant.domain}" - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(_cfg.DOCKER_SSH_HOST, username=_cfg.DOCKER_SSH_USER, timeout=15) - - def _ssh_run(cmd, timeout=60): - """執行 SSH 指令並回傳輸出,超時返回空字串""" - _, stdout, _ = ssh.exec_command(cmd) - stdout.channel.settimeout(timeout) - try: - out = stdout.read().decode().strip() - except Exception: - out = "" - return out - - # 確認是否已設定(occ mail:account:export 回傳帳號列表) - export_out = _ssh_run( - f"docker exec -u www-data {nc_container} " - f"php /var/www/html/occ mail:account:export {account.sso_account} 2>/dev/null" - ) - already_set = len(export_out) > 10 and "imap" in export_out.lower() - - if already_set: - result.nc_mail_result = True + nc = NextcloudClient(tenant.domain, _cfg.NC_ADMIN_USER, _cfg.NC_ADMIN_PASSWORD) + nc_exists = nc.user_exists(account.sso_account) + if nc_exists: + result.nc_result = True else: - display = account.legal_name or account.sso_account - create_cmd = ( - f"docker exec -u www-data {nc_container} " - f"php /var/www/html/occ mail:account:create " - f"'{account.sso_account}' '{display}' '{email}' " - f"10.1.0.254 143 none '{email}' '{account.default_password}' " - f"10.1.0.254 587 none '{email}' '{account.default_password}' 2>&1" - ) - out_text = _ssh_run(create_cmd) - logger.info(f"NC mail:account:create [{account.sso_account}]: {out_text}") - result.nc_mail_result = ( - "error" not in out_text.lower() and "exception" not in out_text.lower() - ) + created = nc.create_user(account.sso_account, account.default_password, account.quota_limit) + result.nc_result = created + # 確保 quota 設定正確(無論新建或已存在) + if result.nc_result and account.quota_limit: + nc.set_user_quota(account.sso_account, account.quota_limit) + result.nc_done_at = now_tw() + except Exception as e: + result.nc_result = False + result.nc_done_at = now_tw() + fail_reasons.append(f"nc: {e}") - ssh.close() - else: + # [3.5] NC 台灣國定假日行事曆訂閱 + TW_HOLIDAYS_ICS_URL = "https://www.officeholidays.com/ics-clean/taiwan" + if result.nc_result: + try: + cal_ok = nc.subscribe_calendar( + account.sso_account, + "taiwan-public-holidays", + "台灣國定假日", + TW_HOLIDAYS_ICS_URL, + "#e9322d", + ) + if cal_ok: + logger.info(f"NC calendar subscribed [{account.sso_account}]: taiwan-public-holidays") + else: + logger.warning(f"NC calendar subscribe failed [{account.sso_account}]") + except Exception as e: + logger.warning(f"NC calendar subscribe error [{account.sso_account}]: {e}") + + # [4] NC Mail 帳號設定 + try: + if result.nc_result and result.mailbox_result: + import paramiko + from app.core.config import settings as _cfg + is_active = tenant.status == "active" + nc_container = f"nc-{tenant.code}" if is_active else f"nc-{tenant.code}-test" + email = account.email or f"{account.sso_account}@{tenant.domain}" + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(_cfg.DOCKER_SSH_HOST, username=_cfg.DOCKER_SSH_USER, timeout=15) + + def _ssh_run(cmd, timeout=60): + _, stdout, _ = ssh.exec_command(cmd) + stdout.channel.settimeout(timeout) + try: + out = stdout.read().decode().strip() + except Exception: + out = "" + return out + + export_out = _ssh_run( + f"docker exec -u www-data {nc_container} " + f"php /var/www/html/occ mail:account:export {account.sso_account} 2>/dev/null" + ) + already_set = len(export_out) > 10 and "imap" in export_out.lower() + + if already_set: + result.nc_mail_result = True + else: + display = account.legal_name or account.sso_account + create_cmd = ( + f"docker exec -u www-data {nc_container} " + f"php /var/www/html/occ mail:account:create " + f"'{account.sso_account}' '{display}' '{email}' " + f"10.1.0.254 143 none '{email}' '{account.default_password}' " + f"10.1.0.254 587 none '{email}' '{account.default_password}' 2>&1" + ) + out_text = _ssh_run(create_cmd) + logger.info(f"NC mail:account:create [{account.sso_account}]: {out_text}") + result.nc_mail_result = ( + "error" not in out_text.lower() and "exception" not in out_text.lower() + ) + + ssh.close() + else: + result.nc_mail_result = False + fail_reasons.append( + f"nc_mail: skipped (nc={result.nc_result}, mailbox={result.mailbox_result})" + ) + result.nc_mail_done_at = now_tw() + except Exception as e: result.nc_mail_result = False - fail_reasons.append( - f"nc_mail: skipped (nc={result.nc_result}, mailbox={result.mailbox_result})" - ) - result.nc_mail_done_at = now_tw() - except Exception as e: - result.nc_mail_result = False - result.nc_mail_done_at = now_tw() - fail_reasons.append(f"nc_mail: {e}") + result.nc_mail_done_at = now_tw() + fail_reasons.append(f"nc_mail: {e}") # [5] Quota try: