Skip to content
 
 

Latest commit

 

History

1,474 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Web DC API 开发指南

web-dc-api 是一个革命性的去中心化 Web 开发 SDK,让您无需任何服务器即可构建完整的互联网应用。

核心特性

🚀 零服务器架构 - 完全去中心化,无需部署后端服务器、数据库或存储服务
🔐 自动统一登录 - 内置钱包登录和身份认证,一次接入,全网通用
💾 分布式存储 - 用户数据库、键值存储、文件系统全部去中心化
💬 内置社交功能 - 评论系统、消息通信,开箱即用
🤖 AI 集成 - 原生支持 AI 代理和 MCP Server 调用
🔧 免运维 - 无需服务器运维、监控、扩容,专注业务逻辑开发
🌉 Web2 到 Web3 的桥梁 - 让传统 Web 开发者无缝过渡到去中心化世界

适用场景

  • 社交应用:博客、论坛、社区、内容平台
  • 协作工具:笔记、文档、任务管理
  • 数据应用:个人数据管理、隐私保护应用
  • AI 应用:集成 AI 能力的去中心化应用
  • 任何需要后端的 Web 应用:DC API 就是您的后端

快速开始 (Quick Start)

以下是一个完整的调用示例,展示了从引入 SDK 到登录、初始化数据库以及使用功能的完整流程。

1. 基础调用 (Raw SDK)

这是最基础的使用方式,直接引入 DC 类进行操作。

import { DC } from 'web-dc-api';

// 1. 配置并初始化 DC 实例
const dc = new DC({
  wssUrl: "wss://dcchain.baybird.cn",
  backWssUrl: "wss://dcchain.baybird.cn",
  appInfo: {
    appId: "your-app-id",
    appName: "Your App Name",
    appVersion: "1.0.0",
    appIcon: "",
    appDesc: ""
  }
});

async function main() {
  // 2. 初始化 DC 实例 (连接节点、启动服务)
  const initSuccess = await dc.init();
  if (!initSuccess) {
    console.warn("DC 初始化失败");
    return;
  }
  console.log("DC 初始化成功");

  // 3. 用户登录 (钱包登录)
  const [accountInfo, loginError] = await dc.auth.accountLoginWithWallet();
  
  if (loginError || !accountInfo) {
    console.warn("登录失败:", loginError);
    return;
  }
  console.log("登录成功, 用户公钥:", dc.publicKey?.string());

  // 4. 初始化用户数据库
  const collections = [{
    name: 'user_notes',
    schema: {
      type: 'object',
      properties: {
        _id: { type: 'string' },
        content: { type: 'string' },
        create_time: { type: 'number' },
        _mod: { type: 'number' }
      },
      required: ["_id"]
    }
  }];
  
  const [, dbError] = await dc.initUserDB(collections, "1.0.0", false);
  if (dbError) {
    console.warn("数据库初始化失败:", dbError);
    return;
  }

  // 5. 使用功能 (例如写入数据)
  const [id, createErr] = await dc.db.create(
    dc.dbThreadId, 
    'user_notes', 
    JSON.stringify({ 
      content: "Hello DC World!", 
      create_time: Date.now(),
      _mod: Date.now() 
    })
  );
  
  if (!createErr) {
    console.log("数据写入成功, ID:", id);
  }
}

main();

2. React 集成 (DCContext)

在 React 项目中,建议使用 Context 来管理 DC 实例。你可以将以下代码保存为 src/contexts/DCContext.tsx

import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
import { DC } from 'web-dc-api';
import { AccountInfo } from 'web-dc-api/lib/common/types/types';

interface DCStatus {
  accountInfo: AccountInfo | null;
  isReady: boolean;
}

interface DCContextType {
  getDC: () => Promise<DC | null>;
  saveAccountInfo: (info: AccountInfo) => void;
  dcStatus: DCStatus;
}

const DCContext = createContext<DCContextType | null>(null);

export const DCProvider = ({ children }: { children: ReactNode }) => {
  const [dcInstance, setDcInstance] = useState<DC | null>(null);
  const [dcStatus, setDcStatus] = useState<DCStatus>({
    accountInfo: null,
    isReady: false
  });

  const getDC = useCallback(async () => {
    if (dcInstance) return dcInstance;

    try {
      // 初始化配置
      const config = {
        wssUrl: "wss://dcchain.baybird.cn",
        backWssUrl: "wss://dcchain.baybird.cn",
        appInfo: {
          appId: "your-app-id",
          appName: "Your App Name",
          appVersion: "1.0.0",
          appIcon: "",
          appDesc: ""
        }
      };

      const dc = new DC(config);
      
      // 初始化 DC 实例
      const initSuccess = await dc.init();
      if (!initSuccess) {
        console.warn("DC 初始化失败");
        return null;
      }
      
      setDcInstance(dc);
      return dc;
    } catch (error) {
      console.warn("DC init failed:", error);
      return null;
    }
  }, [dcInstance]);

  const saveAccountInfo = useCallback((info: AccountInfo) => {
    setDcStatus(prev => ({
      ...prev,
      accountInfo: info,
      isReady: true
    }));
  }, []);

  return (
    <DCContext.Provider value={{ getDC, saveAccountInfo, dcStatus }}>
      {children}
    </DCContext.Provider>
  );
};

export const useDC = () => {
  const context = useContext(DCContext);
  if (!context) {
    throw new Error('useDC must be used within a DCProvider');
  }
  return context;
};

3. React 组件中使用示例

import React from 'react';
import { useDC } from "./contexts/DCContext";

const App = () => {
  const { getDC, saveAccountInfo, dcStatus } = useDC();

  const handleLogin = async () => {
    // getDC() 会自动处理 dc.init()
    const dc = await getDC();
    if (!dc) return;

    const [accountInfo, err] = await dc.auth.accountLoginWithWallet();
    if (accountInfo) {
      // 初始化DB...
      // await dc.initUserDB(...)
      
      saveAccountInfo(accountInfo);
    }
  };

  return (
    <button onClick={handleLogin}>Login</button>
  );
};

1. 初始化与认证 (Auth)

DCAPI 使用示例

import { useDC } from "/src/contexts/DCContext.tsx";
const {getDC} = useDC();
const dc = await getDC();
if (!dc) {
  console.warn("未获取到有效的DC实例或认证信息");
  return;
}

用户登录验证

// 用户登录,登录成功返回的accountInfo信息中包括:1.nftAccount: string类型,用户账号,用于界面显示 2.appAccount: Uint8Array(32)类型,应用账号,用于数据存储的用户标识
// 这条语句必须放在符合语法的地方
const { dcStatus, getDC, saveAccountInfo } = useDC();
const dc = await getDC();
const [accountInfo, loginError] = await dc.auth.accountLoginWithWallet(); 
const publicKeyStr = dc.publicKey.string(); // 获取用户公钥
// 用户登录成功后,必须马上初始化用户数据库
const [,err] = await dc.initUserDB(dbCollections, appVersion, false);
// 用户登录成功后,在dc.initUserDB调用之后,保存用户信息
saveAccountInfo(accountInfo);

// 全局其他地方可以通过以下方式判断是否登录成功
if (dcStatus.accountInfo && dcStatus.accountInfo.appAccount) {
  //已经登录成功,dcStatus.accountInfo.nftAccount 表示登录的用户名

}

2. 文件模块 (File)

// 上传文件并跟踪进度,进度回调参数:status表示状态: 0=成功,1=加密中,2=上传中,3=出错,4=异常,size表示已上传的字节数
const [cid, error] = await dc.file.addFile(
  file, //File对象
  '加密密钥', // 需要文件加密时,使用对32字节长度字符进行base32加密后的字符串; 不需要文件加密时, 使用空字符串
  (status, size) => console.log(`上传状态:${status} 已上传, ${size} 字节`)
);

// 通过CID等待下载完整文件再返回
const [fileContent, error] = await dc.file.getFile('file-cid', '解密密钥'); // 解密密钥为文件上传时的加密密钥

// 通过流式下载文件,可以显示下载进度
const [stream, error] = await dc.file.createFileStream('file-cid', '解密密钥'); // 解密密钥为文件上传时的加密密钥

// 上传文件夹,进度回调参数:status表示状态: 0=成功,1=加密中,2=上传中,3=出错,4=异常,total表示总文件数,process表示已上传的文件数
const folderInput = document.getElementById('folderInput') as HTMLInputElement;
const files = folderInput.files;
const res = await dc.file.addFolder(
    files, // 文件列表
    "加密密钥",  // 需要文件加密时,使用对32字节长度字符进行base32加密后的字符串; 不需要文件加密时, 使用空字符串
    (status: number, total: number, process: number) => console.log(`上传状态: ${status}, 总文件数: ${total}, 进度: ${process} 已上传,`)
);
folderCID = res[0];

// 获取文件夹文件列表,返回的fileList格式为JSON对象:Array<{Name: string; Type: number; Size: number; Hash: string; Path: string}> type=0文件 type=1文件夹,每个文件内容可以将Hash字段的值作为cid通过 dc.file.getFile 获取,
const [fileList, error] = await dc.file.getFolderFileList(
    'folder-cid', // 文件夹CID
     false, // 是否重新寻址
     true // 是否递归获取所有子文件夹的文件列表
      );

3. ThreadDB用户数据库 (db)

用户数据库专门用于存储个人数据,支持跨设备同步。每个用户只能访问自己的数据,适合存储用户设置、个人记录等私密信息。

// =====第一步:定义数据结构=====
// 定义数据集合(类似表结构)
const collections = [
  {
    name: 'user_notes',           // 集合名称(如用户笔记)
    schema: {
      type: 'object',
      properties: {
        _id: { type: 'string' },           // 必需字段,系统自动生成
        title: { type: 'string' },         // 笔记标题
        content: { type: 'string' },       // 笔记内容
        create_time: { type: 'number' },   // 创建时间
        _mod: { type: 'number' }           // 必需字段,修改时间
      },
      required: ["_id"],
      additionalProperties: true
    }
  }
];


// =====第二步:初始化数据库=====
// 初始化用户数据库

// 用户登录成功后,必须进行初始化个人数据库,
const [, dbError] = await dc.initUserDB(collections);

if (dbError) {
  console.warn('初始化失败:', dbError);
  return;
}
console.log('初始化成功,ID:', dc.dbThreadId);


// =====第三步:操作数据库=====
// 创建记录
const noteData = {
  title: '第一条笔记',
  content: '笔记内容',
  create_time: Date.now()
};

const [recordId, createError] = await dc.db.create(
  dc.dbThreadId,
  'user_notes',
  JSON.stringify(noteData)    // 不需要传入_id,系统自动生成
);
console.log('记录创建成功,ID:', recordId);

// 查询记录
const [results, findError] = await dc.db.find(
  dc.dbThreadId,
  'user_notes',
  JSON.stringify({
    sort: { fieldPath: "create_time", desc: true }  // 按创建时间倒序
  })
);

if (results) {
  const noteList = JSON.parse(results);
  console.log('笔记列表:', noteList);
}

// 更新记录
const updateData = {
  _id: recordId,              // 必须包含记录ID
  title: '更新后的标题',
  content: '更新后的内容',
  create_time: Date.now()
};

await dc.db.save(
  dc.dbThreadId,
  'user_notes',
  JSON.stringify(updateData)
);

// 删除记录
await dc.db.delete(dc.dbThreadId, 'user_notes', recordId);

//=====第四步:查询举例=====

// 简单条件查询
const simpleQuery = {
  condition: "title = '我的笔记'"
};

// 复合条件查询
const complexQuery = {
  condition: "create_time > 1640995200000",
  ors: [
    { condition: "title = '重要笔记'" }
  ],
  sort: { fieldPath: "create_time", desc: true },
  seek: "分页标记"     // 用于分页
};

const [queryResults, queryError] = await dc.db.find(
  dc.dbThreadId,
  'user_notes',
  JSON.stringify(complexQuery)
);

//=====第五步:实际应用=====

// 创建用户设置管理
const saveUserSettings = async (settings) => {
  const [id, error] = await dc.db.create(
    dc.dbThreadId,
    'user_settings',
    JSON.stringify({
      ...settings,
      update_time: Date.now()
    })
  );
  return { id, error };
};

// 获取用户最新设置
const getUserSettings = async () => {
  const [results, error] = await dc.db.find(
    dc.dbThreadId,
    'user_settings',
    JSON.stringify({
      sort: { fieldPath: "update_time", desc: true }
    })
  );
  
  if (results) {
    const settings = JSON.parse(results);
    return settings[0];  // 返回最新的设置
  }
  return null;
};

// 使用示例
await saveUserSettings({ theme: 'dark', language: 'zh-CN' });
const currentSettings = await getUserSettings();

4. keyValue DB存储

keyValue DB是一个键值对存储系统,类似于Redis或MongoDB等NoSQL数据库。它的特点是:

功能特性:

  • 主题名称必须以"keyvalue_"开头
  • 支持权限控制,可以设置哪些用户能读取或修改数据
  • 支持索引查询,方便批量获取数据

使用场景对比:

  • 用户数据库(db):专门用于个人数据存储,每个用户只能访问自己的数据
  • keyValue DB:适合多用户共享数据的场景,比如:
    • 应用配置信息(所有用户共享)
    • 公告通知(发布给所有用户)
    • 排行榜数据(用户之间可见)
    • 商品信息(多用户浏览)

权限管理: 创建公共主题时有特殊限制:

  • 只有应用的初始主题作者(APPThemeConfig.appThemeAuthor)才能创建供全体用户访问的公共主题
  • 其他用户可以创建私有主题或邀请制主题
  • 主题名称必须以"keyvalue_"开头,公共主题必须以"_pub"结尾。

简单理解:keyValue DB = 多人共享的数据仓库,ThreadDB用户数据库 (db) = 个人专属的数据柜子 使用keyValueDB时, 需要注意以下几点:

  1. 主题名称必须以"keyvalue_"开头,公共主题必须以"_pub"结尾。
  2. 支持权限控制,可以设置哪些用户能读取或修改数据。
  3. 支持索引查询,方便批量获取数据。

使用示例:

import { APPThemeConfig } from '@config/config.json';


//=====第一步:获取或创建数据存储空间=====

let kvdb = null;
let error = null;

// 尝试获取已存在的公共数据存储
[kvdb, error] = await dc.keyValue.getStore(
    dc.appInfo.appId,           // 应用ID
    'keyvalue_app_config_pub',               //存储主题名称(比如应用配置),必须以"keyvalue_"开头,如果是公共存储则额外的必须以"_pub"结尾
    APPThemeConfig.appThemeAuthor    // 主题作者公钥,这里必须为APPThemeConfig.appThemeAuthor,不要更改
);

// 如果存储不存在,则创建新的
if (!kvdb) {
    // 检查权限:只有应用管理员才能创建公共存储
    if (dc.publicKey.string() === APPThemeConfig.appThemeAuthor) {
        console.log('正在创建公共数据存储...');
        [kvdb, error] = await dc.keyValue.createStore(
            dc.appInfo.appId,
            'keyvalue_app_config_pub',       // 主题必须以"keyvalue_"开头,如果是公共存储必须以"_pub"结尾
            50 * 1024 * 1024,       // 分配50MB存储空间
            2                       // 访问类型:2=公共(所有人可读,写入需授权) 
        );
        
        if (kvdb) {
            console.log('公共数据存储创建成功!');
        }
    } else {
        console.warn('权限不足:只有应用管理员可以创建公共存储');
        return;
    }
}

//=====第二步:配置用户访问权限=====

// 授权所有用户具有写入权限
const [authResult, authError] = await dc.keyValue.configAuth(
    kvdb,
    'all',          // 'all'表示所有用户,也可以填具体用户的公钥
    3,              // 权限级别: 0: 无权限   1: 申请权限    2: 只读    3: 读写    4: 管理员    5: 只写
    '允许所有用户访问应用配置'  // 授权说明
);




// =====第三步:存储数据(支持索引查询)=====


// 存储应用设置,并设置索引便于查询
const appSettings = {
    theme: 'dark',
    language: 'zh-CN',
    version: '1.0.0'
};

//设置值,在设置时,为了后续不同纬度快速检索,可以同时设置索引信息,格式为json字符串,数据格式如下:[{key:"indexkey1",type:"string",value:"value"},{key:"indexkey2",type:"number", value:12}]
const [setSuccess, setTimestamp, setError] = await dc.keyValue.set(
    kvdb,
    'app_settings',                     // 数据的键名
    JSON.stringify(appSettings),        // 数据内容(JSON格式)
    JSON.stringify([{key:"type",type:"string",value:"settings"},{key:"theme",type:"string",value:"dark"},{key:"lang",type:"string",value:"zh-CN"}])  // 索引配置,json字符串格式:类型=设置,主题=暗色,语言=中文 
);

if (setSuccess) {
    console.log('应用设置保存成功, 时间戳:', setTimestamp);
}

// 存储用户偏好设置
const userPrefs = {
    notifications: true,
    autoSave: false,
    userId: 'user123'
};

await dc.keyValue.set(
    kvdb,
    'prefs_user123', //key
    JSON.stringify(userPrefs), //value
    JSON.stringify([{key:"type",type:"string",value:"userprefs"},{key:"userId",type:"string",value:"user123"},{key:"notifications",type:"boolean",value:true}]) //indexs
);

//存储需要用于排名的数据,
const userRankValue = {
    userpubkey: "bb",
    name:"Alice",
    score: 100
};
await dc.keyValue.set(
    kvdb,
    'ranking',
    JSON.stringify(userRankValue),
    JSON.stringify([{key:"index_score",type:"number",value:100}]),
);

//=====第四步:读取数据=====

// 方式1: 获取key最近设置的value,如果writerPubkey存在,则获取该用户针对该key设置的最新值,如果省略掉该参数,或者传入空字符串,
const [value,error] = await dc.keyValue.get(
    kvdb,
    'app_settings', //应用的设置
    writerPubkey //写入者动pubkey,可以省略。如果这里省略,或者输入空字符串,则获取针对该key所有用户写入的value中的最新值
);
if (error) {
    console.warn('获取应用设置失败:', error);
} else {
    console.log('应用设置:', JSON.parse(value));
}




// 方式2:通过索引获取排名,获取kvdb中所有key为"ranking",index中score值最高的前10个记录,其中最后一个参数对象的type必须与设置索引时的type一致
const [rankingList, getError] = await dc.keyValue.getWithIndex(
    kvdb, 
    "index_score",  //索引名称
    null, //索引值,null表示不限定值
     {
    type:"number",//索引值的类型
    limit: 10, //最多返回10个
    seekKey: '', //起始键
    direction: 0, //获取记录的方向: 0-正序获取记录 1-倒序获取数据
    offset: 0 //起始位移
});
if (rankingList) {
    const rankings = JSON.parse(rankingList); //rankingList格式为设置进去的值的数组: '[{"ranking": "{"userpubkey": "bb", "name": "Alice", "score": 100}"}]'
    console.log('当前排行榜:', rankings);
}

// 方式3:通过索引批量查询
const [searchResults, searchError] = await dc.keyValue.getWithIndex(
    kvdb,
    'type',           // 索引名称
    'userprefs',      // 索引值(查找所有用户偏好设置)
    {
      type: "string",  // 索引值的类型
      direction: 0,
      limit: 10 ,      // 最多返回10条结果
      offset: 0 ,     // 起始位移
      seekKey: ''    // 起始键
    }
);

if (searchResults) {
    const userPrefsList = JSON.parse(searchResults);
    console.log('所有用户偏好设置:', userPrefsList);
}


//方式4: 获取指定key下的所有值
const [allValues, allError] = await dc.keyValue.getValues(
    kvdb,
    'prefs_user123', //所有用户针对该key设置的值
    {
      limit: 10,      // 最多返回10条
      seekKey: '',    // 起始键(分页用)
      direction: 0,   // 0-正序 1-倒序
      offset: 0       // 起始位移
    }
);
if (allError) {
    console.warn('获取所有用户设置失败:', allError);
} else {
    console.log('所有用户设置:', JSON.parse(allValues));
}

//=====实际应用场景示例=====

// 场景0:游戏应用排行榜(写入与读取)
// 写入玩家分数(按 score 建索引,便于排行榜查询)
const playerScore = {
  userpubkey: dc.publicKey.string(),
  name: "Alice",
  score: 1280,
  updatedAt: Date.now()
};

await dc.keyValue.set(
  kvdb,
  "ranking", // 统一 key,所有玩家写入同一个 key,依赖索引排序
  JSON.stringify(playerScore),
  JSON.stringify([{key:"index_score",type:"number",value:playerScore.score}])
);

// 读取排行榜(取分数最高的前 10 名)
const [topList, topError] = await dc.keyValue.getWithIndex(
  kvdb,
  "index_score",
  null,
  {
    type: "number",
    limit: 10,
    seekKey: "",
    direction: 1, // 1=倒序,分数从高到低
    offset: 0
  }
);

if (topList && !topError) {
  const rankings = JSON.parse(topList);
  console.log("Top 10:", rankings);
} else {
  console.warn("获取排行榜失败:", topError);
}

// 场景1:存储商品信息
const product = {
    id: 'prod001',
    name: 'iPhone 15',
    price: 5999,
    category: 'electronics'
};

await dc.keyValue.set(
    kvdb,
    'product_' + product.id,
    JSON.stringify(product),
    JSON.stringify([{key:"category",type:"string",value:product.category},{key:"price",type:"number",value:product.price},{key:"type",type:"string",value:"product"}])   //  索引:分类=电子产品,价格=5999,类型=商品
);

// 场景2:查询特定分类的商品
const [electronics, _] = await dc.keyValue.getWithIndex(
    kvdb,
    'category',       // 按分类查询
    'electronics',    // 查找电子产品
   {
    type: "string",
    limit: 20, // 最多返回20条结果
    offset: 0,
    seekKey: ''
});

const productList = JSON.parse(electronics);
console.log('电子产品列表:', productList);

共享型存储(createSharedStore)

普通主题(createStore)按用户分区存储,同一个 key 不同用户各存一份;共享型主题则让同一个 key 全局只保留唯一最新值,按时间戳后写覆盖,适合全局配置、计数器、状态标志等"所有人共用一份、只关心最新值"的数据。

主题名会被内部自动规范化为 keyvalue_shared_ 前缀,调用方无需关心命名约定;行为差异:

  • set/setWithCount:不同用户写入同一 key 会相互覆盖,最终保留时间戳最大的一条(同一微秒并发按内容确定性收敛,多节点结果一致),vaccount 不再区分存储分区。
  • get(不传 writerPubkey)/getValues/getWithIndex/getWithTimeOrder:直接返回该 key 的唯一最新值,时间排序索引天然去重,无需应用层再按用户去重。
  • 写入值里的 dc_opuser 会统一记为主题作者;若需记录"实际操作者",请把用户标识写进 value 内容中。
// 创建共享型存储(无需手动加 shared_ 前缀,内部自动处理)
let [kvdb, error] = await dc.keyValue.getStore(
    dc.appInfo.appId,
    'keyvalue_global_config_pub',
    APPThemeConfig.appThemeAuthor
);
if (!kvdb && dc.publicKey.string() === APPThemeConfig.appThemeAuthor) {
    [kvdb, error] = await dc.keyValue.createSharedStore(
        dc.appInfo.appId,
        'keyvalue_global_config_pub', // 公共共享存储,必须以"keyvalue_"开头、"_pub"结尾
        50 * 1024 * 1024,             // 分配50MB存储空间
        2                             // 2=公共(所有人可读,写入需授权)
    );
}

// 任意被授权用户写入同一个 key,全局只保留最新一条
await dc.keyValue.set(kvdb, 'site_notice', JSON.stringify({ text: '系统维护中', level: 'warn' }), '');

// 读取该 key 的唯一最新值(无需传 writerPubkey)
const [notice, noticeErr] = await dc.keyValue.get(kvdb, 'site_notice');
if (!noticeErr) {
    console.log('当前公告:', JSON.parse(notice));
}

记录总条数统计(getRecordCount)

getRecordCount 返回 keyValue 数据库的记录总条数,由节点自动维护:新增一条全新记录时自动 +1,删除已存在记录时自动 -1。

  • 统计为各节点本地自动维护,数据在多节点间最终收敛后计数也随之收敛。
  • setWithCount/saveWithCount 的数值累加类统计键不计入记录总条数。
  • 普通主题按 (写入用户, key) 维度计数;共享型主题按去重后的 key 计数。
const [count, countErr] = await dc.keyValue.getRecordCount(kvdb);
if (countErr === null) {
    console.log('记录总条数:', count);
}

5. 评论系统 (comment)

评论系统 (comment) 是专门为社交互动设计的功能模块,让用户可以发布、查看和管理评论内容。

核心特点:

  • 时间线存储:所有评论按发布时间顺序保存,可以按时间线浏览
  • 权限控制:支持设置谁能发评论、谁能查看评论
  • 主题管理:可以为不同话题创建独立的评论区

与 keyValue DB 的区别:

  • keyValue DB:像一个文件柜,适合存储配置信息、商品数据等相对固定的内容,且主题名称必须以"keyvalue_"开头
  • 评论系统:像一个留言板,适合存储用户发言、互动讨论等时序性内容

comment使用规则

    • 如果一个生成的代码文件里面包含了对comment主题的创建操作, 必须在文件头部标注使用到的主题名称,方便数据库静态构建. 格式如下: /**comment: news_comments */ 每个主题一行,如果有多个主题,请逐行添加
  • 所有涉及到评论系统 (comment)的操作的代码文件都应该放在services目录下

  • 评论系统:像一个留言板,适合存储用户发言、互动讨论等时序性内容

适用场景:

  • 微博、朋友圈(用户动态和评论)
  • 论坛、社区(帖子和回复)
  • 商品评价(买家评论和商家回复)
  • 新闻评论(读者评论和讨论)

灵活的内容处理: 评论内容可以根据业务需要自定义格式。比如:

  • 在评论中包含特定ID,后续可以通过这个ID来更新评论状态
  • 支持富文本、图片、链接等多种内容格式
  • 可以实现点赞、回复、转发等社交功能

简单理解:keyValue DB = 数据存储柜,comment = 时间线留言板

import { APPThemeConfig } from '@config/config.json';


//=====第一步:创建评论主题(仅应用管理员可操作)=====
// 检查权限:只有应用管理员才能创建公共评论主题
if (dc.publicKey.string() === APPThemeConfig.appThemeAuthor) {
  try {
    console.log('正在创建公共评论主题...');
    
    const [status, err] = await dc.comment.addThemeObj(
      'news_comments',        // 主题ID(比如新闻评论区)
      0,                      // 访问类型:0=公开(任何人可读写) 1=私密(任何登录用户仍可评论,但每条评论默认仅本人+主题作者可见,作者可精选公开) 2=授权(需被授权才能访问)
      50 * 1024 * 1024       // 分配50MB存储空间
    );
    if (status === 0) {
      console.log('评论主题创建成功!');
    }
  } catch(e) {
    console.log('主题已存在,无需重复创建');
  }
} else {
  console.log('权限不足:只有应用管理员可以创建公共评论主题');
}


//=====第二步:配置用户访问权限=====

// 授权所有用户具有评论权限
const [authStatus, authError] = await dc.comment.configAuth(
  APPThemeConfig.appThemeAuthor,  // 主题作者公钥
  'news_comments',           // 主题ID
  'all',                     // 'all'表示所有用户,也可以填具体用户公钥
  3,                         // 权限级别:与keyValue DB的权限级别定义一致
  '允许所有用户参与评论讨论'   // 授权说明
);


//=====第三步:发布评论=====

// 发布普通评论
const newsContent = {
  newsId: 'news_001',
  title: 'DC平台最新功能发布',
  userComment: '这个功能很棒,期待更多更新!'
};

const [commentId, commentError] = await dc.comment.publishCommentToTheme(
  'news_comments',                    // 主题ID
  APPThemeConfig.appThemeAuthor,           // 主题作者公钥
  0,                                  // 评论类型:0=普通评论, 1=点赞, 2=推荐, 3=踩
  JSON.stringify(newsContent),        // 评论内容
  1,                                  // 可见性:0=仅作者可见, 1=公开
  ''                                  // 引用其他评论(空表示不引用)
);

if (commentId) {
  console.log('评论发布成功,ID:', commentId);
}

// 发布点赞评论
const [likeId, likeError] = await dc.comment.publishCommentToTheme(
  'news_comments',
  APPThemeConfig.appThemeAuthor, //主题作者公钥,这里必须为APPThemeConfig.appThemeAuthor,不要更改
  1,                                  // 评论类型:1=点赞
  JSON.stringify({ 
    action: 'like', 
    targetComment: commentId,
    userId: dc.publicKey.string()
  }),
  1,
  '1000/'+commentId               // 引用刚才的评论,格式: 原评论发布时的区块高度/评论ID
);

//=====第四步:获取评论列表=====

// 获取最新的评论列表
const [comments, commentsError] = await dc.comment.getThemeComments(
  'news_comments',           // 主题ID
  APPThemeConfig.appThemeAuthor,  //主题作者公钥,这里必须为APPThemeConfig.appThemeAuthor,不要更改
  0,                         // 起始高度(0表示从最新开始)
  0,                         // 方向:0=最新优先, 1=最旧优先
  0,                         // 偏移量(分页用)
  20                         // 获取数量限制
);

if (comments && !commentsError) {
  const commentList = JSON.parse(comments);
  console.log('评论列表:', commentList);
  
  // 处理评论数据
  commentList.forEach(comment => {
    const content = JSON.parse(comment.content);
    console.log('用户评论: '+ content.userComment);
    console.log('发布时间: '+ new Date(comment.timestamp).toLocaleString()});
  });
} else {
  console.warn('获取评论失败:', commentsError);
}

//=====实际应用场景示例=====

// 场景1:新闻评论系统
const publishNewsComment = async (newsId, commentText) => {
  const commentData = {
    type: 'news_comment',
    newsId: newsId,
    comment: commentText,
    timestamp: Date.now(),
    author: dc.publicKey.string()
  };
  
  const [id, error] = await dc.comment.publishCommentToTheme(
    'news_comments',
    APPThemeConfig.appThemeAuthor, //主题作者公钥,这里必须为APPThemeConfig.appThemeAuthor,不要更改
    0,
    JSON.stringify(commentData),
    1,
    ''
  );
  return { id, error };
};

// 场景2:商品评价系统
const publishProductReview = async (productId, rating, review) => {
  const reviewData = {
    type: 'product_review',
    productId: productId,
    rating: rating,        // 1-5星评分
    review: review,
    timestamp: Date.now(),
    reviewer: dc.publicKey.string()
  };
  
  const [id, error] = await dc.comment.publishCommentToTheme(
    'product_reviews',
    APPThemeConfig.appThemeAuthor,//主题作者公钥,这里必须为APPThemeConfig.appThemeAuthor,不要更改
    0,
    JSON.stringify(reviewData),
    1,
    ''
  );
  
  return { id, error };
};

// 使用示例
await publishNewsComment('news_001', '这篇文章写得很好!');
await publishProductReview('prod_001', 5, '商品质量很棒,物流也很快!');

6. 消息系统 (message)

消息系统 (message) 提供用户之间的私信功能,类似于邮件系统或即时通讯。

核心特点:

  • 点对点通信:用户可以直接给其他用户发送私信
  • 收件箱模式:所有收到的消息都存储在个人收件箱中
  • 简单易用:只需要知道对方的公钥即可发送消息

适用场景:

  • 用户之间的私人对话
  • 系统通知和提醒
  • 客服消息和反馈
  • 好友聊天和交流

与其他模块的区别:

  • 消息系统:一对一的私密通信,类似微信私聊
  • 评论系统:公开的讨论区,类似微博评论
  • 用户数据库:个人数据存储,不涉及通信

简单理解:消息系统 = 私人邮箱,comment = 公共留言板

使用示例:

// =====发送私信给其他用户=====

// 发送简单文本消息
const [status, sendError] = await dc.message.sendMsgToUserBox(
  'anotherUserPublicKey123',    // 接收者的公钥(可通过 dc.publicKey.string() 获取)
  '你好,这是一条测试消息!'     // 消息内容
);

if (status === 0) {
  console.log('消息发送成功');
} else {
  console.warn('消息发送失败:', sendError);
}

// 发送复杂消息(JSON格式)
const messageData = {
  type: 'notification',
  title: '系统通知',
  content: '您有一个新的订单需要处理',
  timestamp: Date.now(),
  sender: dc.publicKey.string()
};

const [status2, sendError2] = await dc.message.sendMsgToUserBox(
  'receiverPublicKey',
  JSON.stringify(messageData)
);

//=====获取收件箱消息=====

// 获取最新的消息列表
const [messages, getError] = await dc.message.getMsgFromUserBox(20); // 获取最新20条消息

if (messages && !getError) {
  console.log('收到的消息:', messages);
  
  // 处理消息列表
  messages.forEach((message, index) => {
    console.log('消息 ' + (index + 1) + ': 发送者=' + message.sender + ', 内容=' + message.content + ', 时间=' + new Date(message.timestamp).toLocaleString());
  });
} else {
  console.warn('获取消息失败:', getError);
}

//=====实际应用场景示例=====

// 场景1:发送系统通知
const sendSystemNotification = async (userPublicKey, title, content) => {
  const notification = {
    type: 'system_notification',
    title: title,
    content: content,
    timestamp: Date.now(),
    priority: 'normal'
  };
  
  const [status, error] = await dc.message.sendMsgToUserBox(
    userPublicKey,
    JSON.stringify(notification)
  );
  
  return { success: status === 0, error };
};

// 场景2:用户聊天功能
const sendChatMessage = async (friendPublicKey, messageText) => {
  const chatMessage = {
    type: 'chat',
    message: messageText,
    sender: dc.publicKey.string(),
    timestamp: Date.now()
  };
  
  const [status, error] = await dc.message.sendMsgToUserBox(
    friendPublicKey,
    JSON.stringify(chatMessage)
  );
  
  return { success: status === 0, error };
};

// 场景3:获取并分类处理消息
const processInboxMessages = async () => {
  const [messages, error] = await dc.message.getMsgFromUserBox(50);
  
  if (messages && !error) {
    const notifications = [];
    const chatMessages = [];
    
    messages.forEach(msg => {
      try {
        const parsed = JSON.parse(msg.content);
        if (parsed.type === 'system_notification') {
          notifications.push(parsed);
        } else if (parsed.type === 'chat') {
          chatMessages.push(parsed);
        }
      } catch (e) {
        // 处理纯文本消息
        chatMessages.push({
          type: 'text',
          message: msg.content,
          sender: msg.sender,
          timestamp: msg.timestamp
        });
      }
    });
    
    console.log('系统通知:', notifications);
    console.log('聊天消息:', chatMessages);
  }
};

// 使用示例
await sendSystemNotification('userPublicKey123', '订单更新', '您的订单已发货');
await sendChatMessage('friendPublicKey456', '你好,最近怎么样?');
await processInboxMessages();

7. AI 代理 (aiproxy)

创建代理配置

// 创建AI代理配置,status=0表示成功
const [status, error] = await dc.aiproxy.createProxyConfig(
  dc.appInfo.appId,
  'default'  // 配置主题名称
);

配置AI服务

// 配置AI模型参数
const modelConfig = {
    Model:         "deepseek-r1",// 模型名称
    Temperature:   0.7,
    MaxTokens:     10000,
    TopP:          0.9, 
    TopK:          40,
    StopSequences: []string{},
    SystemPrompt:  "你是一个软件开发专家.",
    Stream:        true, // 启用流模式
    Tools:         []ToolDefinition{},// 工具定义数组
    Remark:        "这是一个AI代理配置"
}


const serviceConfig =  {
  service: 'ai代理服务', // 服务名称
  isAIModel: 0,    // 0: AI模型 1: MCPServer
  apiType: 0,      // 模型接口类型
  authorization: "Bearer your-api-key", // 授权信息
  endpoint: "https://api.openai.com/v1", // API端点
  organization: "your-organization", // 组织名称或ID 
  apiVersion: "v1",   // api版本号
  modelConfig: modelConfig, // 模型配置
  remark: ""
}

//配置AI服务
const [success, error] = await dc.aiproxy.configAIProxy(
  dc.appInfo.appId,
  dc.publicKey.string(),  // 配置作者公钥
  'default',              // 主题
  'openai-gpt',          // 名称
  serviceConfig          
);

if (success) {
  console.log('AI服务配置成功');
}

用户权限管理

// 为用户分配AI服务访问权限
const authConfig = {
  maxTokensPerDay: 10000,     // 每日最大token数
  allowedModels: ['gpt-3.5-turbo', 'gpt-4'],
  rateLimitPerMinute: 10      // 每分钟调用次数限制
};

const authConfig: ProxyCallConfig = {
    No: 1,
    Tlim: 1000, // 总次数限制
    Dlim: 100, // 日限制
    Wlim: 500, // 周限制
    Mlim: 2000, // 月限制
    Ylim: 10000, // 年限制
    Exp: 12345678 // 过期区块高度
};

const [status, error] = await dc.aiproxy.configAuth(
  dc.appInfo.appId,
  dc.publicKey.string(),      // 配置作者公钥
  'default',                  // 主题
  '用户',                 // 被授权的公钥,all表示所有用户
  3,                          // 权限级别:3=写入权限
  authConfig                  // 授权配置
);

if (status) {
  console.log('配置成功');
}

默认调用配置

在AI请求调用前调用

// 设置AI调用的默认参数
const defaultConfig = {
  appId: dc.appInfo.appId,
  themeAuthor: dc.publicKey.string(),
  configTheme: 'default',
  serviceName: 'openai-gpt'
};
const error = await dc.aiproxy.SetAICallConfig(defaultConfig);

if (!error) {
  console.log('设置成功');
}

执行AI调用

const requestBody = JSON.stringify({
  chatMessages: [
    {
      role: "user",
      content: [
        {
          type: "text",
          text: "你好,请介绍一下DC平台"
        }
      ]
    }
  ]
});

// MCP 通用服务流式调用时,需要显式打开 Dc-Stream
const headers = {
  "Dc-Stream": "true"
};


const controller = new AbortController();
const context = { signal: controller.signal };

// 流式响应处理
// flag: 0表示开始接收数据, 1:权限不足 2:获取失败 3:关闭连接 4: 其他错误   content: 接收到的数据
const handleStreamResponse = (flag, content, error) => {
  if (error) {
    console.warn('流式响应错误:', error);
    return;
  }

  if (flag === 3) {
    console.log('响应完成');
    return;
  }
  if (flag === 1) {
    console.warn('权限不足');
    return;
  }
  if (flag === 2) {
    console.warn('获取AI服务失败:', content);
    return;
  }
  if (flag === 4) {
    console.warn('错误:', content);
    return;
  }
  document.getElementById('ai-response').innerHTML += content;

};

// 执行AI调用
const [_, error] = await dc.aiproxy.DoAIProxyCall(
  context,
  requestBody,
  false,                  // 不强制刷新
  handleStreamResponse,   // 流式响应回调
  // 以下参数为空时使用默认配置
  undefined,              // appId
  undefined,              // themeAuthor
  undefined,              // configTheme
  undefined,              // serviceName
  headers,                // headers (MCP 流式返回时请设置 Dc-Stream: true)
  undefined,              // path
  undefined               // model
);

if (error) {
  console.warn('调用失败', error);
}

实时语音输入输出接入说明和跨环境示例见:

8. 实时消息 (RTM)

RTM 模块提供了实时离线消息、在线状态查询、群聊以及点对点频道的创建和邀请能力。

RTM 使用示例

1. 创建端到端加密频道 (P2P Channel)

此方法可用于创建安全私密的点对点通道:

const { rtm } = dc;

// 用户ID列表,当前仅支持两人或多人建立群聊
const userIds = ["peerId_of_userB", "peerId_of_userC"]; 
const description = "Private Chat Group";

try {
  // 这将生成随机通道ID,并向所有用户发送经过签名的加密邀请
  const channelId = await rtm.createPeerChannel(userIds, description);
  console.log("创建 RTM 频道成功:", channelId);
} catch (error) {
  console.error("创建失败:", error);
}

2. 接收与接受频道邀请

当用户被邀请时,会收到含有 channelId 的离线或在线加密消息。接收端只需校验签名和解密内容并接受:

// 假设 message 是从 RTM / Message 模块收到的 invite 消息
const inviteMessage = {
  isInvite: true,
  appId: "app123",
  sourceUserId: "peerId_of_inviter",
  messageType: "P2P",
  content: "encrypted_base64_content",
  timestamp: 1718290000000,
  signature: "base64_signature..."
};

try {
  // 接受邀请,会自动验证签名、解密,并返回所属频道和加密通信密钥
  const { channelId, channelDescription } = await rtm.acceptPeerChannelInvite(inviteMessage);
  console.log("接受邀请成功,即将加入频道", channelId, channelDescription);
  
  // 主动订阅频道以接收该频道内其他人的消息
  await rtm.subscribeChannel(channelId);
} catch (error) {
  console.error("处理邀请失败:", error);
}

9. 实时音视频 (RTC)

RTC 模块复用了 RTM 通道进行的信令发送,能够提供跨设备的音视频通话能力。这依赖于 RTM 模块发出的加密邀请机制。

RTC 使用示例

1. 创建音视频通话邀请

用户可以通过 createRTCChannel 生成一个安全的 RTC 频道并对指定好友发出邀请。

const { rtc } = dc;

const rtcConfig = { video: true, audio: true };
const userIds = ["peerId_of_userB"]; 

try {
  // 此操作会在内部调用 rtm 模块发送包含 channelId 的 RTC_INVITE 加密消息
  const channelId = await rtc.createRTCChannel(userIds, "Video Call", rtcConfig);
  console.log("RTC 音视频频道创建成功,ID:", channelId);
  
  // 接下来调用 rtc.init / rtc.joinChannel 等进行媒体流绑定
} catch (error) {
  console.error("RTC 频道创建失败:", error);
}

2. 接收与接受音视频通话邀请

try {
  // 解析由于 createRTCChannel 发送过来的 RTC_INVITE 消息
  const rtcInviteMessage = {
    isInvite: true,
    messageType: "RTC_INVITE",
    sourceUserId: "peerId_of_inviter",
    content: "encrypted_base64_content",
    signature: "base64_signature...",
    timestamp: 1718290000000,
    appId: "app123"
  };

  // 通过安全校验和解密,获取通话 channelId
  const { channelId, channelDescription, rtcConfig } = await rtc.parseRTCChannelInvite(rtcInviteMessage);
  console.log("成功接收 RTC 邀请:", channelId, rtcConfig);
  
  // 后续使用该 channelId 初始化 RTC 实例,连接媒体流
} catch(error) {
  console.error("接受 RTC 邀请失败", error);
}

模块选择指南

  • ThreadDB用户数据库(db): 个人数据,隐私数据,跨设备同步
  • keyValue DB: 多用户共享数据,应用配置,商品信息,排行榜
  • 评论系统: 时间线社交互动,评论回复,点赞功能
  • 消息系统: 点对点私密通信,系统通知
  • 文件模块: 加密文件存储,支持文件夹管理
  • AI代理: 应用中需要调用AI模型或者MCPServer时,使用AI代理模块进行配置和调用

About

web-dc-api是一个革命性的去中心化 Web 开发 SDK,让您无需任何服务器即可构建完整的互联网应用。

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages