@using Dpz.Core.Infrastructure
@model Dpz.Core.Public.Entity.Auth.DpzApplication?
@{
ViewBag.Title = Model != null && Model.Id != default ? "编辑客户端" : "创建客户端";
Layout = "_AdminLayout";
var isEdit = Model != null && Model.Id != default;
var selectedPermissions = (Model?.Permissions ?? new List<string>()).ToHashSet();
var groups = AuthHelper.OpenIddictPermissionCache.Value;
}
<div class="page-header mb-3">
<h1 class="mb-1">@(isEdit ? "编辑客户端" : "创建客户端")</h1>
<p class="text-body-secondary">配置 OpenIddict 客户端的基础信息与回调地址</p>
<div class="mt-2">
<a class="btn btn-secondary btn-sm" href="@Url.Action("Index")">返回列表</a>
</div>
</div>
@if (TempData["message"] is string message)
{
<div class="alert alert-danger" role="alert">@message</div>
}
<form method="post" asp-action="@(isEdit ? "Update" : "Create")" autocomplete="off" data-submit-loading="true" data-loading-target=".container-xxl">
@if (isEdit)
{
<input type="hidden" name="Id" value="@Model!.Id"/>
}
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="DisplayName">显示名称<span class="text-danger">*</span></label>
<input id="DisplayName" name="DisplayName" class="form-control" value="@Model?.DisplayName" placeholder="用于展示的友好名称" required />
</div>
<div class="col-md-6">
<label class="form-label" for="ClientId">ClientId<span class="text-danger">*</span></label>
<input id="ClientId" name="ClientId" class="form-control" value="@Model?.ClientId" placeholder="唯一客户端标识" required @(isEdit ? "readonly" : null) />
<div class="form-text">创建后不可修改</div>
</div>
@if (!isEdit)
{
<div class="col-md-6">
<label class="form-label" for="ClientSecret">ClientSecret<span class="text-danger">*</span></label>
<input id="ClientSecret" name="ClientSecret" class="form-control" placeholder="仅在创建时设置" value="@ApplicationTools.GenerateSecret()" />
<div class="form-text">编辑时不允许修改</div>
</div>
}
<div class="col-md-6">
<label class="form-label" for="ApplicationType">应用类型</label>
<input id="ApplicationType" name="ApplicationType" class="form-control" value="@Model?.ApplicationType" placeholder="如 web/native/spa" />
</div>
<div class="col-md-6">
<label class="form-label" for="ClientType">客户端类型<span class="text-danger">*</span></label>
@{
var current = Model?.ClientType;
var confidential = OpenIddict.Abstractions.OpenIddictConstants.ClientTypes.Confidential;
var publicValue = OpenIddict.Abstractions.OpenIddictConstants.ClientTypes.Public;
}
<select id="ClientType" name="ClientType" class="form-select">
@if (string.Equals(current, confidential, StringComparison.OrdinalIgnoreCase))
{
<option value="@confidential" selected>@confidential</option>
}
else
{
<option value="@confidential">@confidential</option>
}
@if (string.Equals(current, publicValue, StringComparison.OrdinalIgnoreCase))
{
<option value="@publicValue" selected>@publicValue</option>
}
else
{
<option value="@publicValue">@publicValue</option>
}
</select>
</div>
<div class="col-12">
<label class="form-label" for="RedirectUris">重定向地址(分号;分隔)</label>
<textarea id="RedirectUris" name="RedirectUris" class="form-control" placeholder="例如:https://example.com/callback;https://example.com/other">@Model?.RedirectUris</textarea>
<div class="form-text">多个地址以分号分隔;将用于授权完成后的回调</div>
</div>
<div class="col-12">
<label class="form-label" for="PostLogoutRedirectUri">登出重定向地址</label>
<input id="PostLogoutRedirectUri" name="PostLogoutRedirectUri" class="form-control" value="@Model?.PostLogoutRedirectUri" placeholder="例如:https://example.com/logout-callback" />
</div>
<div class="col-12">
<label class="form-label" for="Logo">客户端 Logo(URL)</label>
<input id="Logo" name="logo" class="form-control" value="@(Model?.Properties?["Logo"]?.AsString)" placeholder="https://.../logo.png" />
<div class="form-text">用于展示在授权页与列表页的图标</div>
</div>
<div class="col-12">
<label class="form-label">权限</label>
<div class="chip-groups">
@foreach (var group in groups.OrderBy(g => g.Key, StringComparer.Ordinal))
{
<div>
<div class="chip-group-title">@group.Key</div>
<div class="chips">
@foreach (var permission in group.Value)
{
var inputId = $"perm_{Math.Abs(permission.GetHashCode())}";
var isChecked = selectedPermissions.Contains(permission);
<label class="chip" for="@inputId">
<input type="checkbox" id="@inputId" name="PermissionsSelection" value="@permission" @(isChecked ? "checked" : null) />
<span class="chip-text">@permission</span>
</label>
}
</div>
</div>
}
</div>
<div class="form-text">从内置 OpenIddict 权限中选择需要的项,可多选</div>
</div>
<div class="col-md-4">
<label class="form-label" for="pinCode">2FA PIN<span class="text-danger">*</span></label>
<input id="pinCode" name="pinCode" class="form-control" placeholder="6位验证码" inputmode="numeric" pattern="[0-9]{6}" maxlength="6" required />
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-3">
<a class="btn btn-secondary" href="@Url.Action("Index")">返回</a>
<button type="submit" class="btn btn-primary" data-loading-text="正在保存...">@(isEdit ? "保存修改" : "创建客户端")</button>
</div>
</form>
<div class="footer">
<p>请合理配置回调地址与权限,避免泄露敏感信息</p>
</div>
@section Scripts {
<script src="@Url.Content("~/js/ui-components.js")" asp-append-version="true"></script>
<script src="@Url.Content("~/js/application-upsert.js")" asp-append-version="true"></script>
}