feat(tsrpc): enhance TypeScript API generation with improved endpoint handling and imports
- Added a new function `GetApiFile` to generate the TypeScript API file dynamically based on environment variables. - Updated `TSEndpoint` struct to include an `Imports` map for tracking TypeScript imports. - Enhanced `VerifyTypes` method to manage request and response types more effectively, including nullable types. - Modified `ToTs` method to generate TypeScript code with improved type handling. - Introduced `TSFiles` struct for managing generated TypeScript files and saving them to the filesystem. - Implemented formatting of generated TypeScript code using Prettier. - Added new TypeScript files for various endpoints, including user management and system utilities. - Updated existing TypeScript files to reflect changes in API structure and response types.
This commit is contained in:
parent
5b9fe6c9b7
commit
3461395eb3
|
|
@ -13,3 +13,8 @@ DB_dsn=file:./data/data.db?_foreign_keys=on
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
AUTH_SECRET=change-me
|
AUTH_SECRET=change-me
|
||||||
|
|
||||||
|
# TS Generator
|
||||||
|
TS_GENERATOR_URL=http://localhost:3000
|
||||||
|
TS_GENERATOR_PATH= .
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { api } from "./api.ts";
|
||||||
|
import { Nullable } from "./apiTypes.ts";
|
||||||
|
import * as users from "./users.ts";
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=users.[]User
|
||||||
|
// internal/admin/routes.go Line: 12
|
||||||
|
export const listUsers = async (
|
||||||
|
data: ListUsersRequest,
|
||||||
|
): Promise<{ data: users.User[]; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/admin/users", data)) as {
|
||||||
|
data: users.User[];
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=users.User
|
||||||
|
// internal/admin/routes.go Line: 16
|
||||||
|
export const blockUser = async (
|
||||||
|
data: BlockUserRequest,
|
||||||
|
): Promise<{ data: users.User; error: Nullable<string> }> => {
|
||||||
|
return (await api.PUT("/admin/users/:uuid/block", data)) as {
|
||||||
|
data: users.User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface BlockUserRequest {
|
||||||
|
action: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListUsersRequest {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,276 @@
|
||||||
|
//
|
||||||
|
// Typescript API generated from gofiber backend
|
||||||
|
// Copyright (C) 2022 - 2025 Fabio Prada
|
||||||
|
//
|
||||||
|
// This file was generated by github.com/millevolte/ts-rpc
|
||||||
|
//
|
||||||
|
// Apr 14, 2026 21:39:07 UTC
|
||||||
|
//
|
||||||
|
|
||||||
|
export interface ApiRestResponse {
|
||||||
|
data?: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isApiRestResponse(data: unknown): data is ApiRestResponse {
|
||||||
|
return (
|
||||||
|
typeof data === "object" &&
|
||||||
|
data !== null &&
|
||||||
|
Object.prototype.hasOwnProperty.call(data, "data") &&
|
||||||
|
Object.prototype.hasOwnProperty.call(data, "error")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeError(error: unknown): Error {
|
||||||
|
if (error instanceof DOMException && error.name === "AbortError") {
|
||||||
|
return new Error("api.error.timeouterror");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof TypeError && error.message === "Failed to fetch") {
|
||||||
|
return new Error("api.error.connectionerror");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(String(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Api {
|
||||||
|
apiUrl: string;
|
||||||
|
localStorage: Storage | null;
|
||||||
|
|
||||||
|
constructor(apiurl: string) {
|
||||||
|
this.apiUrl = apiurl;
|
||||||
|
this.localStorage = window.localStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async request(
|
||||||
|
method: string,
|
||||||
|
url: string,
|
||||||
|
data: unknown,
|
||||||
|
timeout = 7000,
|
||||||
|
upload = false,
|
||||||
|
): Promise<ApiRestResponse> {
|
||||||
|
const headers: { [key: string]: string } = {
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!upload) {
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localStorage) {
|
||||||
|
const auth = this.localStorage.getItem("Auth-Token");
|
||||||
|
if (auth) {
|
||||||
|
headers["Auth-Token"] = auth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
|
const requestOptions: RequestInit = {
|
||||||
|
method,
|
||||||
|
cache: "no-store",
|
||||||
|
mode: "cors",
|
||||||
|
credentials: "include",
|
||||||
|
headers,
|
||||||
|
signal: controller.signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (upload) {
|
||||||
|
requestOptions.body = data as FormData;
|
||||||
|
} else if (data !== null && data !== undefined) {
|
||||||
|
requestOptions.body = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, requestOptions);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("api.error." + response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localStorage) {
|
||||||
|
const jwt = response.headers.get("Auth-Token");
|
||||||
|
if (jwt) {
|
||||||
|
this.localStorage.setItem("Auth-Token", jwt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = (await response.json()) as unknown;
|
||||||
|
if (!isApiRestResponse(responseData)) {
|
||||||
|
throw new Error("api.error.wrongdatatype");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseData.error) {
|
||||||
|
throw new Error(responseData.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
throw normalizeError(error);
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processResult(result: ApiRestResponse): {
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
} {
|
||||||
|
if (typeof result.data !== "object") {
|
||||||
|
return { data: result.data, error: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.data) {
|
||||||
|
result.data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: result.data, error: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
processError(error: unknown): {
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
} {
|
||||||
|
const normalizedError = normalizeError(error);
|
||||||
|
|
||||||
|
if (normalizedError.message === "api.error.timeouterror") {
|
||||||
|
Object.defineProperty(normalizedError, "__api_error__", {
|
||||||
|
value: normalizedError.message,
|
||||||
|
writable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data: null, error: normalizedError.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedError.message === "api.error.connectionerror") {
|
||||||
|
Object.defineProperty(normalizedError, "__api_error__", {
|
||||||
|
value: normalizedError.message,
|
||||||
|
writable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data: null, error: normalizedError.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
error: normalizedError.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async POST(
|
||||||
|
url: string,
|
||||||
|
data: unknown,
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<{
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const upload = url.includes("/upload/");
|
||||||
|
const result = await this.request(
|
||||||
|
"POST",
|
||||||
|
this.apiUrl + url,
|
||||||
|
data,
|
||||||
|
timeout,
|
||||||
|
upload,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.processResult(result);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return this.processError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async PUT(
|
||||||
|
url: string,
|
||||||
|
data: unknown,
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<{
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const upload = url.includes("/upload/");
|
||||||
|
const result = await this.request(
|
||||||
|
"PUT",
|
||||||
|
this.apiUrl + url,
|
||||||
|
data,
|
||||||
|
timeout,
|
||||||
|
upload,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.processResult(result);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return this.processError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async GET(
|
||||||
|
url: string,
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<{
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const result = await this.request(
|
||||||
|
"GET",
|
||||||
|
this.apiUrl + url,
|
||||||
|
null,
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
return this.processResult(result);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return this.processError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async DELETE(
|
||||||
|
url: string,
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<{
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const result = await this.request(
|
||||||
|
"DELETE",
|
||||||
|
this.apiUrl + url,
|
||||||
|
null,
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
return this.processResult(result);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return this.processError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async UPLOAD(
|
||||||
|
url: string,
|
||||||
|
data: unknown,
|
||||||
|
timeout?: number,
|
||||||
|
): Promise<{
|
||||||
|
data: unknown;
|
||||||
|
error: string | null;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const result = await this.request(
|
||||||
|
"POST",
|
||||||
|
this.apiUrl + url,
|
||||||
|
data,
|
||||||
|
timeout,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.processResult(result);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return this.processError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const api = new Api("http://localhost:3000");
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
// API Declarations
|
||||||
|
export type Nullable<T> = T | null;
|
||||||
|
|
||||||
|
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
|
||||||
|
|
@ -1,621 +0,0 @@
|
||||||
//
|
|
||||||
// Typescript API generated from gofiber backend
|
|
||||||
// Copyright (C) 2022 - 2025 Fabio Prada
|
|
||||||
//
|
|
||||||
// This file was generated by github.com/millevolte/ts-rpc
|
|
||||||
//
|
|
||||||
// Apr 06, 2026 16:56:35 UTC
|
|
||||||
//
|
|
||||||
|
|
||||||
export interface ApiRestResponse {
|
|
||||||
data?: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isApiRestResponse(data: unknown): data is ApiRestResponse {
|
|
||||||
return (
|
|
||||||
typeof data === "object" &&
|
|
||||||
data !== null &&
|
|
||||||
Object.prototype.hasOwnProperty.call(data, "data") &&
|
|
||||||
Object.prototype.hasOwnProperty.call(data, "error")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeError(error: unknown): Error {
|
|
||||||
if (error instanceof DOMException && error.name === "AbortError") {
|
|
||||||
return new Error("api.error.timeouterror");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof TypeError && error.message === "Failed to fetch") {
|
|
||||||
return new Error("api.error.connectionerror");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Error(String(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Api {
|
|
||||||
apiUrl: string;
|
|
||||||
localStorage: Storage | null;
|
|
||||||
|
|
||||||
constructor(apiurl: string) {
|
|
||||||
this.apiUrl = apiurl;
|
|
||||||
this.localStorage = window.localStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
async request(
|
|
||||||
method: string,
|
|
||||||
url: string,
|
|
||||||
data: unknown,
|
|
||||||
timeout = 7000,
|
|
||||||
upload = false,
|
|
||||||
): Promise<ApiRestResponse> {
|
|
||||||
const headers: { [key: string]: string } = {
|
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!upload) {
|
|
||||||
headers["Content-Type"] = "application/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.localStorage) {
|
|
||||||
const auth = this.localStorage.getItem("Auth-Token");
|
|
||||||
if (auth) {
|
|
||||||
headers["Auth-Token"] = auth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
||||||
const requestOptions: RequestInit = {
|
|
||||||
method,
|
|
||||||
cache: "no-store",
|
|
||||||
mode: "cors",
|
|
||||||
credentials: "include",
|
|
||||||
headers,
|
|
||||||
signal: controller.signal,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (upload) {
|
|
||||||
requestOptions.body = data as FormData;
|
|
||||||
} else if (data !== null && data !== undefined) {
|
|
||||||
requestOptions.body = JSON.stringify(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, requestOptions);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("api.error." + response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.localStorage) {
|
|
||||||
const jwt = response.headers.get("Auth-Token");
|
|
||||||
if (jwt) {
|
|
||||||
this.localStorage.setItem("Auth-Token", jwt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = (await response.json()) as unknown;
|
|
||||||
if (!isApiRestResponse(responseData)) {
|
|
||||||
throw new Error("api.error.wrongdatatype");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseData.error) {
|
|
||||||
throw new Error(responseData.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseData;
|
|
||||||
} catch (error: unknown) {
|
|
||||||
throw normalizeError(error);
|
|
||||||
} finally {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processResult(result: ApiRestResponse): {
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
} {
|
|
||||||
if (typeof result.data !== "object") {
|
|
||||||
return { data: result.data, error: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.data) {
|
|
||||||
result.data = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { data: result.data, error: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
processError(error: unknown): {
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
} {
|
|
||||||
const normalizedError = normalizeError(error);
|
|
||||||
|
|
||||||
if (normalizedError.message === "api.error.timeouterror") {
|
|
||||||
Object.defineProperty(normalizedError, "__api_error__", {
|
|
||||||
value: normalizedError.message,
|
|
||||||
writable: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data: null, error: normalizedError.message };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (normalizedError.message === "api.error.connectionerror") {
|
|
||||||
Object.defineProperty(normalizedError, "__api_error__", {
|
|
||||||
value: normalizedError.message,
|
|
||||||
writable: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data: null, error: normalizedError.message };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: null,
|
|
||||||
error: normalizedError.message,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async POST(
|
|
||||||
url: string,
|
|
||||||
data: unknown,
|
|
||||||
timeout?: number,
|
|
||||||
): Promise<{
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const upload = url.includes("/upload/");
|
|
||||||
const result = await this.request(
|
|
||||||
"POST",
|
|
||||||
this.apiUrl + url,
|
|
||||||
data,
|
|
||||||
timeout,
|
|
||||||
upload,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.processResult(result);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return this.processError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async PUT(
|
|
||||||
url: string,
|
|
||||||
data: unknown,
|
|
||||||
timeout?: number,
|
|
||||||
): Promise<{
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const upload = url.includes("/upload/");
|
|
||||||
const result = await this.request(
|
|
||||||
"PUT",
|
|
||||||
this.apiUrl + url,
|
|
||||||
data,
|
|
||||||
timeout,
|
|
||||||
upload,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.processResult(result);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return this.processError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async GET(
|
|
||||||
url: string,
|
|
||||||
timeout?: number,
|
|
||||||
): Promise<{
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const result = await this.request(
|
|
||||||
"GET",
|
|
||||||
this.apiUrl + url,
|
|
||||||
null,
|
|
||||||
timeout,
|
|
||||||
);
|
|
||||||
return this.processResult(result);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return this.processError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async DELETE(
|
|
||||||
url: string,
|
|
||||||
timeout?: number,
|
|
||||||
): Promise<{
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const result = await this.request(
|
|
||||||
"DELETE",
|
|
||||||
this.apiUrl + url,
|
|
||||||
null,
|
|
||||||
timeout,
|
|
||||||
);
|
|
||||||
return this.processResult(result);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return this.processError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async UPLOAD(
|
|
||||||
url: string,
|
|
||||||
data: unknown,
|
|
||||||
timeout?: number,
|
|
||||||
): Promise<{
|
|
||||||
data: unknown;
|
|
||||||
error: string | null;
|
|
||||||
}> {
|
|
||||||
try {
|
|
||||||
const result = await this.request(
|
|
||||||
"POST",
|
|
||||||
this.apiUrl + url,
|
|
||||||
data,
|
|
||||||
timeout,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.processResult(result);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
return this.processError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const api = new Api("http://localhost:3000");
|
|
||||||
|
|
||||||
// Global Declarations
|
|
||||||
export type Nullable<T> = T | null;
|
|
||||||
|
|
||||||
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
|
|
||||||
|
|
||||||
//
|
|
||||||
// package user
|
|
||||||
//
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=models.UserProfile
|
|
||||||
// internal/user/routes.go Line: 13
|
|
||||||
export const getUser = async (
|
|
||||||
uuid: string,
|
|
||||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
|
||||||
return (await api.GET(`/users/${uuid}`)) as {
|
|
||||||
data: UserProfile;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=models.UserCreateInput; response=models.UserProfile
|
|
||||||
// internal/user/routes.go Line: 16
|
|
||||||
|
|
||||||
export const createUser = async (
|
|
||||||
data: UserCreateInput,
|
|
||||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/users", data)) as {
|
|
||||||
data: UserProfile;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=controllers.UpdateUserRequest; response=models.UserProfile
|
|
||||||
// internal/user/routes.go Line: 19
|
|
||||||
|
|
||||||
export const updateUser = async (
|
|
||||||
data: UpdateUserRequest,
|
|
||||||
): Promise<{ data: UserProfile; error: Nullable<string> }> => {
|
|
||||||
return (await api.PUT("/users/:uuid", data)) as {
|
|
||||||
data: UserProfile;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=controllers.SimpleResponse
|
|
||||||
// internal/user/routes.go Line: 22
|
|
||||||
|
|
||||||
export const deleteUser = async (
|
|
||||||
uuid: string,
|
|
||||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
|
||||||
return (await api.DELETE(`/users/${uuid}`)) as {
|
|
||||||
data: SimpleResponse;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=models.UserShort
|
|
||||||
// internal/user/routes.go Line: 25
|
|
||||||
export const me = async (): Promise<{
|
|
||||||
data: UserShort;
|
|
||||||
error: Nullable<string>;
|
|
||||||
}> => {
|
|
||||||
return (await api.GET("/auth/me")) as {
|
|
||||||
data: UserShort;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface UpdateUserRequest {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
roles: models.UserRoles;
|
|
||||||
status: models.UserStatus;
|
|
||||||
types: models.UserTypes;
|
|
||||||
avatar: Nullable<string>;
|
|
||||||
details: Nullable<models.UserDetailsShort>;
|
|
||||||
preferences: Nullable<models.UserPreferencesShort>;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package models
|
|
||||||
//
|
|
||||||
|
|
||||||
export interface UserDetailsShort {
|
|
||||||
title: string;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
address: string;
|
|
||||||
city: string;
|
|
||||||
zipCode: string;
|
|
||||||
country: string;
|
|
||||||
phone: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserPreferencesShort {
|
|
||||||
useIdle: boolean;
|
|
||||||
idleTimeout: number;
|
|
||||||
useIdlePassword: boolean;
|
|
||||||
idlePin: string;
|
|
||||||
useDirectLogin: boolean;
|
|
||||||
useQuadcodeLogin: boolean;
|
|
||||||
sendNoticesMail: boolean;
|
|
||||||
language: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserShort {
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
roles: UserRoles;
|
|
||||||
status: UserStatus;
|
|
||||||
uuid: string;
|
|
||||||
details: Nullable<UserDetailsShort>;
|
|
||||||
preferences: Nullable<UserPreferencesShort>;
|
|
||||||
avatar: Nullable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserCreateInput {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
roles: UserRoles;
|
|
||||||
status: UserStatus;
|
|
||||||
types: UserTypes;
|
|
||||||
avatar: Nullable<string>;
|
|
||||||
details: Nullable<UserDetailsShort>;
|
|
||||||
preferences: Nullable<UserPreferencesShort>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UserRoles = string[];
|
|
||||||
|
|
||||||
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
|
|
||||||
|
|
||||||
export type UserTypes = string[];
|
|
||||||
|
|
||||||
export type UsersShort = UserShort[];
|
|
||||||
|
|
||||||
export const EnumUserStatus = {
|
|
||||||
UserStatusPending: "pending",
|
|
||||||
UserStatusActive: "active",
|
|
||||||
UserStatusDisabled: "disabled",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
//
|
|
||||||
// package admin
|
|
||||||
//
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=models.UserShort
|
|
||||||
// internal/admin/routes.go Line: 16
|
|
||||||
|
|
||||||
export const blockUser = async (
|
|
||||||
data: BlockUserRequest,
|
|
||||||
): Promise<{ data: UserShort; error: Nullable<string> }> => {
|
|
||||||
return (await api.PUT("/admin/users/:uuid/block", data)) as {
|
|
||||||
data: UserShort;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=models.[]UserShort
|
|
||||||
// internal/admin/routes.go Line: 12
|
|
||||||
|
|
||||||
export const listUsers = async (
|
|
||||||
data: ListUsersRequest,
|
|
||||||
): Promise<{ data: UserShort[]; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/admin/users", data)) as {
|
|
||||||
data: UserShort[];
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface BlockUserRequest {
|
|
||||||
action: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListUsersRequest {
|
|
||||||
page: number;
|
|
||||||
pageSize: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package responses
|
|
||||||
//
|
|
||||||
|
|
||||||
export interface SimpleResponse {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package systemUtils
|
|
||||||
//
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
|
|
||||||
// internal/systemUtils/routes.go Line: 34
|
|
||||||
export const health = async (): Promise<{
|
|
||||||
data: string;
|
|
||||||
error: Nullable<string>;
|
|
||||||
}> => {
|
|
||||||
return (await api.GET("/health")) as {
|
|
||||||
data: string;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string
|
|
||||||
// internal/systemUtils/routes.go Line: 37
|
|
||||||
export const metrics = async (): Promise<{
|
|
||||||
data: string;
|
|
||||||
error: Nullable<string>;
|
|
||||||
}> => {
|
|
||||||
return (await api.GET("/metrics")) as {
|
|
||||||
data: string;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=routes.[]MailDebugItem
|
|
||||||
// internal/systemUtils/routes.go Line: 48
|
|
||||||
export const mailDebug = async (): Promise<{
|
|
||||||
data: MailDebugItem[];
|
|
||||||
error: Nullable<string>;
|
|
||||||
}> => {
|
|
||||||
return (await api.GET("/maildebug")) as {
|
|
||||||
data: MailDebugItem[];
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface MailDebugItem {
|
|
||||||
name: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package routes
|
|
||||||
//
|
|
||||||
|
|
||||||
export interface FormRequest {
|
|
||||||
req: string;
|
|
||||||
count: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FormResponse {
|
|
||||||
test: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// package auth
|
|
||||||
//
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=model.ResetPasswordRequest; response=controllers.SimpleResponse
|
|
||||||
// internal/auth/routes.go Line: 32
|
|
||||||
|
|
||||||
export const resetPassword = async (
|
|
||||||
data: ResetPasswordRequest,
|
|
||||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/password/reset", data)) as {
|
|
||||||
data: SimpleResponse;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=controllers.SimpleResponse
|
|
||||||
// internal/auth/routes.go Line: 35
|
|
||||||
|
|
||||||
export const validToken = async (
|
|
||||||
data: string,
|
|
||||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/password/valid", data)) as {
|
|
||||||
data: SimpleResponse;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=model.LoginRequest; response=model.TokenPair
|
|
||||||
// internal/auth/routes.go Line: 20
|
|
||||||
|
|
||||||
export const login = async (
|
|
||||||
data: LoginRequest,
|
|
||||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/login", data)) as {
|
|
||||||
data: TokenPair;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=model.RefreshRequest; response=model.TokenPair
|
|
||||||
// internal/auth/routes.go Line: 23
|
|
||||||
|
|
||||||
export const refresh = async (
|
|
||||||
data: RefreshRequest,
|
|
||||||
): Promise<{ data: TokenPair; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/refresh", data)) as {
|
|
||||||
data: TokenPair;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort
|
|
||||||
// internal/auth/routes.go Line: 26
|
|
||||||
|
|
||||||
export const register = async (
|
|
||||||
data: UserCreateInput,
|
|
||||||
): Promise<{ data: UserShort; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/register", data)) as {
|
|
||||||
data: UserShort;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=model.ForgotPasswordRequest; response=controllers.SimpleResponse
|
|
||||||
// internal/auth/routes.go Line: 29
|
|
||||||
|
|
||||||
export const forgotPassword = async (
|
|
||||||
data: ForgotPasswordRequest,
|
|
||||||
): Promise<{ data: SimpleResponse; error: Nullable<string> }> => {
|
|
||||||
return (await api.POST("/auth/password/forgot", data)) as {
|
|
||||||
data: SimpleResponse;
|
|
||||||
error: Nullable<string>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface TokenPair {
|
|
||||||
access_token: string;
|
|
||||||
refresh_token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoginRequest {
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ForgotPasswordRequest {
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RefreshRequest {
|
|
||||||
refresh_token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResetPasswordRequest {
|
|
||||||
token: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface SimpleResponse {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { api } from "./api.ts";
|
||||||
|
import { Nullable } from "./apiTypes.ts";
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string
|
||||||
|
// internal/systemUtils/routes.go Line: 41
|
||||||
|
export const metrics = async (): Promise<{
|
||||||
|
data: string;
|
||||||
|
error: Nullable<string>;
|
||||||
|
}> => {
|
||||||
|
return (await api.GET("/metrics")) as {
|
||||||
|
data: string;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=[]MailDebugItem
|
||||||
|
// internal/systemUtils/routes.go Line: 53
|
||||||
|
export const mailDebug = async (): Promise<{
|
||||||
|
data: MailDebugItem[];
|
||||||
|
error: Nullable<string>;
|
||||||
|
}> => {
|
||||||
|
return (await api.GET("/maildebug")) as {
|
||||||
|
data: MailDebugItem[];
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
|
||||||
|
// internal/systemUtils/routes.go Line: 38
|
||||||
|
export const health = async (): Promise<{
|
||||||
|
data: string;
|
||||||
|
error: Nullable<string>;
|
||||||
|
}> => {
|
||||||
|
return (await api.GET("/health")) as {
|
||||||
|
data: string;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface MailDebugItem {
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface TokenPair {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
import { api } from "./api.ts";
|
||||||
|
import { Nullable } from "./apiTypes.ts";
|
||||||
|
import * as responses from "./responses.ts";
|
||||||
|
import * as tokens from "./tokens.ts";
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair
|
||||||
|
// internal/user/routes.go Line: 46
|
||||||
|
export const refresh = async (
|
||||||
|
data: RefreshRequest,
|
||||||
|
): Promise<{ data: tokens.TokenPair; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/refresh", data)) as {
|
||||||
|
data: tokens.TokenPair;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=users.ResetPasswordRequest; response=responses.SimpleResponse
|
||||||
|
// internal/user/routes.go Line: 55
|
||||||
|
export const resetPassword = async (
|
||||||
|
data: ResetPasswordRequest,
|
||||||
|
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/password/reset", data)) as {
|
||||||
|
data: responses.SimpleResponse;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=users.User
|
||||||
|
// internal/user/routes.go Line: 27
|
||||||
|
export const getUser = async (
|
||||||
|
uuid: string,
|
||||||
|
): Promise<{ data: User; error: Nullable<string> }> => {
|
||||||
|
return (await api.GET(`/users/${uuid}`)) as {
|
||||||
|
data: User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=users.User
|
||||||
|
// internal/user/routes.go Line: 39
|
||||||
|
export const me = async (): Promise<{
|
||||||
|
data: User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
}> => {
|
||||||
|
return (await api.GET("/auth/me")) as { data: User; error: Nullable<string> };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=users.UserCreateInput; response=users.User
|
||||||
|
// internal/user/routes.go Line: 49
|
||||||
|
export const register = async (
|
||||||
|
data: UserCreateInput,
|
||||||
|
): Promise<{ data: User; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/register", data)) as {
|
||||||
|
data: User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse
|
||||||
|
// internal/user/routes.go Line: 52
|
||||||
|
export const forgotPassword = async (
|
||||||
|
data: ForgotPasswordRequest,
|
||||||
|
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/password/forgot", data)) as {
|
||||||
|
data: responses.SimpleResponse;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User
|
||||||
|
// internal/user/routes.go Line: 33
|
||||||
|
export const updateUser = async (
|
||||||
|
data: UpdateUserRequest,
|
||||||
|
): Promise<{ data: User; error: Nullable<string> }> => {
|
||||||
|
return (await api.PUT("/users/:uuid", data)) as {
|
||||||
|
data: User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
|
||||||
|
// internal/user/routes.go Line: 36
|
||||||
|
export const deleteUser = async (
|
||||||
|
uuid: string,
|
||||||
|
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
|
||||||
|
return (await api.DELETE(`/users/${uuid}`)) as {
|
||||||
|
data: responses.SimpleResponse;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
|
||||||
|
// internal/user/routes.go Line: 43
|
||||||
|
export const login = async (
|
||||||
|
data: LoginRequest,
|
||||||
|
): Promise<{ data: tokens.TokenPair; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/login", data)) as {
|
||||||
|
data: tokens.TokenPair;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/auth/password/valid; name=validToken; method=POST; request=string; response=responses.SimpleResponse
|
||||||
|
// internal/user/routes.go Line: 58
|
||||||
|
export const validToken = async (
|
||||||
|
data: string,
|
||||||
|
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/auth/password/valid", data)) as {
|
||||||
|
data: responses.SimpleResponse;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateInput; response=users.User
|
||||||
|
// internal/user/routes.go Line: 30
|
||||||
|
export const createUser = async (
|
||||||
|
data: UserCreateInput,
|
||||||
|
): Promise<{ data: User; error: Nullable<string> }> => {
|
||||||
|
return (await api.POST("/users", data)) as {
|
||||||
|
data: User;
|
||||||
|
error: Nullable<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface RefreshRequest {
|
||||||
|
refresh_token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
roles: UserRoles;
|
||||||
|
types: UserTypes;
|
||||||
|
status: UserStatus;
|
||||||
|
activatedAt: Date;
|
||||||
|
uuid: string;
|
||||||
|
details: Nullable<UserDetails>;
|
||||||
|
preferences: Nullable<UserPreferences>;
|
||||||
|
avatar: Nullable<string>;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserCreateInput {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
roles: UserRoles;
|
||||||
|
status: UserStatus;
|
||||||
|
types: UserTypes;
|
||||||
|
avatar: Nullable<string>;
|
||||||
|
details: Nullable<UserDetails>;
|
||||||
|
preferences: Nullable<UserPreferences>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserPreferences {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
useIdle: boolean;
|
||||||
|
idleTimeout: number;
|
||||||
|
useIdlePassword: boolean;
|
||||||
|
idlePin: string;
|
||||||
|
useDirectLogin: boolean;
|
||||||
|
useQuadcodeLogin: boolean;
|
||||||
|
sendNoticesMail: boolean;
|
||||||
|
language: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForgotPasswordRequest {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateUserRequest {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
roles: UserRoles;
|
||||||
|
status: UserStatus;
|
||||||
|
types: UserTypes;
|
||||||
|
avatar: Nullable<string>;
|
||||||
|
details: Nullable<UserDetails>;
|
||||||
|
preferences: Nullable<UserPreferences>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetails {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
title: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
address: string;
|
||||||
|
city: string;
|
||||||
|
zipCode: string;
|
||||||
|
country: string;
|
||||||
|
phone: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserProfile {
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
roles: UserRoles;
|
||||||
|
types: UserTypes;
|
||||||
|
status: UserStatus;
|
||||||
|
activatedAt: Date;
|
||||||
|
uuid: string;
|
||||||
|
details: Nullable<UserDetails>;
|
||||||
|
preferences: Nullable<UserPreferences>;
|
||||||
|
avatar: Nullable<string>;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResetPasswordRequest {
|
||||||
|
token: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserRoles = string[];
|
||||||
|
|
||||||
|
export type UserTypes = string[];
|
||||||
|
|
||||||
|
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
|
||||||
|
|
||||||
|
export const EnumUserStatus = {
|
||||||
|
UserStatusPending: "pending",
|
||||||
|
UserStatusActive: "active",
|
||||||
|
UserStatusDisabled: "disabled",
|
||||||
|
} as const;
|
||||||
|
|
@ -62,7 +62,7 @@ func main() {
|
||||||
IdleTimeout: time.Duration(cfg.IdleTimeoutSeconds) * time.Second,
|
IdleTimeout: time.Duration(cfg.IdleTimeoutSeconds) * time.Second,
|
||||||
ErrorHandler: func(c fiber.Ctx, err error) error {
|
ErrorHandler: func(c fiber.Ctx, err error) error {
|
||||||
code := fiber.StatusInternalServerError
|
code := fiber.StatusInternalServerError
|
||||||
msg := "internal server error"
|
msg := "internal server error: " + err.Error()
|
||||||
if e, ok := err.(*fiber.Error); ok {
|
if e, ok := err.(*fiber.Error); ok {
|
||||||
code = e.Code
|
code = e.Code
|
||||||
msg = e.Message
|
msg = e.Message
|
||||||
|
|
@ -70,7 +70,7 @@ func main() {
|
||||||
reqID := requestid.FromContext(c)
|
reqID := requestid.FromContext(c)
|
||||||
log.Printf("error request_id=%s status=%d method=%s path=%s ip=%s ua=%q err=%v", reqID, code, c.Method(), c.Path(), c.IP(), c.Get("User-Agent"), err)
|
log.Printf("error request_id=%s status=%d method=%s path=%s ip=%s ua=%q err=%v", reqID, code, c.Method(), c.Path(), c.IP(), c.Get("User-Agent"), err)
|
||||||
if code >= 500 {
|
if code >= 500 {
|
||||||
msg = "internal server error"
|
msg = "internal server error: " + err.Error()
|
||||||
}
|
}
|
||||||
return c.Status(code).JSON(fiber.Map{"data": nil, "error": msg})
|
return c.Status(code).JSON(fiber.Map{"data": nil, "error": msg})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import (
|
||||||
func RegisterAdminRoutes(app *fiber.App) {
|
func RegisterAdminRoutes(app *fiber.App) {
|
||||||
adminController := NewAdminController()
|
adminController := NewAdminController()
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=models.[]UserShort
|
// Typescript: TSEndpoint= path=/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=users.[]User
|
||||||
app.Post("/admin/users", adminController.ListUsers)
|
app.Post("/admin/users", adminController.ListUsers)
|
||||||
roles.RegisterEndpoint("POST/admin/users", int(roles.AdminPermission))
|
roles.RegisterEndpoint("POST/admin/users", int(roles.AdminPermission))
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=models.UserShort
|
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=users.User
|
||||||
app.Put("/admin/users/:uuid/block", adminController.BlockUser)
|
app.Put("/admin/users/:uuid/block", adminController.BlockUser)
|
||||||
roles.RegisterEndpoint("PUT/admin/users/:uuid/block", int(roles.AdminPermission))
|
roles.RegisterEndpoint("PUT/admin/users/:uuid/block", int(roles.AdminPermission))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ciao
|
||||||
|
|
@ -31,6 +31,10 @@ func healthHandler(c fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterSystemRoutes(app *fiber.App) {
|
func RegisterSystemRoutes(app *fiber.App) {
|
||||||
|
app.Get("/favicon.ico", func(c fiber.Ctx) error {
|
||||||
|
return c.SendString("boo")
|
||||||
|
})
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
|
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
|
||||||
app.Get("/health", healthHandler)
|
app.Get("/health", healthHandler)
|
||||||
|
|
||||||
|
|
@ -42,10 +46,11 @@ func RegisterSystemRoutes(app *fiber.App) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||||
}
|
}
|
||||||
|
c.Set(fiber.HeaderContentType, "text/plain")
|
||||||
return c.SendString(ts)
|
return c.SendString(ts)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=routes.[]MailDebugItem
|
// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=[]MailDebugItem
|
||||||
app.Get("/maildebug", func(c fiber.Ctx) error {
|
app.Get("/maildebug", func(c fiber.Ctx) error {
|
||||||
entries, err := os.ReadDir("data/mail-debug")
|
entries, err := os.ReadDir("data/mail-debug")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -90,5 +95,6 @@ func RegisterSystemRoutes(app *fiber.App) {
|
||||||
app.Get("/", func(c fiber.Ctx) error {
|
app.Get("/", func(c fiber.Ctx) error {
|
||||||
return c.SendFile(filepath.Join(spaDistPath, "index.html"))
|
return c.SendFile(filepath.Join(spaDistPath, "index.html"))
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Use("/", static.New(spaDistPath))
|
app.Use("/", static.New(spaDistPath))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
package tsgenerator
|
package tsgenerator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"path/filepath"
|
||||||
tsrpc "server/pkg/ts-rpc"
|
tsrpc "server/pkg/ts-rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TsGenerate() (string, error) {
|
func TsGenerate() (string, error) {
|
||||||
path := "GeneratedCode"
|
path := "GeneratedCode"
|
||||||
|
|
||||||
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
err := os.Mkdir(path, os.ModePerm)
|
err := os.Mkdir(path, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -18,53 +18,26 @@ func TsGenerate() (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tsSource = tsrpc.GetTSSource(tsrpc.TSConfig{Url: "http://localhost:3000", TsApi: nil, Path: "."})
|
d, err := os.Open(path)
|
||||||
|
|
||||||
formatted, err1 := formatJS(tsSource)
|
|
||||||
if err1 != nil {
|
|
||||||
return "", err1
|
|
||||||
}
|
|
||||||
|
|
||||||
err := os.WriteFile("GeneratedCode/generatedTypescript.ts", []byte(formatted), 0644)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("write local generated typescript: %w", err)
|
return "", fmt.Errorf("open GeneratedCode directory: %w", err)
|
||||||
}
|
}
|
||||||
|
defer d.Close()
|
||||||
frontendAPIPath := os.Getenv("FRONTEND_API_PATH")
|
names, err := d.Readdirnames(-1)
|
||||||
if frontendAPIPath == "" {
|
|
||||||
return "", errors.New("FRONTEND_API_PATH must be set")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Saving generated TypeScript to %s\n", frontendAPIPath)
|
|
||||||
|
|
||||||
err = os.WriteFile(frontendAPIPath+"/api.ts", []byte(formatted), 0644)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("write frontend api.ts: %w", err)
|
return "", fmt.Errorf("read GeneratedCode directory: %w", err)
|
||||||
}
|
}
|
||||||
|
for _, name := range names {
|
||||||
return formatted, nil
|
err = os.RemoveAll(filepath.Join(path, name))
|
||||||
}
|
|
||||||
|
|
||||||
func formatJS(src string) (string, error) {
|
|
||||||
// Se hai prettier globale:
|
|
||||||
cmd := exec.Command("prettier", "--parser", "typescript", "--use-tabs", "false", "--tab-width", "2")
|
|
||||||
|
|
||||||
// Se hai prettier solo come devDependency:
|
|
||||||
// cmd := exec.Command("npx", "prettier", "--parser", "typescript")
|
|
||||||
|
|
||||||
cmd.Stdin = bytes.NewBufferString(src)
|
|
||||||
|
|
||||||
var out bytes.Buffer
|
|
||||||
var stderr bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Stampa anche lo stderr di Prettier, così vedi l’errore reale
|
return "", fmt.Errorf("remove GeneratedCode directory content: %w", err)
|
||||||
return "", fmt.Errorf("prettier error: %v\nstderr: %s", err, stderr.String())
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.String(), nil
|
err = tsrpc.GetTSSource()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("get ts source: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Generation OK \n\n", nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,12 @@ import (
|
||||||
// Typescript: type
|
// Typescript: type
|
||||||
type UserRoles []string
|
type UserRoles []string
|
||||||
|
|
||||||
|
// Typescript: interface
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `json:"id" gorm:"primaryKey"`
|
ID int `json:"id" gorm:"primaryKey"`
|
||||||
Email string `json:"email" gorm:"uniqueIndex;size:255"`
|
Email string `json:"email" gorm:"uniqueIndex;size:255"`
|
||||||
Name string `json:"name" gorm:"size:255"`
|
Name string `json:"name" gorm:"size:255"`
|
||||||
Password string `json:"password" gorm:"size:255"`
|
Password string `json:"-" gorm:"size:255"`
|
||||||
Roles UserRoles `json:"roles" gorm:"type:text;serializer:json"`
|
Roles UserRoles `json:"roles" gorm:"type:text;serializer:json"`
|
||||||
Types UserTypes `json:"types" gorm:"type:text;serializer:json"`
|
Types UserTypes `json:"types" gorm:"type:text;serializer:json"`
|
||||||
Status UserStatus `json:"status" gorm:"type:text;default:'pending'"`
|
Status UserStatus `json:"status" gorm:"type:text;default:'pending'"`
|
||||||
|
|
@ -48,7 +49,7 @@ type UserCreateInput struct {
|
||||||
type UserTypes []string
|
type UserTypes []string
|
||||||
|
|
||||||
// UserProfile is the safe full representation of a user returned by CRUD endpoints.
|
// UserProfile is the safe full representation of a user returned by CRUD endpoints.
|
||||||
//
|
|
||||||
// Typescript: interface
|
// Typescript: interface
|
||||||
type UserProfile struct {
|
type UserProfile struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
|
|
@ -89,6 +90,8 @@ func ToUserProfile(u *User) UserProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserPreferences holds per-user settings stored as JSON.
|
// UserPreferences holds per-user settings stored as JSON.
|
||||||
|
|
||||||
|
// Typescript: interface
|
||||||
type UserPreferences struct {
|
type UserPreferences struct {
|
||||||
ID int `json:"id" gorm:"primaryKey"`
|
ID int `json:"id" gorm:"primaryKey"`
|
||||||
UserID int `json:"userId" gorm:"index"`
|
UserID int `json:"userId" gorm:"index"`
|
||||||
|
|
@ -104,9 +107,9 @@ type UserPreferences struct {
|
||||||
UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"`
|
UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserPreferences holds per-user settings stored as JSON.
|
|
||||||
|
|
||||||
// UserDetails holds optional profile data.
|
// UserDetails holds optional profile data.
|
||||||
|
|
||||||
|
// Typescript: interface
|
||||||
type UserDetails struct {
|
type UserDetails struct {
|
||||||
ID int `json:"id" gorm:"primaryKey"`
|
ID int `json:"id" gorm:"primaryKey"`
|
||||||
UserID int `json:"userId" gorm:"index"`
|
UserID int `json:"userId" gorm:"index"`
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ func RegisterUserRoutes(app *fiber.App) {
|
||||||
|
|
||||||
userController := NewUserController(tockenService)
|
userController := NewUserController(tockenService)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=users.UserProfile
|
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=users.User
|
||||||
app.Get("/users/:uuid", tockenService.Middleware(), userController.GetUser)
|
app.Get("/users/:uuid", tockenService.Middleware(), userController.GetUser)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateInput; response=users.UserProfile
|
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateInput; response=users.User
|
||||||
app.Post("/users", tockenService.Middleware(), userController.CreateUser)
|
app.Post("/users", tockenService.Middleware(), userController.CreateUser)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.UserProfile
|
// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User
|
||||||
app.Put("/users/:uuid", tockenService.Middleware(), userController.UpdateUser)
|
app.Put("/users/:uuid", tockenService.Middleware(), userController.UpdateUser)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
|
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
|
||||||
|
|
@ -43,7 +43,7 @@ func RegisterUserRoutes(app *fiber.App) {
|
||||||
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
|
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
|
||||||
app.Post("/auth/login", authRateLimiter, userController.Login)
|
app.Post("/auth/login", authRateLimiter, userController.Login)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=tokens.RefreshRequest; response=tokens.TokenPair
|
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair
|
||||||
app.Post("/auth/refresh", authRateLimiter, userController.Refresh)
|
app.Post("/auth/refresh", authRateLimiter, userController.Refresh)
|
||||||
|
|
||||||
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=users.UserCreateInput; response=users.User
|
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=users.UserCreateInput; response=users.User
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package tsrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSFiles map[string]string
|
||||||
|
|
||||||
|
func (t *TSFiles) Add(name string, source string) {
|
||||||
|
if t == nil || *t == nil {
|
||||||
|
*t = make(map[string]string)
|
||||||
|
}
|
||||||
|
if (*t)[name] == "" {
|
||||||
|
(*t)[name] = source
|
||||||
|
} else {
|
||||||
|
(*t)[name] += source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSFiles) Get(name string) (string, bool) {
|
||||||
|
if t == nil || *t == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
s, ok := (*t)[name]
|
||||||
|
return s, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSFiles) Save() error {
|
||||||
|
if t == nil || *t == nil {
|
||||||
|
return fmt.Errorf("TS Files not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range *t {
|
||||||
|
fmt.Printf("file: %s\nsource:\n%s\n\n", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, source := range *t {
|
||||||
|
formatted, err := formatJS(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("format JS: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(fmt.Sprintf("GeneratedCode/%s", name), []byte(formatted), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write local generated typescript: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatJS(src string) (string, error) {
|
||||||
|
// Se hai prettier globale:
|
||||||
|
cmd := exec.Command("prettier", "--parser", "typescript", "--use-tabs", "false", "--tab-width", "2")
|
||||||
|
|
||||||
|
// Se hai prettier solo come devDependency:
|
||||||
|
// cmd := exec.Command("npx", "prettier", "--parser", "typescript")
|
||||||
|
|
||||||
|
cmd.Stdin = bytes.NewBufferString(src)
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
// Stampa anche lo stderr di Prettier, così vedi l’errore reale
|
||||||
|
return "", fmt.Errorf("prettier error: %v\nstderr: %s", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String(), nil
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,14 @@
|
||||||
|
|
||||||
package tsrpc
|
package tsrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
const TsApiTemplate = `
|
const TsApiTemplate = `
|
||||||
//
|
//
|
||||||
// Typescript API generated from gofiber backend
|
// Typescript API generated from gofiber backend
|
||||||
|
|
@ -248,5 +256,39 @@ export default class Api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = new Api('{{ .APIURL }}');
|
export const api = new Api('{{ .APIURL }}');
|
||||||
`
|
`
|
||||||
|
|
||||||
|
type templateData struct {
|
||||||
|
APIURL string
|
||||||
|
CreatedOn time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetApiFile() (string, error) {
|
||||||
|
apiurl := ""
|
||||||
|
if value, exists := os.LookupEnv("TS_GENERATOR_URL"); exists {
|
||||||
|
apiurl = value
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("TS Generator URL environment variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// API file
|
||||||
|
tsApiSource := ""
|
||||||
|
data := TsApiTemplate
|
||||||
|
|
||||||
|
tpl, err := template.New("tsRpc").Parse(data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var templateData = templateData{
|
||||||
|
APIURL: apiurl,
|
||||||
|
CreatedOn: time.Now(),
|
||||||
|
}
|
||||||
|
var result bytes.Buffer
|
||||||
|
err = tpl.Execute(&result, templateData)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tsApiSource += result.String()
|
||||||
|
return tsApiSource, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// exportable typescript generated from golang
|
// able typescript generated from golang
|
||||||
// Copyright (C) 2022 Fabio Prada
|
// Copyright (C) 2022 Fabio Prada
|
||||||
|
|
||||||
package tsrpc
|
package tsrpc
|
||||||
|
|
@ -6,6 +6,7 @@ package tsrpc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
@ -16,11 +17,10 @@ type TSEndpoint struct {
|
||||||
Method string
|
Method string
|
||||||
Request string
|
Request string
|
||||||
Response string
|
Response string
|
||||||
RequestTs string
|
|
||||||
ResponseTs string
|
|
||||||
Source string
|
Source string
|
||||||
File string
|
File string
|
||||||
Line int
|
Line int
|
||||||
|
Imports map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseEndpoint(source string, file string, line int) TSEndpoint {
|
func ParseEndpoint(source string, file string, line int) TSEndpoint {
|
||||||
|
|
@ -32,6 +32,7 @@ func ParseEndpoint(source string, file string, line int) TSEndpoint {
|
||||||
endpoint.Source = strings.Trim(source, "\t")
|
endpoint.Source = strings.Trim(source, "\t")
|
||||||
endpoint.File = file
|
endpoint.File = file
|
||||||
endpoint.Line = line
|
endpoint.Line = line
|
||||||
|
endpoint.Imports = make(map[string][]string)
|
||||||
|
|
||||||
for _, v := range a {
|
for _, v := range a {
|
||||||
t := strings.Split(v, "=")
|
t := strings.Split(v, "=")
|
||||||
|
|
@ -83,84 +84,116 @@ type tplData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *TSEndpoint) VerifyTypes(info TSInfo, p string) {
|
func (e *TSEndpoint) VerifyTypes(info TSInfo, p string) {
|
||||||
|
kind := ""
|
||||||
|
|
||||||
|
if e.Name == "listUsers" {
|
||||||
|
fmt.Println("endpoint request", e.Request)
|
||||||
|
}
|
||||||
|
|
||||||
a := strings.Split(e.Request, ".")
|
a := strings.Split(e.Request, ".")
|
||||||
if e.Request != "" {
|
if e.Request != "" {
|
||||||
if len(a) == 2 {
|
if len(a) == 2 {
|
||||||
e.RequestTs = a[1]
|
kind = a[1]
|
||||||
if strings.HasPrefix(a[1], "[]") {
|
if strings.HasPrefix(a[1], "[]") {
|
||||||
a[1] = strings.TrimPrefix(a[1], "[]")
|
kind = fmt.Sprintf("%s[]", strings.TrimPrefix(a[1], "[]"))
|
||||||
e.RequestTs = fmt.Sprintf("%s[]", a[1])
|
|
||||||
}
|
}
|
||||||
|
// nullable pointer
|
||||||
if strings.HasPrefix(a[1], "*") {
|
if strings.HasPrefix(a[1], "*") {
|
||||||
a[1] = strings.TrimPrefix(a[1], "*")
|
kind = fmt.Sprintf("Nullable<%s>", strings.TrimPrefix(a[1], "*"))
|
||||||
e.RequestTs = fmt.Sprintf("Nullable<%s>", a[1])
|
|
||||||
}
|
}
|
||||||
e.Request = fmt.Sprintf("%s.%s", a[0], a[1])
|
|
||||||
if !info.find(a[0], a[1]) {
|
if !info.find(a[0], a[1]) {
|
||||||
exitOnError(fmt.Errorf("endpoint request not found: %s AT %s Line: %d ", e.Request, e.File, e.Line))
|
exitOnError(fmt.Errorf("endpoint request not found: %s AT %s Line: %d ", e.Request, e.File, e.Line))
|
||||||
}
|
}
|
||||||
|
if a[0] == p {
|
||||||
|
e.Request = fmt.Sprintf("%s", kind)
|
||||||
|
} else {
|
||||||
|
e.Request = fmt.Sprintf("%s.%s", kind, a[1])
|
||||||
|
|
||||||
|
if _, ok := e.Imports[a[0]]; !ok {
|
||||||
|
e.Imports[a[0]] = []string{}
|
||||||
|
}
|
||||||
|
allreadyImported := slices.Contains(e.Imports[a[0]], strings.Trim(a[1], "[]*"))
|
||||||
|
if !allreadyImported {
|
||||||
|
e.Imports[a[0]] = append(e.Imports[a[0]], strings.Trim(a[1], "[]*"))
|
||||||
|
}
|
||||||
|
}
|
||||||
info.setTypescript(a[0], a[1], true)
|
info.setTypescript(a[0], a[1], true)
|
||||||
}
|
}
|
||||||
if len(a) == 1 {
|
if len(a) == 1 {
|
||||||
e.RequestTs = a[0]
|
kind = a[0]
|
||||||
if strings.HasPrefix(a[0], "[]") {
|
if strings.HasPrefix(a[0], "[]") {
|
||||||
a[0] = strings.TrimPrefix(a[0], "[]")
|
kind = fmt.Sprintf("%s[]", strings.TrimPrefix(a[0], "[]"))
|
||||||
e.RequestTs = fmt.Sprintf("%s[]", a[0])
|
|
||||||
}
|
}
|
||||||
|
// nullable pointer
|
||||||
if strings.HasPrefix(a[0], "*") {
|
if strings.HasPrefix(a[0], "*") {
|
||||||
a[0] = strings.TrimPrefix(a[0], "*")
|
kind = fmt.Sprintf("Nullable<%s>", strings.TrimPrefix(a[0], "*"))
|
||||||
e.RequestTs = fmt.Sprintf("Nullable<%s>", a[0])
|
|
||||||
}
|
}
|
||||||
e.Request = a[0]
|
e.Request = fmt.Sprintf("%s", kind)
|
||||||
}
|
}
|
||||||
|
// if request is a struct, set it as typescript to generate the ts struct
|
||||||
}
|
}
|
||||||
|
kind = ""
|
||||||
a = strings.Split(e.Response, ".")
|
a = strings.Split(e.Response, ".")
|
||||||
if len(a) == 2 {
|
if len(a) == 2 {
|
||||||
if !info.find(a[0], a[1]) {
|
kind = a[1]
|
||||||
exitOnError(fmt.Errorf("endpoint response not found: %s AT %s Line: %d ", e.Request, e.File, e.Line))
|
|
||||||
}
|
|
||||||
e.ResponseTs = a[1]
|
|
||||||
if strings.HasPrefix(a[1], "[]") {
|
if strings.HasPrefix(a[1], "[]") {
|
||||||
a[1] = strings.TrimPrefix(a[1], "[]")
|
kind = fmt.Sprintf("%s[]", strings.TrimPrefix(a[1], "[]"))
|
||||||
e.ResponseTs = fmt.Sprintf("%s[]", a[1])
|
|
||||||
}
|
}
|
||||||
|
// nullable pointer
|
||||||
if strings.HasPrefix(a[1], "*") {
|
if strings.HasPrefix(a[1], "*") {
|
||||||
a[1] = strings.TrimPrefix(a[1], "*")
|
kind = fmt.Sprintf("Nullable<%s>", strings.TrimPrefix(a[1], "*"))
|
||||||
e.ResponseTs = fmt.Sprintf("Nullable<%s>", a[1])
|
|
||||||
}
|
}
|
||||||
e.Response = fmt.Sprintf("%s.%s", a[0], a[1])
|
|
||||||
|
if !info.find(a[0], a[1]) {
|
||||||
|
exitOnError(fmt.Errorf("endpoint response not found: %s AT %s Line: %d ", e.Response, e.File, e.Line))
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[0] == p {
|
||||||
|
e.Response = fmt.Sprintf("%s", kind)
|
||||||
|
} else {
|
||||||
|
e.Response = fmt.Sprintf("%s.%s", a[0], kind)
|
||||||
|
if _, ok := e.Imports[a[0]]; !ok {
|
||||||
|
e.Imports[a[0]] = []string{}
|
||||||
|
}
|
||||||
|
allreadyImported := slices.Contains(e.Imports[a[0]], strings.Trim(a[1], "[]*"))
|
||||||
|
if !allreadyImported {
|
||||||
|
e.Imports[a[0]] = append(e.Imports[a[0]], strings.Trim(a[1], "[]*"))
|
||||||
|
fmt.Printf("endpoint %s response import: %s.%s\n", e.Name, a[0], a[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.setTypescript(a[0], a[1], true)
|
||||||
}
|
}
|
||||||
if len(a) == 1 {
|
if len(a) == 1 {
|
||||||
e.ResponseTs = a[0]
|
|
||||||
if strings.HasPrefix(a[0], "[]") {
|
if strings.HasPrefix(a[0], "[]") {
|
||||||
a[0] = strings.TrimPrefix(a[0], "[]")
|
kind = fmt.Sprintf("%s[]", strings.TrimPrefix(a[0], "[]"))
|
||||||
e.ResponseTs = fmt.Sprintf("%s[]", a[0])
|
|
||||||
}
|
}
|
||||||
|
// nullable pointer
|
||||||
if strings.HasPrefix(a[0], "*") {
|
if strings.HasPrefix(a[0], "*") {
|
||||||
a[0] = strings.TrimPrefix(a[0], "*")
|
kind = fmt.Sprintf("Nullable<%s>", strings.TrimPrefix(a[0], "*"))
|
||||||
e.ResponseTs = fmt.Sprintf("Nullable<%s>", a[0])
|
}
|
||||||
|
if IsNativeType(a[0]) {
|
||||||
|
e.Response = fmt.Sprintf("%s%s", a[0], kind)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.Response = fmt.Sprintf("%s", kind)
|
||||||
|
// if request is a struct, set it as typescript to generate the ts struct
|
||||||
}
|
}
|
||||||
e.Response = a[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
func (e *TSEndpoint) ToTs() string {
|
||||||
|
|
||||||
func (e *TSEndpoint) ToTs(pkg string) string {
|
|
||||||
data := tplData{E: e, Path: e.Path, Params: []string{}}
|
data := tplData{E: e, Path: e.Path, Params: []string{}}
|
||||||
tpl := `
|
tpl := `
|
||||||
{{ .E.Source }}
|
{{ .E.Source }}
|
||||||
// {{ .E.File }} Line: {{ .E.Line }}
|
// {{ .E.File }} Line: {{ .E.Line }}
|
||||||
{{if eq .E.Method "GET"}}export const {{ .E.Name}} = async ({{range $v := .Params}}{{$v}}{{end}}):Promise<{ data:{{.E.ResponseTs}}; error: Nullable<string> }> => {
|
{{if eq .E.Method "GET"}}export const {{ .E.Name}} = async ({{range $v := .Params}}{{$v}}{{end}}):Promise<{ data:{{.E.Response}}; error: Nullable<string> }> => {
|
||||||
return await api.GET({{ .Path}}) as { data: {{ .E.ResponseTs}}; error: Nullable<string> };
|
return await api.GET({{ .Path}}) as { data: {{ .E.Response}}; error: Nullable<string> };
|
||||||
}{{end}}
|
}{{end}}{{if eq .E.Method "DELETE"}}export const {{ .E.Name}} = async ({{range $v := .Params}}{{$v}}{{end}}):Promise<{ data:{{.E.Response}}; error: Nullable<string> }> => {
|
||||||
{{if eq .E.Method "DELETE"}}export const {{ .E.Name}} = async ({{range $v := .Params}}{{$v}}{{end}}):Promise<{ data:{{.E.ResponseTs}}; error: Nullable<string> }> => {
|
return await api.DELETE({{ .Path}}) as { data: {{ .E.Response}}; error: Nullable<string> };
|
||||||
return await api.DELETE({{ .Path}}) as { data: {{ .E.ResponseTs}}; error: Nullable<string> };
|
}{{end}}{{if eq .E.Method "PUT"}}export const {{ .E.Name}} = async (data: {{ .E.Request}}):Promise<{ data:{{.E.Response}}; error: Nullable<string> }> => {
|
||||||
}{{end}}
|
return await api.PUT("{{ .Path}}", data) as { data: {{ .E.Response}}; error: Nullable<string> };
|
||||||
{{if eq .E.Method "PUT"}}export const {{ .E.Name}} = async (data: {{ .E.RequestTs}}):Promise<{ data:{{.E.ResponseTs}}; error: Nullable<string> }> => {
|
}{{end}}{{if eq .E.Method "POST"}}export const {{ .E.Name}} = async (data: {{ .E.Request}}):Promise<{ data:{{.E.Response}}; error: Nullable<string> }> => {
|
||||||
return await api.PUT("{{ .Path}}", data) as { data: {{ .E.ResponseTs}}; error: Nullable<string> };
|
return await api.POST("{{ .Path}}", data) as { data: {{ .E.Response }}; error: Nullable<string> };
|
||||||
}{{end}}
|
|
||||||
{{if eq .E.Method "POST"}}export const {{ .E.Name}} = async (data: {{ .E.RequestTs}}):Promise<{ data:{{.E.ResponseTs}}; error: Nullable<string> }> => {
|
|
||||||
return await api.POST("{{ .Path}}", data) as { data: {{ .E.ResponseTs}}; error: Nullable<string> };
|
|
||||||
}{{end}}`
|
}{{end}}`
|
||||||
|
|
||||||
if e.Method == "GET" || e.Method == "DELETE" {
|
if e.Method == "GET" || e.Method == "DELETE" {
|
||||||
|
|
@ -177,11 +210,12 @@ func (e *TSEndpoint) ToTs(pkg string) string {
|
||||||
c = ", "
|
c = ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix == ":" {
|
switch prefix {
|
||||||
|
case ":":
|
||||||
f = true
|
f = true
|
||||||
data.Params = append(data.Params, fmt.Sprintf("%s%s: string", c, v[1:]))
|
data.Params = append(data.Params, fmt.Sprintf("%s%s: string", c, v[1:]))
|
||||||
data.Path = strings.Replace(data.Path, v, fmt.Sprintf("${%s}", v[1:]), 1)
|
data.Path = strings.Replace(data.Path, v, fmt.Sprintf("${%s}", v[1:]), 1)
|
||||||
} else if prefix == "*" {
|
case "*":
|
||||||
f = true
|
f = true
|
||||||
data.Params = append(data.Params, fmt.Sprintf("%s%s: Nullable<string>", c, v[1:]))
|
data.Params = append(data.Params, fmt.Sprintf("%s%s: Nullable<string>", c, v[1:]))
|
||||||
data.Path = strings.Replace(data.Path, v, fmt.Sprintf("${%s}", v[1:]), 1)
|
data.Path = strings.Replace(data.Path, v, fmt.Sprintf("${%s}", v[1:]), 1)
|
||||||
|
|
@ -203,6 +237,5 @@ func (e *TSEndpoint) ToTs(pkg string) string {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.String()
|
return result.String()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ type TSInfoPakage struct {
|
||||||
consts map[string]TSConst
|
consts map[string]TSConst
|
||||||
decs map[string]TSDec
|
decs map[string]TSDec
|
||||||
endpoints map[string]TSEndpoint
|
endpoints map[string]TSEndpoint
|
||||||
|
imports map[string][]string
|
||||||
|
isUsed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type TSDec struct {
|
type TSDec struct {
|
||||||
|
|
@ -349,8 +351,11 @@ func (i *TSInfo) Populate(path string) {
|
||||||
if _, ok := i.Packages[pkg].endpoints[e.Name]; ok {
|
if _, ok := i.Packages[pkg].endpoints[e.Name]; ok {
|
||||||
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, l.Source))
|
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, l.Source))
|
||||||
}
|
}
|
||||||
|
pkg_info := i.Packages[pkg]
|
||||||
i.Packages[pkg].endpoints[e.Name] = e
|
pkg_info.endpoints[e.Name] = e
|
||||||
|
pkg_info.imports = e.Imports
|
||||||
|
pkg_info.isUsed = true
|
||||||
|
i.Packages[pkg] = pkg_info
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -388,9 +393,6 @@ func (i *TSInfo) Populate(path string) {
|
||||||
func (ts *TSInfo) findAvailableStruct(n string) (TSStruct, bool) {
|
func (ts *TSInfo) findAvailableStruct(n string) (TSStruct, bool) {
|
||||||
a := strings.Split(n, ".")
|
a := strings.Split(n, ".")
|
||||||
if len(a) == 2 {
|
if len(a) == 2 {
|
||||||
if n == "[]MailDebugItem" {
|
|
||||||
fmt.Printf("MailDebugItem found\n")
|
|
||||||
}
|
|
||||||
a[1] = strings.TrimPrefix(a[1], "[]")
|
a[1] = strings.TrimPrefix(a[1], "[]")
|
||||||
a[1] = strings.TrimPrefix(a[1], "*")
|
a[1] = strings.TrimPrefix(a[1], "*")
|
||||||
if _, ok := ts.Packages[a[0]]; ok {
|
if _, ok := ts.Packages[a[0]]; ok {
|
||||||
|
|
@ -399,6 +401,15 @@ func (ts *TSInfo) findAvailableStruct(n string) (TSStruct, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(a) == 1 {
|
||||||
|
n = strings.TrimPrefix(n, "[]")
|
||||||
|
n = strings.TrimPrefix(n, "*")
|
||||||
|
for p := range ts.Packages {
|
||||||
|
if _, ok := ts.Packages[p].structs[n]; ok {
|
||||||
|
return ts.Packages[p].structs[n], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if n == "" || IsNativeType(n) {
|
if n == "" || IsNativeType(n) {
|
||||||
return TSStruct{}, true
|
return TSStruct{}, true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,12 @@ type TSModule struct {
|
||||||
Consts map[string]string
|
Consts map[string]string
|
||||||
GTypes map[string]string
|
GTypes map[string]string
|
||||||
Endpoints map[string]string
|
Endpoints map[string]string
|
||||||
|
Imports map[string][]string
|
||||||
|
Source string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TSOutputSources []string
|
||||||
|
|
||||||
type TSSouces struct {
|
type TSSouces struct {
|
||||||
Pakages map[string]TSModule
|
Pakages map[string]TSModule
|
||||||
Errors []string
|
Errors []string
|
||||||
|
|
@ -32,6 +36,7 @@ func (ts *TSSouces) ensurePackage(p string) {
|
||||||
Consts: make(map[string]string),
|
Consts: make(map[string]string),
|
||||||
GTypes: make(map[string]string),
|
GTypes: make(map[string]string),
|
||||||
Endpoints: make(map[string]string),
|
Endpoints: make(map[string]string),
|
||||||
|
Imports: make(map[string][]string),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -214,9 +219,11 @@ func (ts *TSSouces) AddDependencies(info TSInfo, p string, s string, dependencie
|
||||||
func (ts *TSSouces) Populate(info TSInfo) {
|
func (ts *TSSouces) Populate(info TSInfo) {
|
||||||
ts.Pakages = make(map[string]TSModule)
|
ts.Pakages = make(map[string]TSModule)
|
||||||
ts.Errors = []string{}
|
ts.Errors = []string{}
|
||||||
for p, _ := range info.Packages {
|
for p := range info.Packages {
|
||||||
ts.ensurePackage(p)
|
ts.ensurePackage(p)
|
||||||
|
|
||||||
|
ts.Errors = append(ts.Errors, fmt.Sprintf("Process pakage %s\n", p))
|
||||||
|
|
||||||
for _, st := range info.Packages[p].structs {
|
for _, st := range info.Packages[p].structs {
|
||||||
if st.Typescript {
|
if st.Typescript {
|
||||||
if len(st.Fields) == 0 {
|
if len(st.Fields) == 0 {
|
||||||
|
|
@ -228,6 +235,8 @@ func (ts *TSSouces) Populate(info TSInfo) {
|
||||||
}
|
}
|
||||||
ts.Pakages[p].Structs[st.Name] = s
|
ts.Pakages[p].Structs[st.Name] = s
|
||||||
ts.AddDependencies(info, p, st.Name, dependencies)
|
ts.AddDependencies(info, p, st.Name, dependencies)
|
||||||
|
|
||||||
|
// check depenndecies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,7 +257,7 @@ func (ts *TSSouces) Populate(info TSInfo) {
|
||||||
ts.Pakages[p].Types[t.Name] = fmt.Sprintf("export type %s = %s\n", t.Name, t.TsType)
|
ts.Pakages[p].Types[t.Name] = fmt.Sprintf("export type %s = %s\n", t.Name, t.TsType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(info.Packages[p].endpoints) > 0 {
|
|
||||||
for _, e := range info.Packages[p].endpoints {
|
for _, e := range info.Packages[p].endpoints {
|
||||||
|
|
||||||
/* if e.Request != "" {
|
/* if e.Request != "" {
|
||||||
|
|
@ -274,12 +283,30 @@ func (ts *TSSouces) Populate(info TSInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} */
|
} */
|
||||||
|
responseAndRequest := ""
|
||||||
|
|
||||||
|
if e.Request != "" {
|
||||||
|
responseAndRequest += fmt.Sprintf(" request: %s ", e.Request)
|
||||||
|
}
|
||||||
|
if e.Response != "" {
|
||||||
|
responseAndRequest += fmt.Sprintf(" response: %s ", e.Response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TSReport += fmt.Sprintf("verify %s %s%s %s\n", e.Name, e.Method, e.Path, responseAndRequest)
|
||||||
|
|
||||||
e.VerifyTypes(info, p)
|
e.VerifyTypes(info, p)
|
||||||
|
pkg := ts.Pakages[p]
|
||||||
|
for k, v := range e.Imports {
|
||||||
|
if _, ok := pkg.Imports[k]; !ok {
|
||||||
|
pkg.Imports[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkg.Endpoints[e.Name] = e.ToTs()
|
||||||
|
ts.Pakages[p] = pkg
|
||||||
|
if p == "users" {
|
||||||
|
fmt.Printf("endpoint %s imports: %v\n", e.Name, pkg.Imports)
|
||||||
|
}
|
||||||
|
|
||||||
endpoint := e.ToTs(p)
|
|
||||||
ts.Pakages[p].Endpoints[e.Name] = endpoint
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,34 +4,29 @@
|
||||||
package tsrpc
|
package tsrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"strings"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
|
|
||||||
type TSConfig struct {
|
var TSReport = ""
|
||||||
Url string
|
|
||||||
TsApi *string
|
var tsFiles = TSFiles{}
|
||||||
Path string
|
|
||||||
|
func GetTSSource() error {
|
||||||
|
path := ""
|
||||||
|
if value, exists := os.LookupEnv("TS_GENERATOR_PATH"); exists {
|
||||||
|
path = value
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("TS Generator PATH environment variable not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
type tsTemplateData struct {
|
|
||||||
APIURL string
|
|
||||||
CreatedOn time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
var tsConfig TSConfig
|
|
||||||
|
|
||||||
func GetTSSource(config TSConfig) string {
|
|
||||||
tsConfig = config
|
|
||||||
var tsInfoData = TSInfo{}
|
var tsInfoData = TSInfo{}
|
||||||
var tsSourcesData = TSSouces{}
|
var tsSourcesData = TSSouces{}
|
||||||
tsInfoData.Populate(tsConfig.Path)
|
tsInfoData.Populate(path)
|
||||||
tsInfoData.TestEndpoints()
|
tsInfoData.TestEndpoints()
|
||||||
tsSourcesData.Populate(tsInfoData)
|
tsSourcesData.Populate(tsInfoData)
|
||||||
|
|
||||||
|
|
@ -43,43 +38,33 @@ func GetTSSource(config TSConfig) string {
|
||||||
exitOnError(fmt.Errorf("some errors...\n %s", err))
|
exitOnError(fmt.Errorf("some errors...\n %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
tsSource := ""
|
// api file
|
||||||
data := ""
|
tsApi, err := GetApiFile()
|
||||||
if tsConfig.TsApi == nil {
|
|
||||||
data = TsApiTemplate
|
|
||||||
} else {
|
|
||||||
d, err := os.ReadFile(*tsConfig.TsApi)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
exitOnError(fmt.Errorf("some errors...\n %s", err))
|
||||||
}
|
|
||||||
data = string(d)
|
|
||||||
}
|
}
|
||||||
|
tsFiles.Add("api.ts", tsApi)
|
||||||
|
|
||||||
t, err := template.New("tsRpc").Parse(data)
|
// index file
|
||||||
if err != nil {
|
tsApiDeclarations := []string{}
|
||||||
panic(err)
|
tsIndexSource := ""
|
||||||
}
|
tsIndexSource += fmt.Sprintln("\n// API Declarations ")
|
||||||
var templateData = tsTemplateData{
|
|
||||||
APIURL: config.Url,
|
|
||||||
CreatedOn: time.Now(),
|
|
||||||
}
|
|
||||||
var result bytes.Buffer
|
|
||||||
err = t.Execute(&result, templateData)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
tsSource += result.String()
|
|
||||||
|
|
||||||
tsSource += fmt.Sprintln("\n// Global Declarations ")
|
|
||||||
for p := range tsSourcesData.Pakages {
|
for p := range tsSourcesData.Pakages {
|
||||||
for _, v1 := range tsSourcesData.Pakages[p].GTypes {
|
for _, v1 := range tsSourcesData.Pakages[p].GTypes {
|
||||||
tsSource += fmt.Sprintln(v1)
|
tsIndexSource += fmt.Sprintln(v1)
|
||||||
|
}
|
||||||
|
for _, v1 := range tsInfoData.Packages[p].decs {
|
||||||
|
tsApiDeclarations = append(tsApiDeclarations, v1.Name[:strings.Index(v1.Name, "<")])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tsFiles.Add("apiTypes.ts", tsIndexSource)
|
||||||
|
|
||||||
for p := range tsSourcesData.Pakages {
|
for p := range tsSourcesData.Pakages {
|
||||||
|
if p == "users" {
|
||||||
|
fmt.Println("users package")
|
||||||
|
}
|
||||||
source := ""
|
source := ""
|
||||||
head := fmt.Sprintf("\n//\n// package %s\n//\n", p)
|
|
||||||
for _, v1 := range tsSourcesData.Pakages[p].Endpoints {
|
for _, v1 := range tsSourcesData.Pakages[p].Endpoints {
|
||||||
source += fmt.Sprintln(v1)
|
source += fmt.Sprintln(v1)
|
||||||
}
|
}
|
||||||
|
|
@ -95,10 +80,59 @@ func GetTSSource(config TSConfig) string {
|
||||||
for _, v1 := range tsSourcesData.Pakages[p].Consts {
|
for _, v1 := range tsSourcesData.Pakages[p].Consts {
|
||||||
source += fmt.Sprintln(v1)
|
source += fmt.Sprintln(v1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(source) > 0 {
|
if len(source) > 0 {
|
||||||
tsSource += head + source + "\n\n"
|
tmp := ""
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, sentence := range strings.Split(source, "\n") {
|
||||||
|
if strings.Contains(sentence, "api.") {
|
||||||
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tsSource
|
if found {
|
||||||
|
tmp += "import { api } from './api.ts'\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tsApiDeclarations) > 0 {
|
||||||
|
decs := []string{}
|
||||||
|
|
||||||
|
for _, d := range tsApiDeclarations {
|
||||||
|
found := false
|
||||||
|
for _, sentence := range strings.Split(source, "\n") {
|
||||||
|
if strings.Contains(sentence, d) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
decs = append(decs, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(decs) > 0 {
|
||||||
|
TSApiDeclarations := "import { "
|
||||||
|
for _, d := range decs {
|
||||||
|
TSApiDeclarations += d + ", "
|
||||||
|
}
|
||||||
|
TSApiDeclarations = TSApiDeclarations[:len(TSApiDeclarations)-2]
|
||||||
|
TSApiDeclarations += " } from './apiTypes.ts'\n"
|
||||||
|
fmt.Println("tsApiDeclarations", TSApiDeclarations)
|
||||||
|
tmp += TSApiDeclarations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imports := ""
|
||||||
|
for f := range tsSourcesData.Pakages[p].Imports {
|
||||||
|
imports += "import * as " + f + " from './" + f + ".ts'\n"
|
||||||
|
}
|
||||||
|
tmp += imports
|
||||||
|
tmp += source
|
||||||
|
tsFiles.Add(p+".ts", tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tsFiles.Save()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("save ts files: %s\n", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue