Initial project upload

This commit is contained in:
Kim.KANGHEE
2026-05-05 17:14:11 +09:00
commit 122b73d254
282 changed files with 72135 additions and 0 deletions
+399
View File
@@ -0,0 +1,399 @@
{% extends "base.html" %}
{% block title %}시스템 설정 - Dell Server Info{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/admin_settings.css') }}">
{% endblock %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">
<i class="bi bi-gear-fill text-primary me-2"></i>시스템 설정
</h2>
<a href="{{ url_for('admin.admin_panel') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>관리자 패널로 돌아가기
</a>
</div>
<!-- TSR 저장소 설정 섹션 -->
<div class="card border shadow-sm mb-4">
<div class="card-header bg-white py-3">
<div class="d-flex align-items-center justify-content-between gap-3">
<div>
<h5 class="mb-1 fw-bold">
<i class="bi bi-folder-symlink text-primary me-2"></i>TSR 저장소 설정
</h5>
<p class="text-muted small mb-0">System Control의 TSR Save가 사용할 CIFS Share URL과 인증 정보를 관리합니다.</p>
</div>
<span class="badge bg-primary-subtle text-primary border border-primary-subtle rounded-pill">
TSR Save
</span>
</div>
</div>
<div class="card-body">
<form action="{{ url_for('admin.save_tsr_share_settings') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="tsr-share-url-box mb-3">
<div class="d-flex flex-column flex-lg-row justify-content-between gap-2 mb-3">
<div>
<label for="tsrShareUrl" class="form-label fw-bold mb-1">Share URL</label>
<p class="text-muted small mb-0">TSR Save 결과가 저장될 CIFS 공유 경로를 입력합니다.</p>
</div>
<span class="tsr-share-url-badge align-self-start">
<i class="bi bi-hdd-network me-1"></i>CIFS Share
</span>
</div>
<div class="input-group input-group-lg tsr-share-url-input">
<span class="input-group-text"><i class="bi bi-link-45deg"></i></span>
<input type="text" class="form-control font-monospace" id="tsrShareUrl" name="share_url"
value="{{ tsr_share_settings.share_url }}" placeholder="//10.10.3.15/share/" required>
</div>
<div class="tsr-share-url-help mt-2">
<i class="bi bi-info-circle me-1"></i>
예: <span class="font-monospace">//10.10.3.15/share/</span>
</div>
</div>
<div class="row g-3 align-items-end">
<div class="col-md-6 col-lg-4">
<label for="tsrShareUser" class="form-label fw-semibold">Share 계정</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-person"></i></span>
<input type="text" class="form-control" id="tsrShareUser" name="share_user"
value="{{ tsr_share_settings.share_user }}" placeholder="User">
</div>
</div>
<div class="col-md-6 col-lg-4">
<label for="tsrSharePassword" class="form-label fw-semibold">Share 패스워드</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="password" class="form-control" id="tsrSharePassword" name="share_password"
value="{{ tsr_share_settings.share_password }}" placeholder="Password"
autocomplete="new-password">
</div>
</div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-2 mt-3">
<div class="text-muted small">
iDRAC 접속 계정은 기존처럼 <span class="font-monospace">IDRAC_USER</span>,
<span class="font-monospace">IDRAC_PASS</span> 환경변수를 사용합니다.
</div>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-save me-1"></i>저장
</button>
</div>
</form>
</div>
</div>
<!-- 텔레그램 봇 설정 섹션 -->
<div class="card border shadow-sm mb-4">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold">
<i class="bi bi-telegram text-info me-2"></i>텔레그램 봇 관리
</h5>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addBotModal">
<i class="bi bi-plus-lg me-1"></i>봇 추가
</button>
</div>
<div class="card-body">
<div class="alert alert-info d-flex align-items-center" role="alert">
<i class="bi bi-info-circle-fill me-2 flex-shrink-0 fs-5"></i>
<div>
등록된 모든 <strong>활성(Active)</strong> 봇에게 알림이 동시에 전송됩니다.<br>
<small class="text-muted">알림 종류: 로그인, 회원가입, 로그아웃, 서버 작업 등</small>
</div>
</div>
{% if bots %}
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4">
{% for bot in bots %}
<div class="col">
<div class="card h-100 border-0 shadow-sm hover-shadow transition-all">
<div
class="card-header bg-white border-0 pt-3 pb-0 d-flex justify-content-between align-items-start">
<div class="d-flex align-items-center">
<div class="avatar-circle bg-primary bg-opacity-10 text-primary me-2 rounded-circle d-flex align-items-center justify-content-center"
style="width: 40px; height: 40px;">
<i class="bi bi-robot fs-5"></i>
</div>
<div>
<h5 class="card-title fw-bold mb-0 text-truncate" style="max-width: 150px;"
title="{{ bot.name }}">
{{ bot.name }}
</h5>
<small class="text-muted" style="font-size: 0.75rem;">ID: {{ bot.id }}</small>
</div>
</div>
{% if bot.is_active %}
<span
class="badge bg-success-subtle text-success border border-success-subtle rounded-pill px-2">
<i class="bi bi-check-circle-fill me-1"></i>Active
</span>
{% else %}
<span
class="badge bg-secondary-subtle text-secondary border border-secondary-subtle rounded-pill px-2">
<i class="bi bi-dash-circle me-1"></i>Inactive
</span>
{% endif %}
</div>
<div class="card-body">
<p class="card-text text-muted small mb-3 text-truncate-2" style="min-height: 2.5em;">
{{ bot.description or "설명이 없습니다." }}
</p>
<div class="bg-light rounded p-2 mb-2">
<div class="d-flex align-items-center text-secondary small mb-1">
<i class="bi bi-key me-2 text-primary"></i>
<span class="fw-semibold me-1">Token:</span>
<span class="font-monospace text-truncate">{{ bot.token[:10] }}...</span>
</div>
<div class="d-flex align-items-center text-secondary small">
<i class="bi bi-chat-dots me-2 text-success"></i>
<span class="fw-semibold me-1">Chat ID:</span>
<span class="font-monospace">{{ bot.chat_id }}</span>
</div>
</div>
<div class="mb-3">
{% set types = (bot.notification_types or "").split(",") %}
{% if 'auth' in types %}
<span
class="badge bg-info-subtle text-info border border-info-subtle rounded-pill me-1"><i
class="bi bi-shield-lock-fill me-1"></i>인증</span>
{% endif %}
{% if 'activity' in types %}
<span
class="badge bg-warning-subtle text-warning border border-warning-subtle rounded-pill me-1"><i
class="bi bi-activity me-1"></i>활동</span>
{% endif %}
{% if 'system' in types %}
<span
class="badge bg-danger-subtle text-danger border border-danger-subtle rounded-pill me-1"><i
class="bi bi-gear-fill me-1"></i>시스템</span>
{% endif %}
</div>
</div>
<div class="card-footer bg-white border-0 pt-0 pb-3">
<div class="btn-group w-100 shadow-sm" role="group">
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal"
data-bs-target="#editBotModal{{ bot.id }}">
<i class="bi bi-pencil me-1"></i>수정
</button>
<button type="submit" form="testForm{{ bot.id }}"
class="btn btn-outline-primary btn-sm">
<i class="bi bi-send me-1"></i>테스트
</button>
<button type="submit" form="deleteForm{{ bot.id }}"
class="btn btn-outline-danger btn-sm" onclick="return confirm('정말 삭제하시겠습니까?');">
<i class="bi bi-trash me-1"></i>삭제
</button>
</div>
<!-- Hidden Forms -->
<form id="testForm{{ bot.id }}" action="{{ url_for('admin.test_bot', bot_id=bot.id) }}"
method="post" class="d-none">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
</form>
<form id="deleteForm{{ bot.id }}" action="{{ url_for('admin.delete_bot', bot_id=bot.id) }}"
method="post" class="d-none">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
</form>
</div>
</div>
</div>
<!-- 수정 모달 -->
<div class="modal fade" id="editBotModal{{ bot.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<form action="{{ url_for('admin.edit_bot', bot_id=bot.id) }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="modal-header bg-light">
<h5 class="modal-title fw-bold">
<i class="bi bi-pencil-square me-2"></i>봇 설정 수정
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">이름 (식별용)</label>
<input type="text" class="form-control" name="name" value="{{ bot.name }}"
required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Bot Token</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="text" class="form-control font-monospace" name="token"
value="{{ bot.token }}" required>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Chat ID</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-chat-dots"></i></span>
<input type="text" class="form-control font-monospace" name="chat_id"
value="{{ bot.chat_id }}" required>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">알림 유형</label>
<div class="d-flex gap-3">
{% set types = (bot.notification_types or "").split(",") %}
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types"
value="auth" id="edit_auth_{{ bot.id }}" {{ 'checked' if 'auth' in
types else '' }}>
<label class="form-check-label" for="edit_auth_{{ bot.id }}">
인증
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types"
value="activity" id="edit_activity_{{ bot.id }}" {{ 'checked'
if 'activity' in types else '' }}>
<label class="form-check-label" for="edit_activity_{{ bot.id }}">
활동
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types"
value="system" id="edit_system_{{ bot.id }}" {{ 'checked'
if 'system' in types else '' }}>
<label class="form-check-label" for="edit_system_{{ bot.id }}">
시스템
</label>
</div>
</div>
<small class="text-muted">선택한 알림 유형만 전송됩니다</small>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">설명</label>
<textarea class="form-control" name="description"
rows="2">{{ bot.description or '' }}</textarea>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="is_active"
id="activeCheck{{ bot.id }}" {{ 'checked' if bot.is_active else '' }}>
<label class="form-check-label" for="activeCheck{{ bot.id }}">
활성화
</label>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">취소</button>
<button type="submit" class="btn btn-primary px-4">저장</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<div class="mb-3">
<div class="d-inline-flex align-items-center justify-content-center bg-light rounded-circle"
style="width: 80px; height: 80px;">
<i class="bi bi-robot fs-1 text-secondary"></i>
</div>
</div>
<h5 class="text-muted fw-normal">등록된 텔레그램 봇이 없습니다.</h5>
<p class="text-muted small mb-4">우측 상단의 '봇 추가' 버튼을 눌러 알림을 설정하세요.</p>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addBotModal">
<i class="bi bi-plus-lg me-1"></i>봇 추가
</button>
</div>
{% endif %}
</div>
</div>
<!-- 봇 추가 모달 -->
<div class="modal fade" id="addBotModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<form action="{{ url_for('admin.add_bot') }}" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="modal-header bg-light">
<h5 class="modal-title fw-bold">
<i class="bi bi-plus-circle me-2"></i>새 텔레그램 봇 추가
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">이름 (식별용)</label>
<input type="text" class="form-control" name="name" placeholder="예: 알림용 봇" required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Bot Token</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="text" class="form-control font-monospace" name="token"
placeholder="123456:ABC..." required>
</div>
<div class="form-text">BotFather에게 받은 API Token</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Chat ID</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-chat-dots"></i></span>
<input type="text" class="form-control font-monospace" name="chat_id"
placeholder="-100..." required>
</div>
<div class="form-text">메시지를 받을 채팅방 ID (그룹은 음수)</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">알림 유형</label>
<div class="d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types" value="auth"
id="add_auth" checked>
<label class="form-check-label" for="add_auth">
인증
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types" value="activity"
id="add_activity" checked>
<label class="form-check-label" for="add_activity">
활동
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_types" value="system"
id="add_system" checked>
<label class="form-check-label" for="add_system">
시스템
</label>
</div>
</div>
<small class="text-muted">선택한 알림 유형만 전송됩니다</small>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">설명</label>
<textarea class="form-control" name="description" rows="2" placeholder="선택 사항"></textarea>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">취소</button>
<button type="submit" class="btn btn-primary px-4">추가</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}