Files
alterdb/templates/database_form.html
T
2026-10-02 13:12:49 +08:00

177 lines
6.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "_shell.html" %}
{% block title %}{% if let Some(db) = editing %}編輯連接:{{ db.name }}{% else %}新增連接{% endif %} — alterdb{% endblock %}
{% block style %}
<style>
.form-card {
padding: 1.4rem 1.6rem;
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
border-radius: 10px;
margin-bottom: 1.5rem;
}
.form-card h2 { margin: 0 0 1.1rem; }
.error {
margin: 0 0 1rem;
padding: .5rem .75rem;
border-radius: 6px;
color: light-dark(#b3261e, #ffb4ab);
background: color-mix(in srgb, currentColor 8%, transparent);
}
.db-form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem 1.4rem;
}
.db-form label {
display: flex;
flex-direction: column;
gap: .35rem;
font-weight: 600;
}
.db-form input, .db-form select {
font: inherit;
padding: .45rem .65rem;
border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
border-radius: 6px;
background: transparent;
color: inherit;
}
.db-form .form-actions {
grid-column: 1 / -1;
display: flex;
gap: .6rem;
}
.db-form .form-actions button,
.db-form .form-actions a {
font: inherit;
font-weight: 600;
padding: .5rem 1.1rem;
border: none;
border-radius: 6px;
cursor: pointer;
color: inherit;
text-decoration: none;
background: color-mix(in srgb, currentColor 12%, transparent);
}
.db-form .form-actions a {
background: transparent;
border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.db-form .form-actions button:hover,
.db-form .form-actions a:hover { background: color-mix(in srgb, currentColor 22%, transparent); }
.db-form .hint { font-size: .85rem; opacity: .7; font-weight: 400; }
@media (max-width: 34rem) { .db-form { grid-template-columns: 1fr; } }
</style>
{% endblock %}
{% block page %}
<div class="form-card">
<h2>{% if let Some(db) = editing %}編輯連接:{{ db.name }}{% else %}新增連接{% endif %}</h2>
<p id="form-error" class="error" role="alert" hidden></p>
<form id="db-form" class="db-form"
data-mode="{% if editing.is_some() %}edit{% else %}create{% endif %}"
{% if let Some(db) = editing %}data-id="{{ db.id }}"{% endif %}>
<label>名稱
<input name="name" required maxlength="128" autocomplete="off"
value="{% if let Some(db) = editing %}{{ db.name }}{% endif %}">
</label>
<label>類型
<select name="type">
<option value="mysql"{% if let Some(db) = editing %}{% if db.db_type.as_str() == "mysql" %} selected{% endif %}{% endif %}>MySQL</option>
<option value="postgres"{% if let Some(db) = editing %}{% if db.db_type.as_str() == "postgres" %} selected{% endif %}{% endif %}>PostgreSQL</option>
</select>
</label>
<label>主機
<input name="host" required maxlength="255" autocomplete="off" placeholder="如 10.0.0.5 或 db.example.com"
value="{% if let Some(db) = editing %}{{ db.host }}{% endif %}">
</label>
<label>埠
<input name="port" type="number" min="1" max="65535" inputmode="numeric"
value="{% if let Some(db) = editing %}{{ db.port }}{% endif %}">
</label>
<label>用戶名
<input name="username" required maxlength="64" autocomplete="off"
value="{% if let Some(db) = editing %}{{ db.username }}{% endif %}">
</label>
<label>密碼
<input name="password" type="password" maxlength="128" autocomplete="new-password"
placeholder="{% if editing.is_some() %}留空保留原密碼{% else %}可留空{% endif %}">
</label>
<label>資料庫名
<input name="database_name" required maxlength="64" autocomplete="off"
value="{% if let Some(db) = editing %}{{ db.database_name }}{% endif %}">
</label>
<div class="form-actions">
<button type="submit">儲存</button>
<a href="/databases">取消</a>
</div>
<p class="hint" style="grid-column: 1 / -1; margin: 0;">埠留空按類型補默認(MySQL 3306 / PostgreSQL 5432);密碼不會顯示與回填。</p>
</form>
</div>
{% endblock %}
{% block script %}
<script>
(() => {
'use strict';
const TYPE_PORTS = { mysql: 3306, postgres: 5432 };
const form = document.getElementById('db-form');
const formError = document.getElementById('form-error');
const typeSelect = form.elements['type'];
const portInput = form.elements['port'];
const passwordInput = form.elements['password'];
const editing = form.dataset.mode === 'edit';
const editingId = form.dataset.id || null;
function updatePortHint() {
portInput.placeholder = '留空使用默認 ' + TYPE_PORTS[typeSelect.value];
}
updatePortHint();
async function api(path, options) {
const response = await fetch(path, options);
if (!response.ok) {
let message = '請求失敗(HTTP ' + response.status + ')';
try {
const body = await response.json();
if (body.error) { message = body.error; }
} catch (_) { /* 非 JSON 錯誤體,保留默認訊息 */ }
throw new Error(message);
}
return response.status === 204 ? null : response.json();
}
form.addEventListener('submit', async (event) => {
event.preventDefault();
formError.hidden = true;
const payload = {
name: form.elements['name'].value.trim(),
type: typeSelect.value,
host: form.elements['host'].value.trim(),
username: form.elements['username'].value.trim(),
database_name: form.elements['database_name'].value.trim(),
};
const port = portInput.value.trim();
if (port !== '') { payload.port = Number(port); }
// 密碼留空時省略該鍵:新增視為空密碼(後端默認)、編輯保留原值
if (passwordInput.value !== '') { payload.password = passwordInput.value; }
const url = editing ? '/api/databases/' + editingId : '/api/databases';
try {
await api(url, {
method: editing ? 'PUT' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
window.location.assign('/databases');
} catch (err) {
formError.textContent = err.message;
formError.hidden = false;
}
});
typeSelect.addEventListener('change', updatePortHint);
})();
</script>
{% endblock %}