{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "plugin-axios", "title": "HTTP Client (Axios)", "description": "Axios instance, interceptors, token yönetimi, CustomAxiosResponse tipi, AuthContext/AuthProvider ve createService kalıbı.", "dependencies": [ "axios@1.16.0" ], "files": [ { "path": "registry/tra-plugins/axios/constants/ApiEnvironment.ts", "content": "/**\n * API ortam yapılandırması.\n * Projenizin ortamlarına göre baseUrl değerlerini düzenleyin.\n */\n\nconst ApiEnvironment = {\n Type: {\n local: \"local\",\n test: \"test\",\n localTest: \"localTest\",\n release: \"release\",\n } as const,\n\n // TODO: Projenizin API URL'lerini buraya ekleyin\n baseUrls: {\n local: \"https://localhost:7001/api\",\n test: \"https://test.example.com/api\",\n localTest: \"http://10.34.60.1:5000/api\",\n release: \"https://api.example.com/api\",\n } as Record,\n};\n\nexport type EnvironmentType = (typeof ApiEnvironment.Type)[keyof typeof ApiEnvironment.Type];\n\nexport default ApiEnvironment;\n", "type": "registry:file", "target": "src/constants/ApiEnvironment.ts" }, { "path": "registry/tra-plugins/axios/constants/StorageKeys.ts", "content": "/**\n * LocalStorage key sabitleri.\n * Tüm localStorage erişimleri bu sabitler üzerinden yapılmalıdır.\n * Key değiştirmek isterseniz sadece burayı güncellemeniz yeterlidir.\n */\nconst StorageKeys = {\n ACCESS_TOKEN: \"access_token\",\n USER: \"user\",\n} as const;\n\nexport default StorageKeys;\n", "type": "registry:file", "target": "src/constants/StorageKeys.ts" }, { "path": "registry/tra-plugins/axios/utilities/Environment.ts", "content": "import ApiEnvironment from \"@/constants/ApiEnvironment\";\n\nconst Environment = {\n getEnvironment: () => {\n if (typeof window === \"undefined\") {\n return process.env.NODE_ENV === \"development\"\n ? ApiEnvironment.Type.local\n : ApiEnvironment.Type.release;\n }\n\n const { host } = window.location;\n\n if (host.includes(\"localhost\") || host.includes(\"127.0.0.1\")) {\n return ApiEnvironment.Type.local;\n }\n if (host.includes(\"test\")) {\n return ApiEnvironment.Type.test;\n }\n if (host.includes(\"ngrok\")) {\n return ApiEnvironment.Type.local;\n }\n if (host.includes(\"10.34.60.\")) {\n return ApiEnvironment.Type.localTest;\n }\n return ApiEnvironment.Type.release;\n },\n\n getBaseUrl: () => {\n const env = Environment.getEnvironment();\n return ApiEnvironment.baseUrls[env] ?? \"/api\";\n },\n\n isLocal: () => Environment.getEnvironment() === ApiEnvironment.Type.local,\n isRelease: () => Environment.getEnvironment() === ApiEnvironment.Type.release,\n};\n\nexport default Environment;\n", "type": "registry:file", "target": "src/utilities/Environment.ts" }, { "path": "registry/tra-plugins/axios/lib/axios-config.ts", "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport axios from \"axios\";\nimport type { AxiosResponse } from \"axios\";\nimport Environment from \"@/utilities/Environment\";\n\n// ─── Custom Response Type ────────────────────────────────────────────────────\nexport interface CustomAxiosResponse extends AxiosResponse {\n data: T;\n error?: boolean;\n message?: string | null;\n}\n\n// ─── Logout Ref (interceptor'dan AuthContext.logout tetiklemek için) ─────────\nlet logoutCallback: (() => void) | null = null;\n\nexport function setAxiosLogoutCallback(callback: () => void) {\n logoutCallback = callback;\n}\n\n// ─── Axios Instance ──────────────────────────────────────────────────────────\nconst RequestService = axios.create({\n baseURL: Environment.getBaseUrl(),\n timeout: 30_000,\n});\n\nRequestService.defaults.headers.head[\"Content-Type\"] = \"application/json\";\nRequestService.defaults.validateStatus = (status) => status < 500;\n\n// ─── Auth Token Management ───────────────────────────────────────────────────\nexport function setAxiosAuthToken(token: string | null) {\n if (token) {\n RequestService.defaults.headers.common.Authorization = `Bearer ${token}`;\n } else {\n delete RequestService.defaults.headers.common.Authorization;\n }\n}\n\n// ─── Response Interceptor ────────────────────────────────────────────────────\nRequestService.interceptors.response.use(\n (response) => {\n // 401 Unauthorized → otomatik logout\n if (response.status === 401 && logoutCallback) {\n logoutCallback();\n }\n\n return {\n ...response,\n message: (response.data as any)?.message ?? null,\n error: (response.data as any)?.error ?? false,\n data: (response.data as any)?.data ?? (response.data as any)?.Result ?? response.data,\n };\n },\n (error) => {\n // Network error veya 5xx durumlarında da 401 kontrolü\n if (error?.response?.status === 401 && logoutCallback) {\n logoutCallback();\n }\n console.error(\"[Axios Error]\", error);\n return Promise.reject(error);\n }\n);\n\n// ─── Request Interceptor ─────────────────────────────────────────────────────\nRequestService.interceptors.request.use(\n (request) => request,\n (error) => Promise.reject(error)\n);\n\nexport default RequestService;\n", "type": "registry:file", "target": "src/lib/axios-config.ts" }, { "path": "registry/tra-plugins/axios/contexts/auth/AuthContext.tsx", "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n createContext,\n createRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport RequestService, {\n setAxiosAuthToken,\n setAxiosLogoutCallback,\n type CustomAxiosResponse,\n} from \"@/lib/axios-config\";\nimport StorageKeys from \"@/constants/StorageKeys\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\nexport interface AuthUser {\n [key: string]: any;\n}\n\nexport interface AuthContextValue {\n isAuthenticated: boolean;\n isLoading: boolean;\n user: AuthUser | null;\n authToken: string | null;\n login: (data: Record) => Promise;\n logout: () => Promise;\n getUserDetail: () => Promise;\n}\n\n// ─── Auth Logout Ref (for interceptors) ──────────────────────────────────────\nexport const authLogoutRef = createRef<{ logout: () => Promise }>();\n\n// ─── Context ─────────────────────────────────────────────────────────────────\nconst AuthContext = createContext(null);\n\n// ─── Helper: Response Check ──────────────────────────────────────────────────\nfunction isResponseSuccessful(response: CustomAxiosResponse): boolean {\n return response && response.status >= 200 && response.status < 300 && !response.error;\n}\n\n// ─── Provider ────────────────────────────────────────────────────────────────\nexport function AuthProvider({ children }: { children: ReactNode }) {\n const [authToken, setAuthToken] = useState(() =>\n localStorage.getItem(StorageKeys.ACCESS_TOKEN)\n );\n const [isAuthenticated, setIsAuthenticated] = useState(\n !!localStorage.getItem(StorageKeys.ACCESS_TOKEN)\n );\n const [isLoading, setIsLoading] = useState(true);\n const [user, setUser] = useState(null);\n\n const clearUserInfo = () => {\n localStorage.removeItem(StorageKeys.ACCESS_TOKEN);\n localStorage.removeItem(StorageKeys.USER);\n setAuthToken(null);\n setIsAuthenticated(false);\n setAxiosAuthToken(null);\n };\n\n const logout = async () => {\n if (!isAuthenticated) return;\n clearUserInfo();\n setUser(null);\n };\n\n const login = async (data: Record) => {\n // TODO: Login endpoint'inize göre düzenleyin\n const response: CustomAxiosResponse = await RequestService.post(\"/auth/login\", data);\n if (isResponseSuccessful(response)) {\n const token = response.data?.token ?? response.data?.accessToken;\n setAuthToken(token);\n localStorage.setItem(StorageKeys.ACCESS_TOKEN, token);\n setAxiosAuthToken(token);\n setIsAuthenticated(true);\n setUser(response.data);\n } else {\n setUser(null);\n throw new Error(response.message ?? \"Giriş başarısız\");\n }\n };\n\n const getUserDetail = async () => {\n // TODO: User detail endpoint'inize göre düzenleyin\n const response = await RequestService.get(\"/auth/me\");\n if (isResponseSuccessful(response)) {\n setUser(response.data);\n }\n };\n\n useImperativeHandle(authLogoutRef, () => ({ logout }));\n\n // Axios interceptor'a logout callback'i kaydet\n useEffect(() => {\n setAxiosLogoutCallback(() => {\n clearUserInfo();\n setUser(null);\n });\n }, []);\n\n // Token doğrulama\n useEffect(() => {\n const verifyToken = async () => {\n setIsLoading(true);\n if (!authToken) {\n clearUserInfo();\n setIsLoading(false);\n return;\n }\n setAxiosAuthToken(authToken);\n try {\n // TODO: Token doğrulama endpoint'inize göre düzenleyin\n const response = await RequestService.get(\"/auth/me\");\n if (isResponseSuccessful(response)) {\n setIsAuthenticated(true);\n setUser(response.data);\n } else {\n clearUserInfo();\n }\n } catch {\n clearUserInfo();\n }\n setIsLoading(false);\n };\n\n verifyToken();\n }, [authToken]);\n\n const value = useMemo(\n () => ({\n isAuthenticated,\n isLoading,\n user,\n authToken,\n login,\n logout,\n getUserDetail,\n }),\n [isAuthenticated, isLoading, user, authToken]\n );\n\n return {children};\n}\n\n// ─── Hook ────────────────────────────────────────────────────────────────────\nexport function useAuth() {\n const context = useContext(AuthContext);\n if (!context) throw new Error(\"useAuth must be used within an AuthProvider\");\n return context;\n}\n", "type": "registry:file", "target": "src/contexts/auth/AuthContext.tsx" }, { "path": "registry/tra-plugins/axios/hooks/useAuth.ts", "content": "export { useAuth, AuthProvider, authLogoutRef } from \"@/contexts/auth/AuthContext\";\nexport type { AuthUser, AuthContextValue } from \"@/contexts/auth/AuthContext\";\n", "type": "registry:file", "target": "src/hooks/useAuth.ts" }, { "path": "registry/tra-plugins/axios/services/BaseService.ts", "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport RequestService from \"@/lib/axios-config\";\nimport type { CustomAxiosResponse } from \"@/lib/axios-config\";\nimport type { AxiosRequestConfig } from \"axios\";\n\n/**\n * Servis oluşturma kalıbı.\n * Her controller için bir servis objesi tanımlayın.\n *\n * @example\n * // services/UserService.ts\n * import { createService } from '@/services/BaseService';\n *\n * const UserService = createService('User/', {\n * login: (data) => UserService.post('Login', data),\n * detail: () => UserService.get(''),\n * list: () => UserService.get('List'),\n * create: (data) => UserService.post('Create', data),\n * update: (data) => UserService.patch('Update', data),\n * });\n *\n * export default UserService;\n */\n\ninterface ServiceMethods {\n get: (endpoint: string, config?: AxiosRequestConfig) => Promise>;\n post: (\n endpoint: string,\n data?: any,\n config?: AxiosRequestConfig\n ) => Promise>;\n put: (\n endpoint: string,\n data?: any,\n config?: AxiosRequestConfig\n ) => Promise>;\n patch: (\n endpoint: string,\n data?: any,\n config?: AxiosRequestConfig\n ) => Promise>;\n delete: (\n endpoint: string,\n config?: AxiosRequestConfig\n ) => Promise>;\n}\n\n/**\n * Controller bazlı servis oluşturur.\n * İkinci parametre ile özel servis metodlarını tanımlayabilirsiniz.\n */\nexport function createService any>>(\n controller: string,\n methods: (service: ServiceMethods) => T\n): T & ServiceMethods {\n const baseMethods: ServiceMethods = {\n get: (endpoint, config) =>\n RequestService.get(`${controller}${endpoint}`, config) as Promise,\n post: (endpoint, data, config) =>\n RequestService.post(`${controller}${endpoint}`, data, config) as Promise,\n put: (endpoint, data, config) =>\n RequestService.put(`${controller}${endpoint}`, data, config) as Promise,\n patch: (endpoint, data, config) =>\n RequestService.patch(\n `${controller}${endpoint}`,\n data,\n config\n ) as Promise,\n delete: (endpoint, config) =>\n RequestService.delete(`${controller}${endpoint}`, config) as Promise,\n };\n\n const customMethods = methods(baseMethods);\n\n return { ...baseMethods, ...customMethods };\n}\n\n/**\n * Response başarılı mı kontrolü.\n */\nexport function isResponseSuccessful(response: CustomAxiosResponse): boolean {\n return response && response.status >= 200 && response.status < 300 && !response.error;\n}\n\nexport default RequestService;\n", "type": "registry:file", "target": "src/services/BaseService.ts" } ], "type": "registry:block" }