@axcode/axcloud-sdk 0.1.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.
- package/README.md +339 -0
- package/dist/index.d.mts +1473 -0
- package/dist/index.d.ts +1473 -0
- package/dist/index.js +1282 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1246 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles/linked-accounts.css +156 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# @axcode/axcloud-sdk
|
|
2
|
+
|
|
3
|
+
SDK for building websites with AxCloud CMS and member management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @axcode/axcloud-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Wrap your app with AxCloudProvider
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { AxCloudProvider } from '@axcode/axcloud-sdk'
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<AxCloudProvider
|
|
21
|
+
orgId="org_ba81807b" // Your organization ID
|
|
22
|
+
apiUrl="https://api.axcloud.app"
|
|
23
|
+
apiKey="sk_live_..." // API key from Settings → API Keys
|
|
24
|
+
auth0={{
|
|
25
|
+
domain: "auth.axcloud.app",
|
|
26
|
+
clientId: "your-client-id",
|
|
27
|
+
redirectUri: window.location.origin
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
<Router>
|
|
31
|
+
<Routes />
|
|
32
|
+
</Router>
|
|
33
|
+
</AxCloudProvider>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Fetch content
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { useNews } from '@axcode/axcloud-sdk'
|
|
42
|
+
|
|
43
|
+
function NewsPage() {
|
|
44
|
+
const { data: news, isLoading, isError } = useNews()
|
|
45
|
+
|
|
46
|
+
if (isLoading) return <Spinner />
|
|
47
|
+
if (isError) return <Error />
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<ul>
|
|
51
|
+
{news.map(item => (
|
|
52
|
+
<li key={item.id}>{item.title}</li>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
> **Content visibility:** When the user is authenticated, `useNews`, `useContent('pages')`, and `useContent('events')` automatically fetch **public + internal + members-only** content based on the user's organization membership. For anonymous users, only public content is returned.
|
|
60
|
+
|
|
61
|
+
### 3. Add authentication
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useAuth, LoginButton, LogoutButton, ProtectedRoute } from '@axcode/axcloud-sdk'
|
|
65
|
+
|
|
66
|
+
function Header() {
|
|
67
|
+
const { isAuthenticated, user } = useAuth()
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<header>
|
|
71
|
+
{isAuthenticated ? (
|
|
72
|
+
<>
|
|
73
|
+
<span>Welcome, {user.name}</span>
|
|
74
|
+
<LogoutButton />
|
|
75
|
+
</>
|
|
76
|
+
) : (
|
|
77
|
+
<LoginButton>Sign In</LoginButton>
|
|
78
|
+
)}
|
|
79
|
+
</header>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function MemberArea() {
|
|
84
|
+
return (
|
|
85
|
+
<ProtectedRoute>
|
|
86
|
+
<MemberContent />
|
|
87
|
+
</ProtectedRoute>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 4. Use sections
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { useMySection } from '@axcode/axcloud-sdk'
|
|
96
|
+
|
|
97
|
+
function MySectionPage() {
|
|
98
|
+
const { section, members, isLoading } = useMySection()
|
|
99
|
+
|
|
100
|
+
if (isLoading) return <Spinner />
|
|
101
|
+
if (!section) return <p>You are not assigned to a section</p>
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div>
|
|
105
|
+
<h1>{section.name}</h1>
|
|
106
|
+
<h2>Members ({members.length})</h2>
|
|
107
|
+
<ul>
|
|
108
|
+
{members.map(m => <li key={m.id}>{m.name}</li>)}
|
|
109
|
+
</ul>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 4b. Filter content by section
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useNews, useMySection } from '@axcode/axcloud-sdk'
|
|
119
|
+
|
|
120
|
+
function SectionNews() {
|
|
121
|
+
const { sectionId } = useMySection()
|
|
122
|
+
|
|
123
|
+
// Fetch news for user's section only
|
|
124
|
+
const { data: sectionNews } = useNews({ sectionId })
|
|
125
|
+
|
|
126
|
+
// Or fetch all organization news
|
|
127
|
+
const { data: allNews } = useNews()
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<div>
|
|
131
|
+
<h2>Section News</h2>
|
|
132
|
+
{sectionNews.map(item => <NewsCard key={item.id} item={item} />)}
|
|
133
|
+
|
|
134
|
+
<h2>All Organization News</h2>
|
|
135
|
+
{allNews.map(item => <NewsCard key={item.id} item={item} />)}
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. Fetch organization branding and media
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { useBranding, useLogo, useMedia } from '@axcode/axcloud-sdk'
|
|
145
|
+
|
|
146
|
+
function Header() {
|
|
147
|
+
const { logoUrl, branding, organizationName } = useBranding()
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<header style={{ backgroundColor: branding?.primaryColor }}>
|
|
151
|
+
{logoUrl && <img src={logoUrl} alt={organizationName} />}
|
|
152
|
+
<h1>{organizationName}</h1>
|
|
153
|
+
</header>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Or just the logo
|
|
158
|
+
function Logo() {
|
|
159
|
+
const { logoUrl, isLoading } = useLogo()
|
|
160
|
+
|
|
161
|
+
if (isLoading) return <Skeleton />
|
|
162
|
+
return logoUrl ? <img src={logoUrl} alt="Logo" /> : <DefaultLogo />
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Fetch specific media file
|
|
166
|
+
function HeroImage() {
|
|
167
|
+
const { mediaUrl } = useMedia('hero-banner.jpg')
|
|
168
|
+
return mediaUrl ? <img src={mediaUrl} alt="Hero" /> : null
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> **Note:** Media URLs are signed S3 URLs that expire after 1 hour. The SDK automatically refreshes them before expiry (staleTime: 30 min).
|
|
173
|
+
|
|
174
|
+
### 6. Gate features by module
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { ModuleGate, useOrg } from '@axcode/axcloud-sdk'
|
|
178
|
+
|
|
179
|
+
function Dashboard() {
|
|
180
|
+
const { hasModule } = useOrg()
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
<NewsSection />
|
|
185
|
+
|
|
186
|
+
<ModuleGate module="training" fallback={<UpgradePrompt />}>
|
|
187
|
+
<TrainingSection />
|
|
188
|
+
</ModuleGate>
|
|
189
|
+
|
|
190
|
+
{hasModule('ai') && <AIChatWidget />}
|
|
191
|
+
</div>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## API Reference
|
|
197
|
+
|
|
198
|
+
### Provider
|
|
199
|
+
|
|
200
|
+
| Component | Description |
|
|
201
|
+
|-----------|-------------|
|
|
202
|
+
| `AxCloudProvider` | Main provider that combines Auth0, React Query, and SDK context |
|
|
203
|
+
|
|
204
|
+
### Hooks
|
|
205
|
+
|
|
206
|
+
| Hook | Description |
|
|
207
|
+
|------|-------------|
|
|
208
|
+
| `useAuth()` | Login/logout with Auth0 Universal Login |
|
|
209
|
+
| `useUser()` | Fetch current user with role and permissions |
|
|
210
|
+
| `useOrg()` | Fetch organization info and check modules |
|
|
211
|
+
| `useNews(options?)` | Fetch news articles. Returns public + internal + members content when authenticated. |
|
|
212
|
+
| `useContent(type, options?)` | Fetch any content type. For `pages` and `events`, returns member content when authenticated. |
|
|
213
|
+
| `useMySection()` | Fetch current user's section with members |
|
|
214
|
+
| `useSections()` | Fetch all organization sections |
|
|
215
|
+
| `useBranding()` | Fetch organization branding (logo, colors) |
|
|
216
|
+
| `useLogo()` | Fetch organization logo URL |
|
|
217
|
+
| `useMedia(mediaId)` | Fetch specific media file URL |
|
|
218
|
+
| `useAvatar(userId)` | Fetch user avatar URL |
|
|
219
|
+
| `useMediaList(type)` | List media files |
|
|
220
|
+
|
|
221
|
+
### Content Query Options
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
interface ContentQueryOptions {
|
|
225
|
+
page?: number // Page number (default: 1)
|
|
226
|
+
pageSize?: number // Items per page (default: 20)
|
|
227
|
+
status?: 'draft' | 'published' | 'archived'
|
|
228
|
+
visibility?: 'public' | 'members' | 'section'
|
|
229
|
+
sectionId?: string // Filter by specific section
|
|
230
|
+
search?: string // Search in title/content
|
|
231
|
+
sortBy?: string // Sort field (default: 'createdAt')
|
|
232
|
+
sortOrder?: 'asc' | 'desc'
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Components
|
|
237
|
+
|
|
238
|
+
| Component | Description |
|
|
239
|
+
|-----------|-------------|
|
|
240
|
+
| `<LoginButton>` | Trigger Auth0 login |
|
|
241
|
+
| `<LogoutButton>` | Trigger logout |
|
|
242
|
+
| `<ProtectedRoute>` | Require authentication to view content |
|
|
243
|
+
| `<ModuleGate>` | Conditionally render based on enabled modules |
|
|
244
|
+
|
|
245
|
+
## Advanced Usage
|
|
246
|
+
|
|
247
|
+
### Custom API calls
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { useAuth } from '@axcode/axcloud-sdk'
|
|
251
|
+
|
|
252
|
+
function MyComponent() {
|
|
253
|
+
const { getAccessToken } = useAuth()
|
|
254
|
+
|
|
255
|
+
const fetchCustomData = async () => {
|
|
256
|
+
const token = await getAccessToken()
|
|
257
|
+
const response = await fetch('/api/custom', {
|
|
258
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
259
|
+
})
|
|
260
|
+
return response.json()
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Access the underlying Auth0 client
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
const { getAuth0Client } = useAuth()
|
|
269
|
+
const auth0 = getAuth0Client()
|
|
270
|
+
|
|
271
|
+
// Full Auth0 API available
|
|
272
|
+
await auth0.getAccessTokenSilently()
|
|
273
|
+
await auth0.getIdTokenClaims()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Security Model
|
|
277
|
+
|
|
278
|
+
### API Key vs Authentication Token
|
|
279
|
+
|
|
280
|
+
The SDK uses two types of credentials:
|
|
281
|
+
|
|
282
|
+
1. **API Key** (`apiKey` prop): A *public identifier* for your organization
|
|
283
|
+
- Safe to include in client-side code
|
|
284
|
+
- Used for public content endpoints
|
|
285
|
+
- Backend validates and rate-limits based on API key
|
|
286
|
+
- Without a valid API key, requests return 401
|
|
287
|
+
|
|
288
|
+
2. **Authentication Token** (via Auth0): A *secret* JWT token
|
|
289
|
+
- Automatically managed by the SDK
|
|
290
|
+
- Required for member-only content and user data
|
|
291
|
+
- Never exposed in client code
|
|
292
|
+
|
|
293
|
+
### What the API Key Protects
|
|
294
|
+
|
|
295
|
+
| Without API Key | With API Key | With Auth Token |
|
|
296
|
+
|-----------------|--------------|-----------------|
|
|
297
|
+
| Nothing | Public news/content | Member content |
|
|
298
|
+
| | Organization branding | User profile |
|
|
299
|
+
| | Public media | Linked accounts |
|
|
300
|
+
| | | Admin functions |
|
|
301
|
+
|
|
302
|
+
### Endpoint Security
|
|
303
|
+
|
|
304
|
+
| Endpoint Type | Authentication | Example |
|
|
305
|
+
|--------------|---------------|---------|
|
|
306
|
+
| Public content | API key (x-api-key header) | `/content/public/{orgId}/news` |
|
|
307
|
+
| Member content | JWT Bearer token | `/content/members/{orgId}/news` |
|
|
308
|
+
| User data | JWT Bearer token | `/api/users/me` |
|
|
309
|
+
| Admin | JWT Bearer token + role | `/api/admin/forms/submissions` |
|
|
310
|
+
|
|
311
|
+
## Error Handling
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import { useNews, isAxCloudError } from '@axcode/axcloud-sdk'
|
|
315
|
+
|
|
316
|
+
function NewsPage() {
|
|
317
|
+
const { data, error, isError } = useNews()
|
|
318
|
+
|
|
319
|
+
if (isError) {
|
|
320
|
+
if (isAxCloudError(error)) {
|
|
321
|
+
switch (error.code) {
|
|
322
|
+
case 'UNAUTHORIZED':
|
|
323
|
+
return <LoginPrompt />
|
|
324
|
+
case 'NOT_FOUND':
|
|
325
|
+
return <NotFound />
|
|
326
|
+
default:
|
|
327
|
+
return <Error message={error.message} />
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return <Error message="Something went wrong" />
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return <NewsList items={data} />
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
UNLICENSED - Internal use only. Contact Axcode AB for access.
|