Refactor TypeScript RPC codebase and API structure

- Simplified error handling in `getFieldInfo` and `getFieldTsInfo` functions.
- Removed unused `getSourceInfo` function and its references.
- Updated `getStruct` method in `TSStruct` to eliminate source info retrieval.
- Cleaned up `TSSouces` population logic by commenting out unnecessary debug statements.
- Adjusted TypeScript source generation to use type imports instead of default imports.
- Consolidated API endpoints into dedicated files for better organization (admin, systemUtils, users).
- Introduced new types for API responses and requests to enhance type safety.
- Removed redundant code and comments from generated API files.
- Updated frontend components to reflect new API structure and types.
- Ensured consistent naming conventions and type usage across the codebase.
This commit is contained in:
fabio 2026-04-26 14:31:25 +02:00
parent 3461395eb3
commit b260daffed
33 changed files with 777 additions and 823 deletions

View File

@ -1,9 +1,10 @@
import { api } from "./api.ts";
import { Nullable } from "./apiTypes.ts";
import * as users from "./users.ts";
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
import type * 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
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 13
export const listUsers = async (
data: ListUsersRequest,
): Promise<{ data: users.User[]; error: Nullable<string> }> => {
@ -14,7 +15,8 @@ export const listUsers = async (
};
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=users.User
// internal/admin/routes.go Line: 16
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 17
export const blockUser = async (
data: BlockUserRequest,
): Promise<{ data: users.User; error: Nullable<string> }> => {
@ -24,11 +26,11 @@ export const blockUser = async (
};
};
export interface BlockUserRequest {
action: string;
}
export interface ListUsersRequest {
page: number;
pageSize: number;
}
export interface BlockUserRequest {
action: string;
}

View File

@ -4,7 +4,7 @@
//
// This file was generated by github.com/millevolte/ts-rpc
//
// Apr 14, 2026 21:39:07 UTC
// Apr 26, 2026 14:23:26 UTC
//
export interface ApiRestResponse {

View File

@ -1,4 +1,4 @@
// API Declarations
export type Nullable<T> = T | null;
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
export type Nullable<T> = T | null;

View File

@ -1,8 +1,22 @@
import { api } from "./api.ts";
import { Nullable } from "./apiTypes.ts";
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 39
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: 41
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 42
export const metrics = async (): Promise<{
data: string;
error: Nullable<string>;
@ -14,7 +28,8 @@ export const metrics = async (): Promise<{
};
// Typescript: TSEndpoint= path=/maildebug; name=mailDebug; method=GET; response=[]MailDebugItem
// internal/systemUtils/routes.go Line: 53
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 54
export const mailDebug = async (): Promise<{
data: MailDebugItem[];
error: Nullable<string>;
@ -25,18 +40,6 @@ export const mailDebug = async (): Promise<{
};
};
// 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;

View File

@ -1,10 +1,11 @@
import { api } from "./api.ts";
import { Nullable } from "./apiTypes.ts";
import * as responses from "./responses.ts";
import * as tokens from "./tokens.ts";
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
import type * as responses from "./responses.ts";
import type * 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
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 47
export const refresh = async (
data: RefreshRequest,
): Promise<{ data: tokens.TokenPair; error: Nullable<string> }> => {
@ -14,61 +15,9 @@ export const refresh = async (
};
};
// 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
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 34
export const updateUser = async (
data: UpdateUserRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
@ -78,30 +27,43 @@ export const updateUser = async (
};
};
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
// internal/user/routes.go Line: 36
export const deleteUser = async (
uuid: string,
// Typescript: TSEndpoint= path=/auth/me; name=me; method=GET; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 40
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/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 53
export const forgotPassword = async (
data: ForgotPasswordRequest,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
return (await api.DELETE(`/users/${uuid}`)) as {
return (await api.POST("/auth/password/forgot", data)) 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;
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=users.ResetPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 56
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=/auth/password/valid; name=validToken; method=POST; request=string; response=responses.SimpleResponse
// internal/user/routes.go Line: 58
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 59
export const validToken = async (
data: string,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
@ -111,10 +73,47 @@ export const validToken = async (
};
};
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateInput; response=users.User
// internal/user/routes.go Line: 30
// Typescript: TSEndpoint= path=/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 37
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=/users/:uuid; name=getUser; method=GET; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 28
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/register; name=register; method=POST; request=users.UserCreateRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 50
export const register = async (
data: UserCreateRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.POST("/auth/register", data)) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 31
export const createUser = async (
data: UserCreateInput,
data: UserCreateRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.POST("/users", data)) as {
data: User;
@ -122,8 +121,28 @@ export const createUser = async (
};
};
export interface RefreshRequest {
refresh_token: string;
// Typescript: TSEndpoint= path=/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 44
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>;
};
};
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 User {
@ -142,16 +161,13 @@ export interface User {
updatedAt: Date;
}
export interface UserCreateInput {
name: string;
export interface ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
roles: UserRoles;
status: UserStatus;
types: UserTypes;
avatar: Nullable<string>;
details: Nullable<UserDetails>;
preferences: Nullable<UserPreferences>;
}
export interface UserPreferences {
@ -169,11 +185,7 @@ export interface UserPreferences {
updatedAt: Date;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface UpdateUserRequest {
export interface UserCreateRequest {
name: string;
email: string;
password: string;
@ -201,37 +213,20 @@ export interface UserDetails {
zipCode: string;
country: string;
phone: string;
createdAt: Date;
updatedAt: Date;
createdAt: Nullable<Date>;
updatedAt: Nullable<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 interface RefreshRequest {
refresh_token: string;
}
export type UserRoles = string[];
export type UserTypes = string[];
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
export type UserTypes = string[];
export const EnumUserStatus = {
UserStatusPending: "pending",
UserStatusActive: "active",

View File

@ -9,7 +9,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/prometheus/client_golang v1.23.2
golang.org/x/crypto v0.48.0
golang.org/x/crypto v0.50.0
gorm.io/driver/postgres v1.6.0
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.1
@ -45,9 +45,11 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.69.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
)

View File

@ -101,15 +101,29 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -106,7 +106,8 @@ func (ac *AdminController) BlockUser(c fiber.Ctx) error {
default:
return fiber.NewError(fiber.StatusBadRequest, "invalid action")
}
u.UpdatedAt = time.Now().UTC()
now := time.Now().UTC()
u.UpdatedAt = &now
if err := db.Save(&u).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "failed to update user status")

View File

@ -73,8 +73,8 @@ func SeedUsers(db *gorm.DB, n int) ([]users.User, []Credential, error) {
Language: gofakeit.Language(),
},
Avatar: nil,
CreatedAt: now,
UpdatedAt: now,
CreatedAt: &now,
UpdatedAt: &now,
ActivatedAt: func() *time.Time {
ts := now
return &ts

View File

@ -31,19 +31,6 @@ func NewUserController(tockenService *tokens.TockenService) *UserController {
}
}
// Typescript: interface
type UpdateUserRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"omitempty,min=8,max=128"`
Roles UserRoles `json:"roles"`
Status UserStatus `json:"status"`
Types UserTypes `json:"types"`
Avatar *string `json:"avatar"`
Details *UserDetails `json:"details"`
Preferences *UserPreferences `json:"preferences"`
}
// GetUser returns a single user by UUID.
func (uc *UserController) GetUser(c fiber.Ctx) error {
user, err := loadUserByUUID(c)
@ -55,7 +42,7 @@ func (uc *UserController) GetUser(c fiber.Ctx) error {
// CreateUser creates a user together with optional details and preferences.
func (uc *UserController) CreateUser(c fiber.Ctx) error {
var req UserCreateInput
var req UserCreateRequest
if err := c.Bind().Body(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
}
@ -107,8 +94,8 @@ func (uc *UserController) CreateUser(c fiber.Ctx) error {
UUID: uuid.NewString(),
Details: req.Details,
Preferences: req.Preferences,
CreatedAt: now,
UpdatedAt: now,
CreatedAt: &now,
UpdatedAt: &now,
}
if err := db.Create(&user).Error; err != nil {
@ -155,7 +142,7 @@ func (uc *UserController) UpdateUser(c fiber.Ctx) error {
user.Name = req.Name
user.Email = req.Email
user.Avatar = req.Avatar
user.UpdatedAt = now
user.UpdatedAt = &now
if req.Status != "" {
user.Status = req.Status
}
@ -277,7 +264,7 @@ func (uc *UserController) Login(c fiber.Ctx) error {
// Register creates a new user with optional roles/types/preferences.
func (uc *UserController) Register(c fiber.Ctx) error {
var req UserCreateInput
var req UserCreateRequest
if err := c.Bind().Body(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid payload")
}
@ -342,8 +329,8 @@ func (uc *UserController) Register(c fiber.Ctx) error {
Language: req.Preferences.Language,
}
}(),
CreatedAt: now,
UpdatedAt: now,
CreatedAt: &now,
UpdatedAt: &now,
}
if err := db.Create(&user).Error; err != nil {

View File

@ -7,11 +7,8 @@ import (
)
// UserRoles is stored as JSON array of strings.
// Typescript: type
type UserRoles []string
// Typescript: interface
type User struct {
ID int `json:"id" gorm:"primaryKey"`
Email string `json:"email" gorm:"uniqueIndex;size:255"`
@ -25,32 +22,15 @@ type User struct {
Details *UserDetails `json:"details" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
Preferences *UserPreferences `json:"preferences" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
Avatar *string `json:"avatar" gorm:"size:512"`
CreatedAt time.Time `json:"createdAt" ts:"type=Date"`
UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index" ts:"type=Date"`
}
// UserCreateInput captures the minimal payload to create a user.
// Typescript: interface
type UserCreateInput struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=128"`
Roles UserRoles `json:"roles"`
Status UserStatus `json:"status"`
Types UserTypes `json:"types"`
Avatar *string `json:"avatar"`
Details *UserDetails `json:"details" `
Preferences *UserPreferences `json:"preferences" `
CreatedAt *time.Time `json:"createdAt" ts:"type=Date"`
UpdatedAt *time.Time `json:"updatedAt" ts:"type=Date"`
DeletedAt *gorm.DeletedAt `json:"-" gorm:"index" ts:"type=Date"`
}
// UserTypes is stored as JSON array (e.g. ["internal","external"]).
type UserTypes []string
// UserProfile is the safe full representation of a user returned by CRUD endpoints.
// Typescript: interface
type UserProfile struct {
ID int `json:"id"`
Email string `json:"email"`
@ -63,8 +43,8 @@ type UserProfile struct {
Details *UserDetails `json:"details"`
Preferences *UserPreferences `json:"preferences"`
Avatar *string `json:"avatar"`
CreatedAt time.Time `json:"createdAt" ts:"type=Date"`
UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"`
CreatedAt *time.Time `json:"createdAt" ts:"type=Date"`
UpdatedAt *time.Time `json:"updatedAt" ts:"type=Date"`
}
// ToUserProfile maps a User to a full response without exposing the password hash.
@ -91,7 +71,6 @@ func ToUserProfile(u *User) UserProfile {
// UserPreferences holds per-user settings stored as JSON.
// Typescript: interface
type UserPreferences struct {
ID int `json:"id" gorm:"primaryKey"`
UserID int `json:"userId" gorm:"index"`
@ -109,7 +88,6 @@ type UserPreferences struct {
// UserDetails holds optional profile data.
// Typescript: interface
type UserDetails struct {
ID int `json:"id" gorm:"primaryKey"`
UserID int `json:"userId" gorm:"index"`
@ -121,8 +99,8 @@ type UserDetails struct {
ZipCode string `json:"zipCode"`
Country string `json:"country"`
Phone string `json:"phone"`
CreatedAt time.Time `json:"createdAt" ts:"type=Date"`
UpdatedAt time.Time `json:"updatedAt" ts:"type=Date"`
CreatedAt time.Time `json:"createdAt" ts:"type=Nullable<Date>"`
UpdatedAt time.Time `json:"updatedAt" ts:"type=Nullable<Date>"`
}
// UserDetails holds optional profile data.
@ -164,24 +142,46 @@ const (
UserStatusDisabled UserStatus = "disabled"
)
// Typescript: interface
type LoginRequest struct {
Username string `json:"username" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=128"`
}
// Typescript: interface
type RefreshRequest struct {
RefreshToken string `json:"refresh_token"`
}
// Typescript: interface
type ForgotPasswordRequest struct {
Email string `json:"email" validate:"required,email"`
}
// Typescript: interface
type ResetPasswordRequest struct {
Token string `json:"token" validate:"required,min=20,max=255"`
Password string `json:"password" validate:"required,min=8,max=128"`
}
type UpdateUserRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"omitempty,min=8,max=128"`
Roles UserRoles `json:"roles"`
Status UserStatus `json:"status"`
Types UserTypes `json:"types"`
Avatar *string `json:"avatar"`
Details *UserDetails `json:"details"`
Preferences *UserPreferences `json:"preferences"`
}
// UserCreateRequest captures the minimal payload to create a user.
type UserCreateRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=128"`
Roles UserRoles `json:"roles"`
Status UserStatus `json:"status"`
Types UserTypes `json:"types"`
Avatar *string `json:"avatar"`
Details *UserDetails `json:"details" `
Preferences *UserPreferences `json:"preferences" `
}

View File

@ -27,7 +27,7 @@ func RegisterUserRoutes(app *fiber.App) {
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=users.User
app.Get("/users/:uuid", tockenService.Middleware(), userController.GetUser)
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateInput; response=users.User
// Typescript: TSEndpoint= path=/users; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User
app.Post("/users", tockenService.Middleware(), userController.CreateUser)
// Typescript: TSEndpoint= path=/users/:uuid; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User
@ -46,7 +46,7 @@ func RegisterUserRoutes(app *fiber.App) {
// Typescript: TSEndpoint= path=/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair
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.UserCreateRequest; response=users.User
app.Post("/auth/register", authRateLimiter, userController.Register)
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse

View File

@ -33,10 +33,6 @@ func (t *TSFiles) Save() error {
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 {

View File

@ -55,7 +55,7 @@ func ParseEndpoint(source string, file string, line int) TSEndpoint {
endpoint.Request = strings.Trim(t[1], " ")
case "response":
n++
endpoint.Response = strings.Trim(t[1], " ")
endpoint.Response = strings.Trim(t[1], " \n")
}
} else {
exitOnError(fmt.Errorf("wrong endpoint props: %s", s))
@ -85,11 +85,6 @@ type tplData struct {
func (e *TSEndpoint) VerifyTypes(info TSInfo, p string) {
kind := ""
if e.Name == "listUsers" {
fmt.Println("endpoint request", e.Request)
}
a := strings.Split(e.Request, ".")
if e.Request != "" {
if len(a) == 2 {
@ -159,7 +154,7 @@ func (e *TSEndpoint) VerifyTypes(info TSInfo, p 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])
//fmt.Printf("endpoint %s response import: %s.%s\n", e.Name, a[0], a[1])
}
}
info.setTypescript(a[0], a[1], true)

View File

@ -4,15 +4,16 @@
package tsrpc
import (
"bufio"
"fmt"
"go/ast"
"go/doc"
"go/parser"
"go/token"
"io"
"log"
"os"
"path/filepath"
"strings"
"golang.org/x/tools/go/packages"
)
type TSInfoPakage struct {
@ -29,7 +30,6 @@ type TSInfoPakage struct {
type TSDec struct {
Name string
Value string
SourceInfo string
}
type TSType struct {
@ -38,7 +38,6 @@ type TSType struct {
TsType string
Typescript bool
dependOn bool
SourceInfo string
}
type TSInfo struct {
@ -128,7 +127,7 @@ func (ts TSInfo) find(p string, n string) bool {
// popola TsInfo con tutte le definizioni dei tipi
func (i *TSInfo) getConst(p string, c *doc.Value, src []TSSourceFile) {
func (i *TSInfo) getConst(p string, c *doc.Value) {
var isTypescript = strings.HasPrefix(c.Doc, "Typescript:")
if isTypescript {
command := strings.TrimPrefix(c.Doc, "Typescript:")
@ -150,7 +149,7 @@ func (i *TSInfo) getConst(p string, c *doc.Value, src []TSSourceFile) {
be, ok := v.Values[0].(*ast.BinaryExpr)
if ok {
x := be.X.(*ast.BasicLit)
exitOnError(fmt.Errorf("enum binary expression not implemented %s %s %s AT: %s", x.Value, be.Op.String(), be.Y, getSourceInfo(int(x.ValuePos), src)))
exitOnError(fmt.Errorf("enum binary expression not implemented %s %s %s", x.Value, be.Op.String(), be.Y))
}
ident, ok := v.Values[0].(*ast.Ident)
if ok {
@ -177,11 +176,10 @@ func (i *TSInfo) getConst(p string, c *doc.Value, src []TSSourceFile) {
i.Packages[p].enums[enumName] = enum
t1 := TSType{
Name: enumName,
Typescript: true,
Typescript: false,
Type: "",
TsType: fmt.Sprintf("typeof Enum%s[keyof typeof Enum%s] ", enumName, enumName), //getFieldTsInfo(expr.Type),
dependOn: false,
SourceInfo: "",
}
i.Packages[p].types[enumName] = t1
}
@ -204,32 +202,10 @@ func (i *TSInfo) getConst(p string, c *doc.Value, src []TSSourceFile) {
}
}
func (i *TSInfo) getType(p string, t *doc.Type, src []TSSourceFile) {
var isType = t.Decl.Tok == token.TYPE && t.Doc == ""
var isTypescript = strings.HasPrefix(t.Doc, "Typescript:")
command := ""
param := ""
if isTypescript {
//fmt.Println(t.Doc)
command = strings.TrimPrefix(t.Doc, "Typescript:")
command = strings.TrimSpace(command)
command = strings.Trim(command, "\n")
if strings.Contains(command, "=") {
a := strings.Split(command, "=")
if len(a) == 2 {
param = a[1]
}
command = strings.Trim(a[0], " ")
}
}
if isType {
//fmt.Println(t.Doc)
command = "interface"
}
func (i *TSInfo) getType(p string, t *doc.Type) {
for _, spec := range t.Decl.Specs {
if len(t.Consts) > 0 {
i.getConst(p, t.Consts[0], src)
i.getConst(p, t.Consts[0])
continue
}
switch spec.(type) {
@ -237,32 +213,28 @@ func (i *TSInfo) getType(p string, t *doc.Type, src []TSSourceFile) {
typeSpec := spec.(*ast.TypeSpec)
switch typeSpec.Type.(type) {
case *ast.StructType:
if (isTypescript || isType) && command != "interface" {
exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
}
// if isType && command != "interface" {
// exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
// }
v := TSStruct{
Name: typeSpec.Name.Name,
Typescript: isTypescript,
Typescript: false,
Fields: []TSSField{},
SourceInfo: getSourceInfo(int(typeSpec.Name.NamePos), src),
SourceInfo: "",
}
v.getStruct(typeSpec, src)
v.getStruct(typeSpec)
i.Packages[p].structs[typeSpec.Name.Name] = v
default:
if isTypescript && command != "type" {
exitOnError(fmt.Errorf("mismatch delaration for type %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
}
// if isType && command != "interface" {
// exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
// }
tsInfo := getFieldTsInfo(typeSpec.Type)
if command == "type" && param != "" {
tsInfo = param
}
t := TSType{
Name: typeSpec.Name.Name,
Typescript: isTypescript,
Typescript: false,
Type: getFieldInfo(typeSpec.Type),
TsType: tsInfo,
dependOn: toBeImported(typeSpec.Type),
SourceInfo: getSourceInfo(int(typeSpec.Name.NamePos), src),
}
//fmt.Printf("Type found: %s Type: %s TsType: %s AT: %s\n", t.Name, t.Type, t.TsType, t.SourceInfo)
i.Packages[p].types[typeSpec.Name.Name] = t
@ -271,53 +243,35 @@ func (i *TSInfo) getType(p string, t *doc.Type, src []TSSourceFile) {
}
}
func (i *TSInfo) Populate(path string) {
i.Packages = make(map[string]TSInfoPakage)
err := filepath.Walk(path,
func(path string, info os.FileInfo, err error) error {
func parseTypescriptDeclarations(n string, pkg TSInfoPakage) {
f, err := os.OpenFile(n, os.O_RDONLY, os.ModePerm)
if err != nil {
return err
log.Fatalf("open file error: %v", err)
return
}
if info.IsDir() {
fset := token.NewFileSet()
packages, err := parser.ParseDir(fset, path, nil, parser.ParseComments)
if err != nil {
exitOnError(err)
}
for pkg, f := range packages {
if _, ok := i.Packages[pkg]; !ok {
i.Packages[pkg] = TSInfoPakage{structs: make(map[string]TSStruct), types: make(map[string]TSType), enums: make(map[string]TSEnum), consts: make(map[string]TSConst), decs: make(map[string]TSDec), endpoints: make(map[string]TSEndpoint)}
}
if pkg == "typescript" || pkg == "tsrpc" {
continue
}
var src = []TSSourceFile{}
for n := range f.Files {
dat, err := os.ReadFile(n)
if err == nil {
defer f.Close()
l := 1
dat := ""
lines := []TSSourceLine{}
pos := 0
line := 1
for p, k := range dat {
if string(k) == "\n" {
l := TSSourceLine{
Pos: pos,
End: p,
Line: line,
Source: string(dat[pos:p]),
rd := bufio.NewReader(f)
for {
line, err := rd.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
lines = append(lines, l)
pos = p + 1
line++
if strings.Contains(l.Source, "// Typescript:") {
if strings.Contains(l.Source, "TStype=") {
p := strings.Index(l.Source, "TStype=")
s := strings.Trim(l.Source[p+len("TStype="):], " ")
log.Fatalf("read file line error: %v", err)
return
}
lines = append(lines, TSSourceLine{Pos: l, End: l + len(line), Line: l, Source: line})
l++
dat += line
if strings.Contains(line, "// Typescript:") {
if strings.Contains(line, "TStype=") {
p := strings.Index(line, "TStype=")
s := strings.Trim(line[p+len("TStype="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSType{
@ -326,105 +280,149 @@ func (i *TSInfo) Populate(path string) {
Type: "UserDefined",
TsType: strings.Trim(a[1], " "),
dependOn: false,
SourceInfo: getSourceInfo(int(l.Pos), src),
}
i.Packages[pkg].types[strings.Trim(a[0], " ")] = t
pkg.types[strings.Trim(a[0], " ")] = t
}
}
if strings.Contains(l.Source, "TSDeclaration=") {
p := strings.Index(l.Source, "TSDeclaration=")
s := strings.Trim(l.Source[p+len("TSDeclaration="):], " ")
if strings.Contains(line, "TSDeclaration=") {
p := strings.Index(line, "TSDeclaration=")
s := strings.Trim(line[p+len("TSDeclaration="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSDec{
Name: strings.Trim(a[0], " "),
Value: strings.Trim(a[1], " "),
SourceInfo: getSourceInfo(int(l.Pos), src),
}
i.Packages[pkg].decs[strings.Trim(a[0], " ")] = t
pkg.decs[strings.Trim(a[0], " ")] = t
}
}
if strings.Contains(l.Source, "TSEndpoint= ") {
e := ParseEndpoint(l.Source, n, l.Line)
if _, ok := i.Packages[pkg].endpoints[e.Name]; ok {
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, l.Source))
if strings.Contains(line, "TSEndpoint= ") {
e := ParseEndpoint(line, n, l)
if _, ok := pkg.endpoints[e.Name]; ok {
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, line))
}
pkg_info := i.Packages[pkg]
pkg_info := pkg
pkg_info.endpoints[e.Name] = e
pkg_info.imports = e.Imports
pkg_info.isUsed = true
i.Packages[pkg] = pkg_info
}
}
}
}
s := TSSourceFile{
Name: n,
Source: string(dat),
Len: len(dat),
Lines: lines,
}
src = append(src, s)
pkg = pkg_info
}
}
p := doc.New(f, "./", 0)
for _, t := range p.Types {
i.getType(pkg, t, src)
}
}
for _, c := range p.Consts {
i.getConst(pkg, c, src)
func (i *TSInfo) Populate(path string) {
i.Packages = make(map[string]TSInfoPakage)
cfg := &packages.Config{
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedSyntax,
Dir: path,
}
}
}
return nil
})
pkgs, err := packages.Load(cfg, "./...")
if err != nil {
log.Fatal(err)
}
if packages.PrintErrors(pkgs) > 0 {
log.Fatal("package loading failed")
}
func (ts *TSInfo) findAvailableStruct(n string) (TSStruct, bool) {
//to get line info for endpoints and typescript declarations
for _, loadedPkg := range pkgs {
pkg := loadedPkg.Name
// skip packages
if pkg == "typescript" || pkg == "tsrpc" {
continue
}
// initialize package info if not exists
if _, ok := i.Packages[pkg]; !ok {
i.Packages[pkg] = TSInfoPakage{structs: make(map[string]TSStruct), types: make(map[string]TSType), enums: make(map[string]TSEnum), consts: make(map[string]TSConst), decs: make(map[string]TSDec), endpoints: make(map[string]TSEndpoint)}
}
// start parsing files
for _, n := range loadedPkg.CompiledGoFiles {
// search for declarative // Typescript: declarations and endpoints
parseTypescriptDeclarations(n, i.Packages[pkg])
}
// parse doc to get all types and consts
docPkg, err := doc.NewFromFiles(loadedPkg.Fset, loadedPkg.Syntax, loadedPkg.PkgPath)
if err != nil {
exitOnError(err)
}
for _, t := range docPkg.Types {
i.getType(pkg, t)
}
for _, c := range docPkg.Consts {
i.getConst(pkg, c)
}
}
}
func (ts *TSInfo) findAvailableStruct(pkg string, n string, deep int) (int, bool) {
a := strings.Split(n, ".")
if n == "" || IsNativeType(n) {
return deep, true
}
deep++
if deep > 10 {
exitOnError(fmt.Errorf("too much deep for struct %s", n))
}
if len(a) == 1 {
n = strings.TrimPrefix(n, "[]")
n = strings.TrimPrefix(n, "*")
if _, ok := ts.Packages[pkg].structs[n]; ok {
s := ts.Packages[pkg].structs[n]
s.Typescript = true
ts.Packages[pkg].structs[n] = s // write back
for _, v := range s.Fields {
deep, _ = ts.findAvailableStruct(pkg, v.Type, deep)
}
deep--
return deep, true
}
}
if len(a) == 2 {
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]].structs[a[1]]; ok {
return ts.Packages[a[0]].structs[a[1]], true
s := ts.Packages[a[0]].structs[a[1]]
if s.Typescript {
deep--
return deep, true
}
s.Typescript = true
ts.Packages[a[0]].structs[a[1]] = s // write back
for _, v := range s.Fields {
ts.findAvailableStruct(pkg, v.Type, deep)
}
deep--
return deep, true
}
}
}
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) {
return TSStruct{}, true
}
return TSStruct{}, false
return deep, false
}
func (i *TSInfo) TestEndpoints() {
for p := range i.Packages {
for _, v1 := range i.Packages[p].endpoints {
_, f := i.findAvailableStruct(v1.Request)
_, f := i.findAvailableStruct(p, v1.Request, 0)
if !f {
fmt.Printf("??Endpoint: request %s not found\n", v1.Request)
}
_, f = i.findAvailableStruct(v1.Response)
_, f = i.findAvailableStruct(p, v1.Response, 0)
if !f {
fmt.Printf("??Endpoint: response %s not found\n", v1.Response)
}

View File

@ -79,9 +79,7 @@ func getFieldInfo(t ast.Expr) string {
case *ast.InterfaceType:
result = "interface{}"
default:
f := fmt.Sprintf("this go type: %T is not evaluated", ft)
fmt.Println(f)
//exitOnError(fmt.Errorf("this go type: %T is not evaluated", ft))
exitOnError(fmt.Errorf("this go type: %T is not evaluated", ft))
}
return result
}
@ -104,25 +102,12 @@ func getFieldTsInfo(t ast.Expr) string {
case *ast.FuncType:
fmt.Println("*ast.FuncType found, skipping...")
default:
//f := fmt.Sprintf("getFieldTsInfo can't evaluate type: %T %T", t, ft)
//fmt.Println(f)
exitOnError(fmt.Errorf("getFieldTsInfo can't evaluate type: %T %T", t, ft))
}
return result
}
func getSourceInfo(pos int, src []TSSourceFile) string {
for _, v := range src {
for _, l := range v.Lines {
if pos >= l.Pos && pos <= l.End {
return fmt.Sprintf("%s Line: %d", v.Name, l.Line)
}
}
}
return ""
}
func (s *TSStruct) getStruct(ts *ast.TypeSpec, src []TSSourceFile) {
func (s *TSStruct) getStruct(ts *ast.TypeSpec) {
if st, ok := ts.Type.(*ast.StructType); ok {
for _, field := range st.Fields.List {
@ -148,7 +133,7 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec, src []TSSourceFile) {
Type: getFieldInfo(field.Type),
TsType: tsType,
DependOn: toBeImported(field.Type),
SourceInfo: getSourceInfo(int(field.Type.Pos()), src),
SourceInfo: "",
}
s.Fields = append(s.Fields, f)
} else {
@ -160,7 +145,7 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec, src []TSSourceFile) {
Type: getFieldInfo(field.Type),
TsType: tsType,
DependOn: toBeImported(field.Type),
SourceInfo: getSourceInfo(int(field.Type.Pos()), src),
SourceInfo: "",
}
s.Fields = append(s.Fields, f)
} else {
@ -172,11 +157,9 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec, src []TSSourceFile) {
Type: se.Name,
TsType: tsType,
DependOn: false,
SourceInfo: getSourceInfo(int(field.Type.Pos()), src),
SourceInfo: "",
}
s.Fields = append(s.Fields, f)
fmt.Println(f)
} else {
exitOnError(fmt.Errorf("this typescript type: %T is not evaluated", field.Type))
}

View File

@ -17,7 +17,6 @@ type TSModule struct {
GTypes map[string]string
Endpoints map[string]string
Imports map[string][]string
Source string
}
type TSOutputSources []string
@ -222,7 +221,7 @@ func (ts *TSSouces) Populate(info TSInfo) {
for p := range info.Packages {
ts.ensurePackage(p)
ts.Errors = append(ts.Errors, fmt.Sprintf("Process pakage %s\n", p))
// ts.Errors = append(ts.Errors, fmt.Sprintf("Process pakage %s\n", p))
for _, st := range info.Packages[p].structs {
if st.Typescript {
@ -259,30 +258,6 @@ func (ts *TSSouces) Populate(info TSInfo) {
}
for _, e := range info.Packages[p].endpoints {
/* if e.Request != "" {
a := strings.Split(e.Request, ".")
if len(a) == 2 {
s, d, err := structToTs(info, a[0], a[1])
if err != nil {
ts.Errors = append(ts.Errors, err.Error())
}
fmt.Println(s, d)
}
}
if e.Response != "" {
a := strings.Split(e.Response, ".")
if len(a) == 2 {
s, d, err := structToTs(info, a[0], a[1])
if err != nil {
ts.Errors = append(ts.Errors, err.Error())
}
fmt.Println(s, d)
}
} */
responseAndRequest := ""
if e.Request != "" {
@ -303,10 +278,6 @@ func (ts *TSSouces) Populate(info TSInfo) {
}
pkg.Endpoints[e.Name] = e.ToTs()
ts.Pakages[p] = pkg
if p == "users" {
fmt.Printf("endpoint %s imports: %v\n", e.Name, pkg.Imports)
}
}
}
}

View File

@ -61,9 +61,6 @@ func GetTSSource() error {
tsFiles.Add("apiTypes.ts", tsIndexSource)
for p := range tsSourcesData.Pakages {
if p == "users" {
fmt.Println("users package")
}
source := ""
for _, v1 := range tsSourcesData.Pakages[p].Endpoints {
source += fmt.Sprintln(v1)
@ -90,7 +87,7 @@ func GetTSSource() error {
}
}
if found {
tmp += "import { api } from './api.ts'\n"
tmp += "import { api } from './api'\n"
}
if len(tsApiDeclarations) > 0 {
@ -108,20 +105,19 @@ func GetTSSource() error {
}
}
if len(decs) > 0 {
TSApiDeclarations := "import { "
TSApiDeclarations := "import type { "
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"
imports += "import type * as " + f + " from './" + f + ".ts'\n"
}
tmp += imports
tmp += source

34
frontend/src/api/admin.ts Normal file
View File

@ -0,0 +1,34 @@
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
import type * as users from "./users.ts";
// 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>;
};
};
// 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>;
};
};
export interface BlockUserRequest {
action: string;
}
export interface ListUsersRequest {
page: number;
pageSize: number;
}

View File

@ -4,7 +4,7 @@
//
// This file was generated by github.com/millevolte/ts-rpc
//
// Apr 05, 2026 17:08:11 UTC
// Apr 15, 2026 18:49:19 UTC
//
export interface ApiRestResponse {
@ -273,341 +273,4 @@ export default class Api {
}
}
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 model
//
export interface RefreshRequest {
refresh_token: string;
}
export interface TokenPair {
access_token: string;
refresh_token: string;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface LoginRequest {
username: string;
password: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
}
//
// package controllers
//
export interface BlockUserRequest {
action: string;
}
export interface ListUsersRequest {
page: number;
pageSize: number;
}
export interface SimpleResponse {
message: 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 routes
//
// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string
// internal/http/routes/system_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/http/routes/system_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>;
};
};
// Typescript: TSEndpoint= path=/users/:uuid; name=getUser; method=GET; response=models.UserProfile
// internal/http/routes/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/:uuid; name=updateUser; method=PUT; request=controllers.UpdateUserRequest; response=models.UserProfile
// internal/http/routes/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=/admin/users; name=listUsers; method=POST; request=controllers.ListUsersRequest; response=models.[]UserShort
// internal/http/routes/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>;
};
};
// Typescript: TSEndpoint= path=/admin/users/:uuid/block; name=blockUser; method=PUT; request=controllers.BlockUserRequest; response=models.UserShort
// internal/http/routes/admin_routes.go Line: 15
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=/users; name=createUser; method=POST; request=models.UserCreateInput; response=models.UserProfile
// internal/http/routes/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=deleteUser; method=DELETE; response=controllers.SimpleResponse
// internal/http/routes/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=/health; name=health; method=GET; response=string
// internal/http/routes/system_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>;
};
};
export interface FormRequest {
req: string;
count: number;
}
export interface FormResponse {
test: string;
}
export interface MailDebugItem {
name: string;
content: string;
}
//
// package endpoint
//
// Typescript: TSEndpoint= path=/auth/password/forgot; name=forgotPassword; method=POST; request=model.ForgotPasswordRequest; response=controllers.SimpleResponse
// internal/auth/endpoint/routes.go Line: 34
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>;
};
};
// Typescript: TSEndpoint= path=/auth/password/reset; name=resetPassword; method=POST; request=model.ResetPasswordRequest; response=controllers.SimpleResponse
// internal/auth/endpoint/routes.go Line: 37
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/endpoint/routes.go Line: 40
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/endpoint/routes.go Line: 22
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/endpoint/routes.go Line: 25
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/me; name=me; method=GET; response=models.UserShort
// internal/auth/endpoint/routes.go Line: 28
export const me = async (): Promise<{
data: UserShort;
error: Nullable<string>;
}> => {
return (await api.GET("/auth/me")) as {
data: UserShort;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/auth/register; name=register; method=POST; request=models.UserCreateInput; response=models.UserShort
// internal/auth/endpoint/routes.go Line: 31
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>;
};
};
//
// package models
//
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 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 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;
export const api = new Api("http://localhost:3000");

View File

@ -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 };

View File

@ -0,0 +1,3 @@
export interface SimpleResponse {
message: string;
}

View File

@ -0,0 +1,43 @@
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
// 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>;
};
};
// 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>;
};
};
export interface MailDebugItem {
name: string;
content: string;
}

View File

@ -0,0 +1,4 @@
export interface TokenPair {
access_token: string;
refresh_token: string;
}

239
frontend/src/api/users.ts Normal file
View File

@ -0,0 +1,239 @@
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
import type * as tokens from "./tokens.ts";
import type * as responses from "./responses.ts";
// 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/: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/register; name=register; method=POST; request=users.UserCreateRequest; response=users.User
// internal/user/routes.go Line: 49
export const register = async (
data: UserCreateRequest,
): 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; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User
// internal/user/routes.go Line: 30
export const createUser = async (
data: UserCreateRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.POST("/users", data)) as {
data: User;
error: Nullable<string>;
};
};
// 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=/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/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/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>;
};
};
export interface LoginRequest {
username: string;
password: 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 ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
token: string;
password: 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 UserCreateRequest {
name: string;
email: string;
password: string;
roles: UserRoles;
status: UserStatus;
types: UserTypes;
avatar: Nullable<string>;
details: Nullable<UserDetails>;
preferences: Nullable<UserPreferences>;
}
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 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 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 type UserTypes = string[];
export type UserRoles = string[];
export type UserStatus = (typeof EnumUserStatus)[keyof typeof EnumUserStatus];
export const EnumUserStatus = {
UserStatusPending: "pending",
UserStatusActive: "active",
UserStatusDisabled: "disabled",
} as const;

View File

@ -108,14 +108,14 @@ import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter, useRoute } from 'vue-router';
import LogoUrl from 'src/assets/home/logo.png';
import { me, type UserShort } from 'src/api/api';
import { me, type User } from 'src/api/users';
import { usePreferencesStore, type LanguageCode } from 'src/stores/preferences-store';
const { t } = useI18n();
const router = useRouter();
const route = useRoute();
const preferencesStore = usePreferencesStore();
const currentUser = ref<UserShort | null>(null);
const currentUser = ref<User | null>(null);
const model = computed({
get: () => preferencesStore.language,
set: (language: LanguageCode) => {

View File

@ -509,21 +509,24 @@ import VuePictureCropper from 'vue-picture-cropper';
import {
blockUser,
createUser,
getUser,
listUsers,
updateUser,
EnumUserStatus,
type BlockUserRequest,
type ListUsersRequest,
}from 'src/api/admin';
import {
createUser,
getUser,
updateUser,
EnumUserStatus,
type UpdateUserRequest,
type UserCreateInput,
type UserDetailsShort,
type UserPreferencesShort,
type UserCreateRequest,
type UserProfile,
type UserShort,
type User,
type UserStatus,
} from 'src/api/api';
type UserDetails,
type UserPreferences,
} from 'src/api/users';
type DialogMode = 'create' | 'edit' | 'view';
@ -536,8 +539,8 @@ interface UserFormState {
roles: string[];
types: string[];
avatar: string;
details: UserDetailsShort;
preferences: UserPreferencesShort;
details: UserDetails;
preferences: UserPreferences;
}
const $q = useQuasar();
@ -550,7 +553,7 @@ const avatarDialogOpen = ref(false);
const dialogMode = ref<DialogMode>('create');
const editorTab = ref<'account' | 'details' | 'preferences'>('account');
const filter = ref('');
const rows = ref<UserShort[]>([]);
const rows = ref<User[]>([]);
const detailsEnabled = ref(true);
const preferencesEnabled = ref(true);
const passwordDialogUserUuid = ref('');
@ -593,7 +596,7 @@ const avatarPresetMode = {
height: 320,
} as const;
const columns: QTableColumn<UserShort>[] = [
const columns: QTableColumn<User>[] = [
{ name: 'name', label: 'Utente', field: 'name', align: 'left', sortable: true },
{ name: 'status', label: 'Status', field: 'status', align: 'left', sortable: true },
{ name: 'roles', label: 'Roles', field: (row) => row.roles.join(', '), align: 'left' },
@ -608,7 +611,7 @@ const passwordForm = reactive({
confirmPassword: '',
});
const payload = computed<UserCreateInput | UpdateUserRequest>(() => ({
const payload = computed<UserCreateRequest | UpdateUserRequest>(() => ({
name: form.name.trim(),
email: form.email.trim(),
password: dialogMode.value === 'create' ? form.password : '',
@ -643,6 +646,10 @@ function emptyForm(): UserFormState {
zipCode: '',
country: '',
phone: '',
id: 0,
userId: 0,
createdAt: undefined,
updatedAt: undefined
},
preferences: {
useIdle: false,
@ -653,6 +660,10 @@ function emptyForm(): UserFormState {
useQuadcodeLogin: false,
sendNoticesMail: false,
language: 'it',
id: 0,
userId: 0,
createdAt: undefined,
updatedAt: undefined
},
};
}
@ -955,7 +966,7 @@ async function saveUser(): Promise<void> {
saving.value = true;
try {
if (dialogMode.value === 'create') {
const response = await createUser(payload.value as UserCreateInput);
const response = await createUser(payload.value as UserCreateRequest);
if (response.error) {
throw new Error(response.error);
}

View File

@ -124,16 +124,22 @@
import { reactive, ref } from 'vue';
import {
forgotPassword,
health,
listUsers,
login,
me,
metrics,
refresh,
register,
resetPassword,
} from 'src/api/api';
import type { UserCreateInput } from 'src/api/api';
type UserCreateRequest,
} from 'src/api/users';
import {
health,
metrics,
} from 'src/api/systemUtils';
import {
listUsers,
} from 'src/api/admin';
type Method = 'GET' | 'POST';
type FieldType = 'text' | 'password' | 'number' | 'textarea';
@ -385,7 +391,7 @@ const endpoints: EndpointConfig[] = [
details: '',
preferences: '',
},
call: (payload) => register(payload as UserCreateInput),
call: (payload) => register(payload as UserCreateRequest),
buildPayload: (form) => ({
name: String(form.name ?? ''),
email: String(form.email ?? ''),

View File

@ -53,7 +53,7 @@
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { login } from 'src/api/api';
import { login } from 'src/api/users';
const router = useRouter();
const $q = useQuasar();

View File

@ -81,7 +81,7 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { useQuasar } from 'quasar';
import { mailDebug, type MailDebugItem } from 'src/api/api';
import { mailDebug, type MailDebugItem } from 'src/api/systemUtils';
const $q = useQuasar();

View File

@ -45,7 +45,7 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useQuasar } from 'quasar';
import { forgotPassword } from 'src/api/api';
import { forgotPassword } from 'src/api/users';
const $q = useQuasar();
const loading = ref(false);

View File

@ -76,7 +76,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { resetPassword } from 'src/api/api';
import { resetPassword } from 'src/api/users';
const route = useRoute();

View File

@ -58,7 +58,7 @@
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, watch } from 'vue';
import { useQuasar } from 'quasar';
import { EnumUserStatus, register, type UserCreateInput } from 'src/api/api';
import { EnumUserStatus, register, type UserCreateRequest } from 'src/api/users';
const $q = useQuasar();
const loading = ref(false);
@ -107,7 +107,7 @@ async function submit(): Promise<void> {
loading.value = true;
try {
const payload: UserCreateInput = {
const payload: UserCreateRequest = {
name: `${form.firstName.trim()} ${form.lastName.trim()}`.trim(),
email: form.email.trim(),
password: form.password,
@ -124,6 +124,10 @@ async function submit(): Promise<void> {
zipCode: '',
country: '',
phone: '',
id: 0,
userId: 0,
createdAt: new Date(),
updatedAt: new Date(),
},
preferences: null,
};