Skip to content

Commit

Permalink
Merge pull request #8 from Selleo/jw/auth-screens
Browse files Browse the repository at this point in the history
feat: auth screens
  • Loading branch information
typeWolffo authored Jul 22, 2024
2 parents 6dbc6d0 + 49e48ee commit 59d4443
Show file tree
Hide file tree
Showing 45 changed files with 2,638 additions and 328 deletions.
1 change: 1 addition & 0 deletions examples/common_nestjs_remix/apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
DATABASE_URL="postgres://postgres:guidebook@localhost:5432/guidebook"
JWT_SECRET=
JWT_REFRESH_SECRET=
CORS_ORIGIN=
5 changes: 5 additions & 0 deletions examples/common_nestjs_remix/apps/api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ async function bootstrap() {

app.use(cookieParser());

app.enableCors({
origin: process.env.CORS_ORIGIN,
credentials: true,
});

const config = new DocumentBuilder()
.setTitle("Guidebook API")
.setDescription("Example usage of Swagger with Typebox")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { writeFile } from 'node:fs';
import { writeFile } from "node:fs";

const SCHEMA_FILE = './src/swagger/api-schema.json';
const SCHEMA_FILE = "./src/swagger/api-schema.json";

export const exportSchemaToFile = (schema: object) => {
const content = JSON.stringify(schema, null, 2);
Expand Down
3 changes: 3 additions & 0 deletions examples/common_nestjs_remix/apps/web/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ module.exports = {
typescript: {},
},
},
rules: {
"react/prop-types": "off",
},
},

// Typescript
Expand Down
27 changes: 26 additions & 1 deletion examples/common_nestjs_remix/apps/web/app/api/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import { useAuthStore } from "~/modules/Auth/authStore";
import { API } from "./generated-api";

export const ApiClient = new API({
baseURL: import.meta.env.API_URL,
baseURL: import.meta.env.VITE_API_URL,
secure: true,
withCredentials: true,
});

ApiClient.instance.interceptors.response.use(
(response) => response,
async (error) => {
const isLoggedIn = useAuthStore.getState().isLoggedIn;
const originalRequest = error.config;

if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
if (!isLoggedIn) return;

try {
await ApiClient.auth.authControllerRefreshTokens();

return ApiClient.instance(originalRequest);
} catch (error) {
return Promise.reject(error);
}
}

return Promise.reject(error);
}
);
210 changes: 192 additions & 18 deletions examples/common_nestjs_remix/apps/web/app/api/generated-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,91 @@
* ---------------------------------------------------------------
*/

export interface CreatePropertyBody {
name: string;
description?: string;
export interface RegisterBody {
/** @format email */
email: string;
/**
* @minLength 8
* @maxLength 64
*/
password: string;
}

export interface CreatePropertyResponse {
export interface RegisterResponse {
data: {
id: string;
createdAt: string;
updatedAt: string;
name: string;
description: string | null;
email: string;
};
}

export type DeletePropertyResponse = null;
export interface LoginBody {
/** @format email */
email: string;
/**
* @minLength 8
* @maxLength 64
*/
password: string;
}

export interface LoginResponse {
data: {
id: string;
createdAt: string;
updatedAt: string;
email: string;
};
}

export type LogoutResponse = null;

export type RefreshTokensResponse = null;

export interface GetUsersResponse {
data: {
id: string;
createdAt: string;
updatedAt: string;
email: string;
}[];
}

export interface GetUserByIdResponse {
data: {
id: string;
createdAt: string;
updatedAt: string;
email: string;
};
}

export interface UpdateUserBody {
/** @format email */
email?: string;
}

export interface UpdateUserResponse {
data: {
id: string;
createdAt: string;
updatedAt: string;
email: string;
};
}

export interface ChangePasswordBody {
/**
* @minLength 8
* @maxLength 64
*/
password: string;
}

export type ChangePasswordResponse = null;

export type DeleteUserResponse = null;

import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from "axios";
import axios from "axios";
Expand Down Expand Up @@ -168,16 +237,16 @@ export class HttpClient<SecurityDataType = unknown> {
* Example usage of Swagger with Typebox
*/
export class API<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
properties = {
auth = {
/**
* No description
*
* @name PropertiesControllerCreateProperty
* @request POST:/properties
* @name AuthControllerRegister
* @request POST:/auth/register
*/
propertiesControllerCreateProperty: (data: CreatePropertyBody, params: RequestParams = {}) =>
this.request<CreatePropertyResponse, any>({
path: `/properties`,
authControllerRegister: (data: RegisterBody, params: RequestParams = {}) =>
this.request<RegisterResponse, any>({
path: `/auth/register`,
method: "POST",
body: data,
type: ContentType.Json,
Expand All @@ -188,15 +257,120 @@ export class API<SecurityDataType extends unknown> extends HttpClient<SecurityDa
/**
* No description
*
* @name PropertiesControllerDeleteProperty
* @request DELETE:/properties/{id}
* @name AuthControllerLogin
* @request POST:/auth/login
*/
propertiesControllerDeleteProperty: (id: string, params: RequestParams = {}) =>
this.request<DeletePropertyResponse, any>({
path: `/properties/${id}`,
authControllerLogin: (data: LoginBody, params: RequestParams = {}) =>
this.request<LoginResponse, any>({
path: `/auth/login`,
method: "POST",
body: data,
type: ContentType.Json,
format: "json",
...params,
}),

/**
* No description
*
* @name AuthControllerLogout
* @request POST:/auth/logout
*/
authControllerLogout: (params: RequestParams = {}) =>
this.request<LogoutResponse, any>({
path: `/auth/logout`,
method: "POST",
format: "json",
...params,
}),

/**
* No description
*
* @name AuthControllerRefreshTokens
* @request POST:/auth/refresh
*/
authControllerRefreshTokens: (params: RequestParams = {}) =>
this.request<RefreshTokensResponse, any>({
path: `/auth/refresh`,
method: "POST",
format: "json",
...params,
}),
};
users = {
/**
* No description
*
* @name UsersControllerGetUsers
* @request GET:/users
*/
usersControllerGetUsers: (params: RequestParams = {}) =>
this.request<GetUsersResponse, any>({
path: `/users`,
method: "GET",
format: "json",
...params,
}),

/**
* No description
*
* @name UsersControllerGetUserById
* @request GET:/users/{id}
*/
usersControllerGetUserById: (id: string, params: RequestParams = {}) =>
this.request<GetUserByIdResponse, any>({
path: `/users/${id}`,
method: "GET",
format: "json",
...params,
}),

/**
* No description
*
* @name UsersControllerUpdateUser
* @request PATCH:/users/{id}
*/
usersControllerUpdateUser: (id: string, data: UpdateUserBody, params: RequestParams = {}) =>
this.request<UpdateUserResponse, any>({
path: `/users/${id}`,
method: "PATCH",
body: data,
type: ContentType.Json,
format: "json",
...params,
}),

/**
* No description
*
* @name UsersControllerDeleteUser
* @request DELETE:/users/{id}
*/
usersControllerDeleteUser: (id: string, params: RequestParams = {}) =>
this.request<DeleteUserResponse, any>({
path: `/users/${id}`,
method: "DELETE",
format: "json",
...params,
}),

/**
* No description
*
* @name UsersControllerChangePassword
* @request PATCH:/users/{id}/change-password
*/
usersControllerChangePassword: (id: string, data: ChangePasswordBody, params: RequestParams = {}) =>
this.request<ChangePasswordResponse, any>({
path: `/users/${id}/change-password`,
method: "PATCH",
body: data,
type: ContentType.Json,
format: "json",
...params,
}),
};
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useMutation } from "@tanstack/react-query";
import { ApiClient } from "../api-client";
import { LoginBody } from "../generated-api";
import { useAuthStore } from "~/modules/Auth/authStore";
import { toast } from "sonner";
import { AxiosError } from "axios";

type LoginUserOptions = {
data: LoginBody;
};

export function useLoginUser() {
const { setLoggedIn } = useAuthStore();
return useMutation({
mutationFn: async (options: LoginUserOptions) => {
const response = await ApiClient.auth.authControllerLogin(options.data);

return response.data;
},
onSuccess: () => {
setLoggedIn(true);
},
onError: (error) => {
if (error instanceof AxiosError) {
return toast.error(error.response?.data.message);
}
toast.error(error.message);
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useAuthStore } from "./../../modules/Auth/authStore";
import { useMutation } from "@tanstack/react-query";
import { ApiClient } from "../api-client";
import { toast } from "sonner";
import { AxiosError } from "axios";

export function useLogoutUser() {
const { setLoggedIn } = useAuthStore();
return useMutation({
mutationFn: async () => {
const response = await ApiClient.auth.authControllerLogout();

return response.data;
},
onSuccess: () => {
setLoggedIn(false);
},
onError: (error) => {
if (error instanceof AxiosError) {
return toast.error(error.response?.data.message);
}
toast.error(error.message);
},
});
}
Loading

0 comments on commit 59d4443

Please sign in to comment.