@rezamirzapour/pod-sdk 1.0.5 → 1.0.8
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 +364 -45
- package/dist/index.d.mts +1412 -15
- package/dist/index.d.ts +1412 -15
- package/dist/index.js +1646 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1634 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @rezamirzapour/pod-sdk
|
|
2
2
|
|
|
3
|
-
> Unified, type-safe, enterprise-grade SDK for **POD Platform** microservices (CMS Content, CMS Product, SSO, CustomPost, Podspace, Podform, Notification, Social, IUMS), engineered specifically for **Next.js App Router**, **Server Components**, and **Server Actions**.
|
|
3
|
+
> Unified, type-safe, enterprise-grade SDK for **POD Platform** microservices (CMS Content, CMS Product, SSO, Captcha, CustomPost, Podspace, Podform, Notification, Social, IUMS), engineered specifically for **Next.js App Router**, **Server Components**, and **Server Actions**.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@rezamirzapour/pod-sdk)
|
|
6
6
|
[](LICENSE)
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Features
|
|
12
|
-
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Captcha Protection**: Generate configurable SVG/PNG challenges and validate user answers with a single, typed service.
|
|
13
14
|
- 🌐 **Unified Multi-Service Architecture**: Single entry-point managing official POD microservices with official endpoints and configurations.
|
|
14
15
|
- ⚡ **Powered by `@rezamirzapour/http`**: Automatic exponential backoff retries, Next.js incremental static regeneration (ISR) caching (`next.revalidate`, tags), and isomorphic execution.
|
|
15
16
|
- 🧬 **First-Class TypeScript Generics (`<T, P>`)**: CMS Content, CMS Product, and CustomPost endpoints allow you to define and receive strongly typed metadata structures according to your business schemas.
|
|
@@ -20,18 +21,19 @@
|
|
|
20
21
|
- 🚀 **Next.js App Router Ready**: Seamless support for Server Components, Server Actions, Route Handlers, and Client Components.
|
|
21
22
|
- 🌳 **Treeshakeable & Dual ESM/CJS**: Ships clean ES modules (`.mjs`) and CommonJS (`.js`) with 100% complete TypeScript declarations (`.d.ts`).
|
|
22
23
|
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Microservices Included
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Microservices Included
|
|
26
27
|
|
|
27
28
|
| Service | Accessor | Swagger Tag | Description |
|
|
28
29
|
| :--- | :--- | :--- | :--- |
|
|
29
30
|
| **CMS Content** | `sdk.cms` | `content` | Fetch, publish, edit, draft, archive, and categorize CMS articles with generic metadata `<T, P>`. |
|
|
30
31
|
| **CMS Product** | `sdk.product` / `sdk.cms.products` | `product` | Product catalog, price & discount range filters, barcode lookup, batch publish, and AI search. |
|
|
31
32
|
| **CMS Tags & Tree** | `sdk.tags` / `sdk.cms.tags` | `tags` | Manage tag categories and hierarchical tag trees (nodes, parent updates, ancestors, codes). |
|
|
32
|
-
| **CustomPost** | `sdk.customPost` | - | Search timeline by metadata `<T>`, custom post CRUD, and high-level typed repository. |
|
|
33
|
-
| **SSO** | `sdk.sso` | - | OAuth2 handshake, OTP dispatch with digital RSA signature, OTP verify, token generation, and user profile. |
|
|
34
|
-
| **
|
|
33
|
+
| **CustomPost** | `sdk.customPost` | - | Search timeline by metadata `<T>`, custom post CRUD, and high-level typed repository. |
|
|
34
|
+
| **SSO** | `sdk.sso` | - | OAuth2 handshake, OTP dispatch with digital RSA signature, OTP verify, token generation, and user profile. |
|
|
35
|
+
| **Captcha** | `sdk.captcha` | `captcha` | Generate SVG/PNG captcha images with configurable options and verify submitted values. |
|
|
36
|
+
| **Podspace** | `sdk.podspace` | `public-apis` | Complete Cloud Storage: file/image uploads, resumable chunked uploads, folders, versions, share links, workspaces, trash, tags, & metadata. |
|
|
35
37
|
| **Podform** | `sdk.podform` | - | Survey & form response submission, question/form structure retrieval. |
|
|
36
38
|
| **Notification** | `sdk.notification` | - | SMS delivery and bulk messaging with tracking. |
|
|
37
39
|
| **Social** | `sdk.social` | - | User comments, reactions (likes/dislikes), rates, and social post interactions. |
|
|
@@ -58,9 +60,10 @@ NEXT_PUBLIC_API_POD_URL=https://api.pod.ir
|
|
|
58
60
|
NEXT_PUBLIC_API_POD_SANDBOX_URL=https://api.sandpod.ir
|
|
59
61
|
NEXT_PUBLIC_CMS_URL=https://api.pod.ir/srv/cms-server
|
|
60
62
|
NEXT_PUBLIC_IUMS_URL=https://indra.khatam.ac.ir/srv
|
|
61
|
-
NEXT_PUBLIC_NOTIFICATION_URL=https://api.pod.ir/srv/notification
|
|
62
|
-
NEXT_PUBLIC_PODREPORT_URL=https://reporting.pod.ir
|
|
63
|
-
NEXT_PUBLIC_PODSPACE_URL=https://podspace.pod.ir
|
|
63
|
+
NEXT_PUBLIC_NOTIFICATION_URL=https://api.pod.ir/srv/notification
|
|
64
|
+
NEXT_PUBLIC_PODREPORT_URL=https://reporting.pod.ir
|
|
65
|
+
NEXT_PUBLIC_PODSPACE_URL=https://podspace.pod.ir
|
|
66
|
+
NEXT_PUBLIC_PODFORM_URL=https://podform.pod.ir
|
|
64
67
|
|
|
65
68
|
# Sandbox Endpoints (Development & Staging)
|
|
66
69
|
NEXT_PUBLIC_SANDBOX_CMS_URL=http://api.sandpod.ir/srv/cms-sandbox
|
|
@@ -69,8 +72,10 @@ NEXT_PUBLIC_SANDBOX_PODSPACE_URL=http://podspace.sandpod.ir
|
|
|
69
72
|
NEXT_PUBLIC_SANDBOX_STREAM_URL=https://sandbox-offline-stream.sandpod.ir
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
> [!TIP]
|
|
73
|
-
> If you have defined these `NEXT_PUBLIC_*` variables in your `.env` or `.env.local`, `@rezamirzapour/pod-sdk` will automatically detect and apply them without any manual URL mapping.
|
|
75
|
+
> [!TIP]
|
|
76
|
+
> If you have defined these `NEXT_PUBLIC_*` variables in your `.env` or `.env.local`, `@rezamirzapour/pod-sdk` will automatically detect and apply them without any manual URL mapping.
|
|
77
|
+
|
|
78
|
+
Captcha requests use the resolved `apiPod` URL (`NEXT_PUBLIC_API_POD_URL` in production or `NEXT_PUBLIC_API_POD_SANDBOX_URL` when `sandbox: true`); there is no separate captcha URL setting.
|
|
74
79
|
|
|
75
80
|
---
|
|
76
81
|
|
|
@@ -604,9 +609,259 @@ export async function getPublishedPosts() {
|
|
|
604
609
|
|
|
605
610
|
---
|
|
606
611
|
|
|
607
|
-
##
|
|
612
|
+
## Deep Dive 5: Podspace Cloud Storage (Official Swagger API)
|
|
613
|
+
|
|
614
|
+
The **Podspace** module provides complete enterprise integration with the official POD Podspace Cloud Storage service ([Swagger documentation: `podspace.sandpod.ir/api/docs`](http://podspace.sandpod.ir/api/docs)). It supports standard and resumable uploads, public/private files, dynamic CDN image transformations, folder hierarchy, file versioning, public share links, trash recovery, team workspaces, user groups, and custom metadata.
|
|
615
|
+
|
|
616
|
+
### Sub-Services Architecture
|
|
617
|
+
|
|
618
|
+
All Podspace operations are accessible via specialized, modular sub-services under `podSdk.podspace`:
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
podSdk.podspace.files // File details, rename, move, copy, search, versions, zip
|
|
622
|
+
podSdk.podspace.folders // Folder creation, nested paths, children, actions
|
|
623
|
+
podSdk.podspace.resumable // Chunked/large file upload (tus protocol: create, append, finalize)
|
|
624
|
+
podSdk.podspace.links // Public/password-protected shareable download links
|
|
625
|
+
podSdk.podspace.shares // Sharing files/folders with specific users & permissions
|
|
626
|
+
podSdk.podspace.trash // Recycle bin: list, restore, empty, auto-cleanup
|
|
627
|
+
podSdk.podspace.bookmarks // Starred / favorite files and folders
|
|
628
|
+
podSdk.podspace.tags // File & folder tagging
|
|
629
|
+
podSdk.podspace.metadata // Custom key-value metadata & descriptions
|
|
630
|
+
podSdk.podspace.userGroups // Group storage, member uploads & quota usage
|
|
631
|
+
podSdk.podspace.workspaces // Team workspaces & member roles
|
|
632
|
+
podSdk.podspace.me // User storage usage, plans, personal/chat folders
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
> [!TIP]
|
|
636
|
+
> Commonly used methods (`uploadFile`, `downloadFile`, `getFileUrl`, `getImageUrl`, `getThumbnailUrl`, `createFolder`, `searchFiles`) are also directly available on `podSdk.podspace` for maximum developer convenience.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
### Key Usage Examples
|
|
641
|
+
|
|
642
|
+
#### 1. File Upload & CDN URLs (100% Backward Compatible)
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
// app/actions/upload.ts
|
|
646
|
+
'use server';
|
|
647
|
+
|
|
648
|
+
import { podSdk } from '@/lib/pod';
|
|
649
|
+
|
|
650
|
+
export async function uploadDocument(formData: FormData) {
|
|
651
|
+
// Supports both positional args (formData, path, isPublic) and options object
|
|
652
|
+
const res = await podSdk.podspace.uploadFile(formData, {
|
|
653
|
+
path: '/documents/invoices',
|
|
654
|
+
isPublic: true,
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
if (res.hasError) {
|
|
658
|
+
throw new Error(res.message || 'Upload failed');
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const file = res.result;
|
|
662
|
+
|
|
663
|
+
// Resolve direct download URL
|
|
664
|
+
const downloadUrl = podSdk.podspace.getFileUrl(file.hash, true);
|
|
665
|
+
|
|
666
|
+
return {
|
|
667
|
+
hash: file.hash,
|
|
668
|
+
name: file.name,
|
|
669
|
+
size: file.size,
|
|
670
|
+
url: downloadUrl,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
#### 2. Image & Thumbnail CDN URL Helper
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
// Helper functions generate instant CDN URLs without network calls:
|
|
679
|
+
const fullImageUrl = podSdk.podspace.getImageUrl(imageHash, {
|
|
680
|
+
isPublic: true,
|
|
681
|
+
size: '1200x800',
|
|
682
|
+
quality: 85,
|
|
683
|
+
crop: true,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
const thumbUrl = podSdk.podspace.getThumbnailUrl(imageHash, {
|
|
687
|
+
isPublic: true,
|
|
688
|
+
size: '150x150',
|
|
689
|
+
});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
#### 3. Base64 Image Upload
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
// Upload image directly from a Base64 data URL
|
|
696
|
+
const uploadRes = await podSdk.podspace.uploadImageBase64({
|
|
697
|
+
base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAE...',
|
|
698
|
+
filename: 'avatar.png',
|
|
699
|
+
path: '/avatars',
|
|
700
|
+
isPublic: true,
|
|
701
|
+
});
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
#### 4. Resumable Chunked Upload (Large Files)
|
|
705
|
+
|
|
706
|
+
For reliable multi-gigabyte uploads with pause/resume support:
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
// Step 1: Initialize resumable session
|
|
710
|
+
const session = await podSdk.podspace.resumable.create({
|
|
711
|
+
filename: 'presentation.mp4',
|
|
712
|
+
fileSize: totalBytes,
|
|
713
|
+
path: '/videos',
|
|
714
|
+
isPublic: false,
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
const uploadUrl = session.uploadUrl;
|
|
718
|
+
|
|
719
|
+
// Step 2: Check current server offset if resuming
|
|
720
|
+
const offset = await podSdk.podspace.resumable.getStatus(uploadUrl);
|
|
721
|
+
|
|
722
|
+
// Step 3: Append chunks
|
|
723
|
+
await podSdk.podspace.resumable.append({
|
|
724
|
+
uploadUrl,
|
|
725
|
+
offset,
|
|
726
|
+
chunk: chunkBuffer,
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// Step 4: Finalize when all bytes are uploaded
|
|
730
|
+
const completedFile = await podSdk.podspace.resumable.finalizeUpload(uploadUrl);
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
#### 5. Folder Hierarchy & Listing
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
// Create a folder
|
|
737
|
+
await podSdk.podspace.folders.createFolder({
|
|
738
|
+
name: 'Reports-2026',
|
|
739
|
+
path: '/finance',
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// Recursively create nested directory paths
|
|
743
|
+
await podSdk.podspace.folders.createDirectories('/finance/2026/Q1/receipts');
|
|
744
|
+
|
|
745
|
+
// List folder contents with pagination
|
|
746
|
+
const contents = await podSdk.podspace.folders.getFolderChildren({
|
|
747
|
+
path: '/finance/Reports-2026',
|
|
748
|
+
offset: 0,
|
|
749
|
+
size: 50,
|
|
750
|
+
});
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
#### 6. Shareable Public / Password-Protected Links
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
// Create an expiring, password-protected download link
|
|
757
|
+
const linkRes = await podSdk.podspace.links.createLink(fileHash, {
|
|
758
|
+
type: 'DOWNLOAD',
|
|
759
|
+
password: 'SecurePassword123!',
|
|
760
|
+
expiresAt: Date.now() + 7 * 24 * 3600 * 1000, // 7 days
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// Share with a specific user
|
|
764
|
+
await podSdk.podspace.shares.shareWithUser(fileHash, {
|
|
765
|
+
username: 'john_doe',
|
|
766
|
+
permission: 'READ',
|
|
767
|
+
});
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### 7. File Versions & Rollback
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
// List previous versions of a file
|
|
774
|
+
const versions = await podSdk.podspace.files.getFileVersions(fileHash);
|
|
775
|
+
|
|
776
|
+
// Rollback to a specific historical version
|
|
777
|
+
await podSdk.podspace.files.rollbackFileVersion(fileHash, versionId);
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
#### 8. Trash & Recycling
|
|
781
|
+
|
|
782
|
+
```typescript
|
|
783
|
+
// Move file or folder to trash
|
|
784
|
+
await podSdk.podspace.files.trashEntity(fileHash);
|
|
785
|
+
|
|
786
|
+
// List recycle bin
|
|
787
|
+
const trashItems = await podSdk.podspace.trash.getTrashList({ size: 20 });
|
|
788
|
+
|
|
789
|
+
// Restore or permanently delete
|
|
790
|
+
await podSdk.podspace.trash.restore(fileHash);
|
|
791
|
+
await podSdk.podspace.trash.deletePermanently(fileHash);
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
### Podspace Methods Reference
|
|
797
|
+
|
|
798
|
+
| Sub-Service | Method | HTTP | Path | Description |
|
|
799
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
800
|
+
| **Upload / Download** | `uploadFile(formData, params)` | `POST` | `/api/files` | Upload single file with metadata. |
|
|
801
|
+
| | `uploadMultipleFiles(formData, params)` | `POST` | `/api/files/batch` | Upload multiple files simultaneously. |
|
|
802
|
+
| | `uploadImageBase64(params)` | `POST` | `/api/images/base64` | Upload base64 encoded image. |
|
|
803
|
+
| | `replaceFile(hash, formData)` | `PUT` | `/api/files/{hash}` | Overwrite existing file content. |
|
|
804
|
+
| | `uploadByLink(params)` | `POST` | `/api/files/link` | Download remote file into Podspace. |
|
|
805
|
+
| | `downloadFile(hash, opts)` | `GET` | `/api/files/{hash}` | Download raw binary file. |
|
|
806
|
+
| | `downloadImage(hash, opts)` | `GET` | `/api/images/{hash}` | Download transformed image. |
|
|
807
|
+
| | `downloadThumbnail(hash, opts)` | `GET` | `/api/images/thumbnails/{hash}` | Download image thumbnail. |
|
|
808
|
+
| | `downloadFilesAsZip(hashes)` | `POST` | `/api/files/zip` | Batch download files as a ZIP archive. |
|
|
809
|
+
| | `getFileUrl(hash, isPublic)` | - | Client Helper | Generates direct file CDN link. |
|
|
810
|
+
| | `getImageUrl(hash, opts)` | - | Client Helper | Generates transformed image CDN link. |
|
|
811
|
+
| | `getThumbnailUrl(hash, opts)` | - | Client Helper | Generates thumbnail CDN link. |
|
|
812
|
+
| **Files** | `getFileDetail(hash)` | `GET` | `/api/files/{hash}/metadata` | Fetch full metadata for file. |
|
|
813
|
+
| | `getFileDetailByPath(path)` | `GET` | `/api/files/path` | Fetch metadata by absolute path. |
|
|
814
|
+
| | `checkEntityExist(hash)` | `GET` | `/api/entities/{hash}/exist` | Check existence of file/folder. |
|
|
815
|
+
| | `renameEntity(hash, newName)` | `PUT` | `/api/entities/{hash}/rename` | Rename file or folder. |
|
|
816
|
+
| | `moveEntity(hash, targetPath)` | `PUT` | `/api/entities/{hash}/move` | Move file or folder. |
|
|
817
|
+
| | `copyEntity(hash, targetPath)` | `POST` | `/api/entities/{hash}/copy` | Duplicate file or folder. |
|
|
818
|
+
| | `trashEntity(hash)` | `DELETE` | `/api/entities/{hash}` | Move file/folder to trash. |
|
|
819
|
+
| | `searchFiles(params)` | `GET` | `/api/files/search` | Search files by name, type, date, or tags. |
|
|
820
|
+
| | `getFileVersions(hash)` | `GET` | `/api/files/{hash}/versions` | List all historical versions. |
|
|
821
|
+
| | `rollbackFileVersion(hash, id)` | `POST` | `/api/files/{hash}/versions/{id}/rollback` | Rollback to specific version. |
|
|
822
|
+
| | `compressToZip(params)` | `POST` | `/api/files/compress` | Compress files into a ZIP archive. |
|
|
823
|
+
| | `extractZip(params)` | `POST` | `/api/files/extract` | Extract ZIP archive in Podspace. |
|
|
824
|
+
| **Folders** | `createFolder(params)` | `POST` | `/api/folders` | Create a new folder. |
|
|
825
|
+
| | `createDirectories(path)` | `POST` | `/api/folders/directories` | Create nested directory tree. |
|
|
826
|
+
| | `getFolderChildren(params)` | `GET` | `/api/folders/{hash}/children` | List folder contents. |
|
|
827
|
+
| | `getRecentFolders(params)` | `GET` | `/api/folders/recent` | List recently accessed folders. |
|
|
828
|
+
| **Resumable** | `create(params)` | `POST` | `/api/files/resumable` | Initialize resumable upload. |
|
|
829
|
+
| | `append(params)` | `PATCH` | `{uploadUrl}` | Upload chunk to offset. |
|
|
830
|
+
| | `getStatus(uploadUrl)` | `HEAD` | `{uploadUrl}` | Get current uploaded byte offset. |
|
|
831
|
+
| | `finalizeUpload(uploadUrl)` | `POST` | `{uploadUrl}/finalize` | Complete resumable upload. |
|
|
832
|
+
| **Links** | `createLink(hash, params)` | `POST` | `/api/entities/{hash}/links` | Create shareable download link. |
|
|
833
|
+
| | `getUserLinks(params)` | `GET` | `/api/links` | List all created links. |
|
|
834
|
+
| | `revokeLink(linkHash)` | `DELETE` | `/api/links/{linkHash}` | Deactivate share link. |
|
|
835
|
+
| **Shares** | `shareWithUser(hash, params)` | `POST` | `/api/entities/{hash}/shares` | Share with another user. |
|
|
836
|
+
| | `makePublic(hash, isPublic)` | `PUT` | `/api/entities/{hash}/public` | Toggle public/private access. |
|
|
837
|
+
| | `getSharedWithMe(params)` | `GET` | `/api/shares/shared-with-me` | List items shared with user. |
|
|
838
|
+
| **Trash** | `getTrashList(params)` | `GET` | `/api/trash` | List items in recycle bin. |
|
|
839
|
+
| | `restore(hash)` | `POST` | `/api/trash/{hash}/restore` | Restore file/folder. |
|
|
840
|
+
| | `deletePermanently(hash)` | `DELETE` | `/api/trash/{hash}` | Permanently destroy file/folder. |
|
|
841
|
+
| | `emptyTrash()` | `DELETE` | `/api/trash` | Empty entire recycle bin. |
|
|
842
|
+
| **Bookmarks** | `getBookmarks(params)` | `GET` | `/api/bookmarks` | List starred files/folders. |
|
|
843
|
+
| | `addBookmark(hash)` | `POST` | `/api/bookmarks/{hash}` | Star a file or folder. |
|
|
844
|
+
| | `removeBookmark(hash)` | `DELETE` | `/api/bookmarks/{hash}` | Unstar a file or folder. |
|
|
845
|
+
| **Tags** | `addTags(hash, tags)` | `POST` | `/api/entities/{hash}/tags` | Add tags to entity. |
|
|
846
|
+
| | `getEntityTags(hash)` | `GET` | `/api/entities/{hash}/tags` | List entity tags. |
|
|
847
|
+
| | `getEntitiesByTag(tag)` | `GET` | `/api/tags/{tag}/entities` | Search entities by tag. |
|
|
848
|
+
| **Metadata** | `setMetadata(hash, metadata)` | `PUT` | `/api/entities/{hash}/metadata` | Set custom key-value metadata. |
|
|
849
|
+
| | `setDescription(hash, desc)` | `PUT` | `/api/entities/{hash}/description` | Set entity description. |
|
|
850
|
+
| **User Groups** | `createUserGroup(params)` | `POST` | `/api/user-groups` | Create user group. |
|
|
851
|
+
| | `uploadToUserGroup(group, fd)`| `POST` | `/api/user-groups/{group}/files` | Upload file to user group. |
|
|
852
|
+
| | `getUserGroupUsage(group)` | `GET` | `/api/user-groups/{group}/usage` | Get storage quota & usage. |
|
|
853
|
+
| **Workspaces** | `createWorkspace(params)` | `POST` | `/api/workspaces` | Create team workspace. |
|
|
854
|
+
| | `getWorkspaceMembers(id)` | `GET` | `/api/workspaces/{id}/members` | List workspace members. |
|
|
855
|
+
| **Me** | `getUser()` | `GET` | `/api/users/me` | Current user profile. |
|
|
856
|
+
| | `getUserUsageReport()` | `GET` | `/api/users/me/usage` | Total storage usage report. |
|
|
857
|
+
| | `getPersonalFolder()` | `GET` | `/api/users/me/personal-folder` | Get or create root personal folder. |
|
|
858
|
+
| | `getOrCreateChatFolder()` | `GET` | `/api/users/me/chat-folder` | Get or create chat attachments folder. |
|
|
608
859
|
|
|
609
|
-
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
## Other POD Microservices
|
|
863
|
+
|
|
864
|
+
### 1. POD SSO (OTP Handshake with Web Crypto RSA Signature)
|
|
610
865
|
|
|
611
866
|
```typescript
|
|
612
867
|
// app/actions/auth.ts
|
|
@@ -644,33 +899,97 @@ export async function verifyOtp(authorization: string, phoneNumber: string, code
|
|
|
644
899
|
export async function getProfile(accessToken: string) {
|
|
645
900
|
const profile = await podSdk.sso.getUserProfile(accessToken);
|
|
646
901
|
return profile.result;
|
|
647
|
-
}
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
---
|
|
651
|
-
|
|
652
|
-
###
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
---
|
|
906
|
+
|
|
907
|
+
### 2. Captcha (Generate and Verify)
|
|
908
|
+
|
|
909
|
+
The captcha service exposes the POD captcha API through `podSdk.captcha`. It creates a short-lived challenge image and checks the value entered by the user. Both calls return the SDK's standard `PodResponse<T>` shape (`hasError`, `result`, `message`, and `errorCode`).
|
|
910
|
+
|
|
911
|
+
`Client-Id` and `Access-Token` headers are added automatically from `createPodSdk({ clientId, apiToken })`. Keep these credentials on the server and call captcha methods from a Server Action or Route Handler; do not expose the SDK token in a Client Component.
|
|
912
|
+
|
|
913
|
+
#### Generate a captcha
|
|
914
|
+
|
|
915
|
+
```typescript
|
|
916
|
+
// app/actions/captcha.ts
|
|
917
|
+
'use server';
|
|
918
|
+
|
|
919
|
+
import { podSdk } from '@/lib/pod';
|
|
920
|
+
|
|
921
|
+
export async function createCaptcha() {
|
|
922
|
+
const response = await podSdk.captcha.get({
|
|
923
|
+
width: 280,
|
|
924
|
+
height: 96,
|
|
925
|
+
length: 6,
|
|
926
|
+
type: 'alphanumeric',
|
|
927
|
+
format: 'png',
|
|
928
|
+
noise: true,
|
|
929
|
+
bgColor: '#ffffff',
|
|
930
|
+
fontColor: '#1f2937',
|
|
931
|
+
fontSize: 36,
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
if (response.hasError || !response.result) {
|
|
935
|
+
throw new Error(response.message || 'Captcha generation failed');
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// `image` is a data URI or SVG string, depending on `format`.
|
|
939
|
+
return {
|
|
940
|
+
captchaId: response.result.captchaId,
|
|
941
|
+
image: response.result.image,
|
|
942
|
+
mimeType: response.result.mimeType,
|
|
943
|
+
expiresAt: response.result.expiresAt,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
#### Verify the submitted value
|
|
949
|
+
|
|
950
|
+
```typescript
|
|
951
|
+
import { podSdk } from '@/lib/pod';
|
|
952
|
+
|
|
953
|
+
export async function verifyCaptcha(captchaId: string, captchaValue: string) {
|
|
954
|
+
const response = await podSdk.captcha.check(captchaId, { captchaValue });
|
|
955
|
+
|
|
956
|
+
if (response.hasError) {
|
|
957
|
+
return { valid: false, message: response.message || 'Captcha verification failed' };
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
return {
|
|
961
|
+
valid: response.result?.valid === true,
|
|
962
|
+
message: response.result?.message,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
#### Captcha methods
|
|
968
|
+
|
|
969
|
+
| Method | HTTP | Path | Description |
|
|
970
|
+
| :--- | :--- | :--- | :--- |
|
|
971
|
+
| `get(options?)` | `POST` | `/api/core/captchas/generate` | Generate a captcha image. Options are sent as query parameters. |
|
|
972
|
+
| `check(captchaId, { captchaValue })` | `POST` | `/api/core/captchas/check/{captchaId}` | Verify the value entered for a generated captcha. |
|
|
973
|
+
|
|
974
|
+
`GenerateCaptchaOptions` supports the following fields:
|
|
975
|
+
|
|
976
|
+
| Option | Type | Default | Description |
|
|
977
|
+
| :--- | :--- | :--- | :--- |
|
|
978
|
+
| `width` | `number` | `200` | Image width in pixels. |
|
|
979
|
+
| `height` | `number` | `80` | Image height in pixels. |
|
|
980
|
+
| `length` | `number` | `5` | Number of characters in the challenge. |
|
|
981
|
+
| `type` | `string` | Server default | Challenge type, such as `numeric` or `alphanumeric`. |
|
|
982
|
+
| `format` | `'svg' \| 'png'` | `'svg'` | Image output format. |
|
|
983
|
+
| `noise` | `boolean` | `true` | Add visual noise to the image. |
|
|
984
|
+
| `bgColor` | `string` | Server default | Background color in hex format. |
|
|
985
|
+
| `fontColor` | `string` | Server default | Character color in hex format. |
|
|
986
|
+
| `fontSize` | `number` | Server default | Character font size. |
|
|
987
|
+
|
|
988
|
+
The generated response contains `captchaId`, `image`, `expiresAt`, and `mimeType`; `image` is returned as a data URI or SVG string. Always treat the challenge as temporary and request a new one after expiration or a failed attempt, according to your server-side policy.
|
|
989
|
+
|
|
990
|
+
---
|
|
991
|
+
|
|
992
|
+
### 3. Notification (SMS Delivery)
|
|
674
993
|
|
|
675
994
|
```typescript
|
|
676
995
|
// app/actions/notify.ts
|
|
@@ -688,7 +1007,7 @@ export async function sendSms(phoneNumber: string, text: string) {
|
|
|
688
1007
|
|
|
689
1008
|
---
|
|
690
1009
|
|
|
691
|
-
###
|
|
1010
|
+
### 4. Social (Comments & Likes)
|
|
692
1011
|
|
|
693
1012
|
```typescript
|
|
694
1013
|
// app/actions/social.ts
|
|
@@ -710,7 +1029,7 @@ export async function likePost(postId: number) {
|
|
|
710
1029
|
|
|
711
1030
|
---
|
|
712
1031
|
|
|
713
|
-
###
|
|
1032
|
+
### 5. IUMS (Identity & University Management Service)
|
|
714
1033
|
|
|
715
1034
|
```typescript
|
|
716
1035
|
// app/actions/iums.ts
|