feat: initial commit - kratos admin service

This commit is contained in:
yvan
2026-08-13 18:22:43 +08:00
commit be5362913b
183 changed files with 16186 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
import { createAdminServiceClient } from "@/services/kratos/admin/v1";
import { request } from "@umijs/max";
type Request = {
path: string;
method: string;
body: string | null;
};
function requestHandler({ path, method, body }: Request) {
// Use protojson so proto semantics (oneof, well-known types like Timestamp)
// are preserved in both directions. kratos selects the codec from the
// Content-Type / Accept subtype, so both headers must name protojson.
const headers: Record<string, string> = {
Accept: "application/protojson",
};
// Generated clients pass a body only on POST / PUT / PATCH.
if (body) {
headers["Content-Type"] = "application/protojson";
}
return request("/" + path, {
method: method,
data: body,
headers: headers,
});
}
// Generated clients are stateless closures over the handler, so one instance
// per service is enough. Add a resource by adding one line here; call sites
// keep importing `services` unchanged.
export const services = {
admin: createAdminServiceClient(requestHandler),
};
/**
* The structured error body the server returns (kratos `errors.Status` in its
* protojson form). This is a data shape, not an Error subclass: narrow it with
* `asServiceError` rather than `instanceof`.
*/
export type ServiceError = {
code?: number;
reason?: string;
message?: string;
metadata?: Record<string, string>;
};
/**
* Read the structured error body out of whatever `request` rejected with.
* Returns undefined when the failure carries no structured body — a gateway
* plain-text 502, a proxy timeout, an aborted request.
*/
export function asServiceError(error: unknown): ServiceError | undefined {
const data = (error as { response?: { data?: unknown } })?.response?.data;
return data && typeof data === "object" ? (data as ServiceError) : undefined;
}
// Re-export DTOs so pages import types and clients from the same place. If a
// second domain ever collides on a type name, switch to namespaced exports
// (`export * as adminV1 from "@/services/kratos/admin/v1"`).
export type * from "@/services/kratos/admin/v1";
+350
View File
@@ -0,0 +1,350 @@
// Code generated by protoc-gen-typescript-http. DO NOT EDIT.
/* eslint-disable camelcase */
// @ts-nocheck
// Admin is the admin message.
export type Admin = {
// The unique ID of the user.
id: number | undefined;
// The name of the user.
name: string | undefined;
// The email of the user.
email: string | undefined;
// The phone number of the user.
phone: string | undefined;
// The avatar URL of the user.
avatar: string | undefined;
// The access level of the user.
// Possible values are: "admin", "user", etc.
access: string | undefined;
// The password of the user.
password: string | undefined;
// The timestamp at which the user was created.
createTime: wellKnownTimestamp | undefined;
// The latest timestamp at which the user was updated.
updateTime: wellKnownTimestamp | undefined;
};
// Encoded using RFC 3339, where generated output will always be Z-normalized
// and uses 0, 3, 6 or 9 fractional digits.
// Offsets other than "Z" are also accepted.
type wellKnownTimestamp = string;
// AdminSet is the set of admins.
export type AdminSet = {
// The set of admins.
admins: Admin[] | undefined;
// The next page token.
nextPageToken: string | undefined;
};
// LoginRequest is the request message for the Login method.
export type LoginRequest = {
// Required. Password of the user.
//
// Behaviors: REQUIRED
password: string | undefined;
username?: string;
email?: string;
};
// GetAdminRequest is the request message for the GetAdmin method.
export type GetAdminRequest = {
// The ID of the admin to retrieve.
//
// Behaviors: REQUIRED
id: number | undefined;
};
// ListAdminsResponse is the response message for the ListAdmins method.
export type ListAdminsRequest = {
// Optional. The number of admins per page.
pageSize: number | undefined;
// Optional. The page token.
pageToken: string | undefined;
// Optional. The standard list filter.
// Supported fields:
// * `name` (i.e. `name="John Doe"`)
// * `email` (i.e. `email="admin@go-kratos.dev"`)
// * `phone` (i.e. `phone="+1234567890"`)
// * `create_time` range (i.e. `timestamp>="2025-01-31T11:30:00-04:00"` where
// the timestamp is in RFC 3339 format)
// More detail in [AIP-160](https://google.aip.dev/160).
filter: string | undefined;
// Optional. A comma-separated list of fields to order by, sorted in ascending
// order. Use "desc" after a field name for descending. Supported fields:
// - `create_time`
// Example: `create_time desc`.
orderBy: string | undefined;
};
// CreateAdminRequest is the request message for the CreateAdmin method.
export type CreateAdminRequest = {
// Required. The admin to create.
//
// Behaviors: REQUIRED
admin: Admin | undefined;
};
// UpdateAdminRequest is the request message for the UpdateAdmin method.
export type UpdateAdminRequest = {
// Required. The admin to update.
//
// Behaviors: REQUIRED
admin: Admin | undefined;
// Required. Mask of fields to update.
//
// Behaviors: REQUIRED
updateMask: wellKnownFieldMask | undefined;
};
// In JSON, a field mask is encoded as a single string where paths are
// separated by a comma. Fields name in each path are converted
// to/from lower-camel naming conventions.
// As an example, consider the following message declarations:
//
// message Profile {
// User user = 1;
// Photo photo = 2;
// }
// message User {
// string display_name = 1;
// string address = 2;
// }
//
// In proto a field mask for `Profile` may look as such:
//
// mask {
// paths: "user.display_name"
// paths: "photo"
// }
//
// In JSON, the same mask is represented as below:
//
// {
// mask: "user.displayName,photo"
// }
type wellKnownFieldMask = string;
// DeleteAdminRequest is the request message for the DeleteAdmin method.
export type DeleteAdminRequest = {
// Required. The ID of the admin to delete.
//
// Behaviors: REQUIRED
id: number | undefined;
};
// AdminService is the admin service definition.
export interface AdminService {
// Login a user and return the username.
Login(request: LoginRequest): Promise<Admin>;
// Logout the currently logged-in user.
Logout(request: wellKnownEmpty): Promise<wellKnownEmpty>;
// Current returns the currently logged-in user.
Current(request: wellKnownEmpty): Promise<Admin>;
// ListAdmins returns a list of admins.
ListAdmins(request: ListAdminsRequest): Promise<AdminSet>;
// CreateAdmin creates a new admin.
CreateAdmin(request: CreateAdminRequest): Promise<Admin>;
// UpdateAdmin updates an existing admin.
UpdateAdmin(request: UpdateAdminRequest): Promise<Admin>;
// DeleteAdmin deletes an admin by ID.
DeleteAdmin(request: DeleteAdminRequest): Promise<wellKnownEmpty>;
// GetAdmin retrieves an admin by ID.
GetAdmin(request: GetAdminRequest): Promise<Admin>;
}
type RequestType = {
path: string;
method: string;
body: string | null;
};
type RequestHandler = (request: RequestType, meta: { service: string, method: string }) => Promise<unknown>;
export function createAdminServiceClient(
handler: RequestHandler
): AdminService {
return {
Login(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/login`; // eslint-disable-line quotes
const body = JSON.stringify(request);
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "POST",
body,
}, {
service: "AdminService",
method: "Login",
}) as Promise<Admin>;
},
Logout(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/logout`; // eslint-disable-line quotes
const body = JSON.stringify(request);
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "POST",
body,
}, {
service: "AdminService",
method: "Logout",
}) as Promise<wellKnownEmpty>;
},
Current(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/current`; // eslint-disable-line quotes
const body = null;
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "GET",
body,
}, {
service: "AdminService",
method: "Current",
}) as Promise<Admin>;
},
ListAdmins(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/list`; // eslint-disable-line quotes
const body = null;
const queryParams: string[] = [];
if (request.pageSize) {
queryParams.push(`pageSize=${encodeURIComponent(request.pageSize.toString())}`)
}
if (request.pageToken) {
queryParams.push(`pageToken=${encodeURIComponent(request.pageToken.toString())}`)
}
if (request.filter) {
queryParams.push(`filter=${encodeURIComponent(request.filter.toString())}`)
}
if (request.orderBy) {
queryParams.push(`orderBy=${encodeURIComponent(request.orderBy.toString())}`)
}
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "GET",
body,
}, {
service: "AdminService",
method: "ListAdmins",
}) as Promise<AdminSet>;
},
CreateAdmin(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/create`; // eslint-disable-line quotes
const body = JSON.stringify(request?.admin ?? {});
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "POST",
body,
}, {
service: "AdminService",
method: "CreateAdmin",
}) as Promise<Admin>;
},
UpdateAdmin(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
const path = `v1/admins/update`; // eslint-disable-line quotes
const body = JSON.stringify(request?.admin ?? {});
const queryParams: string[] = [];
if (request.updateMask) {
queryParams.push(`updateMask=${encodeURIComponent(request.updateMask.toString())}`)
}
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "PUT",
body,
}, {
service: "AdminService",
method: "UpdateAdmin",
}) as Promise<Admin>;
},
DeleteAdmin(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.id) {
throw new Error("missing required field request.id");
}
const path = `v1/admins/${request.id}`; // eslint-disable-line quotes
const body = null;
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "DELETE",
body,
}, {
service: "AdminService",
method: "DeleteAdmin",
}) as Promise<wellKnownEmpty>;
},
GetAdmin(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
if (!request.id) {
throw new Error("missing required field request.id");
}
const path = `v1/admins/${request.id}`; // eslint-disable-line quotes
const body = null;
const queryParams: string[] = [];
let uri = path;
if (queryParams.length > 0) {
uri += `?${queryParams.join("&")}`
}
return handler({
path: uri,
method: "GET",
body,
}, {
service: "AdminService",
method: "GetAdmin",
}) as Promise<Admin>;
},
};
}
// An empty JSON object
type wellKnownEmpty = Record<never, never>;
// ErrorReason is the stable vocabulary for the `reason` field of the error
// body. Values are wire-visible: protojson encodes them as their names, so
// clients branch on the name (e.g. "ADMIN_NOT_FOUND") rather than on an HTTP
// status. Surfaced in `biz` as `Err<Resource><Cause>`.
export type ErrorReason =
// Unset. Never returned; reserved by proto3 for the zero value.
| "ERROR_REASON_UNSPECIFIED"
// The requested admin does not exist.
| "ADMIN_NOT_FOUND"
// Login failed. Returned for both an unknown account and a wrong password
// so callers cannot probe which accounts are registered.
| "INVALID_CREDENTIALS"
// The login request carried no identity, or one this server does not accept.
| "INVALID_IDENTITY_TYPE"
// The request carried no usable credential (missing or invalid token).
| "UNAUTHENTICATED"
// The caller is authenticated but lacks the required access level.
| "PERMISSION_DENIED";
// @@protoc_insertion_point(typescript-http-eof)