This commit is contained in:
2026-07-15 00:37:35 +07:00
parent 0f205bf7d4
commit f5052cbe44
14 changed files with 1 additions and 20092 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
"type-check": "vue-tsc --build",
"lint": "eslint . --fix",
"format": "prettier --write src/",
"proto:gen": "protoc --plugin=protoc-gen-ts_proto=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=src/api/generated --ts_proto_opt=outputEncodeMethods=true,outputDecodeMethods=true,outputClientImpl=false,esModuleInterop=true,useExactTypes=false,addGrpcMetadata=false,useOptionals=all --proto_path=proto proto/main.proto"
"proto:gen": "protoc --typescript-http_out=src/api/generated --proto_path=proto api/main.proto"
},
"dependencies": {
"axios": "^1.18.1",
-270
View File
@@ -1,270 +0,0 @@
import axios, { type AxiosInstance, type InternalAxiosRequestConfig, AxiosError } from 'axios';
import type {
LoginReq,
LoginRsp,
SignupReq,
SignupRsp,
RefreshPasswordReq,
RefreshPasswordRsp,
RefreshReq,
RefreshRsp,
GetUsersReq,
GetUsersRsp,
GetUserByIdReq,
GetUserByIdRsp,
GetMeReq,
GetMeRsp,
AddUserRoleReq,
AddUserRoleRsp,
DeleteUserRoleReq,
DeleteUserRoleRsp,
GetPermissionsReq,
GetPermissionsRsp,
UploadFileReq,
UploadFileRsp,
DownloadFileReq,
AddScenarioReq,
AddScenarioRsp,
GetMyScenariosReq,
GetMyScenariosRsp,
GetScenarioReq,
GetScenarioRsp,
UpdateScenarioReq,
UpdateScenarioRsp,
DeleteScenarioReq,
DeleteScenarioRsp,
AddScenarioPlaceReq,
AddScenarioPlaceRsp,
UpdateScenarioPlaceReq,
UpdateScenarioPlaceRsp,
DeleteScenarioPlaceReq,
DeleteScenarioPlaceRsp,
PingReq,
PingRsp,
EchoReq,
EchoRsp,
} from './generated/main';
// Типизированный ответ с error полем
type ApiResponse<T> = T & { error?: string };
class ApiClient {
private client: AxiosInstance;
private baseUrl: string;
constructor() {
this.baseUrl = import.meta.env.VITE_API_URL || 'http://localhost:8080';
this.client = axios.create({
baseURL: this.baseUrl,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// Интерсептор для токена
this.client.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Интерсептор для рефреша токена
this.client.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
throw new Error('No refresh token');
}
const response = await this.refresh({ refreshToken });
localStorage.setItem('accessToken', response.accessToken!);
localStorage.setItem('refreshToken', response.refreshToken!);
originalRequest.headers.Authorization = `Bearer ${response.accessToken}`;
return this.client(originalRequest);
} catch (refreshError) {
// Рефреш не удался — разлогиниваем
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
}
// ---------- AUTH ----------
async ping(params: PingReq = {}): Promise<ApiResponse<PingRsp>> {
const { data } = await this.client.get('/api/test/ping');
return data;
}
async echo(params: EchoReq): Promise<ApiResponse<EchoRsp>> {
const { data } = await this.client.post('/api/test/echo', params);
return data;
}
async signup(params: SignupReq): Promise<ApiResponse<SignupRsp>> {
const { data } = await this.client.post('/api/auth/signup', params);
return data;
}
async refreshPassword(params: RefreshPasswordReq): Promise<ApiResponse<RefreshPasswordRsp>> {
const { data } = await this.client.post('/api/auth/refresh-password', params);
return data;
}
async login(params: LoginReq): Promise<ApiResponse<LoginRsp>> {
const { data } = await this.client.post('/api/auth/login', params);
if (data.accessToken) {
localStorage.setItem('accessToken', data.accessToken);
if (data.refreshToken) {
localStorage.setItem('refreshToken', data.refreshToken);
}
}
return data;
}
async refresh(params: RefreshReq): Promise<ApiResponse<RefreshRsp>> {
const { data } = await this.client.post('/api/auth/refresh', params);
return data;
}
async logout(): Promise<void> {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
}
// ---------- USERS ----------
async getUsers(params: GetUsersReq = {}): Promise<ApiResponse<GetUsersRsp>> {
const { data } = await this.client.get('/api/users');
return data;
}
async getUserById(params: GetUserByIdReq): Promise<ApiResponse<GetUserByIdRsp>> {
const { data } = await this.client.get(`/api/users/${params.id}`);
return data;
}
async getMe(params: GetMeReq = {}): Promise<ApiResponse<GetMeRsp>> {
const { data } = await this.client.get('/api/users/me');
return data;
}
async addUserRole(params: AddUserRoleReq): Promise<ApiResponse<AddUserRoleRsp>> {
const { id, role } = params;
const { data } = await this.client.post(`/api/users/${id}/role/add`, { role });
return data;
}
async deleteUserRole(params: DeleteUserRoleReq): Promise<ApiResponse<DeleteUserRoleRsp>> {
const { id, role } = params;
const { data } = await this.client.post(`/api/users/${id}/role/delete`, { role });
return data;
}
// ---------- PERMISSIONS ----------
async getPermissions(params: GetPermissionsReq = {}): Promise<ApiResponse<GetPermissionsRsp>> {
const { data } = await this.client.get('/api/ui/permissions');
return data;
}
// ---------- FILES ----------
async uploadFile(params: UploadFileReq): Promise<ApiResponse<UploadFileRsp>> {
if (!params.data) {
throw new Error('File data is required');
}
const formData = new FormData();
formData.append('filename', params.filename!);
// Преобразуем bytes в Blob
const dataArray = new Uint8Array(params.data);
const blob = new Blob([dataArray]);
formData.append('data', blob);
const { data } = await this.client.post('/api/files/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
return data;
}
async downloadFile(params: DownloadFileReq): Promise<Blob> {
const response = await this.client.get(`/api/files/${params.filename}`, {
responseType: 'blob',
});
return response.data;
}
// ---------- SCENARIOS ----------
async addScenario(params: AddScenarioReq): Promise<ApiResponse<AddScenarioRsp>> {
const { data } = await this.client.post('/api/scenario', params);
return data;
}
async getMyScenarios(params: GetMyScenariosReq = {}): Promise<ApiResponse<GetMyScenariosRsp>> {
const { data } = await this.client.get('/api/my-scenarios');
return data;
}
async getScenario(params: GetScenarioReq): Promise<ApiResponse<GetScenarioRsp>> {
const { data } = await this.client.get(`/api/scenarios/${params.id}`);
return data;
}
async updateScenario(params: UpdateScenarioReq): Promise<ApiResponse<UpdateScenarioRsp>> {
const { id, ...body } = params;
const { data } = await this.client.put(`/api/scenarios/${id}`, body);
return data;
}
async deleteScenario(params: DeleteScenarioReq): Promise<ApiResponse<DeleteScenarioRsp>> {
const { data } = await this.client.delete(`/api/scenarios/${params.id}`);
return data;
}
// ---------- SCENARIO PLACES ----------
async addScenarioPlace(params: AddScenarioPlaceReq): Promise<ApiResponse<AddScenarioPlaceRsp>> {
const { id, place } = params;
const { data } = await this.client.post(`/api/scenarios/${id}/places`, place);
return data;
}
async updateScenarioPlace(params: UpdateScenarioPlaceReq): Promise<ApiResponse<UpdateScenarioPlaceRsp>> {
const { id, code, place } = params;
const { data } = await this.client.put(`/api/scenarios/${id}/places/${code}`, place);
return data;
}
async deleteScenarioPlace(params: DeleteScenarioPlaceReq): Promise<ApiResponse<DeleteScenarioPlaceRsp>> {
const { id, code } = params;
const { data } = await this.client.delete(`/api/scenarios/${id}/places/${code}`);
return data;
}
}
// Единый экспорт
export const api = new ApiClient();
// Экспорт типов для удобства
export * from './generated/main';
@@ -1,9 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/api/annotations.proto
/* eslint-disable */
export const protobufPackage = "google.api";
-802
View File
@@ -1,802 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/api/http.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "google.api";
/**
* Defines the HTTP configuration for an API service. It contains a list of
* [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
* to one or more HTTP REST API methods.
*/
export interface Http {
/**
* A list of HTTP configuration rules that apply to individual API methods.
*
* **NOTE:** All service configuration rules follow "last one wins" order.
*/
rules?:
| HttpRule[]
| undefined;
/**
* When set to true, URL path parameters will be fully URI-decoded except in
* cases of single segment matches in reserved expansion, where "%2F" will be
* left encoded.
*
* The default behavior is to not decode RFC 6570 reserved characters in multi
* segment matches.
*/
fullyDecodeReservedExpansion?: boolean | undefined;
}
/**
* # gRPC Transcoding
*
* gRPC Transcoding is a feature for mapping between a gRPC method and one or
* more HTTP REST endpoints. It allows developers to build a single API service
* that supports both gRPC APIs and REST APIs. Many systems, including [Google
* APIs](https://github.com/googleapis/googleapis),
* [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
* Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
* and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
* and use it for large scale production services.
*
* `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
* how different portions of the gRPC request message are mapped to the URL
* path, URL query parameters, and HTTP request body. It also controls how the
* gRPC response message is mapped to the HTTP response body. `HttpRule` is
* typically specified as an `google.api.http` annotation on the gRPC method.
*
* Each mapping specifies a URL path template and an HTTP method. The path
* template may refer to one or more fields in the gRPC request message, as long
* as each field is a non-repeated field with a primitive (non-message) type.
* The path template controls how fields of the request message are mapped to
* the URL path.
*
* Example:
*
* service Messaging {
* rpc GetMessage(GetMessageRequest) returns (Message) {
* option (google.api.http) = {
* get: "/v1/{name=messages/*}"
* };
* }
* }
* message GetMessageRequest {
* string name = 1; // Mapped to URL path.
* }
* message Message {
* string text = 1; // The resource content.
* }
*
* This enables an HTTP REST to gRPC mapping as below:
*
* HTTP | gRPC
* -----|-----
* `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")`
*
* Any fields in the request message which are not bound by the path template
* automatically become HTTP query parameters if there is no HTTP request body.
* For example:
*
* service Messaging {
* rpc GetMessage(GetMessageRequest) returns (Message) {
* option (google.api.http) = {
* get:"/v1/messages/{message_id}"
* };
* }
* }
* message GetMessageRequest {
* message SubMessage {
* string subfield = 1;
* }
* string message_id = 1; // Mapped to URL path.
* int64 revision = 2; // Mapped to URL query parameter `revision`.
* SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
* }
*
* This enables a HTTP JSON to RPC mapping as below:
*
* HTTP | gRPC
* -----|-----
* `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
* `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
* "foo"))`
*
* Note that fields which are mapped to URL query parameters must have a
* primitive type or a repeated primitive type or a non-repeated message type.
* In the case of a repeated type, the parameter can be repeated in the URL
* as `...?param=A&param=B`. In the case of a message type, each field of the
* message is mapped to a separate parameter, such as
* `...?foo.a=A&foo.b=B&foo.c=C`.
*
* For HTTP methods that allow a request body, the `body` field
* specifies the mapping. Consider a REST update method on the
* message resource collection:
*
* service Messaging {
* rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
* option (google.api.http) = {
* patch: "/v1/messages/{message_id}"
* body: "message"
* };
* }
* }
* message UpdateMessageRequest {
* string message_id = 1; // mapped to the URL
* Message message = 2; // mapped to the body
* }
*
* The following HTTP JSON to RPC mapping is enabled, where the
* representation of the JSON in the request body is determined by
* protos JSON encoding:
*
* HTTP | gRPC
* -----|-----
* `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
* "123456" message { text: "Hi!" })`
*
* The special name `*` can be used in the body mapping to define that
* every field not bound by the path template should be mapped to the
* request body. This enables the following alternative definition of
* the update method:
*
* service Messaging {
* rpc UpdateMessage(Message) returns (Message) {
* option (google.api.http) = {
* patch: "/v1/messages/{message_id}"
* body: "*"
* };
* }
* }
* message Message {
* string message_id = 1;
* string text = 2;
* }
*
* The following HTTP JSON to RPC mapping is enabled:
*
* HTTP | gRPC
* -----|-----
* `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
* "123456" text: "Hi!")`
*
* Note that when using `*` in the body mapping, it is not possible to
* have HTTP parameters, as all fields not bound by the path end in
* the body. This makes this option more rarely used in practice when
* defining REST APIs. The common usage of `*` is in custom methods
* which don't use the URL at all for transferring data.
*
* It is possible to define multiple HTTP methods for one RPC by using
* the `additional_bindings` option. Example:
*
* service Messaging {
* rpc GetMessage(GetMessageRequest) returns (Message) {
* option (google.api.http) = {
* get: "/v1/messages/{message_id}"
* additional_bindings {
* get: "/v1/users/{user_id}/messages/{message_id}"
* }
* };
* }
* }
* message GetMessageRequest {
* string message_id = 1;
* string user_id = 2;
* }
*
* This enables the following two alternative HTTP JSON to RPC mappings:
*
* HTTP | gRPC
* -----|-----
* `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
* `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
* "123456")`
*
* ## Rules for HTTP mapping
*
* 1. Leaf request fields (recursive expansion nested messages in the request
* message) are classified into three categories:
* - Fields referred by the path template. They are passed via the URL path.
* - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They
* are passed via the HTTP
* request body.
* - All other fields are passed via the URL query parameters, and the
* parameter name is the field path in the request message. A repeated
* field can be represented as multiple query parameters under the same
* name.
* 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL
* query parameter, all fields
* are passed via URL path and HTTP request body.
* 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP
* request body, all
* fields are passed via URL path and URL query parameters.
*
* ### Path template syntax
*
* Template = "/" Segments [ Verb ] ;
* Segments = Segment { "/" Segment } ;
* Segment = "*" | "**" | LITERAL | Variable ;
* Variable = "{" FieldPath [ "=" Segments ] "}" ;
* FieldPath = IDENT { "." IDENT } ;
* Verb = ":" LITERAL ;
*
* The syntax `*` matches a single URL path segment. The syntax `**` matches
* zero or more URL path segments, which must be the last part of the URL path
* except the `Verb`.
*
* The syntax `Variable` matches part of the URL path as specified by its
* template. A variable template must not contain other variables. If a variable
* matches a single path segment, its template may be omitted, e.g. `{var}`
* is equivalent to `{var=*}`.
*
* The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
* contains any reserved character, such characters should be percent-encoded
* before the matching.
*
* If a variable contains exactly one path segment, such as `"{var}"` or
* `"{var=*}"`, when such a variable is expanded into a URL path on the client
* side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
* server side does the reverse decoding. Such variables show up in the
* [Discovery
* Document](https://developers.google.com/discovery/v1/reference/apis) as
* `{var}`.
*
* If a variable contains multiple path segments, such as `"{var=foo/*}"`
* or `"{var=**}"`, when such a variable is expanded into a URL path on the
* client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
* The server side does the reverse decoding, except "%2F" and "%2f" are left
* unchanged. Such variables show up in the
* [Discovery
* Document](https://developers.google.com/discovery/v1/reference/apis) as
* `{+var}`.
*
* ## Using gRPC API Service Configuration
*
* gRPC API Service Configuration (service config) is a configuration language
* for configuring a gRPC service to become a user-facing product. The
* service config is simply the YAML representation of the `google.api.Service`
* proto message.
*
* As an alternative to annotating your proto file, you can configure gRPC
* transcoding in your service config YAML files. You do this by specifying a
* `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
* effect as the proto annotation. This can be particularly useful if you
* have a proto that is reused in multiple services. Note that any transcoding
* specified in the service config will override any matching transcoding
* configuration in the proto.
*
* Example:
*
* http:
* rules:
* # Selects a gRPC method and applies HttpRule to it.
* - selector: example.v1.Messaging.GetMessage
* get: /v1/messages/{message_id}/{sub.subfield}
*
* ## Special notes
*
* When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
* proto to JSON conversion must follow the [proto3
* specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
*
* While the single segment variable follows the semantics of
* [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
* Expansion, the multi segment variable **does not** follow RFC 6570 Section
* 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
* does not expand special characters like `?` and `#`, which would lead
* to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
* for multi segment variables.
*
* The path variables **must not** refer to any repeated or mapped field,
* because client libraries are not capable of handling such variable expansion.
*
* The path variables **must not** capture the leading "/" character. The reason
* is that the most common use case "{var}" does not capture the leading "/"
* character. For consistency, all path variables must share the same behavior.
*
* Repeated message fields must not be mapped to URL query parameters, because
* no client library can support such complicated mapping.
*
* If an API needs to use a JSON array for request or response body, it can map
* the request or response body to a repeated field. However, some gRPC
* Transcoding implementations may not support this feature.
*/
export interface HttpRule {
/**
* Selects a method to which this rule applies.
*
* Refer to [selector][google.api.DocumentationRule.selector] for syntax
* details.
*/
selector?:
| string
| undefined;
/**
* Maps to HTTP GET. Used for listing and getting information about
* resources.
*/
get?:
| string
| undefined;
/** Maps to HTTP PUT. Used for replacing a resource. */
put?:
| string
| undefined;
/** Maps to HTTP POST. Used for creating a resource or performing an action. */
post?:
| string
| undefined;
/** Maps to HTTP DELETE. Used for deleting a resource. */
delete?:
| string
| undefined;
/** Maps to HTTP PATCH. Used for updating a resource. */
patch?:
| string
| undefined;
/**
* The custom pattern is used for specifying an HTTP method that is not
* included in the `pattern` field, such as HEAD, or "*" to leave the
* HTTP method unspecified for this rule. The wild-card rule is useful
* for services that provide content to Web (HTML) clients.
*/
custom?:
| CustomHttpPattern
| undefined;
/**
* The name of the request field whose value is mapped to the HTTP request
* body, or `*` for mapping all request fields not captured by the path
* pattern to the HTTP body, or omitted for not having any HTTP request body.
*
* NOTE: the referred field must be present at the top-level of the request
* message type.
*/
body?:
| string
| undefined;
/**
* Optional. The name of the response field whose value is mapped to the HTTP
* response body. When omitted, the entire response message will be used
* as the HTTP response body.
*
* NOTE: The referred field must be present at the top-level of the response
* message type.
*/
responseBody?:
| string
| undefined;
/**
* Additional HTTP bindings for the selector. Nested bindings must
* not contain an `additional_bindings` field themselves (that is,
* the nesting may only be one level deep).
*/
additionalBindings?: HttpRule[] | undefined;
}
/** A custom pattern is used for defining custom HTTP verb. */
export interface CustomHttpPattern {
/** The name of this custom HTTP verb. */
kind?:
| string
| undefined;
/** The path matched by this custom verb. */
path?: string | undefined;
}
function createBaseHttp(): Http {
return { rules: [], fullyDecodeReservedExpansion: false };
}
export const Http: MessageFns<Http> = {
encode(message: Http, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.rules !== undefined && message.rules.length !== 0) {
for (const v of message.rules) {
HttpRule.encode(v!, writer.uint32(10).fork()).join();
}
}
if (message.fullyDecodeReservedExpansion !== undefined && message.fullyDecodeReservedExpansion !== false) {
writer.uint32(16).bool(message.fullyDecodeReservedExpansion);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Http {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseHttp();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
const el = HttpRule.decode(reader, reader.uint32());
if (el !== undefined) {
message.rules!.push(el);
}
continue;
}
case 2: {
if (tag !== 16) {
break;
}
message.fullyDecodeReservedExpansion = reader.bool();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Http {
return {
rules: globalThis.Array.isArray(object?.rules) ? object.rules.map((e: any) => HttpRule.fromJSON(e)) : [],
fullyDecodeReservedExpansion: isSet(object.fullyDecodeReservedExpansion)
? globalThis.Boolean(object.fullyDecodeReservedExpansion)
: isSet(object.fully_decode_reserved_expansion)
? globalThis.Boolean(object.fully_decode_reserved_expansion)
: false,
};
},
toJSON(message: Http): unknown {
const obj: any = {};
if (message.rules?.length) {
obj.rules = message.rules.map((e) => HttpRule.toJSON(e));
}
if (message.fullyDecodeReservedExpansion !== undefined && message.fullyDecodeReservedExpansion !== false) {
obj.fullyDecodeReservedExpansion = message.fullyDecodeReservedExpansion;
}
return obj;
},
create(base?: DeepPartial<Http>): Http {
return Http.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Http>): Http {
const message = createBaseHttp();
message.rules = object.rules?.map((e) => HttpRule.fromPartial(e)) || [];
message.fullyDecodeReservedExpansion = object.fullyDecodeReservedExpansion ?? false;
return message;
},
};
function createBaseHttpRule(): HttpRule {
return {
selector: "",
get: undefined,
put: undefined,
post: undefined,
delete: undefined,
patch: undefined,
custom: undefined,
body: "",
responseBody: "",
additionalBindings: [],
};
}
export const HttpRule: MessageFns<HttpRule> = {
encode(message: HttpRule, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.selector !== undefined && message.selector !== "") {
writer.uint32(10).string(message.selector);
}
if (message.get !== undefined) {
writer.uint32(18).string(message.get);
}
if (message.put !== undefined) {
writer.uint32(26).string(message.put);
}
if (message.post !== undefined) {
writer.uint32(34).string(message.post);
}
if (message.delete !== undefined) {
writer.uint32(42).string(message.delete);
}
if (message.patch !== undefined) {
writer.uint32(50).string(message.patch);
}
if (message.custom !== undefined) {
CustomHttpPattern.encode(message.custom, writer.uint32(66).fork()).join();
}
if (message.body !== undefined && message.body !== "") {
writer.uint32(58).string(message.body);
}
if (message.responseBody !== undefined && message.responseBody !== "") {
writer.uint32(98).string(message.responseBody);
}
if (message.additionalBindings !== undefined && message.additionalBindings.length !== 0) {
for (const v of message.additionalBindings) {
HttpRule.encode(v!, writer.uint32(90).fork()).join();
}
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): HttpRule {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseHttpRule();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.selector = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.get = reader.string();
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.put = reader.string();
continue;
}
case 4: {
if (tag !== 34) {
break;
}
message.post = reader.string();
continue;
}
case 5: {
if (tag !== 42) {
break;
}
message.delete = reader.string();
continue;
}
case 6: {
if (tag !== 50) {
break;
}
message.patch = reader.string();
continue;
}
case 8: {
if (tag !== 66) {
break;
}
message.custom = CustomHttpPattern.decode(reader, reader.uint32());
continue;
}
case 7: {
if (tag !== 58) {
break;
}
message.body = reader.string();
continue;
}
case 12: {
if (tag !== 98) {
break;
}
message.responseBody = reader.string();
continue;
}
case 11: {
if (tag !== 90) {
break;
}
const el = HttpRule.decode(reader, reader.uint32());
if (el !== undefined) {
message.additionalBindings!.push(el);
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): HttpRule {
return {
selector: isSet(object.selector) ? globalThis.String(object.selector) : "",
get: isSet(object.get) ? globalThis.String(object.get) : undefined,
put: isSet(object.put) ? globalThis.String(object.put) : undefined,
post: isSet(object.post) ? globalThis.String(object.post) : undefined,
delete: isSet(object.delete) ? globalThis.String(object.delete) : undefined,
patch: isSet(object.patch) ? globalThis.String(object.patch) : undefined,
custom: isSet(object.custom) ? CustomHttpPattern.fromJSON(object.custom) : undefined,
body: isSet(object.body) ? globalThis.String(object.body) : "",
responseBody: isSet(object.responseBody)
? globalThis.String(object.responseBody)
: isSet(object.response_body)
? globalThis.String(object.response_body)
: "",
additionalBindings: globalThis.Array.isArray(object?.additionalBindings)
? object.additionalBindings.map((e: any) => HttpRule.fromJSON(e))
: globalThis.Array.isArray(object?.additional_bindings)
? object.additional_bindings.map((e: any) => HttpRule.fromJSON(e))
: [],
};
},
toJSON(message: HttpRule): unknown {
const obj: any = {};
if (message.selector !== undefined && message.selector !== "") {
obj.selector = message.selector;
}
if (message.get !== undefined) {
obj.get = message.get;
}
if (message.put !== undefined) {
obj.put = message.put;
}
if (message.post !== undefined) {
obj.post = message.post;
}
if (message.delete !== undefined) {
obj.delete = message.delete;
}
if (message.patch !== undefined) {
obj.patch = message.patch;
}
if (message.custom !== undefined) {
obj.custom = CustomHttpPattern.toJSON(message.custom);
}
if (message.body !== undefined && message.body !== "") {
obj.body = message.body;
}
if (message.responseBody !== undefined && message.responseBody !== "") {
obj.responseBody = message.responseBody;
}
if (message.additionalBindings?.length) {
obj.additionalBindings = message.additionalBindings.map((e) => HttpRule.toJSON(e));
}
return obj;
},
create(base?: DeepPartial<HttpRule>): HttpRule {
return HttpRule.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<HttpRule>): HttpRule {
const message = createBaseHttpRule();
message.selector = object.selector ?? "";
message.get = object.get ?? undefined;
message.put = object.put ?? undefined;
message.post = object.post ?? undefined;
message.delete = object.delete ?? undefined;
message.patch = object.patch ?? undefined;
message.custom = (object.custom !== undefined && object.custom !== null)
? CustomHttpPattern.fromPartial(object.custom)
: undefined;
message.body = object.body ?? "";
message.responseBody = object.responseBody ?? "";
message.additionalBindings = object.additionalBindings?.map((e) => HttpRule.fromPartial(e)) || [];
return message;
},
};
function createBaseCustomHttpPattern(): CustomHttpPattern {
return { kind: "", path: "" };
}
export const CustomHttpPattern: MessageFns<CustomHttpPattern> = {
encode(message: CustomHttpPattern, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.kind !== undefined && message.kind !== "") {
writer.uint32(10).string(message.kind);
}
if (message.path !== undefined && message.path !== "") {
writer.uint32(18).string(message.path);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): CustomHttpPattern {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCustomHttpPattern();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.kind = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.path = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): CustomHttpPattern {
return {
kind: isSet(object.kind) ? globalThis.String(object.kind) : "",
path: isSet(object.path) ? globalThis.String(object.path) : "",
};
},
toJSON(message: CustomHttpPattern): unknown {
const obj: any = {};
if (message.kind !== undefined && message.kind !== "") {
obj.kind = message.kind;
}
if (message.path !== undefined && message.path !== "") {
obj.path = message.path;
}
return obj;
},
create(base?: DeepPartial<CustomHttpPattern>): CustomHttpPattern {
return CustomHttpPattern.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<CustomHttpPattern>): CustomHttpPattern {
const message = createBaseCustomHttpPattern();
message.kind = object.kind ?? "";
message.path = object.path ?? "";
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create(base?: DeepPartial<T>): T;
fromPartial(object: DeepPartial<T>): T;
}
-220
View File
@@ -1,220 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/api/httpbody.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { Any } from "../protobuf/any";
export const protobufPackage = "google.api";
/**
* Message that represents an arbitrary HTTP body. It should only be used for
* payload formats that can't be represented as JSON, such as raw binary or
* an HTML page.
*
* This message can be used both in streaming and non-streaming API methods in
* the request as well as the response.
*
* It can be used as a top-level request field, which is convenient if one
* wants to extract parameters from either the URL or HTTP template into the
* request fields and also want access to the raw HTTP body.
*
* Example:
*
* message GetResourceRequest {
* // A unique request id.
* string request_id = 1;
*
* // The raw HTTP body is bound to this field.
* google.api.HttpBody http_body = 2;
*
* }
*
* service ResourceService {
* rpc GetResource(GetResourceRequest)
* returns (google.api.HttpBody);
* rpc UpdateResource(google.api.HttpBody)
* returns (google.protobuf.Empty);
*
* }
*
* Example with streaming methods:
*
* service CaldavService {
* rpc GetCalendar(stream google.api.HttpBody)
* returns (stream google.api.HttpBody);
* rpc UpdateCalendar(stream google.api.HttpBody)
* returns (stream google.api.HttpBody);
*
* }
*
* Use of this type only changes how the request and response bodies are
* handled, all other features will continue to work unchanged.
*/
export interface HttpBody {
/** The HTTP Content-Type header value specifying the content type of the body. */
contentType?:
| string
| undefined;
/** The HTTP request/response body as raw binary. */
data?:
| Uint8Array
| undefined;
/**
* Application specific response metadata. Must be set in the first response
* for streaming APIs.
*/
extensions?: Any[] | undefined;
}
function createBaseHttpBody(): HttpBody {
return { contentType: "", data: new Uint8Array(0), extensions: [] };
}
export const HttpBody: MessageFns<HttpBody> = {
encode(message: HttpBody, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.contentType !== undefined && message.contentType !== "") {
writer.uint32(10).string(message.contentType);
}
if (message.data !== undefined && message.data.length !== 0) {
writer.uint32(18).bytes(message.data);
}
if (message.extensions !== undefined && message.extensions.length !== 0) {
for (const v of message.extensions) {
Any.encode(v!, writer.uint32(26).fork()).join();
}
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): HttpBody {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseHttpBody();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.contentType = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.data = reader.bytes();
continue;
}
case 3: {
if (tag !== 26) {
break;
}
const el = Any.decode(reader, reader.uint32());
if (el !== undefined) {
message.extensions!.push(el);
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): HttpBody {
return {
contentType: isSet(object.contentType)
? globalThis.String(object.contentType)
: isSet(object.content_type)
? globalThis.String(object.content_type)
: "",
data: isSet(object.data) ? bytesFromBase64(object.data) : new Uint8Array(0),
extensions: globalThis.Array.isArray(object?.extensions)
? object.extensions.map((e: any) => Any.fromJSON(e))
: [],
};
},
toJSON(message: HttpBody): unknown {
const obj: any = {};
if (message.contentType !== undefined && message.contentType !== "") {
obj.contentType = message.contentType;
}
if (message.data !== undefined && message.data.length !== 0) {
obj.data = base64FromBytes(message.data);
}
if (message.extensions?.length) {
obj.extensions = message.extensions.map((e) => Any.toJSON(e));
}
return obj;
},
create(base?: DeepPartial<HttpBody>): HttpBody {
return HttpBody.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<HttpBody>): HttpBody {
const message = createBaseHttpBody();
message.contentType = object.contentType ?? "";
message.data = object.data ?? new Uint8Array(0);
message.extensions = object.extensions?.map((e) => Any.fromPartial(e)) || [];
return message;
},
};
function bytesFromBase64(b64: string): Uint8Array {
if ((globalThis as any).Buffer) {
return Uint8Array.from((globalThis as any).Buffer.from(b64, "base64"));
} else {
const bin = globalThis.atob(b64);
const arr = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; ++i) {
arr[i] = bin.charCodeAt(i);
}
return arr;
}
}
function base64FromBytes(arr: Uint8Array): string {
if ((globalThis as any).Buffer) {
return (globalThis as any).Buffer.from(arr).toString("base64");
} else {
const bin: string[] = [];
arr.forEach((byte) => {
bin.push(globalThis.String.fromCharCode(byte));
});
return globalThis.btoa(bin.join(""));
}
}
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create(base?: DeepPartial<T>): T;
fromPartial(object: DeepPartial<T>): T;
}
-207
View File
@@ -1,207 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/protobuf/any.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "google.protobuf";
/**
* `Any` contains an arbitrary serialized protocol buffer message along with a
* URL that describes the type of the serialized message.
*
* In its binary encoding, an `Any` is an ordinary message; but in other wire
* forms like JSON, it has a special encoding. The format of the type URL is
* described on the `type_url` field.
*
* Protobuf APIs provide utilities to interact with `Any` values:
*
* - A 'pack' operation accepts a message and constructs a generic `Any` wrapper
* around it.
* - An 'unpack' operation reads the content of an `Any` message, either into an
* existing message or a new one. Unpack operations must check the type of the
* value they unpack against the declared `type_url`.
* - An 'is' operation decides whether an `Any` contains a message of the given
* type, i.e. whether it can 'unpack' that type.
*
* The JSON format representation of an `Any` follows one of these cases:
*
* - For types without special-cased JSON encodings, the JSON format
* representation of the `Any` is the same as that of the message, with an
* additional `@type` field which contains the type URL.
* - For types with special-cased JSON encodings (typically called 'well-known'
* types, listed in https://protobuf.dev/programming-guides/json/#any), the
* JSON format representation has a key `@type` which contains the type URL
* and a key `value` which contains the JSON-serialized value.
*
* The text format representation of an `Any` is like a message with one field
* whose name is the type URL in brackets. For example, an `Any` containing a
* `foo.Bar` message may be written `[type.googleapis.com/foo.Bar] { a: 2 }`.
*/
export interface Any {
/**
* Identifies the type of the serialized Protobuf message with a URI reference
* consisting of a prefix ending in a slash and the fully-qualified type name.
*
* Example: type.googleapis.com/google.protobuf.StringValue
*
* This string must contain at least one `/` character, and the content after
* the last `/` must be the fully-qualified name of the type in canonical
* form, without a leading dot. Do not write a scheme on these URI references
* so that clients do not attempt to contact them.
*
* The prefix is arbitrary and Protobuf implementations are expected to
* simply strip off everything up to and including the last `/` to identify
* the type. `type.googleapis.com/` is a common default prefix that some
* legacy implementations require. This prefix does not indicate the origin of
* the type, and URIs containing it are not expected to respond to any
* requests.
*
* All type URL strings must be legal URI references with the additional
* restriction (for the text format) that the content of the reference
* must consist only of alphanumeric characters, percent-encoded escapes, and
* characters in the following set (not including the outer backticks):
* `/-.~_!$&()*+,;=`. Despite our allowing percent encodings, implementations
* should not unescape them to prevent confusion with existing parsers. For
* example, `type.googleapis.com%2FFoo` should be rejected.
*
* In the original design of `Any`, the possibility of launching a type
* resolution service at these type URLs was considered but Protobuf never
* implemented one and considers contacting these URLs to be problematic and
* a potential security issue. Do not attempt to contact type URLs.
*/
typeUrl?:
| string
| undefined;
/** Holds a Protobuf serialization of the type described by type_url. */
value?: Uint8Array | undefined;
}
function createBaseAny(): Any {
return { typeUrl: "", value: new Uint8Array(0) };
}
export const Any: MessageFns<Any> = {
encode(message: Any, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.typeUrl !== undefined && message.typeUrl !== "") {
writer.uint32(10).string(message.typeUrl);
}
if (message.value !== undefined && message.value.length !== 0) {
writer.uint32(18).bytes(message.value);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Any {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAny();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.typeUrl = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.value = reader.bytes();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Any {
return {
typeUrl: isSet(object.typeUrl)
? globalThis.String(object.typeUrl)
: isSet(object.type_url)
? globalThis.String(object.type_url)
: "",
value: isSet(object.value) ? bytesFromBase64(object.value) : new Uint8Array(0),
};
},
toJSON(message: Any): unknown {
const obj: any = {};
if (message.typeUrl !== undefined && message.typeUrl !== "") {
obj.typeUrl = message.typeUrl;
}
if (message.value !== undefined && message.value.length !== 0) {
obj.value = base64FromBytes(message.value);
}
return obj;
},
create(base?: DeepPartial<Any>): Any {
return Any.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Any>): Any {
const message = createBaseAny();
message.typeUrl = object.typeUrl ?? "";
message.value = object.value ?? new Uint8Array(0);
return message;
},
};
function bytesFromBase64(b64: string): Uint8Array {
if ((globalThis as any).Buffer) {
return Uint8Array.from((globalThis as any).Buffer.from(b64, "base64"));
} else {
const bin = globalThis.atob(b64);
const arr = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; ++i) {
arr[i] = bin.charCodeAt(i);
}
return arr;
}
}
function base64FromBytes(arr: Uint8Array): string {
if ((globalThis as any).Buffer) {
return (globalThis as any).Buffer.from(arr).toString("base64");
} else {
const bin: string[] = [];
arr.forEach((byte) => {
bin.push(globalThis.String.fromCharCode(byte));
});
return globalThis.btoa(bin.join(""));
}
}
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create(base?: DeepPartial<T>): T;
fromPartial(object: DeepPartial<T>): T;
}
File diff suppressed because it is too large Load Diff
-627
View File
@@ -1,627 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/protobuf/struct.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "google.protobuf";
/**
* Represents a JSON `null`.
*
* `NullValue` is a sentinel, using an enum with only one value to represent
* the null value for the `Value` type union.
*
* A field of type `NullValue` with any value other than `0` is considered
* invalid. Most ProtoJSON serializers will emit a Value with a `null_value` set
* as a JSON `null` regardless of the integer value, and so will round trip to
* a `0` value.
*/
export enum NullValue {
/** NULL_VALUE - Null value. */
NULL_VALUE = 0,
UNRECOGNIZED = -1,
}
export function nullValueFromJSON(object: any): NullValue {
switch (object) {
case 0:
case "NULL_VALUE":
return NullValue.NULL_VALUE;
case -1:
case "UNRECOGNIZED":
default:
return NullValue.UNRECOGNIZED;
}
}
export function nullValueToJSON(object: NullValue): string {
switch (object) {
case NullValue.NULL_VALUE:
return "NULL_VALUE";
case NullValue.UNRECOGNIZED:
default:
return "UNRECOGNIZED";
}
}
/**
* Represents a JSON object.
*
* An unordered key-value map, intending to perfectly capture the semantics of a
* JSON object. This enables parsing any arbitrary JSON payload as a message
* field in ProtoJSON format.
*
* This follows RFC 8259 guidelines for interoperable JSON: notably this type
* cannot represent large Int64 values or `NaN`/`Infinity` numbers,
* since the JSON format generally does not support those values in its number
* type.
*
* If you do not intend to parse arbitrary JSON into your message, a custom
* typed message should be preferred instead of using this type.
*/
export interface Struct {
/** Unordered map of dynamically typed values. */
fields?: { [key: string]: any | undefined } | undefined;
}
export interface Struct_FieldsEntry {
key: string;
value?: any | undefined;
}
/**
* Represents a JSON value.
*
* `Value` represents a dynamically typed value which can be either
* null, a number, a string, a boolean, a recursive struct value, or a
* list of values. A producer of value is expected to set one of these
* variants. Absence of any variant is an invalid state.
*/
export interface Value {
/** Represents a JSON `null`. */
nullValue?:
| NullValue
| undefined;
/**
* Represents a JSON number. Must not be `NaN`, `Infinity` or
* `-Infinity`, since those are not supported in JSON. This also cannot
* represent large Int64 values, since JSON format generally does not
* support them in its number type.
*/
numberValue?:
| number
| undefined;
/** Represents a JSON string. */
stringValue?:
| string
| undefined;
/** Represents a JSON boolean (`true` or `false` literal in JSON). */
boolValue?:
| boolean
| undefined;
/** Represents a JSON object. */
structValue?:
| { [key: string]: any }
| undefined;
/** Represents a JSON array. */
listValue?: Array<any> | undefined;
}
/** Represents a JSON array. */
export interface ListValue {
/** Repeated field of dynamically typed values. */
values?: any[] | undefined;
}
function createBaseStruct(): Struct {
return { fields: {} };
}
export const Struct: MessageFns<Struct> & StructWrapperFns = {
encode(message: Struct, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
globalThis.Object.entries(message.fields || {}).forEach(([key, value]: [string, any | undefined]) => {
if (value !== undefined) {
Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}
});
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Struct {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseStruct();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32());
if (entry1.value !== undefined) {
message.fields![entry1.key] = entry1.value;
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Struct {
return {
fields: isObject(object.fields)
? (globalThis.Object.entries(object.fields) as [string, any][]).reduce(
(acc: { [key: string]: any | undefined }, [key, value]: [string, any]) => {
acc[key] = value as any | undefined;
return acc;
},
{},
)
: {},
};
},
toJSON(message: Struct): unknown {
const obj: any = {};
if (message.fields) {
const entries = globalThis.Object.entries(message.fields) as [string, any | undefined][];
if (entries.length > 0) {
obj.fields = {};
entries.forEach(([k, v]) => {
obj.fields[k] = v;
});
}
}
return obj;
},
create(base?: DeepPartial<Struct>): Struct {
return Struct.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Struct>): Struct {
const message = createBaseStruct();
message.fields = (globalThis.Object.entries(object.fields ?? {}) as [string, any | undefined][]).reduce(
(acc: { [key: string]: any | undefined }, [key, value]: [string, any | undefined]) => {
if (value !== undefined) {
acc[key] = value;
}
return acc;
},
{},
);
return message;
},
wrap(object: { [key: string]: any } | undefined): Struct {
const struct = createBaseStruct();
struct.fields ??= {};
if (object !== undefined) {
for (const key of globalThis.Object.keys(object)) {
struct.fields[key] = object[key];
}
}
return struct;
},
unwrap(message: Struct): { [key: string]: any } {
const object: { [key: string]: any } = {};
if (message.fields) {
for (const key of globalThis.Object.keys(message.fields)) {
object[key] = message.fields[key];
}
}
return object;
},
};
function createBaseStruct_FieldsEntry(): Struct_FieldsEntry {
return { key: "", value: undefined };
}
export const Struct_FieldsEntry: MessageFns<Struct_FieldsEntry> = {
encode(message: Struct_FieldsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== "") {
writer.uint32(10).string(message.key);
}
if (message.value !== undefined) {
Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Struct_FieldsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseStruct_FieldsEntry();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.key = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.value = Value.unwrap(Value.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Struct_FieldsEntry {
return {
key: isSet(object.key) ? globalThis.String(object.key) : "",
value: isSet(object?.value) ? object.value : undefined,
};
},
toJSON(message: Struct_FieldsEntry): unknown {
const obj: any = {};
if (message.key !== "") {
obj.key = message.key;
}
if (message.value !== undefined) {
obj.value = message.value;
}
return obj;
},
create(base?: DeepPartial<Struct_FieldsEntry>): Struct_FieldsEntry {
return Struct_FieldsEntry.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Struct_FieldsEntry>): Struct_FieldsEntry {
const message = createBaseStruct_FieldsEntry();
message.key = object.key ?? "";
message.value = object.value ?? undefined;
return message;
},
};
function createBaseValue(): Value {
return {
nullValue: undefined,
numberValue: undefined,
stringValue: undefined,
boolValue: undefined,
structValue: undefined,
listValue: undefined,
};
}
export const Value: MessageFns<Value> & AnyValueWrapperFns = {
encode(message: Value, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.nullValue !== undefined) {
writer.uint32(8).int32(message.nullValue);
}
if (message.numberValue !== undefined) {
writer.uint32(17).double(message.numberValue);
}
if (message.stringValue !== undefined) {
writer.uint32(26).string(message.stringValue);
}
if (message.boolValue !== undefined) {
writer.uint32(32).bool(message.boolValue);
}
if (message.structValue !== undefined) {
Struct.encode(Struct.wrap(message.structValue), writer.uint32(42).fork()).join();
}
if (message.listValue !== undefined) {
ListValue.encode(ListValue.wrap(message.listValue), writer.uint32(50).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Value {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseValue();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 8) {
break;
}
message.nullValue = reader.int32() as any;
continue;
}
case 2: {
if (tag !== 17) {
break;
}
message.numberValue = reader.double();
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.stringValue = reader.string();
continue;
}
case 4: {
if (tag !== 32) {
break;
}
message.boolValue = reader.bool();
continue;
}
case 5: {
if (tag !== 42) {
break;
}
message.structValue = Struct.unwrap(Struct.decode(reader, reader.uint32()));
continue;
}
case 6: {
if (tag !== 50) {
break;
}
message.listValue = ListValue.unwrap(ListValue.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Value {
return {
nullValue: isSet(object.nullValue)
? nullValueFromJSON(object.nullValue)
: isSet(object.null_value)
? nullValueFromJSON(object.null_value)
: undefined,
numberValue: isSet(object.numberValue)
? globalThis.Number(object.numberValue)
: isSet(object.number_value)
? globalThis.Number(object.number_value)
: undefined,
stringValue: isSet(object.stringValue)
? globalThis.String(object.stringValue)
: isSet(object.string_value)
? globalThis.String(object.string_value)
: undefined,
boolValue: isSet(object.boolValue)
? globalThis.Boolean(object.boolValue)
: isSet(object.bool_value)
? globalThis.Boolean(object.bool_value)
: undefined,
structValue: isObject(object.structValue)
? object.structValue
: isObject(object.struct_value)
? object.struct_value
: undefined,
listValue: globalThis.Array.isArray(object.listValue)
? [...object.listValue]
: globalThis.Array.isArray(object.list_value)
? [...object.list_value]
: undefined,
};
},
toJSON(message: Value): unknown {
const obj: any = {};
if (message.nullValue !== undefined) {
obj.nullValue = nullValueToJSON(message.nullValue);
}
if (message.numberValue !== undefined) {
obj.numberValue = message.numberValue;
}
if (message.stringValue !== undefined) {
obj.stringValue = message.stringValue;
}
if (message.boolValue !== undefined) {
obj.boolValue = message.boolValue;
}
if (message.structValue !== undefined) {
obj.structValue = message.structValue;
}
if (message.listValue !== undefined) {
obj.listValue = message.listValue;
}
return obj;
},
create(base?: DeepPartial<Value>): Value {
return Value.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Value>): Value {
const message = createBaseValue();
message.nullValue = object.nullValue ?? undefined;
message.numberValue = object.numberValue ?? undefined;
message.stringValue = object.stringValue ?? undefined;
message.boolValue = object.boolValue ?? undefined;
message.structValue = object.structValue ?? undefined;
message.listValue = object.listValue ?? undefined;
return message;
},
wrap(value: any): Value {
const result = createBaseValue();
if (value === null) {
result.nullValue = NullValue.NULL_VALUE;
} else if (typeof value === "boolean") {
result.boolValue = value;
} else if (typeof value === "number") {
result.numberValue = value;
} else if (typeof value === "string") {
result.stringValue = value;
} else if (globalThis.Array.isArray(value)) {
result.listValue = value;
} else if (typeof value === "object") {
result.structValue = value;
} else if (typeof value !== "undefined") {
throw new globalThis.Error("Unsupported any value type: " + typeof value);
}
return result;
},
unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined {
if (message.stringValue !== undefined) {
return message.stringValue;
} else if (message?.numberValue !== undefined) {
return message.numberValue;
} else if (message?.boolValue !== undefined) {
return message.boolValue;
} else if (message?.structValue !== undefined) {
return message.structValue as any;
} else if (message?.listValue !== undefined) {
return message.listValue;
} else if (message?.nullValue !== undefined) {
return null;
}
return undefined;
},
};
function createBaseListValue(): ListValue {
return { values: [] };
}
export const ListValue: MessageFns<ListValue> & ListValueWrapperFns = {
encode(message: ListValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.values !== undefined && message.values.length !== 0) {
for (const v of message.values) {
Value.encode(Value.wrap(v!), writer.uint32(10).fork()).join();
}
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): ListValue {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseListValue();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
const el = Value.unwrap(Value.decode(reader, reader.uint32()));
if (el !== undefined) {
message.values!.push(el);
}
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): ListValue {
return { values: globalThis.Array.isArray(object?.values) ? [...object.values] : [] };
},
toJSON(message: ListValue): unknown {
const obj: any = {};
if (message.values?.length) {
obj.values = message.values;
}
return obj;
},
create(base?: DeepPartial<ListValue>): ListValue {
return ListValue.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<ListValue>): ListValue {
const message = createBaseListValue();
message.values = object.values?.map((e) => e) || [];
return message;
},
wrap(array: Array<any> | undefined): ListValue {
const result = createBaseListValue();
result.values = array ?? [];
return result;
},
unwrap(message: ListValue): Array<any> {
if (message?.hasOwnProperty("values") && globalThis.Array.isArray(message.values)) {
return message.values;
} else {
return message as any;
}
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function isObject(value: any): boolean {
return typeof value === "object" && value !== null;
}
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create(base?: DeepPartial<T>): T;
fromPartial(object: DeepPartial<T>): T;
}
export interface StructWrapperFns {
wrap(object: { [key: string]: any } | undefined): Struct;
unwrap(message: Struct): { [key: string]: any };
}
export interface AnyValueWrapperFns {
wrap(value: any): Value;
unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined;
}
export interface ListValueWrapperFns {
wrap(array: Array<any> | undefined): ListValue;
unwrap(message: ListValue): Array<any>;
}
@@ -1,228 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: google/protobuf/timestamp.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "google.protobuf";
/**
* A Timestamp represents a point in time independent of any time zone or local
* calendar, encoded as a count of seconds and fractions of seconds at
* nanosecond resolution. The count is relative to an epoch at UTC midnight on
* January 1, 1970, in the proleptic Gregorian calendar which extends the
* Gregorian calendar backwards to year one.
*
* All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
* second table is needed for interpretation, using a [24-hour linear
* smear](https://developers.google.com/time/smear).
*
* The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
* restricting to that range, we ensure that we can convert to and from [RFC
* 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
*
* # Examples
*
* Example 1: Compute Timestamp from POSIX `time()`.
*
* Timestamp timestamp;
* timestamp.set_seconds(time(NULL));
* timestamp.set_nanos(0);
*
* Example 2: Compute Timestamp from POSIX `gettimeofday()`.
*
* struct timeval tv;
* gettimeofday(&tv, NULL);
*
* Timestamp timestamp;
* timestamp.set_seconds(tv.tv_sec);
* timestamp.set_nanos(tv.tv_usec * 1000);
*
* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
*
* FILETIME ft;
* GetSystemTimeAsFileTime(&ft);
* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
*
* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
* Timestamp timestamp;
* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
*
* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
*
* long millis = System.currentTimeMillis();
*
* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
* .setNanos((int) ((millis % 1000) * 1000000)).build();
*
* Example 5: Compute Timestamp from Java `Instant.now()`.
*
* Instant now = Instant.now();
*
* Timestamp timestamp =
* Timestamp.newBuilder().setSeconds(now.getEpochSecond())
* .setNanos(now.getNano()).build();
*
* Example 6: Compute Timestamp from current time in Python.
*
* timestamp = Timestamp()
* timestamp.GetCurrentTime()
*
* # JSON Mapping
*
* In JSON format, the Timestamp type is encoded as a string in the
* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
* where {year} is always expressed using four digits while {month}, {day},
* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
* is required. A ProtoJSON serializer should always use UTC (as indicated by
* "Z") when printing the Timestamp type and a ProtoJSON parser should be
* able to accept both UTC and other timezones (as indicated by an offset).
*
* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
* 01:30 UTC on January 15, 2017.
*
* In JavaScript, one can convert a Date object to this format using the
* standard
* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
* method. In Python, a standard `datetime.datetime` object can be converted
* to this format using
* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
* the Joda Time's [`ISODateTimeFormat.dateTime()`](
* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
* ) to obtain a formatter capable of generating timestamps in this format.
*/
export interface Timestamp {
/**
* Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must
* be between -62135596800 and 253402300799 inclusive (which corresponds to
* 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).
*/
seconds?:
| number
| undefined;
/**
* Non-negative fractions of a second at nanosecond resolution. This field is
* the nanosecond portion of the duration, not an alternative to seconds.
* Negative second values with fractions must still have non-negative nanos
* values that count forward in time. Must be between 0 and 999,999,999
* inclusive.
*/
nanos?: number | undefined;
}
function createBaseTimestamp(): Timestamp {
return { seconds: 0, nanos: 0 };
}
export const Timestamp: MessageFns<Timestamp> = {
encode(message: Timestamp, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.seconds !== undefined && message.seconds !== 0) {
writer.uint32(8).int64(message.seconds);
}
if (message.nanos !== undefined && message.nanos !== 0) {
writer.uint32(16).int32(message.nanos);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Timestamp {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseTimestamp();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 8) {
break;
}
message.seconds = longToNumber(reader.int64());
continue;
}
case 2: {
if (tag !== 16) {
break;
}
message.nanos = reader.int32();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Timestamp {
return {
seconds: isSet(object.seconds) ? globalThis.Number(object.seconds) : 0,
nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0,
};
},
toJSON(message: Timestamp): unknown {
const obj: any = {};
if (message.seconds !== undefined && message.seconds !== 0) {
obj.seconds = Math.round(message.seconds);
}
if (message.nanos !== undefined && message.nanos !== 0) {
obj.nanos = Math.round(message.nanos);
}
return obj;
},
create(base?: DeepPartial<Timestamp>): Timestamp {
return Timestamp.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<Timestamp>): Timestamp {
const message = createBaseTimestamp();
message.seconds = object.seconds ?? 0;
message.nanos = object.nanos ?? 0;
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
function longToNumber(int64: { toString(): string }): number {
const num = globalThis.Number(int64.toString());
if (num > globalThis.Number.MAX_SAFE_INTEGER) {
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
}
if (num < globalThis.Number.MIN_SAFE_INTEGER) {
throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER");
}
return num;
}
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create(base?: DeepPartial<T>): T;
fromPartial(object: DeepPartial<T>): T;
}
File diff suppressed because it is too large Load Diff
@@ -1,9 +0,0 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.12.0
// protoc v7.35.1
// source: protoc-gen-openapiv2/options/annotations.proto
/* eslint-disable */
export const protobufPackage = "grpc.gateway.protoc_gen_openapiv2.options";
File diff suppressed because it is too large Load Diff
-65
View File
@@ -1,65 +0,0 @@
import { ref, type Ref } from 'vue';
export type ApiMethod<T, P = void> = (params: P) => Promise<T>;
interface UseApiOptions<T> {
immediate?: boolean;
onSuccess?: (data: T) => void;
onError?: (error: Error) => void;
}
export function useApi<T, P = void>(
method: ApiMethod<T, P>,
params?: P,
options: UseApiOptions<T> = {}
) {
const data = ref<T | null>(null) as Ref<T | null>;
const loading = ref(false);
const error = ref<Error | null>(null);
const execute = async (execParams?: P) => {
loading.value = true;
error.value = null;
try {
const result = await method(execParams !== undefined ? execParams : (params as P));
data.value = result;
options.onSuccess?.(result);
return result;
} catch (e) {
const err = e instanceof Error ? e : new Error('Unknown error');
error.value = err;
options.onError?.(err);
throw err;
} finally {
loading.value = false;
}
};
const reset = () => {
data.value = null;
loading.value = false;
error.value = null;
};
// Автоматический запуск
if (options.immediate !== false) {
execute();
}
return {
data,
loading,
error,
execute,
reset,
};
}
// Упрощенная версия для GET-запросов
export function useApiData<T, P = void>(
method: ApiMethod<T, P>,
params?: P
) {
return useApi(method, params, { immediate: true });
}
-73
View File
@@ -1,73 +0,0 @@
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { api } from '@/api/client';
import type { User } from '@/api/generated/main';
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
const isAuthenticated = computed(() => !!user.value);
const isAdmin = computed(() => user.value?.roles?.includes('admin') ?? false);
async function login(email: string, password: string) {
loading.value = true;
error.value = null;
try {
const response = await api.login({ email, password });
if (response.error) {
error.value = response.error;
return false;
}
await fetchMe();
return true;
} catch (err) {
error.value = err instanceof Error ? err.message : 'Login failed';
return false;
} finally {
loading.value = false;
}
}
async function fetchMe() {
loading.value = true;
try {
const response = await api.getMe();
if (response.error) {
error.value = response.error;
user.value = null;
} else if (response.user) {
user.value = response.user;
}
} catch (err) {
user.value = null;
throw err;
} finally {
loading.value = false;
}
}
async function logout() {
await api.logout();
user.value = null;
error.value = null;
}
function hasRole(role: string): boolean {
return user.value?.roles?.includes(role) ?? false;
}
return {
user,
loading,
error,
isAuthenticated,
isAdmin,
login,
fetchMe,
logout,
hasRole,
};
});