@ph-cms/client-sdk 0.1.1 → 0.1.3
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.
- package/README.md +129 -17
- package/dist/client.d.ts +2 -0
- package/dist/client.js +2 -0
- package/dist/context.d.ts +18 -0
- package/dist/context.js +46 -7
- package/dist/hooks/useAuth.d.ts +179 -63
- package/dist/hooks/useAuth.js +40 -33
- package/dist/hooks/useMedia.d.ts +32 -0
- package/dist/hooks/useMedia.js +33 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/modules/media.d.ts +20 -0
- package/dist/modules/media.js +49 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,24 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
Unified PH-CMS client SDK for browser and React applications.
|
|
4
4
|
|
|
5
|
-
This package
|
|
5
|
+
This package provides:
|
|
6
6
|
|
|
7
7
|
- A typed API client
|
|
8
8
|
- Auth provider interfaces and implementations
|
|
9
9
|
- React context and hooks for consuming the client in UI code
|
|
10
|
+
- **Integrated React Query support** (from v0.1.1)
|
|
10
11
|
|
|
11
12
|
## Installation
|
|
12
13
|
|
|
13
|
-
Install the SDK itself with its runtime dependencies:
|
|
14
|
-
|
|
15
14
|
```bash
|
|
16
|
-
npm install @ph-cms/client-sdk
|
|
15
|
+
npm install @ph-cms/client-sdk
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
> **Note:** `@tanstack/react-query` is a direct dependency from v0.1.1. You no longer need to install it manually unless you use it in your own application code.
|
|
19
|
+
|
|
20
|
+
If you use the React bindings, ensure you have `react` installed:
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
npm install react
|
|
23
|
+
npm install react react-dom
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
If you use Firebase auth integration:
|
|
@@ -28,7 +29,7 @@ If you use Firebase auth integration:
|
|
|
28
29
|
npm install firebase
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
## Usage
|
|
32
|
+
## Usage (Core SDK)
|
|
32
33
|
|
|
33
34
|
```ts
|
|
34
35
|
import { PHCMSClient } from '@ph-cms/client-sdk';
|
|
@@ -37,36 +38,147 @@ const client = new PHCMSClient({
|
|
|
37
38
|
baseURL: 'https://api.example.com',
|
|
38
39
|
});
|
|
39
40
|
|
|
41
|
+
// Use the modules directly
|
|
40
42
|
const contents = await client.content.list({
|
|
41
43
|
page: 1,
|
|
42
44
|
limit: 20,
|
|
43
45
|
});
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
## React Usage
|
|
48
|
+
## React Usage (v0.1.1+)
|
|
49
|
+
|
|
50
|
+
From version 0.1.1, `PHCMSProvider` automatically includes a `QueryClientProvider`. You don't need to wrap your app with `QueryClientProvider` manually to use PH-CMS hooks.
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
### Basic Setup
|
|
49
53
|
|
|
50
54
|
```tsx
|
|
51
|
-
import { PHCMSClient, PHCMSProvider
|
|
55
|
+
import { PHCMSClient, PHCMSProvider } from '@ph-cms/client-sdk';
|
|
56
|
+
import { client } from './lib/sdk'; // Your pre-configured client
|
|
57
|
+
|
|
58
|
+
export function App() {
|
|
59
|
+
return (
|
|
60
|
+
<PHCMSProvider client={client}>
|
|
61
|
+
<YourComponents />
|
|
62
|
+
</PHCMSProvider>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
### Using Hooks
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
```tsx
|
|
70
|
+
import { useContent, useUser } from '@ph-cms/client-sdk';
|
|
71
|
+
|
|
72
|
+
function MyComponent() {
|
|
73
|
+
const { data: user, isLoading: userLoading } = useUser();
|
|
74
|
+
const { data: contents, isLoading: contentLoading } = useContent();
|
|
75
|
+
|
|
76
|
+
if (userLoading || contentLoading) return <div>Loading...</div>;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div>
|
|
80
|
+
<h1>Hello, {user?.email}</h1>
|
|
81
|
+
<ul>
|
|
82
|
+
{contents?.items.map(item => (
|
|
83
|
+
<li key={item.uid}>{item.title}</li>
|
|
84
|
+
))}
|
|
85
|
+
</ul>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
59
88
|
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Custom QueryClient (Optional)
|
|
92
|
+
|
|
93
|
+
If your application already uses React Query and you want to share the cache/settings:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
97
|
+
import { PHCMSClient, PHCMSProvider } from '@ph-cms/client-sdk';
|
|
98
|
+
|
|
99
|
+
const queryClient = new QueryClient();
|
|
100
|
+
const client = new PHCMSClient({ baseURL: '...' });
|
|
60
101
|
|
|
61
102
|
export function App() {
|
|
62
103
|
return (
|
|
63
|
-
<PHCMSProvider client={client}>
|
|
64
|
-
|
|
104
|
+
<PHCMSProvider client={client} queryClient={queryClient}>
|
|
105
|
+
{/* Both your app and PH-CMS hooks will use the same queryClient */}
|
|
106
|
+
<YourComponents />
|
|
65
107
|
</PHCMSProvider>
|
|
66
108
|
);
|
|
67
109
|
}
|
|
68
110
|
```
|
|
69
111
|
|
|
112
|
+
## Admin SDK (@ph-cms/client-sdk-admin)
|
|
113
|
+
|
|
114
|
+
For administrative tasks, use `@ph-cms/client-sdk-admin`. It follows the same provider pattern:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { PHCMSAdminProvider } from '@ph-cms/client-sdk-admin';
|
|
118
|
+
import { adminClient } from './lib/sdk';
|
|
119
|
+
|
|
120
|
+
export function App() {
|
|
121
|
+
return (
|
|
122
|
+
<PHCMSAdminProvider client={adminClient}>
|
|
123
|
+
<AdminComponents />
|
|
124
|
+
</PHCMSAdminProvider>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Media & File Upload (v0.1.2+)
|
|
130
|
+
|
|
131
|
+
Uploading media files follows a 3-step process: Request a Ticket -> Upload to S3 -> Create/Update Content.
|
|
132
|
+
|
|
133
|
+
### 1. Simple Workflow Example
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { useMediaUploadTickets, useUploadToS3, useCreateContent } from '@ph-cms/client-sdk';
|
|
137
|
+
|
|
138
|
+
function MediaUploader() {
|
|
139
|
+
const { mutateAsync: getTickets } = useMediaUploadTickets();
|
|
140
|
+
const { mutateAsync: uploadToS3 } = useUploadToS3();
|
|
141
|
+
const { mutateAsync: createContent } = useCreateContent();
|
|
142
|
+
|
|
143
|
+
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
144
|
+
const file = event.target.files?.[0];
|
|
145
|
+
if (!file) return;
|
|
146
|
+
|
|
147
|
+
// Step 1: Get Upload Ticket (Presigned URL)
|
|
148
|
+
const tickets = await getTickets([{
|
|
149
|
+
filename: file.name,
|
|
150
|
+
contentType: file.type,
|
|
151
|
+
fileSize: file.size,
|
|
152
|
+
// width: 1024, (optional)
|
|
153
|
+
// height: 768 (optional)
|
|
154
|
+
}]);
|
|
155
|
+
|
|
156
|
+
const { mediaUid, uploadUrl } = tickets[0];
|
|
157
|
+
|
|
158
|
+
// Step 2: Upload File directly to S3
|
|
159
|
+
await uploadToS3({ url: uploadUrl, file });
|
|
160
|
+
|
|
161
|
+
// Step 3: Use the mediaUid to create content
|
|
162
|
+
await createContent({
|
|
163
|
+
channelSlug: 'my-channel',
|
|
164
|
+
type: 'post',
|
|
165
|
+
title: 'Post with image',
|
|
166
|
+
mediaAttachments: [mediaUid]
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return <input type="file" onChange={handleFileChange} />;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 2. Supported Media Types
|
|
175
|
+
The system automatically classifies media based on the `contentType`:
|
|
176
|
+
- `image/*` -> `image`
|
|
177
|
+
- `video/*` -> `video`
|
|
178
|
+
- `audio/*` -> `audio`
|
|
179
|
+
- `application/pdf`, `.docx`, etc. -> `document`
|
|
180
|
+
- Others -> `file`
|
|
181
|
+
|
|
70
182
|
## License
|
|
71
183
|
|
|
72
184
|
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { AuthModule } from './modules/auth';
|
|
|
4
4
|
import { ContentModule } from './modules/content';
|
|
5
5
|
import { ChannelModule } from './modules/channel';
|
|
6
6
|
import { TermsModule } from './modules/terms';
|
|
7
|
+
import { MediaModule } from './modules/media';
|
|
7
8
|
export interface PHCMSClientConfig {
|
|
8
9
|
baseURL: string;
|
|
9
10
|
apiPrefix?: string;
|
|
@@ -17,5 +18,6 @@ export declare class PHCMSClient {
|
|
|
17
18
|
readonly content: ContentModule;
|
|
18
19
|
readonly channel: ChannelModule;
|
|
19
20
|
readonly terms: TermsModule;
|
|
21
|
+
readonly media: MediaModule;
|
|
20
22
|
constructor(config: PHCMSClientConfig);
|
|
21
23
|
}
|
package/dist/client.js
CHANGED
|
@@ -10,6 +10,7 @@ const auth_1 = require("./modules/auth");
|
|
|
10
10
|
const content_1 = require("./modules/content");
|
|
11
11
|
const channel_1 = require("./modules/channel");
|
|
12
12
|
const terms_1 = require("./modules/terms");
|
|
13
|
+
const media_1 = require("./modules/media");
|
|
13
14
|
class PHCMSClient {
|
|
14
15
|
constructor(config) {
|
|
15
16
|
this.config = config;
|
|
@@ -69,6 +70,7 @@ class PHCMSClient {
|
|
|
69
70
|
this.content = new content_1.ContentModule(this.axiosInstance);
|
|
70
71
|
this.channel = new channel_1.ChannelModule(this.axiosInstance);
|
|
71
72
|
this.terms = new terms_1.TermsModule(this.axiosInstance);
|
|
73
|
+
this.media = new media_1.MediaModule(this.axiosInstance);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
exports.PHCMSClient = PHCMSClient;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import { QueryClient } from '@tanstack/react-query';
|
|
3
3
|
import { PHCMSClient } from './client';
|
|
4
|
+
import { UserDto } from '@ph-cms/api-contract';
|
|
5
|
+
export interface PHCMSContextType {
|
|
6
|
+
client: PHCMSClient;
|
|
7
|
+
user: UserDto | null;
|
|
8
|
+
isAuthenticated: boolean;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
}
|
|
4
11
|
export interface PHCMSProviderProps {
|
|
5
12
|
client: PHCMSClient;
|
|
6
13
|
queryClient?: QueryClient;
|
|
7
14
|
children: ReactNode;
|
|
8
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Root Provider for PH-CMS
|
|
18
|
+
* Automatically includes QueryClientProvider
|
|
19
|
+
*/
|
|
9
20
|
export declare const PHCMSProvider: React.FC<PHCMSProviderProps>;
|
|
21
|
+
/**
|
|
22
|
+
* Access the full PH-CMS Context (client, user, auth status)
|
|
23
|
+
*/
|
|
24
|
+
export declare const usePHCMSContext: () => PHCMSContextType;
|
|
25
|
+
/**
|
|
26
|
+
* Access the PH-CMS Client instance
|
|
27
|
+
*/
|
|
10
28
|
export declare const usePHCMS: () => PHCMSClient;
|
package/dist/context.js
CHANGED
|
@@ -33,21 +33,60 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.usePHCMS = exports.PHCMSProvider = void 0;
|
|
36
|
+
exports.usePHCMS = exports.usePHCMSContext = exports.PHCMSProvider = void 0;
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const react_query_1 = require("@tanstack/react-query");
|
|
39
39
|
const PHCMSContext = (0, react_1.createContext)(null);
|
|
40
|
+
/**
|
|
41
|
+
* Internal component to handle auth state and provide to context
|
|
42
|
+
*/
|
|
43
|
+
const PHCMSInternalProvider = ({ client, children }) => {
|
|
44
|
+
const { data: user, isLoading, isError } = (0, react_query_1.useQuery)({
|
|
45
|
+
queryKey: ['auth', 'me'],
|
|
46
|
+
queryFn: () => client.auth.me(),
|
|
47
|
+
retry: false,
|
|
48
|
+
staleTime: 1000 * 60 * 5,
|
|
49
|
+
});
|
|
50
|
+
const value = (0, react_1.useMemo)(() => ({
|
|
51
|
+
client,
|
|
52
|
+
user: user || null,
|
|
53
|
+
isAuthenticated: !!user && !isError,
|
|
54
|
+
isLoading,
|
|
55
|
+
}), [client, user, isError, isLoading]);
|
|
56
|
+
return (react_1.default.createElement(PHCMSContext.Provider, { value: value }, children));
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Root Provider for PH-CMS
|
|
60
|
+
* Automatically includes QueryClientProvider
|
|
61
|
+
*/
|
|
40
62
|
const PHCMSProvider = ({ client, queryClient, children }) => {
|
|
41
|
-
const internalQueryClient = (0, react_1.useMemo)(() => queryClient ?? new react_query_1.QueryClient(
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
const internalQueryClient = (0, react_1.useMemo)(() => queryClient ?? new react_query_1.QueryClient({
|
|
64
|
+
defaultOptions: {
|
|
65
|
+
queries: {
|
|
66
|
+
refetchOnWindowFocus: false,
|
|
67
|
+
retry: false,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}), [queryClient]);
|
|
71
|
+
return (react_1.default.createElement(react_query_1.QueryClientProvider, { client: internalQueryClient },
|
|
72
|
+
react_1.default.createElement(PHCMSInternalProvider, { client: client }, children)));
|
|
44
73
|
};
|
|
45
74
|
exports.PHCMSProvider = PHCMSProvider;
|
|
46
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Access the full PH-CMS Context (client, user, auth status)
|
|
77
|
+
*/
|
|
78
|
+
const usePHCMSContext = () => {
|
|
47
79
|
const context = (0, react_1.useContext)(PHCMSContext);
|
|
48
80
|
if (!context) {
|
|
49
|
-
throw new Error('
|
|
81
|
+
throw new Error('usePHCMSContext must be used within a PHCMSProvider');
|
|
50
82
|
}
|
|
51
|
-
return context
|
|
83
|
+
return context;
|
|
84
|
+
};
|
|
85
|
+
exports.usePHCMSContext = usePHCMSContext;
|
|
86
|
+
/**
|
|
87
|
+
* Access the PH-CMS Client instance
|
|
88
|
+
*/
|
|
89
|
+
const usePHCMS = () => {
|
|
90
|
+
return (0, exports.usePHCMSContext)().client;
|
|
52
91
|
};
|
|
53
92
|
exports.usePHCMS = usePHCMS;
|
package/dist/hooks/useAuth.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Unified Auth Hook
|
|
3
|
+
* Returns both the current auth status and the actions
|
|
4
|
+
*/
|
|
5
|
+
export declare const useAuth: () => {
|
|
3
6
|
user: {
|
|
4
7
|
uid: string;
|
|
5
8
|
email: string;
|
|
@@ -17,39 +20,148 @@ export declare const useLogin: () => import("@tanstack/react-query").UseMutation
|
|
|
17
20
|
last_login_at: string | null;
|
|
18
21
|
created_at: string;
|
|
19
22
|
updated_at: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
} | null;
|
|
24
|
+
isAuthenticated: boolean;
|
|
25
|
+
isLoading: boolean;
|
|
26
|
+
login: import("@tanstack/react-query").UseMutateAsyncFunction<{
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
user: {
|
|
29
|
+
uid: string;
|
|
30
|
+
email: string;
|
|
31
|
+
username: string | null;
|
|
32
|
+
display_name: string;
|
|
33
|
+
avatar_url: string | null;
|
|
34
|
+
phone_number: string | null;
|
|
35
|
+
email_verified_at: string | null;
|
|
36
|
+
phone_verified_at: string | null;
|
|
37
|
+
locale: string;
|
|
38
|
+
timezone: string;
|
|
39
|
+
status: string;
|
|
40
|
+
role: string[];
|
|
41
|
+
profile_data: Record<string, any>;
|
|
42
|
+
last_login_at: string | null;
|
|
43
|
+
created_at: string;
|
|
44
|
+
updated_at: string;
|
|
45
|
+
};
|
|
46
|
+
accessToken: string;
|
|
47
|
+
}, Error, {
|
|
48
|
+
email: string;
|
|
49
|
+
password: string;
|
|
50
|
+
}, unknown>;
|
|
51
|
+
loginWithFirebase: import("@tanstack/react-query").UseMutateAsyncFunction<{
|
|
52
|
+
refreshToken: string;
|
|
53
|
+
user: {
|
|
54
|
+
uid: string;
|
|
55
|
+
email: string;
|
|
56
|
+
username: string | null;
|
|
57
|
+
display_name: string;
|
|
58
|
+
avatar_url: string | null;
|
|
59
|
+
phone_number: string | null;
|
|
60
|
+
email_verified_at: string | null;
|
|
61
|
+
phone_verified_at: string | null;
|
|
62
|
+
locale: string;
|
|
63
|
+
timezone: string;
|
|
64
|
+
status: string;
|
|
65
|
+
role: string[];
|
|
66
|
+
profile_data: Record<string, any>;
|
|
67
|
+
last_login_at: string | null;
|
|
68
|
+
created_at: string;
|
|
69
|
+
updated_at: string;
|
|
70
|
+
};
|
|
71
|
+
accessToken: string;
|
|
72
|
+
}, Error, {
|
|
73
|
+
idToken: string;
|
|
74
|
+
}, unknown>;
|
|
75
|
+
register: import("@tanstack/react-query").UseMutateAsyncFunction<{
|
|
76
|
+
refreshToken: string;
|
|
77
|
+
user: {
|
|
78
|
+
uid: string;
|
|
79
|
+
email: string;
|
|
80
|
+
username: string | null;
|
|
81
|
+
display_name: string;
|
|
82
|
+
avatar_url: string | null;
|
|
83
|
+
phone_number: string | null;
|
|
84
|
+
email_verified_at: string | null;
|
|
85
|
+
phone_verified_at: string | null;
|
|
86
|
+
locale: string;
|
|
87
|
+
timezone: string;
|
|
88
|
+
status: string;
|
|
89
|
+
role: string[];
|
|
90
|
+
profile_data: Record<string, any>;
|
|
91
|
+
last_login_at: string | null;
|
|
92
|
+
created_at: string;
|
|
93
|
+
updated_at: string;
|
|
94
|
+
};
|
|
95
|
+
accessToken: string;
|
|
96
|
+
}, Error, {
|
|
30
97
|
email: string;
|
|
31
|
-
username: string | null;
|
|
32
98
|
display_name: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
99
|
+
username?: string | null | undefined;
|
|
100
|
+
password?: string | undefined;
|
|
101
|
+
provider?: string | undefined;
|
|
102
|
+
provider_subject?: string | undefined;
|
|
103
|
+
termCodes?: string[] | undefined;
|
|
104
|
+
}, unknown>;
|
|
105
|
+
logout: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, void, unknown>;
|
|
106
|
+
loginStatus: import("@tanstack/react-query").UseMutationResult<{
|
|
107
|
+
refreshToken: string;
|
|
108
|
+
user: {
|
|
109
|
+
uid: string;
|
|
110
|
+
email: string;
|
|
111
|
+
username: string | null;
|
|
112
|
+
display_name: string;
|
|
113
|
+
avatar_url: string | null;
|
|
114
|
+
phone_number: string | null;
|
|
115
|
+
email_verified_at: string | null;
|
|
116
|
+
phone_verified_at: string | null;
|
|
117
|
+
locale: string;
|
|
118
|
+
timezone: string;
|
|
119
|
+
status: string;
|
|
120
|
+
role: string[];
|
|
121
|
+
profile_data: Record<string, any>;
|
|
122
|
+
last_login_at: string | null;
|
|
123
|
+
created_at: string;
|
|
124
|
+
updated_at: string;
|
|
125
|
+
};
|
|
126
|
+
accessToken: string;
|
|
127
|
+
}, Error, {
|
|
128
|
+
email: string;
|
|
129
|
+
password: string;
|
|
130
|
+
}, unknown>;
|
|
131
|
+
registerStatus: import("@tanstack/react-query").UseMutationResult<{
|
|
132
|
+
refreshToken: string;
|
|
133
|
+
user: {
|
|
134
|
+
uid: string;
|
|
135
|
+
email: string;
|
|
136
|
+
username: string | null;
|
|
137
|
+
display_name: string;
|
|
138
|
+
avatar_url: string | null;
|
|
139
|
+
phone_number: string | null;
|
|
140
|
+
email_verified_at: string | null;
|
|
141
|
+
phone_verified_at: string | null;
|
|
142
|
+
locale: string;
|
|
143
|
+
timezone: string;
|
|
144
|
+
status: string;
|
|
145
|
+
role: string[];
|
|
146
|
+
profile_data: Record<string, any>;
|
|
147
|
+
last_login_at: string | null;
|
|
148
|
+
created_at: string;
|
|
149
|
+
updated_at: string;
|
|
150
|
+
};
|
|
151
|
+
accessToken: string;
|
|
152
|
+
}, Error, {
|
|
153
|
+
email: string;
|
|
154
|
+
display_name: string;
|
|
155
|
+
username?: string | null | undefined;
|
|
156
|
+
password?: string | undefined;
|
|
157
|
+
provider?: string | undefined;
|
|
158
|
+
provider_subject?: string | undefined;
|
|
159
|
+
termCodes?: string[] | undefined;
|
|
160
|
+
}, unknown>;
|
|
161
|
+
logoutStatus: import("@tanstack/react-query").UseMutationResult<void, Error, void, unknown>;
|
|
162
|
+
};
|
|
163
|
+
export declare const useUser: () => {
|
|
164
|
+
data: {
|
|
53
165
|
uid: string;
|
|
54
166
|
email: string;
|
|
55
167
|
username: string | null;
|
|
@@ -66,33 +178,37 @@ export declare const useRegister: () => import("@tanstack/react-query").UseMutat
|
|
|
66
178
|
last_login_at: string | null;
|
|
67
179
|
created_at: string;
|
|
68
180
|
updated_at: string;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
},
|
|
181
|
+
} | null;
|
|
182
|
+
isLoading: boolean;
|
|
183
|
+
isAuthenticated: boolean;
|
|
184
|
+
};
|
|
185
|
+
export declare const useLogin: () => {
|
|
186
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<{
|
|
187
|
+
refreshToken: string;
|
|
188
|
+
user: {
|
|
189
|
+
uid: string;
|
|
190
|
+
email: string;
|
|
191
|
+
username: string | null;
|
|
192
|
+
display_name: string;
|
|
193
|
+
avatar_url: string | null;
|
|
194
|
+
phone_number: string | null;
|
|
195
|
+
email_verified_at: string | null;
|
|
196
|
+
phone_verified_at: string | null;
|
|
197
|
+
locale: string;
|
|
198
|
+
timezone: string;
|
|
199
|
+
status: string;
|
|
200
|
+
role: string[];
|
|
201
|
+
profile_data: Record<string, any>;
|
|
202
|
+
last_login_at: string | null;
|
|
203
|
+
created_at: string;
|
|
204
|
+
updated_at: string;
|
|
205
|
+
};
|
|
206
|
+
accessToken: string;
|
|
207
|
+
}, Error, {
|
|
208
|
+
email: string;
|
|
209
|
+
password: string;
|
|
210
|
+
}, unknown>;
|
|
211
|
+
};
|
|
212
|
+
export declare const useLogout: () => {
|
|
213
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, void, unknown>;
|
|
214
|
+
};
|
package/dist/hooks/useAuth.js
CHANGED
|
@@ -1,62 +1,69 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.useLogout = exports.useLogin = exports.useUser = exports.useAuth = void 0;
|
|
4
4
|
const react_query_1 = require("@tanstack/react-query");
|
|
5
5
|
const context_1 = require("../context");
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Unified Auth Hook
|
|
8
|
+
* Returns both the current auth status and the actions
|
|
9
|
+
*/
|
|
10
|
+
const useAuth = () => {
|
|
11
|
+
const { user, isAuthenticated, isLoading } = (0, context_1.usePHCMSContext)();
|
|
7
12
|
const client = (0, context_1.usePHCMS)();
|
|
8
13
|
const queryClient = (0, react_query_1.useQueryClient)();
|
|
9
|
-
|
|
14
|
+
const loginMutation = (0, react_query_1.useMutation)({
|
|
10
15
|
mutationFn: (data) => client.auth.login(data),
|
|
11
|
-
onSuccess: (
|
|
12
|
-
// Invalidate user query to fetch fresh user data
|
|
16
|
+
onSuccess: () => {
|
|
13
17
|
queryClient.invalidateQueries({ queryKey: ['auth', 'me'] });
|
|
14
18
|
},
|
|
15
19
|
});
|
|
16
|
-
|
|
17
|
-
exports.useLogin = useLogin;
|
|
18
|
-
const useLoginWithFirebase = () => {
|
|
19
|
-
const client = (0, context_1.usePHCMS)();
|
|
20
|
-
const queryClient = (0, react_query_1.useQueryClient)();
|
|
21
|
-
return (0, react_query_1.useMutation)({
|
|
20
|
+
const loginWithFirebaseMutation = (0, react_query_1.useMutation)({
|
|
22
21
|
mutationFn: (data) => client.auth.loginWithFirebase(data),
|
|
23
|
-
onSuccess: (
|
|
24
|
-
// Invalidate user query to fetch fresh user data
|
|
22
|
+
onSuccess: () => {
|
|
25
23
|
queryClient.invalidateQueries({ queryKey: ['auth', 'me'] });
|
|
26
24
|
},
|
|
27
25
|
});
|
|
28
|
-
|
|
29
|
-
exports.useLoginWithFirebase = useLoginWithFirebase;
|
|
30
|
-
const useRegister = () => {
|
|
31
|
-
const client = (0, context_1.usePHCMS)();
|
|
32
|
-
const queryClient = (0, react_query_1.useQueryClient)();
|
|
33
|
-
return (0, react_query_1.useMutation)({
|
|
26
|
+
const registerMutation = (0, react_query_1.useMutation)({
|
|
34
27
|
mutationFn: (data) => client.auth.register(data),
|
|
35
28
|
onSuccess: () => {
|
|
36
29
|
queryClient.invalidateQueries({ queryKey: ['auth', 'me'] });
|
|
37
30
|
},
|
|
38
31
|
});
|
|
39
|
-
|
|
40
|
-
exports.useRegister = useRegister;
|
|
41
|
-
const useLogout = () => {
|
|
42
|
-
const client = (0, context_1.usePHCMS)();
|
|
43
|
-
const queryClient = (0, react_query_1.useQueryClient)();
|
|
44
|
-
return (0, react_query_1.useMutation)({
|
|
32
|
+
const logoutMutation = (0, react_query_1.useMutation)({
|
|
45
33
|
mutationFn: () => client.auth.logout(),
|
|
46
34
|
onSuccess: () => {
|
|
47
35
|
queryClient.setQueryData(['auth', 'me'], null);
|
|
48
36
|
queryClient.invalidateQueries();
|
|
49
37
|
},
|
|
50
38
|
});
|
|
39
|
+
return {
|
|
40
|
+
user,
|
|
41
|
+
isAuthenticated,
|
|
42
|
+
isLoading,
|
|
43
|
+
login: loginMutation.mutateAsync,
|
|
44
|
+
loginWithFirebase: loginWithFirebaseMutation.mutateAsync,
|
|
45
|
+
register: registerMutation.mutateAsync,
|
|
46
|
+
logout: logoutMutation.mutateAsync,
|
|
47
|
+
// Access to mutation objects for loading/error states if needed
|
|
48
|
+
loginStatus: loginMutation,
|
|
49
|
+
registerStatus: registerMutation,
|
|
50
|
+
logoutStatus: logoutMutation,
|
|
51
|
+
};
|
|
51
52
|
};
|
|
52
|
-
exports.
|
|
53
|
+
exports.useAuth = useAuth;
|
|
54
|
+
// Keep individual hooks for backward compatibility or more specific use cases
|
|
53
55
|
const useUser = () => {
|
|
54
|
-
const
|
|
55
|
-
return
|
|
56
|
-
queryKey: ['auth', 'me'],
|
|
57
|
-
queryFn: () => client.auth.me(),
|
|
58
|
-
retry: false, // Don't retry if 401
|
|
59
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
60
|
-
});
|
|
56
|
+
const { user, isLoading, isAuthenticated } = (0, context_1.usePHCMSContext)();
|
|
57
|
+
return { data: user, isLoading, isAuthenticated };
|
|
61
58
|
};
|
|
62
59
|
exports.useUser = useUser;
|
|
60
|
+
const useLogin = () => {
|
|
61
|
+
const { login } = (0, exports.useAuth)();
|
|
62
|
+
return { mutateAsync: login };
|
|
63
|
+
};
|
|
64
|
+
exports.useLogin = useLogin;
|
|
65
|
+
const useLogout = () => {
|
|
66
|
+
const { logout } = (0, exports.useAuth)();
|
|
67
|
+
return { mutateAsync: logout };
|
|
68
|
+
};
|
|
69
|
+
exports.useLogout = useLogout;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const mediaKeys: {
|
|
2
|
+
all: readonly ["media"];
|
|
3
|
+
details: () => readonly ["media", "detail"];
|
|
4
|
+
detail: (uid: string) => readonly ["media", "detail", string];
|
|
5
|
+
};
|
|
6
|
+
export declare const useMediaUploadTickets: () => import("@tanstack/react-query").UseMutationResult<{
|
|
7
|
+
mediaUid: string;
|
|
8
|
+
uploadUrl: string;
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
}[], Error, {
|
|
11
|
+
filename: string;
|
|
12
|
+
contentType: string;
|
|
13
|
+
fileSize: number;
|
|
14
|
+
width?: number | undefined;
|
|
15
|
+
height?: number | undefined;
|
|
16
|
+
}[], unknown>;
|
|
17
|
+
export declare const useUploadToS3: () => import("@tanstack/react-query").UseMutationResult<void, Error, {
|
|
18
|
+
url: string;
|
|
19
|
+
file: File | Blob;
|
|
20
|
+
}, unknown>;
|
|
21
|
+
export declare const useMediaDetail: (uid: string) => import("@tanstack/react-query").UseQueryResult<{
|
|
22
|
+
uid: string;
|
|
23
|
+
type: "image" | "video" | "audio" | "document" | "file";
|
|
24
|
+
url: string;
|
|
25
|
+
name: string;
|
|
26
|
+
mimeType: string;
|
|
27
|
+
size: number;
|
|
28
|
+
order: number;
|
|
29
|
+
width?: number | undefined;
|
|
30
|
+
height?: number | undefined;
|
|
31
|
+
description?: string | null | undefined;
|
|
32
|
+
}, Error>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMediaDetail = exports.useUploadToS3 = exports.useMediaUploadTickets = exports.mediaKeys = void 0;
|
|
4
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
5
|
+
const context_1 = require("../context");
|
|
6
|
+
exports.mediaKeys = {
|
|
7
|
+
all: ['media'],
|
|
8
|
+
details: () => [...exports.mediaKeys.all, 'detail'],
|
|
9
|
+
detail: (uid) => [...exports.mediaKeys.details(), uid],
|
|
10
|
+
};
|
|
11
|
+
const useMediaUploadTickets = () => {
|
|
12
|
+
const client = (0, context_1.usePHCMS)();
|
|
13
|
+
return (0, react_query_1.useMutation)({
|
|
14
|
+
mutationFn: (data) => client.media.getUploadTickets(data),
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
exports.useMediaUploadTickets = useMediaUploadTickets;
|
|
18
|
+
const useUploadToS3 = () => {
|
|
19
|
+
const client = (0, context_1.usePHCMS)();
|
|
20
|
+
return (0, react_query_1.useMutation)({
|
|
21
|
+
mutationFn: ({ url, file }) => client.media.uploadToS3(url, file),
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
exports.useUploadToS3 = useUploadToS3;
|
|
25
|
+
const useMediaDetail = (uid) => {
|
|
26
|
+
const client = (0, context_1.usePHCMS)();
|
|
27
|
+
return (0, react_query_1.useQuery)({
|
|
28
|
+
queryKey: exports.mediaKeys.detail(uid),
|
|
29
|
+
queryFn: () => client.media.getMedia(uid),
|
|
30
|
+
enabled: !!uid,
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
exports.useMediaDetail = useMediaDetail;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export * from './modules/auth';
|
|
|
7
7
|
export * from './modules/content';
|
|
8
8
|
export * from './modules/channel';
|
|
9
9
|
export * from './modules/terms';
|
|
10
|
+
export * from './modules/media';
|
|
10
11
|
export * from './context';
|
|
11
12
|
export * from './hooks/useAuth';
|
|
12
13
|
export * from './hooks/useContent';
|
|
13
14
|
export * from './hooks/useChannel';
|
|
14
15
|
export * from './hooks/useTerms';
|
|
16
|
+
export * from './hooks/useMedia';
|
|
15
17
|
export * from './types';
|
package/dist/index.js
CHANGED
|
@@ -23,9 +23,11 @@ __exportStar(require("./modules/auth"), exports);
|
|
|
23
23
|
__exportStar(require("./modules/content"), exports);
|
|
24
24
|
__exportStar(require("./modules/channel"), exports);
|
|
25
25
|
__exportStar(require("./modules/terms"), exports);
|
|
26
|
+
__exportStar(require("./modules/media"), exports);
|
|
26
27
|
__exportStar(require("./context"), exports);
|
|
27
28
|
__exportStar(require("./hooks/useAuth"), exports);
|
|
28
29
|
__exportStar(require("./hooks/useContent"), exports);
|
|
29
30
|
__exportStar(require("./hooks/useChannel"), exports);
|
|
30
31
|
__exportStar(require("./hooks/useTerms"), exports);
|
|
32
|
+
__exportStar(require("./hooks/useMedia"), exports);
|
|
31
33
|
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
import { MediaUploadTicketBatchRequest, MediaUploadTicketBatchResponse, ContentMediaDto } from "@ph-cms/api-contract";
|
|
3
|
+
export declare class MediaModule {
|
|
4
|
+
private client;
|
|
5
|
+
constructor(client: AxiosInstance);
|
|
6
|
+
/**
|
|
7
|
+
* 미디어 업로드 티켓(Presigned URL) 발급 요청
|
|
8
|
+
*/
|
|
9
|
+
getUploadTickets(data: MediaUploadTicketBatchRequest): Promise<MediaUploadTicketBatchResponse>;
|
|
10
|
+
/**
|
|
11
|
+
* 미디어 상세 정보 조회
|
|
12
|
+
*/
|
|
13
|
+
getMedia(uid: string): Promise<ContentMediaDto>;
|
|
14
|
+
/**
|
|
15
|
+
* S3 Presigned URL을 사용하여 파일 직접 업로드
|
|
16
|
+
* @param url getUploadTickets를 통해 발급받은 uploadUrl
|
|
17
|
+
* @param file 업로드할 파일 객체 (File 또는 Blob)
|
|
18
|
+
*/
|
|
19
|
+
uploadToS3(url: string, file: File | Blob): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MediaModule = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const api_contract_1 = require("@ph-cms/api-contract");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
class MediaModule {
|
|
11
|
+
constructor(client) {
|
|
12
|
+
this.client = client;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 미디어 업로드 티켓(Presigned URL) 발급 요청
|
|
16
|
+
*/
|
|
17
|
+
async getUploadTickets(data) {
|
|
18
|
+
const validation = api_contract_1.MediaUploadTicketBatchRequestSchema.safeParse(data);
|
|
19
|
+
if (!validation.success) {
|
|
20
|
+
throw new errors_1.ValidationError("Invalid upload ticket request", validation.error.errors);
|
|
21
|
+
}
|
|
22
|
+
return this.client.post('/api/media/upload-tickets', data);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 미디어 상세 정보 조회
|
|
26
|
+
*/
|
|
27
|
+
async getMedia(uid) {
|
|
28
|
+
if (!uid)
|
|
29
|
+
throw new errors_1.ValidationError("Media UID is required", []);
|
|
30
|
+
return this.client.get(`/api/media/${uid}`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* S3 Presigned URL을 사용하여 파일 직접 업로드
|
|
34
|
+
* @param url getUploadTickets를 통해 발급받은 uploadUrl
|
|
35
|
+
* @param file 업로드할 파일 객체 (File 또는 Blob)
|
|
36
|
+
*/
|
|
37
|
+
async uploadToS3(url, file) {
|
|
38
|
+
if (!url)
|
|
39
|
+
throw new errors_1.ValidationError("Upload URL is required", []);
|
|
40
|
+
// CMS API 전용 interceptor가 없는 순수 axios 인스턴스로 요청
|
|
41
|
+
// S3는 PUT 메서드를 사용하며, 티켓 발급 시 지정한 Content-Type과 일치해야 함
|
|
42
|
+
await axios_1.default.put(url, file, {
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': file.type,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.MediaModule = MediaModule;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ph-cms/client-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Unified PH-CMS Client SDK (React + Core)",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ph-cms/api-contract": "^0.1.
|
|
24
|
+
"@ph-cms/api-contract": "^0.1.1",
|
|
25
25
|
"@tanstack/react-query": "^5.0.0",
|
|
26
26
|
"axios": "^1.6.0",
|
|
27
27
|
"zod": "^3.22.4"
|