177 lines
6.8 KiB
HTML
177 lines
6.8 KiB
HTML
{% 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 %}
|