Files
oidc-server/templates/admin_clients.html

281 lines
11 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.
{{template "header" .}}
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="text-dark">
<i class="bi bi-grid-3x3-gap-fill me-2"></i>客户端管理
</h2>
<button type="button" class="btn btn-primary px-4 py-2 d-flex align-items-center" data-bs-toggle="modal" data-bs-target="#newClientModal">
<i class="bi bi-plus-lg me-2"></i>新建客户端
</button>
</div>
<div class="card shadow-sm">
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead class="bg-light">
<tr>
<th class="px-4" style="width: 20%">客户端ID</th>
<th class="px-4" style="width: 20%">客户端密钥</th>
<th class="px-4" style="width: 10%">名称</th>
<th class="px-4" style="width: 30%">重定向URI</th>
<th class="px-4" style="width: 12%">创建时间</th>
<th class="px-4 text-end" style="width: 8%">操作</th>
</tr>
</thead>
<tbody>
{{range .clients}}
<tr>
<td class="px-4 align-middle text-truncate" title="{{.ClientID}}">{{.ClientID}}</td>
<td class="px-4 align-middle text-truncate" title="{{.ClientSecret}}">{{.ClientSecret}}</td>
<td class="px-4 align-middle text-truncate" title="{{.ClientName}}">{{.ClientName}}</td>
<td class="px-4 align-middle">
<div class="text-truncate" title="{{range $i, $uri := .RedirectURIs}}{{if $i}}, {{end}}{{$uri}}{{end}}">
{{range $i, $uri := .RedirectURIs}}{{if $i}}, {{end}}{{$uri}}{{end}}
</div>
</td>
<td class="px-4 align-middle">{{.ClientIDIssuedAt.Format "2006-01-02 15:04:05"}}</td>
<td class="px-4 align-middle text-end">
{{$redirectURIsStr := ""}}
{{range $i, $uri := .RedirectURIs}}
{{if $i}}
{{$redirectURIsStr = printf "%s,%s" $redirectURIsStr $uri}}
{{else}}
{{$redirectURIsStr = $uri}}
{{end}}
{{end}}
<div class="btn-group">
<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#editClientModal"
data-client-id="{{.ClientID}}"
data-client-name="{{.ClientName}}"
data-redirect-uris="{{$redirectURIsStr}}">
<i class="bi bi-pencil-square"></i>
</button>
<button type="button" class="btn btn-outline-danger btn-sm" onclick="deleteClient('{{.ClientID}}')">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
<nav class="mt-4">
<ul class="pagination justify-content-center">
{{if gt .page 1}}
<li class="page-item">
<a class="page-link" href="?page={{subtract .page 1}}&page_size={{.pageSize}}">
<i class="bi bi-chevron-left"></i>
</a>
</li>
{{end}}
<li class="page-item">
<a class="page-link" href="?page={{add .page 1}}&page_size={{.pageSize}}">
<i class="bi bi-chevron-right"></i>
</a>
</li>
</ul>
</nav>
</div>
<!-- 新建客户端模态框 -->
<div class="modal fade" id="newClientModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header border-bottom-0">
<h5 class="modal-title">
<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 px-4">
<form id="newClientForm">
<div class="mb-4">
<label for="newClientName" class="form-label">名称</label>
<input type="text" class="form-control" id="newClientName" required>
</div>
<div class="mb-4">
<label for="newRedirectURIs" class="form-label">重定向URI</label>
<textarea class="form-control" id="newRedirectURIs" rows="3" required></textarea>
<div class="form-text text-muted">每行一个URI或用逗号分隔多个URI</div>
</div>
</form>
</div>
<div class="modal-footer border-top-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary px-4" onclick="createClient()">保存</button>
</div>
</div>
</div>
</div>
<!-- 编辑客户端模态框 -->
<div class="modal fade" id="editClientModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header border-bottom-0">
<h5 class="modal-title">
<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 px-4">
<form id="editClientForm">
<input type="hidden" id="editClientID">
<div class="mb-4">
<label for="editClientName" class="form-label">名称</label>
<input type="text" class="form-control" id="editClientName" required>
</div>
<div class="mb-4">
<label for="editRedirectURIs" class="form-label">重定向URI</label>
<textarea class="form-control" id="editRedirectURIs" rows="3" required></textarea>
<div class="form-text text-muted">每行一个URI或用逗号分隔多个URI</div>
</div>
</form>
</div>
<div class="modal-footer border-top-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary px-4" onclick="updateClient()">保存</button>
</div>
</div>
</div>
</div>
<style>
.table th {
font-weight: 600;
border-bottom-width: 1px;
}
.table td {
vertical-align: middle;
white-space: nowrap;
max-width: 0;
}
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.btn-group .btn {
padding: 0.375rem 0.75rem;
}
.modal-dialog {
max-width: 500px;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}
.card {
border-radius: 0.5rem;
border: none;
}
.page-link {
padding: 0.5rem 0.75rem;
border-radius: 0.25rem;
margin: 0 0.25rem;
}
.page-link:hover {
background-color: #e9ecef;
}
.btn-group .btn i {
font-size: 14px;
}
</style>
<script>
// 创建新客户端
function createClient() {
const clientName = document.getElementById('newClientName').value;
const redirectURIs = document.getElementById('newRedirectURIs').value
.split(/[\n,]/)
.map(uri => uri.trim())
.filter(uri => uri.length > 0);
fetch('/api/clients', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_name: clientName,
redirect_uris: redirectURIs,
token_endpoint_auth_method: 'client_secret_basic',
grant_types: ['authorization_code'],
response_types: ['code']
})
})
.then(response => {
if (response.ok) {
location.reload();
} else {
alert('创建失败,请重试');
}
});
}
// 编辑客户端模态框数据填充
document.getElementById('editClientModal').addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const clientId = button.getAttribute('data-client-id');
const clientName = button.getAttribute('data-client-name');
const redirectURIs = button.getAttribute('data-redirect-uris');
document.getElementById('editClientID').value = clientId;
document.getElementById('editClientName').value = clientName;
document.getElementById('editRedirectURIs').value = redirectURIs.split(',').join('\n');
});
// 更新客户端
function updateClient() {
const clientID = document.getElementById('editClientID').value;
const clientName = document.getElementById('editClientName').value;
const redirectURIs = document.getElementById('editRedirectURIs').value
.split(/[\n,]/)
.map(uri => uri.trim())
.filter(uri => uri.length > 0);
fetch(`/api/clients/${clientID}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_name: clientName,
redirect_uris: redirectURIs,
token_endpoint_auth_method: 'client_secret_basic',
grant_types: ['authorization_code'],
response_types: ['code']
})
})
.then(response => {
if (response.ok) {
location.reload();
} else {
alert('更新失败,请重试');
}
});
}
// 删除客户端
function deleteClient(clientID) {
if (!confirm('确定要删除这个客户端吗?')) {
return;
}
fetch(`/api/clients/${clientID}`, {
method: 'DELETE'
})
.then(response => {
if (response.ok) {
location.reload();
} else {
alert('删除失败,请重试');
}
});
}
</script>
{{template "footer" .}}