kmod-cli 1.4.15 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -994,33 +994,70 @@ export const useDataProvider = () => {
994
994
 
995
995
  /**
996
996
  * Create HTTP client with authentication
997
+ * @param url Base URL for the HTTP client
998
+ * @param options Configuration options for the HTTP client
999
+ * @param options.tokenName Name of the token to use for authentication
1000
+ * @param options.tokenStorage Storage mechanism for the token
1001
+ * @param options.authorizationType Type of authorization (e.g., Bearer, Basic)
1002
+ * @param options.withCredentials Whether to send cookies with requests (defaults to true if tokenStorage is "http-only")
1003
+ * @returns Configured Axios instance
997
1004
  */
998
- export function createHttpClient(
999
- baseURL: string,
1000
- authTokenKey: string = 'token',
1001
- authTokenStorage: 'localStorage' | 'sessionStorage' | 'cookie' = 'cookie',
1002
- typeAuthorization: "Bearer" | "Basic" | string = "Bearer"
1003
- ): AxiosInstance {
1005
+
1006
+ export interface ICreateHttpClientOptions {
1007
+ tokenName?: string ;
1008
+ tokenStorage?: "local" | "session" | "cookie" | "http-only";
1009
+ authorizationType?: "Bearer" | "Basic" | string;
1010
+ withCredentials?: boolean;
1011
+ }
1012
+
1013
+ export interface ICreateHttpClient {
1014
+ url?: string;
1015
+ options?: ICreateHttpClientOptions
1016
+ }
1017
+ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosInstance {
1018
+ const {
1019
+ tokenName = "token",
1020
+ tokenStorage = "http-only",
1021
+ authorizationType = "Bearer",
1022
+ } = options;
1023
+
1024
+ const withCredentials =
1025
+ options.withCredentials ?? tokenStorage === "http-only";
1026
+
1027
+
1004
1028
  const axiosInstance = axios.create({
1005
- baseURL: baseURL || 'https://api.example.com',
1029
+ baseURL: url || "https://api.example.com",
1030
+ withCredentials,
1006
1031
  });
1007
1032
 
1008
- axiosInstance.interceptors.request.use(config => {
1033
+ axiosInstance.interceptors.request.use((config) => {
1009
1034
  let token: string | null = null;
1010
-
1011
- if (authTokenStorage === 'localStorage') {
1012
- token = localStorage.getItem(authTokenKey);
1013
- } else if (authTokenStorage === 'sessionStorage') {
1014
- token = sessionStorage.getItem(authTokenKey);
1015
- } else if (authTokenStorage === 'cookie') {
1016
- const match = document.cookie.match(new RegExp('(^| )' + authTokenKey + '=([^;]+)'));
1017
- if (match) token = match[2];
1035
+
1036
+ switch (options.tokenStorage) {
1037
+ case "local":
1038
+ token = localStorage.getItem(tokenName);
1039
+ break;
1040
+
1041
+ case "session":
1042
+ token = sessionStorage.getItem(tokenName);
1043
+ break;
1044
+
1045
+ case "cookie":
1046
+ token = cookiesProvider.get(tokenName) ?? null;
1047
+ break;
1048
+
1049
+ case "http-only":
1050
+ // NO READ
1051
+ // HttpOnly cookies are not accessible via JavaScript
1052
+ // Browser will send it automatically
1053
+ break;
1018
1054
  }
1019
-
1055
+
1056
+ // Just set token if available in storage
1020
1057
  if (token) {
1021
- config.headers.Authorization = `${typeAuthorization} ${token}`;
1022
- }
1023
-
1058
+ config.headers.Authorization = `${authorizationType} ${token}`;
1059
+ }
1060
+
1024
1061
  return config;
1025
1062
  });
1026
1063
 
@@ -1050,15 +1087,36 @@ interface AuthContextValue {
1050
1087
  isAuthenticated: () => boolean;
1051
1088
  setUser: (user: AuthUser | null) => void;
1052
1089
  login: (payload: LoginPayload, type?: "full" | "simple") => Promise<any>;
1053
- logout: () => void;
1090
+ logout: (params: CustomParams, type?: TypeResponse) => Promise<any>;
1054
1091
  getMe: (type?: "full" | "simple") => Promise<any>;
1055
1092
  }
1056
1093
 
1094
+ /**
1095
+ * Authentication Provider Urls Props
1096
+ * @param loginUrl - URL for login API
1097
+ * @param logoutUrl - URL for logout API
1098
+ * @param meUrl - URL for fetching current user info
1099
+ * @returns AuthProviderUrls
1100
+ */
1101
+ export interface AuthProviderUrls {
1102
+ loginUrl: string;
1103
+ logoutUrl?: string;
1104
+ meUrl?: string;
1105
+ }
1106
+
1107
+ /**
1108
+ * Authentication Provider Props
1109
+ * @param children - React children nodes
1110
+ * @param urls - AuthProviderUrls
1111
+ * @param tokenKey - Key for token storage
1112
+ * @param keysCleanUpOnLogout - Additional keys to clean up on logout
1113
+ * @returns AuthProviderProps
1114
+ */
1057
1115
  export interface AuthProviderProps {
1058
1116
  children: React.ReactNode;
1059
- loginUrl: string;
1060
- meUrl: string;
1117
+ urls: AuthProviderUrls;
1061
1118
  tokenKey: string;
1119
+ keysCleanUpOnLogout?: string[] | string;
1062
1120
  }
1063
1121
 
1064
1122
  export type TypeResponse = "full" | "simple";
@@ -1075,13 +1133,21 @@ export const useAuth = () => {
1075
1133
 
1076
1134
  export const AuthProvider: React.FC<AuthProviderProps> = ({
1077
1135
  children,
1078
- loginUrl = '/auth/login',
1079
- meUrl = '/auth/me',
1136
+ urls,
1080
1137
  tokenKey = 'token',
1138
+ keysCleanUpOnLogout = ["token"],
1081
1139
  }) => {
1082
1140
  const dataProvider = useDataProvider();
1083
1141
  const [user, setUser] = useState<AuthUser | null>(null);
1084
1142
 
1143
+ const { loginUrl, meUrl, logoutUrl } = urls;
1144
+
1145
+ function removeKeys(key: string) {
1146
+ cookiesProvider.remove(key);
1147
+ localStorage.removeItem(key);
1148
+ sessionStorage.removeItem(key);
1149
+ }
1150
+
1085
1151
  const login = useCallback(async (payload: LoginPayload, type: TypeResponse = "full") => {
1086
1152
  try {
1087
1153
  const res = await dataProvider.custom<any>({
@@ -1100,15 +1166,37 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1100
1166
  }
1101
1167
  , [dataProvider, loginUrl]);
1102
1168
 
1103
- const logout = useCallback(() => {
1104
- cookiesProvider.remove(tokenKey);
1105
- localStorage.clear();
1106
- sessionStorage.clear();
1107
- dataProvider.clearAllCache();
1108
- }, [dataProvider]);
1169
+ const logout = useCallback(async (params: CustomParams, type: TypeResponse = "full") => {
1170
+
1171
+ try {
1172
+ const res = await dataProvider.custom<any>({
1173
+ url: params.url || logoutUrl,
1174
+ ...params,
1175
+ });
1176
+
1177
+ if (type === "simple") {
1178
+ return res.data;
1179
+ }
1180
+
1181
+ return res;
1182
+ } catch (error) {
1183
+ throw error;
1184
+ } finally {
1185
+ setUser(null);
1186
+ dataProvider.clearAllCache();
1187
+ if(Array.isArray(keysCleanUpOnLogout)){
1188
+ keysCleanUpOnLogout.forEach((key) => {
1189
+ removeKeys(key);
1190
+ });
1191
+ } else {
1192
+ removeKeys(keysCleanUpOnLogout);
1193
+ }
1194
+ }
1195
+
1196
+ }, [dataProvider, logoutUrl]);
1109
1197
 
1110
1198
  const isAuthenticated = () => {
1111
- return (cookiesProvider.get(tokenKey) !== null && cookiesProvider.get(tokenKey) !== undefined && cookiesProvider.get(tokenKey) !== "" && typeof cookiesProvider.get(tokenKey) === "string" && user !== null) ? true : false;
1199
+ return user !== null;
1112
1200
  };
1113
1201
  const getToken = useCallback(() => {
1114
1202
  return cookiesProvider.get(tokenKey);
@@ -1128,12 +1216,17 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
1128
1216
  } catch {
1129
1217
  return null;
1130
1218
  }
1131
- }, [dataProvider, meUrl, logout]);
1219
+ }, [dataProvider, meUrl]);
1220
+
1221
+ useEffect(() => {
1222
+ getMe().then((u: any) => u && setUser(u));
1223
+ }, [getMe]);
1224
+
1132
1225
 
1133
1226
  const value: AuthContextValue = {
1134
1227
  user,
1135
1228
  setUser,
1136
- token: getToken(),
1229
+ token: getToken() ?? null,
1137
1230
  isAuthenticated: isAuthenticated,
1138
1231
  login,
1139
1232
  logout,
@@ -1171,28 +1264,39 @@ export const cookiesProvider = {
1171
1264
 
1172
1265
  // =================== Example ===================
1173
1266
 
1174
- // create httpClient
1267
+ // create httpClient - (can create multiple httpClients for different apis)
1175
1268
 
1176
1269
  // const TOKEN = "token";
1177
1270
 
1178
- // const httpClient = createHttpClient(
1179
- // `${process.env.NEXT_PUBLIC_API_URL}`,
1180
- // TOKEN, --> key_name_cookie
1181
- // "cookie", --> storage
1182
- // "Bearer" --> prefix
1183
- // );
1271
+ // const httpClient = createHttpClient({
1272
+ // url: `${process.env.NEXT_PUBLIC_API_URL}`,
1273
+ // options: {
1274
+ // authorizationType: "Bearer",
1275
+ // tokenName: TOKEN,
1276
+ // tokenStorage: "cookie",
1277
+ // withCredentials: true, --- optionals (default to true if tokenStorage is "http-only")
1278
+ // },
1279
+ // });
1184
1280
 
1185
1281
 
1186
1282
  // create dataProvider
1187
1283
 
1188
1284
  // const dataProvider = useDataProvider(httpClient);
1189
1285
 
1286
+ // const urls = {
1287
+ // loginUrl: "/auth/login", --> api_login
1288
+ // logoutUrl: "/auth/logout", --> api_logout
1289
+ // meUrl: "/auth/me", --> api_get_me_by_token
1290
+ // }
1291
+
1292
+ // const keysRemoveOnLogout = [TOKEN, "refreshToken", "user"];
1293
+
1190
1294
  // wrapped all into:
1191
1295
  // <DataProvider dataProvider={dataProvider}>
1192
1296
  // <AuthProvider
1193
- // loginUrl={"/auth/login"} --> api_login
1297
+ // urls={urls} --> api_login
1194
1298
  // tokenKey={TOKEN}
1195
- // meUrl='/auth/me' --> api_get_me_by_token
1299
+ // keysCleanUpOnLogout={keysRemoveOnLogout} --> optional (default to ["token"]) - additional keys to clean up on logout
1196
1300
  // >
1197
1301
  // <App />
1198
1302
  // </AuthProvider>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kmod-cli",
3
- "version": "1.4.15",
3
+ "version": "1.5.0",
4
4
  "description": "Stack components utilities fast setup in projects",
5
5
  "author": "kumo_d",
6
6
  "license": "MIT",