@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 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.