diff --git a/docs/en/goatcraft/.nav.yml b/docs/en/goatcraft/.nav.yml new file mode 100644 index 0000000..6f55ea8 --- /dev/null +++ b/docs/en/goatcraft/.nav.yml @@ -0,0 +1 @@ +title: GoatCraft diff --git a/docs/en/goatcraft/auth.md b/docs/en/goatcraft/auth.md new file mode 100755 index 0000000..106cc94 --- /dev/null +++ b/docs/en/goatcraft/auth.md @@ -0,0 +1,48 @@ +--- +title: GoatCraft Auth +description: How to use GoatCraft Auth to log in to the server +--- + +GoatCraft Auth (short for GCA) is an external authentication tool that allows players to log in to the GoatCraft server without using an official Minecraft account. +This is particularly useful for players who do not own an official account but still wish to experience GoatCraft. + +To use GCA, you first need a Minecraft launcher that supports external authentication. +Below are some Minecraft launchers that support this feature: + +- PojavLauncher +- HMCL +- ... + +Additionally, you will need a Solarpass account to authorize GoatCraft Auth for login. + +!!! note + + GoatCraft Auth is a deployment instance of the open-source project Drasl. Thanks to the development team for their contributions. + If you want an external authentication service but dislike the PHP and bloated features of Blessing, you can check out the [Drasl](https://github.com/unmojang/drasl) project. + +## Direct Usage of GCA + +1. Visit the [GoatCraft Auth](https://authmc.solsynth.dev) website. +2. Click the "Sign in with Solarpass" button. +3. Authorize GoatCraft Auth to access your Solarpass account. +4. Follow the prompts to set a Minecraft username. + - **Note: If you have previously logged in to GoatCraft using an official account, please link your existing Mojang account here. Otherwise, it may cause player data conflicts and result in loss of gameplay progress.** + **Refer to the section below, 'Players Who Previously Used Official Login', for more details.** +5. Upon completion, you will receive an MC Token and Username. Make a note of them. +6. Open your Minecraft launcher and navigate to the settings page. +7. Locate the "External Login" or similar option, and enter the MC Token and Username obtained from GCA. + - The setup process may vary depending on the launcher; please refer to your launcher's documentation. + - The username field should be filled with the character name set in GCA; the password field should be filled with the obtained MC Token. + - Set the server address to `authmc.solsynth.dev/authlib-injector`. +8. Save the settings and launch the game. You should now be able to log in to the GoatCraft server using GCA. + +## Players Who Previously Used Official Login + +Even if you have previously logged in to GoatCraft using an official Minecraft account, you can still use GCA. This brings several benefits, such as features that interact with Solar Network, and you won't be prompted with "Link your Solarpass account to unlock more features!" upon rejoining the server. + +When following the steps above and reaching step 4, please do not create a new player character. Instead, link your existing Mojang account: + +1. On the GCA website, when you see the "Register from an existing account" option, enter the username of your official Minecraft account as prompted. +2. GCA will then ask you to download a skin file, which you need to set as the skin for your official Minecraft account. This is to verify your ownership of the official account. +3. After setting the skin, return to the GCA website and click the "Register" button. +4. You're all set! You can now proceed to step 5 of the previous section to continue with the client configuration. diff --git a/docs/en/goatcraft/culture.md b/docs/en/goatcraft/culture.md new file mode 100644 index 0000000..d15170b --- /dev/null +++ b/docs/en/goatcraft/culture.md @@ -0,0 +1,48 @@ +--- +title: GoatCraft Culture +description: An introduction to the various nations +--- + +In GoatCraft, players have established numerous nations using the Towny plugin. This chapter introduces you to the unique culture and characteristics of these regions. + +## The Chinese Sheep Nation + +The Chinese Sheep Nation was founded by Littlesheep, the server owner and founder of Solsynth. It is the nation with the most developed military industry on the server. The Happy Ghast fighter jets they developed boast high maneuverability and strong combat capabilities. At the same time, the Chinese Sheep Nation has a highly developed economy, standing as the largest economic entity in GoatCraft. + +The Chinese Sheep Nation governs three cities: + +- Nanjing + +Nanjing is the capital of the Chinese Sheep Nation, located in the southeast of the Hehuan Plateau. The main urban area is surrounded by mountains on all sides, serving as the economic and political center of the nation. + +- Xi'an + +Xi'an is the largest industrial base of the Chinese Sheep Nation, established by Longyuan. Located in the northeast of Taiping Island, it possesses the most comprehensive air defense system and railway control system in all of GoatCraft. + +- Hong Kong + +Hong Kong is the window for the Chinese Sheep Nation's overseas trade, established by AIRBUS. Located in the south of the Zhaomo Peninsula, it hosts the largest luxury goods trading market in GoatCraft. + +## The Corporate Consortium + +The Corporate Consortium is a nation founded by the player Texas0295 and is the most industrially advanced nation on the server. Almost all infrastructure facilities in GoatCraft were built by the nation's owner, Texas0295, including but not limited to: the Swamp Mob Farm, the End XP Farm, and the Sand Duper. + +Currently, the Corporate Consortium governs only one city: + +- Kimura Heavy Industries Co., Ltd. + +Kimura Heavy Industries Co., Ltd. is the most industrially developed city in GoatCraft, located on the Dapingyang Plain in the north of the Central Island. The former mayor of Hong Kong, AIRBUS, once stayed here for a long time. + +## The Flora Socialist Federation + +The Flora Socialist Federation is a nation founded by the player Jason_CJ and is the only developing country on the server. + +The Flora Socialist Federation governs two cities: + +- Sakura Continent + +Sakura Continent is the capital of the Flora Socialist Federation, located in the east of the Qianlong Peninsula in the south of the Hehuan Plateau. It is only about 300 blocks away from Nanjing at its closest point. It is a tourist destination in GoatCraft, and the first GoatCraft Spring Festival Gala was held here. This city is famous for its abundance of cherry blossom trees. + +- Golden Flower Continent + +Golden Flower Continent is a town within the Flora Socialist Federation, established by deng. Located on Jinhua Island in the west of the Qianlong Peninsula. diff --git a/docs/en/goatcraft/getting-started.md b/docs/en/goatcraft/getting-started.md new file mode 100755 index 0000000..bfd5af9 --- /dev/null +++ b/docs/en/goatcraft/getting-started.md @@ -0,0 +1,54 @@ +--- +title: Getting Started +description: Something important for new players of GoatCraft +--- + +GoatCraft is a server that use plugins,so we have some different tips from common edition game. +Get to know these information can help you enjoy the time in GoatCraft and integrate our community. + + +## Solar Network Integration + +As both are products of Solsynth, GoatCraft and Solar Network have achieved chat integration. +This means you can send messages between GoatCraft and Solar Network. + +The currently integrated chat room is the `摸鱼 | General` chat room under the `Solsynth` realm. +You can join the Solsynth realm from (https://solian.app/realms/solsynth?invite=1). + +Additionally, the server provides a `/sn` command, allowing you to convert in-game currency into Solar Network's Source Points. +The exchange rate is 1:100 (i.e., 1 Source Point equals 100 in-game currency), and an additional 20% handling fee will be charged during withdrawal. +You can also purchase in-game currency using Source Points, and there is no handling fee for purchases. + +## Web Map + +GoatCraft features a web-based map that you can access via your browser at (https://playmc.solsynth.dev). +This map displays the locations of all online players, as well as the structures they have built and other information. + +Powered by BlueMap. + +## Teleportation System + +Any GoatCraft player can use the `/tpa` and `/tpahere` commands to request to teleport to a selected player or invite them to teleport to your current location. + +You can press the T key (to open the chat box) and click Accept or Reject to decide whether to accept the request. +Alternatively, you can type the command `/tpaccept` to accept the teleport request. + +## Land Claim System + +GoatCraft uses Towny to manage player claims. Since it can be slightly complex, +please refer to the Towny plugin documentation to learn more. + +## Chest Shops + +You can create a chest shop by holding an item, left-clicking on most containers, and then typing your desired price in the chat box. +For more features, please refer to the QuickShop-Hikari plugin documentation. + +Powered by QuickShop-Hikari. + +## Slimefun + +GoatCraft has Slimefun4 installed. However, since its development has ceased, +the current experimental version for 1.21.8 may not be stable. +Nevertheless, if you feel like you've exhausted the vanilla gameplay, you might want to give it a try. + +*Let's observe a moment of silence for Slimefun4...* diff --git a/docs/en/goatcraft/index.md b/docs/en/goatcraft/index.md new file mode 100755 index 0000000..83e8663 --- /dev/null +++ b/docs/en/goatcraft/index.md @@ -0,0 +1,15 @@ +--- +title: What is GoatCraft? +description: Can you eat it? +--- + +GoatCraft is a Minecraft server hosted by Solsynth, +aiming to provide players with a fun and creative gaming environment. Whether you are a master builder, +an adventurer, or a redstone engineer, GoatCraft welcomes you to join! + +For the server IP and other information, you can visit +[this post](https://solian.app/posts/0199bee5-8494-74b5-83f9-3380777db628) +to learn more. + +The server supports direct login with an official Minecraft account, or external login via GoatCraft Auth. +For instructions on how to use GCA, please refer to the GCA section. diff --git a/docs/en/solar-network/.nav.yml b/docs/en/solar-network/.nav.yml new file mode 100644 index 0000000..65db2b5 --- /dev/null +++ b/docs/en/solar-network/.nav.yml @@ -0,0 +1,5 @@ +title: Solar Network +nav: + - "*.md" + - developers + - apis \ No newline at end of file diff --git a/docs/en/solar-network/account-status.md b/docs/en/solar-network/account-status.md new file mode 100644 index 0000000..02ee1d4 --- /dev/null +++ b/docs/en/solar-network/account-status.md @@ -0,0 +1,99 @@ +--- +title: Account Status and Online Presence +description: Learn how to manage your online status, customize your personal settings, and view the status of others. +--- + +# Account Status and Online Presence + +Learn how to manage your online status, customize your personal settings, and view the status of others. + +## Online Status Types + +Solar Network offers four online statuses that you can switch between freely based on your needs: + +| Status Type | Value | Description | What Others See | +|-------------|-------|-------------|-----------------| +| Normal | 0 | Normal visible status | Displays "Online" | +| Busy | 1 | Visible busy status | Displays "Busy" | +| Do Not Disturb | 2 | Visible DND status | Displays "Do Not Disturb" | +| Invisible | 3 | Completely hidden status | Displays "Offline" | + +### Status Details + +- **Normal**: Your default status; others can see that you are online as usual. +- **Busy**: Indicates you are occupied with other things and prefer not to be disturbed. +- **Do Not Disturb**: When enabled, push notifications will be disabled. +- **Invisible**: Completely hides your online status; others will see you as offline. + +!!! tip + + If you set your status to "Invisible", others will see "Offline" when viewing your profile, with no way of knowing you are actually online. + +## Setting Your Status + +### Where to Set +Go to **Account** > Click **Status**. + +### Configurable Fields + +| Field | Description | Limit | +|-------|-------------|-------| +| Status Type | Select your online status | Required | +| Status Label | Custom status text | Max 1024 characters | +| Icon | Status icon | Max 128 characters | +| Clear After | Time to automatically clear the status | Optional | + +### Auto Clear + +You can set your status to automatically clear at a specific time. For example: + +- Set to "Busy" during a meeting, and automatically revert to "Normal" after 1 hour. +- Set to "Do Not Disturb" while resting, and automatically revert after work hours. + +## Status Sentiment + +In addition to your online status, you can set a "Status Sentiment" to express your current mood: + +| Sentiment Type | Description | +|----------------|-------------| +| Positive | Indicates a positive and upbeat mood | +| Neutral | Indicates a general or neutral mood | +| Negative | Indicates a negative or down mood | + +Your status sentiment will be displayed in your activity calendar. + +## Online Members in Chat Rooms + +In chat rooms, you can view the members who are currently online. + +### Group Chat Rooms + +In group chat rooms, you can see: + +- The number of online users +- A list of nicknames of online members +- Detailed information of online members (click to view) + +### Direct Messages (DM) + +In private messages, you can see: + +- The other party's online status +- Their specific status message (e.g., "In a meeting") +- Their online/offline status + +## Online Status FAQ + +### Why is my status displaying incorrectly? + +Status display might have a slight delay because the system caches your status information. If you just changed your status, please wait a moment before checking. + +### Why do others see me as offline when I'm actually online? + +This could be because you have set your status to "Invisible". In this case, your online status is hidden from other users. + +Alternatively, the client's WebSocket connection might have been disconnected, failing to accurately report your online status to the server. + +### Can I set multiple statuses at the same time? + +No. You can only have one current status at a time. However, you can set a "Clear After" time to let the status automatically revert to the default after a specified period. diff --git a/docs/en/solar-network/account.md b/docs/en/solar-network/account.md new file mode 100755 index 0000000..7f962c6 --- /dev/null +++ b/docs/en/solar-network/account.md @@ -0,0 +1,115 @@ +--- +title: Solarpass +--- + +Accounts on Solar Network are also known as Solarpass, which can be used across all Solsynth products. + +!!! warning + + According to Article 3.5 of the Solar Network User Agreement, you are not allowed to create a second account on Solar Network. Creating "alt accounts" (smurf accounts) is a violation of the User Agreement. + This includes creating new accounts to evade bans after being penalized. **However, creating a new account after deleting your previous account data is not included in this restriction.** + + > A natural person can only register and own one Solarpass account. We reserve the right to delete data from any sub-accounts belonging to the same user. + + Fact Check: [Solsynth ToS](https://solsynth.dev/en/legal/user-agreement/) + +## Creating an Account + +You can create an account within Solian. To create an account, you need to provide the following information: + +- **Username:** Can contain English letters, numbers, underscores, and hyphens. + It must be globally unique. **The username cannot be changed after creation, except in special cases by contacting customer support.** +- **Display Name:** There are few restrictions on the display name; any 4-32 characters will suffice. +- **Email:** Please provide a valid email address, as you will need to receive a registration confirmation email. + To enforce the policy against creating sub-accounts, this email must be globally unique. +- **Password:** The account password used for future logins. Please ensure it is secure. + +After registering an account, you need to check the inbox of your bound email for a message from the Solarpass service, +which contains a link to confirm your registration. Before you use this link to confirm your registration, your account will lack permissions, +causing most features to be unusable. Any errors generated during this time are normal; please do not report them. + +### Logging In + +Solar Network uses a self-developed Multi-Factor Authentication (MFA) system, which is more convenient than traditional Two-Factor Authentication (2FA). You can choose any verification factor that is convenient for you to log in. + +### Account Recovery Code + +The Account Recovery Code is an important security backup for your account. When you cannot log in via other methods (such as email verification codes or authenticator app codes), the recovery code can help you regain access to your account. + +!!! warning + + **Please keep your recovery code safe!** The recovery code is displayed only once and cannot be retrieved if lost. + +#### Why You Need a Recovery Code + +- If you change your phone and cannot receive email verification codes or app codes. +- If you accidentally delete your authenticator app. +- If you temporarily cannot access your email. + +The recovery code ensures you always have a way to regain access to your account. + +#### How to Enable Recovery Code + +1. Log in to your account. +2. Go to **Account** > **Account Settings** > **Verification Factors**. +3. Find the "Authentication" or "Two-Factor Authentication" settings. +4. Follow the instructions to enable the recovery code. +5. **Immediately save the recovery code in a secure place** (a password manager is recommended). + +#### Logging In with a Recovery Code + +If you cannot use other verification methods to log in: + +1. Select "Use Recovery Code" on the login page. +2. Enter your account (username or email). +3. Enter your recovery code. +4. Complete the CAPTCHA (human verification). +5. The system will allow you to log in. + +After logging in with a recovery code, the system will: + +- Automatically disable that recovery code (you will need to generate a new one). +- Temporarily disable other verification methods. +- Log out all other devices. + +#### Regenerating a Recovery Code + +A recovery code becomes invalid after a single use. If you need a new recovery code: + +1. Log in to your account. +2. Go to **Account** > **Account Settings** > **Verification Factors**. +3. Find the "Authentication" settings. +4. Re-enable the recovery code. +5. **Immediately save the new code.** + +#### Security Recommendations + +- **Generate only once:** Each generation displays a new code, and the old code becomes invalid immediately. +- **Store securely:** It is recommended to use a password manager or write it down and store it in a safe place. +- **Do not share:** Never share your recovery code with anyone. +- **Check regularly:** Ensure you still remember where you saved it. + +### Deleting an Account + +You may want to delete your Solarpass account because you no longer need it. This is divided into two sections: + +- Things to know when deleting an account. +- How to delete my account. + +### Things to Note When Deleting an Account + +1. Publishers under your account will be deleted, including posts and stickers created by them. +2. You will no longer be able to log in to your account. +3. Data deletion will be processed with a delay, up to a maximum of 48 hours. + +!!! warning + + Deleting a Solarpass will only automatically clear your data on Solar Network. For accounts and data on other affiliated services such as SolarWatt and Solsynth Git, please delete/clear them before deregistering your Solarpass account; otherwise, you will lose access rights to them. + +### How to Delete an Account + +1. Click on "Account". +2. Scroll down and find "Account Settings". +3. Click "Delete Account". +4. The server will send a confirmation email to your bound primary email address. + You need to confirm the deletion within the valid time frame. diff --git a/docs/en/solar-network/activitypub.md b/docs/en/solar-network/activitypub.md new file mode 100755 index 0000000..f27e800 --- /dev/null +++ b/docs/en/solar-network/activitypub.md @@ -0,0 +1,45 @@ +--- +title: ActivityPub +--- + +# ActivityPub + +Starting from DysonNetwork commit `fb15930`, Solar Network's server-side software began providing stable ActivityPub support. + +This means you can use Solar Network as a member of the Fediverse to interact with Mastodon and other software instances. + +## Actor + +The ActivityPub protocol is built upon Actors. On Solar Network, the Actor is not the user, but the **Publisher**. +For user-level actions such as reacting to posts, we will attempt to find the first Publisher created by the user and check if that Publisher has an Actor enabled. + +Solar Network's ActivityPub feature requires manual activation by the user. You can go to the Creator Center, select the Publisher you wish to enable, and activate the corresponding Actor to join the Fediverse. + +## Supported Scope + +Currently, Solar Network supports the following Activities: + +- Create +- Update +- Delete +- Follow +- Like +- Accept + - Follow +- Undo + - Follow + - Like + +Additionally, Solar Network supports displaying local post attachments on the Fediverse, as well as displaying posts and attachments from the Fediverse (a suitable network connection is required). + +However, Solar Network does not support Mastodon's Content Warning or Polls. It also will not deliver polls, source checks, or other embeds to the Fediverse. + +Since Solar Network posts store rich text in Markdown format, while ActivityPub typically uses HTML, the client converts HTML to Markdown for rendering, and the server converts it to HTML before delivering it to the ActivityPub Inbox. Therefore, certain special syntaxes, such as spoilers and highlights, are unavailable on the Fediverse. + +Solar Network currently does not support Follower management and adopts an auto-accept strategy. + +## Boxes + +ActivityPub's Inbox and Outbox are subject to Solar Network's global Rate Limiting (120 reqs/min). Please keep this in mind when building third-party software that interacts with Solar Network's ActivityPub. + +Additionally, since Solar Network does not store Activities, all Activities are dynamically constructed. Therefore, the delivered ActivityId and the Id provided in the Outbox are highly likely to be different. It is recommended to use the Object URI for identification. diff --git a/docs/en/solar-network/apis/.nav.yml b/docs/en/solar-network/apis/.nav.yml new file mode 100644 index 0000000..14b5573 --- /dev/null +++ b/docs/en/solar-network/apis/.nav.yml @@ -0,0 +1 @@ +title: API 参考 \ No newline at end of file diff --git a/docs/en/solar-network/apis/develop.md b/docs/en/solar-network/apis/develop.md new file mode 100644 index 0000000..ab69454 --- /dev/null +++ b/docs/en/solar-network/apis/develop.md @@ -0,0 +1,13 @@ +--- +title: Develop API +description: 开发者服务 +hide: + - toc +--- + +DysonNetwork.Develop 主要负责调度各个服务,创建管理开发者资源。 +我觉得你没有什么理由需要自动化调用这些接口……吧。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/drive.md b/docs/en/solar-network/apis/drive.md new file mode 100644 index 0000000..2a08065 --- /dev/null +++ b/docs/en/solar-network/apis/drive.md @@ -0,0 +1,13 @@ +--- +title: Drive API +description: 文件,上传,附件,网盘 +hide: + - toc +--- + +DysonNetwork.Drive 主要负责文件的托管,上传分析,等一系列和文件相关的业务。 +单独阅读 API 文档可能让部份信息晦涩难懂,请结合「开发者服务」章节中有关 Drive 的内容一起阅读。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/fitness.md b/docs/en/solar-network/apis/fitness.md new file mode 100644 index 0000000..c0ac396 --- /dev/null +++ b/docs/en/solar-network/apis/fitness.md @@ -0,0 +1,12 @@ +--- +title: Fitness API +description: 健身与健康 +hide: + - toc +--- + +DysonNetwork.Fitness 是非常近期新增的服务。主要负责健身等各项有关的业务。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/index.md b/docs/en/solar-network/apis/index.md new file mode 100755 index 0000000..42beb9c --- /dev/null +++ b/docs/en/solar-network/apis/index.md @@ -0,0 +1,13 @@ +--- +title: Swagger 文档 +description: 基于 SwaggerGen 的自动生成文档 +--- + +本条目下列内容是提供给开发者阅读的,需要有一些技术基础才能理解。 + +!!! warning + + 在开始使用 Solar Network 开发之前,请确保您已充分理解并同意 [Solar Network 开发者协议](https://solsynth.dev/zh/legal/solar-network-dev) + +自动生成的文档可能不好理解,但是最新,最全(毕竟是自动生成的)。 +你可以参考上方开发者服务章节综合阅读理解,如果实在不懂可以询问希尔。 \ No newline at end of file diff --git a/docs/en/solar-network/apis/insight.md b/docs/en/solar-network/apis/insight.md new file mode 100644 index 0000000..4b6dd12 --- /dev/null +++ b/docs/en/solar-network/apis/insight.md @@ -0,0 +1,13 @@ +--- +title: Insight API +description: 咩酱、希尔和人工智能 +hide: + - toc +--- + +DysonNetwork.Insight 负责 AI 相关的服务。 +不过在不久前的重构,WebFeed(订阅源)和链接预览也变成了 Insight 的职责。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/messager.md b/docs/en/solar-network/apis/messager.md new file mode 100644 index 0000000..c2c0d53 --- /dev/null +++ b/docs/en/solar-network/apis/messager.md @@ -0,0 +1,13 @@ +--- +title: Messager API +description: 即时通讯与群组聊天 +hide: + - toc +--- + +DysonNetwork.Messager 曾经是 Sphere 服务的一部份,在蛮久前的重构之后抽离成为了一个单独的服务。 +主要负责即时通讯与语音聊天等聊天页面的功能。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/padlock.md b/docs/en/solar-network/apis/padlock.md new file mode 100644 index 0000000..ef5f7a1 --- /dev/null +++ b/docs/en/solar-network/apis/padlock.md @@ -0,0 +1,12 @@ +--- +title: Padlock API +description: 登陆,授权,以及帐户安全 +hide: + - toc +--- + +DysonNetwork.Padlock 主要负责用户登陆,审计等一系列安全的服务。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/passport.md b/docs/en/solar-network/apis/passport.md new file mode 100644 index 0000000..00ea20e --- /dev/null +++ b/docs/en/solar-network/apis/passport.md @@ -0,0 +1,14 @@ +--- +title: Passport API +description: 用户个人资料,帐号设置,杂七杂八的帐号功能 +hide: + - toc +--- + +DysonNetwork.Passport 是主要负责用户信息的服务,包括签到、个人资料(头像,简介等); +曾经其也负责登陆、授权等业务,不过在近期的重构被分配到 DysonNetwork.Padlock 服务中去了。 + +以下是自动生成的 API 文档,作为参考用途。 + + + diff --git a/docs/en/solar-network/apis/ring.md b/docs/en/solar-network/apis/ring.md new file mode 100644 index 0000000..839bdd8 --- /dev/null +++ b/docs/en/solar-network/apis/ring.md @@ -0,0 +1,12 @@ +--- +title: Ring API +description: 推送以及通知服务 +hide: + - toc +--- + +DysonNetwork.Ring 服务是负责通知的部份。 + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/apis/sphere.md b/docs/en/solar-network/apis/sphere.md new file mode 100644 index 0000000..78aaa56 --- /dev/null +++ b/docs/en/solar-network/apis/sphere.md @@ -0,0 +1,12 @@ +--- +title: Sphere API +description: 社群服务的核心组件,主要负责发布者、帖子等相关的资源 +hide: + - toc +--- + +DysonNetwork.Sphere 服务是社群服务的核心组件,主要负责发布者、帖子等相关的资源; + +以下是自动生成的 API 文档,作为参考用途。 + + \ No newline at end of file diff --git a/docs/en/solar-network/checkin.md b/docs/en/solar-network/checkin.md new file mode 100644 index 0000000..736db8b --- /dev/null +++ b/docs/en/solar-network/checkin.md @@ -0,0 +1,54 @@ +--- +title: Daily Check-in +description: Earn rewards by checking in every day! +--- + +# Daily Check-in + +Earn rewards by checking in every day! + +## How to Check In + +1. Go to your **Dashboard** +2. Click the Check-in button + +## Check-in Rewards + +| Reward | Description | +|--------|-------------| +| Experience Points (XP) | Fixed 100 points | +| Credits | Fixed 10 Credits | + +## Daily Fortune + +When you check in, you will randomly receive a fortune result: + +| Fortune | Probability | +|---------|-------------| +| Very Bad (大凶) | 10% | +| Bad (凶) | 20% | +| Average (中平) | 40% | +| Good (吉) | 20% | +| Very Good (大吉) | 10% | + +!!! note + + If you check in on your birthday, you will receive the special "Happy Birthday" fortune! + +## Check-in Tips + +After checking in, you will receive two positive and two negative tips to help you understand your daily fortune and get some advice. + +## Frequently Asked Questions + +### When are check-in rewards distributed? + +Check-in rewards are distributed to your account immediately. Experience points are automatically added to your level progress, and credits are added to your wallet. + +### Can I check in every day? + +Yes, you can check in once per day. The daily check-in status resets at 0:00 UTC every day. + +### Can I make up for a missed check-in? + +The make-up check-in feature is currently under development and is temporarily unavailable in the client. diff --git a/docs/en/solar-network/developers/.nav.yml b/docs/en/solar-network/developers/.nav.yml new file mode 100644 index 0000000..da79990 --- /dev/null +++ b/docs/en/solar-network/developers/.nav.yml @@ -0,0 +1 @@ +title: Open Platform diff --git a/docs/en/solar-network/developers/app-connect.md b/docs/en/solar-network/developers/app-connect.md new file mode 100644 index 0000000..1a154db --- /dev/null +++ b/docs/en/solar-network/developers/app-connect.md @@ -0,0 +1,404 @@ +# App Connect 认证协议 + +本文档介绍使用 Solian 应用进行原生应用认证的推荐方式,即 App Connect 协议。 +通过深度链接协议处理: + +- `solian://auth/web` + +该协议专为需要通过 Solian 应用进行挑战和令牌交换的原生应用设计(例如 iOS/Android 自定义 URI 方案)。 + +## 准备工作 + +在您的系统中安装 Solian 应用,并在开发者控制台中创建应用(Custom Apps)和密钥,密钥类型选择 App Connect。 + +## 概述 + +支持两种深度链接调用: + +1. 挑战请求 +2. 令牌交换请求 + +两种调用都需要 `redirect_uri`,以便 Solian 将结果数据返回给您的应用。 + +## 挑战请求 + +使用以下 URL 格式: + +```text +solian://auth/web?app=&redirect_uri=&state= +``` + +参数: + +- `app`:应用 slug(必填)。Solian 通过 `/develop/apps/` 解析应用元数据。 +- `redirect_uri`:您的应用回调 URI(必须包含方案),例如 `acme://auth/callback`。 +- `state`(可选):Solian 会回显的不透明值。 + +成功回调: + +```text +?status=ok&challenge=&state= +``` + +拒绝回调: + +```text +?status=denied&state= +``` + +错误回调: + +```text +?status=error&error=&state= +``` + +## 令牌交换请求 + +在您的应用使用 App Connect 密钥签署挑战后,使用: + +```text +solian://auth/web?signed_challenge=&redirect_uri=&state= +``` + +参数: + +- `signed_challenge`:App Connect 签名(蛇形命名字段)。 +- `redirect_uri`:您的应用回调 URI。 +- `state`(可选):回显的不透明值。 + +成功回调: + +```text +?status=success&token=&state= +``` + +错误回调: + +```text +?status=error&error=&state= +``` + +## SDK 辅助工具 + +`WebAuthClient` 提供辅助构建器: + +- `getProtocolChallengeUrl({ appSlug, redirectUri, state })` +- `getProtocolExchangeUrl({ signedChallenge, redirectUri, state })` + +这些方法会生成正确编码的 `solian://auth/web` URL。 + +## Flutter 示例代码 + +
+点击展开 Flutter 示例代码 + +### 1. 构建深度链接 URL + +```dart +import 'package:url_launcher/url_launcher.dart'; +import 'dart:convert'; + +class AppConnectHelper { + static const String _scheme = 'solian'; + static const String _host = 'auth/web'; + + /// 构建挑战请求 URL + static Uri buildChallengeUrl({ + required String appSlug, + required String redirectUri, + String? state, + }) { + final params = { + 'app': appSlug, + 'redirect_uri': redirectUri, + if (state != null) 'state': state, + }; + + return Uri( + scheme: _scheme, + host: _host, + queryParameters: params, + ); + } + + /// 构建令牌交换请求 URL + static Uri buildExchangeUrl({ + required String signedChallenge, + required String redirectUri, + String? state, + }) { + final params = { + 'signed_challenge': signedChallenge, + 'redirect_uri': redirectUri, + if (state != null) 'state': state, + }; + + return Uri( + scheme: _scheme, + host: _host, + queryParameters: params, + ); + } +} +``` + +### 2. 发起认证流程 + +```dart +class AuthService { + final String _appSlug = 'your-app-slug'; + final String _redirectUri = 'myapp://auth/callback'; + + /// 步骤 1: 请求挑战 + Future requestChallenge() async { + final state = DateTime.now().millisecondsSinceEpoch.toString(); + final url = AppConnectHelper.buildChallengeUrl( + appSlug: _appSlug, + redirectUri: _redirectUri, + state: state, + ); + + if (await canLaunchUrl(url)) { + await launchUrl(url, mode: LaunchMode.externalApplication); + return state; + } + throw Exception('无法打开 Solian 应用'); + } + + /// 步骤 2: 交换令牌 + Future exchangeToken(String signedChallenge) async { + final state = DateTime.now().millisecondsSinceEpoch.toString(); + final url = AppConnectHelper.buildExchangeUrl( + signedChallenge: signedChallenge, + redirectUri: _redirectUri, + state: state, + ); + + if (await canLaunchUrl(url)) { + await launchUrl(url, mode: LaunchMode.externalApplication); + return state; + } + throw Exception('无法打开 Solian 应用'); + } +} +``` + +### 3. 处理回调 + +```dart +import 'package:uni_links/uni_links.dart'; + +class CallbackHandler { + StreamSubscription? _subscription; + + /// 监听深度链接回调 + void listen(void Function(Uri? uri) onCallback) { + _subscription = uriLinkStream.listen((uri) { + onCallback(uri); + }); + } + + /// 解析回调参数 + static Map parseCallback(Uri uri) { + return uri.queryParameters; + } + + /// 处理挑战回调 + static ChallengeResult handleChallengeCallback(Uri uri) { + final params = parseCallback(uri); + final status = params['status']; + + if (status == 'ok') { + return ChallengeResult.success( + challenge: params['challenge']!, + state: params['state'], + ); + } else if (status == 'denied') { + return ChallengeResult.denied(state: params['state']); + } else { + return ChallengeResult.error( + error: params['error'] ?? '未知错误', + state: params['state'], + ); + } + } + + /// 处理令牌交换回调 + static ExchangeResult handleExchangeCallback(Uri uri) { + final params = parseCallback(uri); + final status = params['status']; + + if (status == 'success') { + return ExchangeResult.success( + token: params['token']!, + state: params['state'], + ); + } else { + return ExchangeResult.error( + error: params['error'] ?? '未知错误', + state: params['state'], + ); + } + } + + void dispose() { + _subscription?.cancel(); + } +} + +/// 挑战回调结果 +class ChallengeResult { + final ChallengeStatus status; + final String? challenge; + final String? error; + final String? state; + + ChallengeResult._({ + required this.status, + this.challenge, + this.error, + this.state, + }); + + factory ChallengeResult.success({ + required String challenge, + String? state, + }) => + ChallengeResult._( + status: ChallengeStatus.success, + challenge: challenge, + state: state, + ); + + factory ChallengeResult.denied({String? state}) => + ChallengeResult._( + status: ChallengeStatus.denied, + state: state, + ); + + factory ChallengeResult.error({ + required String error, + String? state, + }) => + ChallengeResult._( + status: ChallengeStatus.error, + error: error, + state: state, + ); +} + +enum ChallengeStatus { success, denied, error } + +/// 令牌交换回调结果 +class ExchangeResult { + final ExchangeStatus status; + final String? token; + final String? error; + final String? state; + + ExchangeResult._({ + required this.status, + this.token, + this.error, + this.state, + }); + + factory ExchangeResult.success({ + required String token, + String? state, + }) => + ExchangeResult._( + status: ExchangeStatus.success, + token: token, + state: state, + ); + + factory ExchangeResult.error({ + required String error, + String? state, + }) => + ExchangeResult._( + status: ExchangeStatus.error, + error: error, + state: state, + ); +} + +enum ExchangeStatus { success, error } +``` + +### 4. 完整使用示例 + +```dart +class LoginPage extends StatefulWidget { + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final AuthService _authService = AuthService(); + final CallbackHandler _callbackHandler = CallbackHandler(); + String? _pendingState; + + @override + void initState() { + super.initState(); + _callbackHandler.listen(_handleCallback); + } + + @override + void dispose() { + _callbackHandler.dispose(); + super.dispose(); + } + + Future _handleCallback(Uri? uri) async { + if (uri == null) return; + + final result = CallbackHandler.handleChallengeCallback(uri); + + if (result.status == ChallengeStatus.success) { + // 使用 App Connect 密钥签署挑战 + final signedChallenge = await _signChallenge(result.challenge!); + + // 交换令牌 + await _authService.exchangeToken(signedChallenge); + } else if (result.status == ChallengeStatus.denied) { + // 用户拒绝授权 + } else { + // 发生错误 + } + } + + Future _signChallenge(String challenge) async { + // 使用您的 App Connect 密钥签署挑战 + final key = 'your_app_connect_secret'; + final hmac = Hmac(sha256, utf8.encode(key)); + final digest = hmac.convert(utf8.encode(challenge)); + return digest.toString(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('登录')), + body: Center( + child: ElevatedButton( + onPressed: () async { + _pendingState = await _authService.requestChallenge(); + }, + child: Text('使用 Solian 登录'), + ), + ), + ); + } +} +``` + +
+ +## 注意事项 + +- Solian 期望 App Connect 负载使用蛇形命名字段。 +- `redirect_uri` 必须是带有方案的有效 URI。 +- 在回调中保留并验证 `state` 以防止请求混淆。 diff --git a/docs/en/solar-network/developers/file-upload.md b/docs/en/solar-network/developers/file-upload.md new file mode 100755 index 0000000..bd8672c --- /dev/null +++ b/docs/en/solar-network/developers/file-upload.md @@ -0,0 +1,101 @@ +--- +title: 文件上传 +description: 了解如何使用 Solar Network 的 API 来完成文件上传。 +--- + +在通过网关访问时,需要将 `/api` 替换成服务 ID `/drive`。 + +原有的 `/api/tus` 接口已经过时,即将迎来移除的命运。还请使用新的 Solar Network Multipart Upload 协议上传文件。 + +本文档概述了使用多部分上传 API 分块上传大文件的过程。 + +## 1. 创建上传任务 + +要开始文件上传,您首先需要创建一个上传任务。这是通过向 `/api/files/upload/create` 端点发送 `POST` 请求来完成的。 + +**端点:** `POST /api/files/upload/create` + +**请求体:** + +```json +{ + "hash": "string (文件哈希,例如 MD5 或 SHA256)", + "file_name": "string", + "file_size": "long (字节数)", + "content_type": "string (例如 'image/jpeg')", + "pool_id": "string (GUID,可选)", + "bundle_id": "string (GUID,可选)", + "encrypt_password": "string (可选)", + "expired_at": "string (ISO 8601 格式,可选)", + "chunk_size": "long (字节数,可选,默认 5MB)" +} +``` + +**响应:** + +如果具有相同哈希的文件已存在,服务器将返回 `200 OK`,响应体如下: + +```json +{ + "file_exists": true, + "file": { ... (CloudFile 对象,以 snake_case 格式) ... } +} +``` + +如果文件不存在,服务器将返回 `200 OK`,包含任务 ID 和分块信息: + +```json +{ + "file_exists": false, + "task_id": "string", + "chunk_size": "long", + "chunks_count": "int" +} +``` + +您将需要 `task_id`、`chunk_size` 和 `chunks_count` 用于后续步骤。 + +## 2. 上传文件分块 + +一旦您有了 `task_id`,就可以开始分块上传文件。每个分块作为带有 `multipart/form-data` 的 `POST` 请求发送。 + +**端点:** `POST /api/files/upload/chunk/{taskId}/{chunkIndex}` + +- `taskId`:上一步上传任务的 ID。 +- `chunkIndex`:您正在上传的分块的从 0 开始的索引。 + +**请求体:** + +请求体的格式应为 `multipart/form-data`,包含一个名为 `chunk` 的表单字段,其中包含该分块的二进制数据。 + +每个分块的大小应等于"创建上传任务"步骤中返回的 `chunk_size`,除了最后一个分块可能更小。 + +**响应:** + +成功的分块上传将返回 `200 OK`,响应体为空。 + +您应该上传从 `0` 到 `chunks_count - 1` 的所有分块。 + +## 3. 完成上传 + +所有分块成功上传后,您必须发送最终请求以完成上传过程。这将合并所有分块为单个文件并进行处理。 + +**端点:** `POST /api/files/upload/complete/{taskId}` + +- `taskId`:上传任务的 ID。 + +**请求体:** + +请求体应为空。 + +**响应:** + +成功的请求将返回 `200 OK`,包含新上传文件的 `CloudFile` 对象。 + +```json +{ + ... (CloudFile 对象) ... +} +``` + +如果任何分块缺失或合并过程中发生错误,服务器将返回 `400 Bad Request` 以及错误消息。 diff --git a/docs/en/solar-network/developers/index.md b/docs/en/solar-network/developers/index.md new file mode 100755 index 0000000..942ea0b --- /dev/null +++ b/docs/en/solar-network/developers/index.md @@ -0,0 +1,18 @@ +--- +title: 开发者服务 +description: 了解如何使用 Solar Network 的 API +--- + +本条目下列内容是提供给开发者阅读的,可能需要有一些技术基础才能理解。 + +!!! warning + + 在开始使用 Solar Network 开发之前,请确保您已充分理解并同意 [Solar Network 开发者协议](https://solsynth.dev/zh/legal/solar-network-dev) + +因为大部份接口都是 CRUD,没有什么具体好说的,因此本章节不会设计全部接口和功能,详情请参考下方的 API 参考章节。 +此处只会涉及重要内容,或者 API 文档不容易理解的。若遇到问题可以询问希尔。 + +----- + +希尔(旧名 SN 酱)已于近日接入 Suki! +这代表你可以询问她关于 Solar Network API 以及使用相关的问题了,助力你的开发。 \ No newline at end of file diff --git a/docs/en/solar-network/developers/livestream.md b/docs/en/solar-network/developers/livestream.md new file mode 100644 index 0000000..8ab23d0 --- /dev/null +++ b/docs/en/solar-network/developers/livestream.md @@ -0,0 +1,1169 @@ +--- +title: 直播开发 +--- + +## 概述 + +直播功能使用户能够使用 LiveKit 作为实时基础设施创建和广播直播视频流。用户可以: + +- 创建带有元数据的直播流 +- 通过 RTMP(使用 OBS 或类似软件)进行流媒体传输 +- 允许观众通过 WebRTC(Flutter/Web 客户端)加入 +- 启用 HLS 播放以实现更广泛的平台兼容性 +- 通过 Egress 将流推送到外部平台(YouTube、Bilibili 等) +- 跟踪观看者数量和流统计信息 + +## 架构 + +### 组件 + +1. **LiveKit 基础设施** + - **Room**:每个直播流会话的 WebRTC 房间 + - **Ingress**:流媒体的 RTMP 输入端点(OBS → LiveKit) + - **Egress (RTMP)**:到外部平台的 RTMP 输出 + - **Egress (HLS)**:用于播放的 HLS 段生成 + +2. **Sphere API** (DysonNetwork.Sphere) + - 用于流管理的 RESTful API + - 用于 WebRTC 身份验证的令牌生成 + - 通过 Entity Framework 进行数据库持久化 + +3. **模型** + - `SnLiveStream`:直播流元数据的数据库模型 + - `LiveStreamStatus`:待处理、活跃、已结束、错误 + - `LiveStreamType`:常规、互动 + - `LiveStreamVisibility`:公开、未列出、私有 + +## API 端点 + +**注意:** 所有返回直播流数据的端点直接返回 `SnLiveStream` 数据库对象(不是 DTO),包括相关的 `Publisher` 对象。只有令牌生成等特定端点返回自定义匿名对象。 + +**安全性:** 敏感字段(`ingress_id`、`ingress_stream_key`、`egress_id`)会自动从公共响应中移除。这些字段仅对在流的发布者上具有编辑者角色的用户可见。`开始流媒体` 端点仅向授权用户返回 RTMP 凭据。 + +### 流管理 + +#### 创建直播流 + +为指定的发布者创建新的直播流。如果未指定发布者,则使用用户的第一个个人发布者。 + +```http +POST /sphere/livestreams?pub=mypublisher +Authorization: Bearer {token} +Content-Type: application/json + +{ + "title": "我的精彩直播", + "description": "直播一些精彩内容", + "slug": "awesome-stream-2026", + "thumbnail_id": "file-uuid-here", + "type": "Regular", + "visibility": "Public" +} + +Response 200 OK: +{ + "id": "uuid", + "room_name": "livestream_abc123", + "title": "我的精彩直播", + "description": "直播一些精彩内容", + "slug": "awesome-stream-2026", + "type": "Regular", + "visibility": "Public", + "status": "Pending", + "thumbnail": { + "id": "file-uuid-here", + "name": "thumbnail.jpg", + "url": "https://...", + "mime_type": "image/jpeg", + "size": 12345 + }, + "publisher_id": "uuid", + "publisher": { + "id": "uuid", + "name": "mypublisher", + "nick": "My Publisher", + "picture": {...} + }, + "created_at": "2026-02-19T15:00:00Z", + "updated_at": "2026-02-19T15:00:00Z" +} +``` + +**查询参数:** + +| 参数 | 类型 | 描述 | +| ----- | ------ | ---------------------------------------------------------- | +| `pub` | string | 可选的发布者名称。如果未指定,使用用户的第一个个人发布者。 | + +**授权:** 需要身份验证并且至少在一个发布者中拥有成员身份。如果指定了 `pub`,则需要在该发布者上至少具有编辑者角色。 + +**缩略图:** `thumbnail_id` 应该是来自文件服务的文件 ID。缩略图存储在 jsonb 列中的 `SnCloudFileReferenceObject` 中。 + +#### 开始流媒体 + +启动直播流。对于应用内流媒体(无入口),使用 `no_ingress: true`,或者为外部流媒体指定入口类型(RTMP/WHIP)。需要在发布者上具有编辑者角色。 + +**RTMP 流媒体(OBS):** + +```http +POST /sphere/livestreams/{id}/start +Authorization: Bearer {token} +Content-Type: application/json + +{ + "participant_name": "Streamer Name" +} + +Response 200 OK: +{ + "url": "rtmp://your-livekit-server.com/live", + "stream_key": "live_xxx", + "room_name": "livestream_abc123" +} +``` + +**WHIP 流媒体(WebRTC 摄取):** + +```http +POST /sphere/livestreams/{id}/start +Authorization: Bearer {token} +Content-Type: application/json + +{ + "participant_name": "Streamer Name", + "use_whip": true, + "enable_transcoding": false +} + +Response 200 OK: +{ + "url": "whip://your-livekit-server.com/live", + "stream_key": "live_xxx", + "room_name": "livestream_abc123" +} +``` + +**应用内流媒体(移动/Web):** + +```http +POST /sphere/livestreams/{id}/start +Authorization: Bearer {token} +Content-Type: application/json + +{ + "participant_name": "Streamer Name", + "no_ingress": true +} + +Response 200 OK: +{ + "room_name": "livestream_abc123", + "url": "wss://your-livekit-server.com" +} +``` + +**请求字段:** + +| 字段 | 类型 | 描述 | +| -------------------- | ------- | --------------------------------------------------------- | +| `participant_name` | string | 流媒体者的可选显示名称 | +| `no_ingress` | boolean | 如果为 `true`,完全跳过入口创建(用于应用内 WebRTC 发布) | +| `use_whip` | boolean | 如果为 `true`,创建 WHIP 入口而不是 RTMP(默认:false) | +| `enable_transcoding` | boolean | 为入口启用转码(默认:true,如果不支持则忽略 WHIP) | + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +**入口类型:** + +- **RTMP** (`use_whip: false`):用于 OBS 或其他 RTMP 编码器的传统 RTMP 入口 +- **WHIP** (`use_whip: true`):用于基于浏览器的流媒体的 WebRTC HTTP 摄取协议 +- **无** (`no_ingress: true`):无入口 - 流媒体者直接通过 WebRTC 发布 + +**OBS 配置(RTMP):** + +- 服务:自定义 +- 服务器:`{url}` +- 流密钥:`{stream_key}` + +**WHIP 使用:** + +- 使用 `url` 作为 WHIP 端点 +- 通常用于基于浏览器的 WebRTC 流媒体 + +**应用内使用(`no_ingress: true`):** + +1. 通过 `GET /sphere/livestreams/{id}/token?identity=streamer_{userId}` 获取带有流媒体者身份的令牌 +2. 使用 LiveKit SDK 连接并发布音频/视频 + +#### 结束直播流 + +结束直播流并清理资源。需要在发布者上具有编辑者角色。 + +```http +POST /sphere/livestreams/{id}/end +Authorization: Bearer {token} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +### 观众端点 + +#### 列出活跃流 + +返回带有完整数据库对象的活跃公开直播流列表。 + +```http +GET /sphere/livestreams?limit=20&offset=0 + +Response 200 OK: +[ + { + "id": "uuid", + "title": "我的精彩直播", + "description": "直播一些精彩内容", + "room_name": "livestream_abc123", + "type": "Regular", + "visibility": "Public", + "status": "Active", + "viewer_count": 150, + "peak_viewer_count": 200, + "publisher_id": "uuid", + "publisher": { + "id": "uuid", + "name": "channel", + "nick": "Channel Name", + "picture": {...} + }, + "started_at": "2026-02-19T15:30:00Z", + "created_at": "2026-02-19T15:00:00Z", + "updated_at": "2026-02-19T15:00:00Z" + } +] +``` + +**授权:** 公开,无需身份验证。 + +#### 按发布者获取流 + +返回特定发布者的所有直播流(包括已结束的)。 + +```http +GET /sphere/livestreams/publisher/{publisherId}?limit=20&offset=0 + +Response 200 OK: +[ + { + "id": "uuid", + "title": "过去的直播", + "description": "这是一个过去的直播", + "room_name": "livestream_def456", + "status": "Ended", + "viewer_count": 1000, + "peak_viewer_count": 1500, + "publisher_id": "uuid", + "publisher": {...}, + "started_at": "2026-02-18T14:00:00Z", + "ended_at": "2026-02-18T16:00:00Z", + "created_at": "2026-02-18T14:00:00Z" + } +] +``` + +**授权:** 公开,无需身份验证。 + +#### 获取流详细信息 + +返回完整的直播流数据库对象,包括发布者详细信息。 + +```http +GET /sphere/livestreams/{id} + +Response 200 OK: +{ + "id": "uuid", + "title": "我的精彩直播", + "description": "直播一些精彩内容", + "slug": "awesome-stream-2026", + "room_name": "livestream_abc123", + "type": "Regular", + "visibility": "Public", + "status": "Active", + "ingress_id": "ingress_xxx", + "ingress_stream_key": "live_xxx", + "egress_id": "egress_xxx", + "viewer_count": 150, + "peak_viewer_count": 200, + "publisher_id": "uuid", + "publisher": { + "id": "uuid", + "name": "channel", + "nick": "Channel Name", + "picture": {...} + }, + "thumbnail": {...}, + "metadata": {...}, + "started_at": "2026-02-19T15:30:00Z", + "ended_at": null, + "created_at": "2026-02-19T15:00:00Z", + "updated_at": "2026-02-19T15:00:00Z" +} +``` + +**授权:** 公开,无需身份验证。 + +#### 更新直播流 + +更新直播流的元数据(标题、描述、slug、类型、可见性、元数据)。需要在发布者上具有编辑者角色。 + +```http +PATCH /sphere/livestreams/{id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "title": "更新的流标题", + "description": "更新的描述", + "slug": "updated-slug-2026", + "type": "Interactive", + "visibility": "Unlisted", + "metadata": { + "category": "gaming", + "tags": ["fps", "competitive"] + } +} + +Response 200 OK: +{ + "id": "uuid", + "title": "更新的流标题", + "description": "更新的描述", + "slug": "updated-slug-2026", + "type": "Interactive", + "visibility": "Unlisted", + "metadata": { + "category": "gaming", + "tags": ["fps", "competitive"] + }, + "updated_at": "2026-02-19T16:00:00Z", + ... +} +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +**注意:** 仅更新提供的字段。省略字段以保持现有值。流活跃时无法更新(先结束流,然后更新)。 + +#### 删除直播流 + +永久删除直播流和所有相关资源。如果流是活跃的,将首先停止它。需要在发布者上具有编辑者角色。 + +```http +DELETE /sphere/livestreams/{id} +Authorization: Bearer {token} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +**警告:** 此操作无法撤销。所有流数据,包括 HLS 段和录制内容,将被永久删除。 + +#### 加入流(获取令牌) + +为加入流生成 LiveKit 令牌。如果用户在发布者上具有编辑者角色,会自动检测用户是否应为流媒体者。 + +```http +GET /sphere/livestreams/{id}/token?streamer=false +Authorization: Bearer {token} (optional for public streams) + +Response 200 OK: +{ + "token": "jwt-token-here", + "room_name": "livestream_abc123", + "url": "wss://your-livekit-server.com", + "isStreamer": false, + "identity": "username" +} +``` + +**查询参数:** + +| 参数 | 类型 | 默认值 | 描述 | +| ---------- | ------- | ------ | ---------------------------------------------------- | +| `streamer` | boolean | false | 如果为 true 且用户具有编辑者角色,则授予流媒体者权限 | + +**授权:** 公开,可选身份验证。如果经过身份验证且用户在发布者上具有编辑者角色,他们将获得流媒体者权限(可以发布音频/视频)。否则,他们获得观众权限(只能订阅)。 + +**令牌身份:** + +- 已认证用户:`account.Name`(如果 Name 为 null,则为 `user_{accountId}`) +- 匿名用户:`guest_{uuid}` +- 令牌元数据包括:`livestream_id`、`is_streamer`、`account_id` + +使用此令牌与 LiveKit Flutter SDK: + +```dart +final room = Room(); +await room.connect(response.url, response.token); +``` + +### Egress(外部流媒体) + +#### 开始 Egress(推送到外部平台) + +开始将直播流推送到外部 RTMP 端点。也可以录制到文件。需要在发布者上具有编辑者角色。 + +```http +POST /sphere/livestreams/{id}/egress +Authorization: Bearer {token} +Content-Type: application/json + +{ + "rtmp_urls": [ + "rtmp://live-push.bilivideo.com/live-bvc/...", + "rtmp://a.rtmp.youtube.com/live2/..." + ], + "file_path": "recordings/stream-2026-02-19.mp4" +} + +Response 200 OK: +{ + "egress_id": "EG_xxx" +} +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +#### 停止 Egress + +停止 egress(外部流媒体)。需要在发布者上具有编辑者角色。 + +```http +POST /sphere/livestreams/{id}/egress/stop +Authorization: Bearer {token} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +### HLS Egress(播放支持) + +HLS(HTTP 直播流)egress 允许观众使用标准 HLS 播放器(例如 HTML5 视频播放器、VLC、iOS 原生播放器)观看流,而不是 WebRTC。这对于以下情况很有用: + +- 更好的跨平台兼容性 +- WebRTC 不受支持时的回退 +- 录制以供以后播放 +- 为大量观众减少服务器负载 + +#### 开始 HLS Egress + +开始从直播流生成 HLS 段和播放列表。需要在发布者上具有编辑者角色。 + +```http +POST /sphere/livestreams/{id}/hls +Authorization: Bearer {token} +Content-Type: application/json + +{ + "playlist_name": "playlist.m3u8", + "segment_duration": 6, + "segment_count": 0, + "layout": "default", + "hls_base_url": "https://your-cdn.com/hls" +} + +Response 200 OK: +{ + "egress_id": "EG_xxx", + "playlist_url": "https://your-cdn.com/hls/{stream-id}/playlist.m3u8", + "playlist_name": "playlist.m3u8" +} +``` + +**请求体字段:** + +| 字段 | 类型 | 默认值 | 描述 | +| ------------------ | ------ | --------------- | ------------------------------------------------- | +| `playlist_name` | string | "playlist.m3u8" | HLS 播放列表文件的名称 | +| `segment_duration` | uint | 6 | 每个段的持续时间(秒) | +| `segment_count` | int | 0 | 要保留的段数(0 = 无限制) | +| `layout` | string | "default" | 视频合成布局 | +| `hls_base_url` | string | Auto | HLS 播放的基础 URL(默认为 `https://{host}/hls`) | + +**授权:** 需要身份验证并且在流的发布者上具有管理员(IsSuperuser)角色。 + +**注意事项:** + +- 每个流只能有一个 HLS egress 处于活跃状态。如果要重新启动,请先调用 stop。 +- `playlist_url` 存储在直播流中,并在 GET 响应中返回(对所有观众可见)。 +- 段存储在 LiveKit 中配置的 `{filename_prefix}` 位置。 + +#### 停止 HLS Egress + +停止 HLS egress 生成。注意:停止后,HLS 播放列表 URL 会被**保留**,因此观众仍然可以播放录制的内容。 + +```http +POST /sphere/livestreams/{id}/hls/stop +Authorization: Bearer {token} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +#### HLS 播放 + +一旦 HLS egress 启动,完整的播放列表 URL 会自动构建并在流响应中可用(从 `HlsPlaylistPath` + `PlaybackUrl` 配置组合): + +```http +GET /sphere/livestreams/{id} + +Response 200 OK: +{ + "id": "uuid", + "title": "我的精彩直播", + "status": "Active", + "hls_egress_id": "EG_xxx", + "hls_playlist_path": "https://your-cdn.com/hls/{stream-id}/playlist.m3u8", + "hls_started_at": "2026-02-19T15:30:00Z", + ... +} +``` + +**HTML5 视频播放器示例:** + +```html + +``` + +**HLS.js 示例(用于不支持原生 HLS 的浏览器):** + +```javascript +import Hls from "hls.js"; + +const video = document.getElementById("video"); +const hlsUrl = "https://your-cdn.com/hls/{stream-id}/playlist.m3u8"; + +if (Hls.isSupported()) { + const hls = new Hls(); + hls.loadSource(hlsUrl); + hls.attachMedia(video); + hls.on(Hls.Events.MANIFEST_PARSED, () => { + video.play(); + }); +} else if (video.canPlayType("application/vnd.apple.mpegurl")) { + video.src = hlsUrl; + video.addEventListener("loadedmetadata", () => { + video.play(); + }); +} +``` + +#### 更新缩略图 + +更新或删除直播流缩略图。需要在发布者上具有编辑者角色。 + +```http +PATCH /sphere/livestreams/{id}/thumbnail +Authorization: Bearer {token} +Content-Type: application/json + +{ + "thumbnail_id": "new-file-uuid-here" +} + +Response 200 OK: +{ + "id": "uuid", + "title": "我的精彩直播", + "thumbnail": { + "id": "new-file-uuid-here", + "name": "new-thumbnail.jpg", + "url": "https://...", + "mime_type": "image/jpeg", + "size": 23456 + }, + "updated_at": "2026-02-19T15:30:00Z" +} +``` + +要删除缩略图,发送 `null`: + +```http +PATCH /sphere/livestreams/{id}/thumbnail +Authorization: Bearer {token} +Content-Type: application/json + +{ + "thumbnail_id": null +} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +#### 获取房间详细信息 + +返回实时房间统计信息,包括参与者数量。 + +```http +GET /sphere/livestreams/{id}/details + +Response 200 OK: +{ + "participant_count": 150, + "viewer_count": 150, + "peak_viewer_count": 200 +} +``` + +**授权:** 公开,无需身份验证。 + +### 帖子集成 + +帖子可以将直播流作为嵌入附加,类似于投票和基金。 + +#### 创建带有直播流的帖子 + +```http +POST /sphere/posts +Authorization: Bearer {token} +Content-Type: application/json + +{ + "title": "加入我的直播!", + "content": "我正在直播,快来加入!", + "live_stream_id": "uuid-of-livestream" +} + +Response 200 OK: +{ + "id": "post-uuid", + "title": "加入我的直播!", + "metadata": { + "embeds": [ + { + "type": "livestream", + "id": "uuid-of-livestream" + } + ] + } +} +``` + +#### 更新帖子直播流 + +```http +PATCH /sphere/posts/{post-id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "live_stream_id": "uuid-of-another-livestream" +} + +Response 200 OK +``` + +要从帖子中移除直播流,为 `live_stream_id` 发送 `null`: + +```http +PATCH /sphere/posts/{post-id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "live_stream_id": null +} +``` + +**要求:** + +- 直播流必须存在 +- 直播流必须属于与帖子相同的发布者 +- 只有发布者所有者/编辑者可以附加他们的直播流 + +### 订阅 + +#### 获取订阅发布者的直播流 + +返回当前用户订阅的发布者的完整直播流对象。 + +```http +GET /sphere/publishers/subscriptions/live +Authorization: Bearer {token} + +Response 200 OK: +[ + { + "id": "uuid", + "title": "来自订阅发布者的直播", + "description": "正在直播!", + "room_name": "livestream_xyz789", + "type": "Regular", + "visibility": "Public", + "status": "Active", + "viewer_count": 500, + "peak_viewer_count": 600, + "publisher_id": "uuid", + "publisher": { + "id": "uuid", + "name": "channel", + "nick": "Channel Name", + "picture": {...} + }, + "started_at": "2026-02-19T15:30:00Z", + "created_at": "2026-02-19T15:00:00Z", + "updated_at": "2026-02-19T15:00:00Z" + } +] +``` + +**授权:** 需要身份验证。 + +### 直播聊天 + +直播聊天允许观众发送消息,这些消息通过 LiveKit 数据包实时广播给所有参与者,并存储在数据库中以供历史记录。 + +#### 获取聊天消息 + +返回直播流的聊天消息历史记录。 + +```http +GET /sphere/livestreams/{id}/chat?limit=50&offset=0 +Authorization: Bearer {token} + +Response 200 OK: +[ + { + "id": "uuid", + "live_stream_id": "uuid", + "sender_id": "uuid", + "sender_name": "User123", + "content": "大家好!", + "created_at": "2026-02-19T15:35:00Z", + "deleted_at": null, + "timeout_until": null, + "sender": { + "id": "uuid", + "name": "username", + "nick": "Display Name", + "picture": {...} + } + } +] +``` + +**注意:** `sender` 字段包含预加载的账户数据(未存储在 DB 中)。 + +**授权:** 需要身份验证。 + +#### 发送聊天消息 + +向直播流发送聊天消息。消息保存到数据库并通过 LiveKit 广播给所有参与者。 + +```http +POST /sphere/livestreams/{id}/chat +Authorization: Bearer {token} +Content-Type: application/json + +{ + "content": "大家好!" +} + +Response 200 OK: +{ + "id": "uuid", + "live_stream_id": "uuid", + "sender_id": "uuid", + "sender_name": "User123", + "content": "大家好!", + "created_at": "2026-02-19T15:35:00Z", + "deleted_at": null, + "timeout_until": null +} +``` + +**授权:** 需要身份验证。流必须是活跃的。 + +**实时广播:** 发送消息时,它也会通过 LiveKit 数据包广播给所有连接的参与者。客户端应监听传入的数据消息以显示实时聊天。 + +**客户端处理(Flutter 示例):** + +```dart +room.onDataReceived = (data) { + final message = jsonDecode(utf8.decode(data)); + if (message['senderId'] != null) { + // 收到聊天消息 + addToChatList(ChatMessage( + id: message['id'], + senderId: message['senderId'], + senderName: message['senderName'], + content: message['content'], + createdAt: DateTime.parse(message['createdAt']), + )); + } else if (message['type'] == 'timeout') { + // 用户被禁言 + showMessage('您已被禁言 ${message['durationMinutes']} 分钟'); + } +}; +``` + +#### 删除聊天消息 + +删除聊天消息(软删除)。需要在发布者上具有编辑者角色。 + +```http +DELETE /sphere/livestreams/{id}/chat/{messageId} +Authorization: Bearer {token} + +Response 200 OK +``` + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +#### 禁言用户 + +暂时阻止用户发送聊天消息。需要在发布者上具有编辑者角色。 + +```http +POST /sphere/livestreams/{id}/chat/{messageId}/timeout +Authorization: Bearer {token} +Content-Type: application/json + +{ + "duration_minutes": 10 +} + +esponse 200 OK +``` + +**请求字段:** + +| 字段 | 类型 | 默认值 | 描述 | +| ------------------ | ------- | ------ | -------------------- | +| `duration_minutes` | integer | 10 | 禁言持续时间(分钟) | + +**授权:** 需要身份验证并且在流的发布者上具有编辑者角色。 + +**广播:** 当用户被禁言时,禁言通知会广播给所有参与者。 + +### 实时流更新 + +直播事件(开始、结束、更新)通过 LiveKit 数据包广播给所有连接的参与者。客户端应监听这些事件以实时更新 UI。 + +#### 事件类型 + +| 事件 | 描述 | +| ---------------- | ---------------------------- | +| `stream_started` | 流已开始 | +| `stream_ended` | 流已结束 | +| `stream_updated` | 流元数据(标题、描述)已更新 | + +#### 客户端处理(Flutter 示例) + +```dart +room.onDataReceived = (data) { + final payload = jsonDecode(utf8.decode(data)); + + switch (payload['type']) { + case 'stream_started': + showNotification('${payload['title']} 现在正在直播!'); + break; + case 'stream_ended': + showNotification('直播已结束'); + break; + case 'stream_updated': + updateStreamInfo( + title: payload['title'], + description: payload['description'], + ); + break; + case 'chat_message': + // 处理聊天消息(参见聊天部分) + break; + case 'timeout': + // 处理禁言(参见聊天部分) + break; + } +}; +``` + +#### 事件载荷 + +**stream_started:** + +```json +{ + "type": "stream_started", + "livestream_id": "uuid", + "title": "Stream Title", + "started_at": "2026-02-19T15:30:00Z" +} +``` + +**stream_ended:** + +```json +{ + "type": "stream_ended", + "livestream_id": "uuid", + "ended_at": "2026-02-19T16:00:00Z" +} +``` + +**stream_updated:** + +```json +{ + "type": "stream_updated", + "livestream_id": "uuid", + "title": "New Title", + "description": "New description" +} +``` + +### 直播打赏 + +观众可以为直播流打赏积分以示支持。打赏有助于流在发现中的排名。 + +**打赏分配:** 当直播流结束时,90% 的总正向打赏分配给主播的钱包。负向打赏不计入分配。 + +#### 获取打赏 + +返回直播流的打赏历史记录。 + +```http +GET /sphere/livestreams/{id}/awards?limit=20&offset=0 +Authorization: Bearer {token} + +Response 200 OK: +[ + { + "id": "uuid", + "amount": 100.00, + "attitude": "Positive", + "message": "精彩直播!", + "live_stream_id": "uuid", + "account_id": "uuid", + "created_at": "2026-02-19T15:35:00Z" + } +] +``` + +**授权:** 公开,无需身份验证。 + +#### 打赏直播流 + +打赏直播流(创建支付订单)。 + +```http +POST /sphere/livestreams/{id}/awards +Authorization: Bearer {token} +Content-Type: application/json + +{ + "amount": 100, + "attitude": "Positive", + "message": "精彩直播!" +} + +Response 200 OK: +{ + "order_id": "uuid" +} +``` + +**请求字段:** + +| 字段 | 类型 | 描述 | +| --------- | ------- | -------------- | +| `amount` | decimal | 要打赏的积分数 | +| `message` | string | 可选消息 | + +**授权:** 需要身份验证。 + +**发现排名:** 直播流按 `(TotalAwardScore * 10) + ViewerCount` 排名,因此获得打赏的流在发现中排名更高。 + +**实时通知:** 当直播流收到打赏时,所有参与者通过 LiveKit 数据包接收 `stream_awarded` 事件。 + +#### 获取打赏排行榜 + +返回直播流的顶级贡献者。 + +```http +GET /sphere/livestreams/{id}/awards/leaderboard?limit=10 +Authorization: Bearer {token} + +Response 200 OK: +[ + { + "rank": 1, + "account_id": "uuid", + "sender_name": "TopFan", + "total_amount": 500.00, + "award_count": 5, + "account": { + "id": "uuid", + "name": "username", + "nick": "Display Name", + "picture": {...} + } + } +] +``` + +**授权:** 公开,无需身份验证。 + +**发现排名:** 直播流按 `(TotalAwardScore * 10) + ViewerCount` 排名,因此获得打赏的流在发现中排名更高。 + +**实时通知:** 当直播流收到打赏时,所有参与者通过 LiveKit 数据包接收 `stream_awarded` 事件。 + +#### 获取活跃打赏 + +返回直播流的活跃打赏及其剩余高亮持续时间。当主播高亮打赏时,打赏变为活跃状态(1 积分 = 2 秒高亮时间)。 + +```http +GET /sphere/livestreams/{id}/awards/active +Authorization: Bearer {token} + +Response 200 OK: +[ + { + "id": "uuid", + "amount": 100.00, + "attitude": "Positive", + "message": "精彩直播!", + "live_stream_id": "uuid", + "account_id": "uuid", + "sender_name": "FanUser", + "created_at": "2026-02-19T15:35:00Z", + "highlight_duration_seconds": 200, + "highlight_started_at": "2026-02-19T15:40:00Z", + "highlight_ends_at": "2026-02-19T15:43:20Z", + "account": { + "id": "uuid", + "name": "username", + "nick": "Display Name", + "picture": {...} + } + } +] +``` + +**注意:** `highlight_duration_seconds` 在高亮时计算(金额 × 2 秒),不存储在 DB 中。`highlight_ends_at` 从 `highlight_started_at` + 持续时间计算。 + +**授权:** 公开,无需身份验证。 + +## 使用流程 + +### 对于主播 + +**RTMP 流媒体(OBS):** + +1. **创建流** → `POST /sphere/livestreams` +2. **开始流媒体** → `POST /sphere/livestreams/{id}/start`(返回 RTMP URL) +3. **配置 OBS** → 设置 RTMP URL 和流密钥 +4. **开始直播** → 在 OBS 中开始流媒体 +5. **可选:开始 HLS Egress** → 为观众启用 HLS 播放 +6. **可选:开始 RTMP Egress** → 推送到 YouTube/Bilibili +7. **结束流** → `POST /sphere/livestreams/{id}/end` + +**应用内流媒体(移动/Web):** + +1. **创建流** → `POST /sphere/livestreams` +2. **开始流媒体** → `POST /sphere/livestreams/{id}/start` 并设置 `{"no_ingress": true}` +3. **获取令牌** → `GET /sphere/livestreams/{id}/token?identity=streamer_{userId}` +4. **连接并发布** → 使用 LiveKit SDK 广播 +5. **可选:开始 HLS Egress** → 为观众启用 HLS 播放 +6. **结束流** → `POST /sphere/livestreams/{id}/end` + +**HLS 播放流程:** + +1. **开始 HLS Egress** → `POST /sphere/livestreams/{id}/hls` +2. **获取播放列表 URL** → 在响应中返回或 `GET /sphere/livestreams/{id}` +3. **分发 URL** → 与观众分享以进行 HLS 播放 +4. **停止 HLS** → `POST /sphere/livestreams/{id}/hls/stop`(流结束后 URL 被保留以供播放) + +### 对于观众 + +1. **浏览流** → `GET /sphere/livestreams` +2. **获取令牌** → `GET /sphere/livestreams/{id}/token` +3. **连接** → 使用 LiveKit Flutter/Web SDK 和令牌 +4. **订阅** → 监听轨道并渲染视频 + +## 安全考虑 + +1. **授权**:使用基于角色的权限: + - **创建**:需要在任何发布者中拥有成员身份 + - **管理**(开始、停止):需要在流的发布者中具有 `Editor` 角色 + - **Egress / HLS**:需要 `Editor` 角色 **AND** 账户上的 `IsSuperuser`(管理员) + - **聊天**(发送/读取):需要身份验证且流必须是活跃的 + - **聊天**(删除/禁言):需要在流的发布者上具有 `Editor` 角色 +2. **令牌过期**:生成的令牌默认在 4 小时后过期 +3. **可见性**: + - `Public`:任何人都可以查看和加入 + - `Unlisted`:只能通过直接链接访问 + - `Private`:只有授权观看者 +4. **流媒体者检测**:如果用户在发布者上具有编辑者角色,会自动授予流媒体者权限 + +## 故障排除 + +### 流未出现在列表中 + +- 检查 `Status` 是否为 `Active`(不是 `Pending`) +- 验证 OBS 是否已连接并正在流媒体 +- 检查 LiveKit 服务器日志以了解入口问题 + +### 无法更新流 + +- 流必须处于 `Pending` 或 `Ended` 状态才能更新元数据 +- 如果流是 `Active`,必须先结束它:`POST /sphere/livestreams/{id}/end` +- 然后更新:`PATCH /sphere/livestreams/{id}` +- 验证您在发布者上具有编辑者角色 + +### 删除失败 + +- 验证您在发布者上具有编辑者角色 +- 检查 LiveKit 服务器是否可访问(需要清理资源) +- 如果 LiveKit 已关闭,您可能需要手动清理数据库 + +### 令牌过期 + +- 通过 `GET /sphere/livestreams/{id}/token` 重新获取令牌 +- 令牌有效期为 4 小时 + +### Egress 失败 + +- 确保 egress 服务正在运行 +- 验证 RTMP URL 是否有效 +- 检查 Redis 连接性(egress 所需) + +### HLS 播放问题 + +- **播放列表无法访问**:确保 `hls_base_url` 可公开访问或配置 CDN +- **段 404**:验证 LiveKit egress 对存储位置有写入权限 +- **缓冲/延迟**:增加 `segment_duration` 以获得更稳定的播放(权衡:更高延迟) +- **旧段未删除**:设置 `segment_count` 以限制存储使用(例如 20 个段约 2 分钟历史) + +### HLS Egress 已活跃 + +如果您收到 "HLS egress is already active for this stream": + +1. 停止现有的 HLS egress:`POST /sphere/livestreams/{id}/hls/stop` +2. 等待几秒钟进行清理 +3. 再次开始:`POST /sphere/livestreams/{id}/hls` + +## 相关文件 + +如果你想要开发直播房间内的扩展,请参考 [LiveKit 文档](https://docs.livekit.io) + diff --git a/docs/en/solar-network/developers/presence-activity.md b/docs/en/solar-network/developers/presence-activity.md new file mode 100644 index 0000000..de3f109 --- /dev/null +++ b/docs/en/solar-network/developers/presence-activity.md @@ -0,0 +1,330 @@ +--- +title: 在线状态活动 +--- + +## 概述 + +在线状态活动 API 允许用户管理当前活动(如游戏、音乐、健身),通过基于租约的系统实现自动过期。活动可以创建、更新和删除,支持灵活的元数据以及系统生成和用户定义的标识符。 + +## 核心功能 + +- **基于租约的过期**:活动在 1-60 分钟内自动过期,除非续期 +- **灵活的标识符**:支持自动生成的 GUID 和用户定义的 ManualId +- **可扩展的元数据**:JSON 存储的元数据字典,用于自定义开发者数据 +- **软删除**:活动采用软删除方式,自动被过滤 +- **性能优化**:缓存的活动具有 1 分钟过期时间 +- **需要认证**:所有端点都需要有效的用户认证 + +## API 端点 + +## 获取活动 + +获取认证用户的所有当前活动(未过期的在线状态活动)。 + +**端点:** `GET /pass/activities` + +**响应:** + +```json +[ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "type": "Gaming", + "manualId": "game-session-1", + "title": "Playing Cyberpunk 2077", + "subtitle": "Night City Exploration", + "caption": "Missions completed: 15", + "meta": { + "appName": "Cyberpunk 2077", + "platform": "Steam", + "customProperty": "additional data" + }, + "leaseMinutes": 10, + "leaseExpiresAt": "2024-01-15T14:30:00Z", + "accountId": "user-guid", + "createdAt": "2024-01-15T14:25:00Z", + "updatedAt": "2024-01-15T14:25:00Z", + "deletedAt": null + } +] +``` + +**常见响应码:** + +- `200 OK` - 成功,返回活动数组 +- `401 Unauthorized` - 认证无效或缺失 + +--- + +## 创建新活动 + +创建具有可配置租约期的新在线状态活动。 + +**端点:** `POST /pass/activities` + +**请求体:** + +```json +{ + "type": "Gaming", + "manualId": "my-game-session", + "title": "Playing Cyberpunk 2077", + "subtitle": "Night City Mission", + "caption": "Currently exploring downtown", + "meta": { + "appName": "Cyberpunk 2077", + "platform": "Steam", + "difficulty": "Hard", + "mods": ["mod1", "mod2"] + }, + "leaseMinutes": 15 +} +``` + +**响应:** 返回创建的 `SnPresenceActivity` 对象,包含已填充的字段。 + +**字段详情:** + +- `type`:PresenceType 枚举(Unknown、Gaming、Music、Workout) +- `manualId`:可选的用户定义字符串标识符 +- `title`、`subtitle`、`caption`:显示字符串(每个最多 4096 字符) +- `meta`:可选的 `Dictionary`,用于自定义数据 +- `leaseMinutes`:1-60 分钟(默认:5) + +**响应码:** + +- `200 OK` - 活动创建成功 +- `400 Bad Request` - 租约分钟无效或数据格式错误 +- `401 Unauthorized` - 认证无效 + +--- + +## 更新活动 + +使用 GUID 或 ManualId 更新现有活动。仅更新提供的字段。 + +**端点:** `PUT /pass/activities` + +**查询参数:**(需要其一) + +- `id` - 系统生成的 GUID(字符串) +- `manualId` - 用户定义的标识符(字符串) + +**请求体:**(所有字段可选) + +```json +{ + "title": "Updated: Playing Cyberpunk 2077", + "meta": { + "appName": "Cyberpunk 2077", + "platform": "Steam", + "newProperty": "updated data" + }, + "leaseMinutes": 20 +} +``` + +**响应:** 返回更新的 `SnPresenceActivity` 对象。 + +**响应码:** + +- `200 OK` - 活动更新成功 +- `400 Bad Request` - 缺少或无效的 ID 参数 +- `401 Unauthorized` - 认证无效 +- `404 Not Found` - 活动未找到或不属于该用户 + +**示例 cURL:** + +```bash +# 通过 ManualId 更新 +curl -X PUT "/pass/activities?manualId=my-game-session" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"leaseMinutes": 20}' + +# 通过 GUID 更新 +curl -X PUT "/pass/activities?id=550e8400-e29b-41d4-a716-446655440000" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"title": "Updated Title"}' +``` + +--- + +## 删除活动 + +使用 GUID 或 ManualId 软删除活动。 + +**端点:** `DELETE /pass/activities` + +**查询参数:**(需要其一) + +- `id` - 系统生成的 GUID(字符串) +- `manualId` - 用户定义的标识符(字符串) + +**请求体:** 无 + +**响应:** 无内容(204) + +**响应码:** + +- `204 No Content` - 活动删除成功 +- `400 Bad Request` - 缺少或无效的 ID 参数 +- `401 Unauthorized` - 认证无效 +- `404 Not Found` - 活动未找到或不属于该用户 + +**示例 cURL:** + +```bash +# 通过 ManualId 删除 +curl -X DELETE "/pass/activities?manualId=my-game-session" \ + -H "Authorization: Bearer " +``` + +--- + +## 其他端点 + +### 通过账户 ID 获取活动 + +**端点:** `GET /pass/activities/{accountId:guid}` + +用于管理或调试目的。返回指定账户 ID 的活动,无视认证状态。 + +--- + +## 数据模型 + +### PresenceType 枚举 + +```csharp +public enum PresenceType +{ + Unknown, + Gaming, + Music, + Workout +} +``` + +### SnPresenceActivity + +```csharp +public class SnPresenceActivity : ModelBase +{ + public Guid Id { get; set; } // 系统生成的 GUID + public PresenceType Type { get; set; } + public string? ManualId { get; set; } // 用户定义的 ID + public string? Title { get; set; } + public string? Subtitle { get; set; } + public string? Caption { get; set; } + public Dictionary? Meta { get; set; } // JSON 元数据 + public int LeaseMinutes { get; set; } // 租约时长 + public Instant LeaseExpiresAt { get; set; } // 过期时间戳 + + // 继承自 ModelBase + public Guid AccountId { get; set; } + public Instant CreatedAt { get; set; } + public Instant UpdatedAt { get; set; } + public Instant? DeletedAt { get; set; } // 软删除 +} +``` + +## 行为与约束 + +### 租约过期 + +- 当 `SystemClock.Instance.GetCurrentInstant() > LeaseExpiresAt` 时,活动自动过期 +- 过期检查在数据库查询中进行,因此过期的活动会在 GET 操作中被过滤 +- 客户端必须定期更新/续租以保持活动活跃 + +### ID 灵活性 + +- **ManualId**:用户定义的字符串,在用户活动范围内唯一 +- **GUID**:系统生成的,始终唯一,在 API 响应中返回 +- 两者可以互换用于更新和删除 + +### 性能优化 + +- 活动缓存 1 分钟以处理频繁更新 +- 创建/更新/删除操作会使缓存失效 +- 数据库查询自动过滤过期的活动 + +### 安全 + +- 所有操作都限定在认证用户的账户范围内 +- 用户只能管理自己的活动 +- 无效或过期的认证令牌返回 401 Unauthorized + +### 数据存储 + +- 活动存储在 PostgreSQL 中,支持 JSONB 元数据 +- 软删除使用时间戳而非硬删除 +- EF Core 中间件自动处理 CreatedAt/UpdatedAt 时间戳 + +## 使用示例 + +### 游戏会话管理 + +```javascript +// 开始游戏会话 +const activity = await fetch("/pass/activities", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + type: "Gaming", + manualId: "game-session-1", + title: "Playing Cyberpunk 2077", + meta: { appId: "cyberpunk2077", mods: ["photorealistic"] }, + leaseMinutes: 15, + }), +}); + +// 更新进度(延长租约) +await fetch("/pass/activities?manualId=game-session-1", { + method: "PUT", + body: JSON.stringify({ + title: "Playing Cyberpunk 2077 - Level 25", + leaseMinutes: 15, + }), +}); + +// 结束会话 +await fetch("/pass/activities?manualId=game-session-1", { + method: "DELETE", +}); +``` + +### 元数据扩展 + +```javascript +// 丰富的元数据支持 +const activity = { + type: "Music", + manualId: "spotify-session", + title: "Listening to Electronic", + meta: { + spotifyTrackId: "1Je1IMUlBXcx1FzbcXRuWw", + artist: "Purity Ring", + album: "Shrines", + duration: 240000, // 毫秒 + custom: { userRating: 5, genre: "Electronic" }, + }, + leaseMinutes: 30, +}; +``` + +## 错误处理 + +常见错误响应遵循 REST API 约定: + +```json +{ + "type": "Microsoft.AspNetCore.Mvc.ValidationProblemDetails", + "title": "One or more validation errors occurred.", + "status": 400, + "errors": { + "leaseMinutes": ["Lease minutes must be between 1 and 60."] + } +} +``` diff --git a/docs/en/solar-network/developers/realtime-call.md b/docs/en/solar-network/developers/realtime-call.md new file mode 100644 index 0000000..552c0f4 --- /dev/null +++ b/docs/en/solar-network/developers/realtime-call.md @@ -0,0 +1,397 @@ +--- +title: 实时通话 +--- + +本文档介绍 Dysonnetwork 中用于语音/视频通话的实时通话 API。该实现使用 LiveKit 作为底层实时通信提供商。 + +## 概述 + +实时通话 API 提供以下端点: + +- 开始/结束通话 +- 使用认证令牌加入通话 +- 管理通话参与者(踢出、静音) +- 通过定期轮询获取参与者信息 + +**注意:** Webhook 已被定期 GET 请求取代,用于参与者同步。 + +## 基础 URL + +``` +/messager/chat/realtime +``` + +## 认证 + +所有端点都需要在 `Authorization` 头中提供有效的 Bearer 令牌。 + +## 端点 + +### 1. 获取进行中的通话 + +获取聊天室中正在进行的通话信息。 + +**端点:** `GET /{roomId:guid}` + +**响应:** `SnRealtimeCall` + +```json +{ + "id": "uuid", + "roomId": "uuid", + "senderId": "uuid", + "sessionId": "string", + "providerName": "LiveKit", + "endedAt": null, + "createdAt": "2024-01-01T00:00:00Z" +} +``` + +--- + +### 2. 加入通话 + +加入正在进行的通话并获取 LiveKit 认证令牌。 + +**端点:** `GET /{roomId:guid}/join` + +**响应:** `JoinCallResponse` + +```json +{ + "provider": "LiveKit", + "endpoint": "wss://livekit.example.com", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "callId": "uuid", + "roomName": "Call_xxx", + "isAdmin": true, + "participants": [ + { + "identity": "username", + "name": "Display Name", + "accountId": "uuid", + "joinedAt": "2024-01-01T00:00:00Z", + "trackSid": "TR_xxx", + "profile": { + "id": "uuid", + "nick": "nickname", + "joinedAt": "2024-01-01T00:00:00Z" + } + } + ] +} +``` + +**注意:** `isAdmin` 字段表示用户是否可以踢出/静音参与者。以下用户为管理员: + +- 聊天室所有者 +- 私信对话(双方都是管理员) + +--- + +### 3. 获取参与者 + +获取通话中的当前参与者。此端点将参与者从 LiveKit 同步到缓存。 + +**端点:** `GET /{roomId:guid}/participants` + +**响应:** `List` + +```json +[ + { + "identity": "username", + "name": "Display Name", + "accountId": "uuid", + "joinedAt": "2024-01-01T00:00:00Z", + "trackSid": "TR_xxx", + "profile": { ... } + } +] +``` + +**用法:** 定期轮询此端点(例如每 5-10 秒)以获取更新的参与者列表,而不是依赖 webhook。 + +--- + +### 4. 开始通话 + +在聊天室中开始新的通话。 + +**端点:** `POST /{roomId:guid}` + +**响应:** `SnRealtimeCall` + +**错误:** + +- `403` - 非成员或超时 +- `423` - 通话正在进行中 + +--- + +### 5. 结束通话 + +结束正在进行的通话。 + +**端点:** `DELETE /{roomId:guid}` + +**响应:** `204 No Content` + +--- + +### 6. 踢出参与者 + +从通话中踢出参与者。可选择禁止他们进入聊天室。 + +**端点:** `POST /{roomId:guid}/kick/{targetAccountId:guid}` + +**请求体:** + +```json +{ + "banDurationMinutes": 30, + "reason": "Violation of community guidelines" +} +``` + +| 字段 | 类型 | 必填 | 描述 | +| -------------------- | ------ | ---- | ---------------------------------------- | +| `banDurationMinutes` | int | 否 | 禁止进入聊室的时长(0 或 null = 不禁止) | +| `reason` | string | 否 | 踢出/禁止的原因 | + +**响应:** `204 No Content` + +**授权:** 只有聊天室所有者/管理员可以踢出参与者。 + +**行为:** + +- 从 LiveKit 房间中移除参与者 +- 如果 `banDurationMinutes > 0`,则在成员上设置 `TimeoutUntil` 以阻止加入 + +--- + +### 7. 静音参与者 + +静音参与者的音频轨道。 + +**端点:** `POST /{roomId:guid}/mute/{targetAccountId:guid}` + +**响应:** `204 No Content` + +--- + +### 8. 取消静音参与者 + +取消静音参与者的音频轨道。 + +**端点:** `POST /{roomId:guid}/unmute/{targetAccountId:guid}` + +**响应:** `204 No Content` + +--- + +## 数据模型 + +### JoinCallResponse + +```csharp +public class JoinCallResponse +{ + public string Provider { get; set; } // 例如,"LiveKit" + public string Endpoint { get; set; } // LiveKit WebSocket 端点 + public string Token { get; set; } // 用于认证的 JWT 令牌 + public Guid CallId { get; set; } // 通话标识符 + public string RoomName { get; set; } // LiveKit 房间名称 + public bool IsAdmin { get; set; } // 用户是否可以管理参与者 + public List Participants { get; set; } +} +``` + +### CallParticipant + +```csharp +public class CallParticipant +{ + public string Identity { get; set; } // LiveKit 身份(用户名) + public string Name { get; set; } // 显示名称 + public Guid? AccountId { get; set; } // DysonNetwork 账户 ID + public DateTime JoinedAt { get; set; } // 参与者加入时间 + public string? TrackSid { get; set; } // 用于静音的轨道 SID + public SnChatMember? Profile { get; set; } // 聊天成员资料 +} +``` + +### KickParticipantRequest + +```csharp +public class KickParticipantRequest +{ + public int? BanDurationMinutes { get; set; } // 禁止时长(分钟) + public string? Reason { get; set; } // 踢出/禁止的原因 +} +``` + +--- + +## 客户端实现指南 + +### 加入通话 + +```typescript +interface CallJoinResponse { + provider: string; + endpoint: string; + token: string; + callId: string; + roomName: string; + isAdmin: boolean; + participants: CallParticipant[]; +} + +async function joinCall( + roomId: string, + authToken: string, +): Promise { + const response = await fetch(`/api/chat/realtime/${roomId}/join`, { + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + + if (!response.ok) { + throw new Error("Failed to join call"); + } + + return response.json(); +} +``` + +### 轮询参与者 + +```typescript +interface CallParticipant { + identity: string; + name: string; + accountId: string | null; + joinedAt: string; + trackSid: string | null; +} + +async function getParticipants( + roomId: string, + authToken: string, +): Promise { + const response = await fetch(`/api/chat/realtime/${roomId}/participants`, { + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + + if (!response.ok) { + throw new Error("Failed to get participants"); + } + + return response.json(); +} + +// 每 5 秒轮询 +setInterval(async () => { + const participants = await getParticipants(roomId, authToken); + updateParticipantList(participants); +}, 5000); +``` + +### 踢出参与者 + +```typescript +async function kickParticipant( + roomId: string, + targetAccountId: string, + authToken: string, + options?: { banMinutes?: number; reason?: string }, +): Promise { + const response = await fetch( + `/api/chat/realtime/${roomId}/kick/${targetAccountId}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + banDurationMinutes: options?.banMinutes, + reason: options?.reason, + }), + }, + ); + + if (!response.ok) { + throw new Error("Failed to kick participant"); + } +} +``` + +### 静音参与者 + +```typescript +async function muteParticipant( + roomId: string, + targetAccountId: string, + authToken: string, +): Promise { + const response = await fetch( + `/api/chat/realtime/${roomId}/mute/${targetAccountId}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }, + ); + + if (!response.ok) { + throw new Error("Failed to mute participant"); + } +} +``` + +--- + +## 最佳实践 + +1. **轮询策略**:每 5-10 秒轮询 `/participants` 以获取准确的参与者列表。不要依赖 webhook(它们不被使用)。 + +2. **令牌刷新**:LiveKit 令牌在 1 小时后过期。重新获取加入端点以获取新令牌。 + +3. **权限检查**:仅为管理员用户显示踢出/静音按钮(加入响应中的 `isAdmin: true`)。 + +4. **轨道处理**:调用静音/取消静音端点时,请使用参与者数据中的 `trackSid`。请注意,如果参与者尚未发布任何轨道,`trackSid` 可能为 null。 + +5. **错误处理**:妥善处理 403(未授权)、404(无进行中的通话)和网络错误。 + +6. **重连**:实现重连逻辑 - 如果通话结束(获取参与者返回 404),显示适当的 UI。 + +--- + +## 从 Webhook 迁移 + +之前的实现使用 LiveKit webhook 进行参与者更新。这已被定期轮询取代: + +| 之前 | 之后 | +| --------------------- | ---------------------------- | +| Webhook 端点接收事件 | 客户端轮询 GET /participants | +| 通过 webhook 实时更新 | 每 5-10 秒轮询 | +| 服务端参与者追踪 | 客户端在加入时获取并轮询 | + +**迁移步骤:** + +1. 移除 webhook 接收器代码 +2. 在客户端实现轮询 +3. 在通话加入时调用 `/participants` +4. 设置间隔轮询 `/participants` + +--- + +## 相关文档 + +如果你需要开发扩展功能,请参考 [LiveKit 文档](https://docs.livekit.io) diff --git a/docs/en/solar-network/developers/routing.md b/docs/en/solar-network/developers/routing.md new file mode 100755 index 0000000..45ea48b --- /dev/null +++ b/docs/en/solar-network/developers/routing.md @@ -0,0 +1,47 @@ +--- +title: 路由及网关 +description: 了解 Solar Network API 路由的方式 +--- + +众所周知,Solar Network 的服务器是一个微服务项目,所以在访问 API 的时候,您需要注意路径的指定。 + +其构造基本为 `//` + +例如,你需要访问推送服务 (DysonNetwork.Ring) 的通知 API。 + +```bash +export BASE_URL="https://api.solian.app" +export SERVICE_ID="ring" +export PATH="/notifications" +echo $BASE_URL/$SERVICE_ID$PATH +# https://api.solian.app/ring/notifications +``` + +## 服务分工 + +目前来说,Solar Network 服务端有四个各司其职的服务。 + +- Pass 负责身份验证(使用 `id` 访问) +- Ring 负责推送和通知 +- Sphere 负责聊天和帖子以及领域相关的功能 +- Develop 负责开发者相关功能 +- Drive 负责文件上传 + +其各服务的服务 ID 即为小写服务名(不包含 DysonNetwork. 前缀) + +## WebSocket + +WebSocket 由推送服务负责,但是不使用 `/ring` 服务 ID 访问,网关直接处理 `/ws` 的请求。 + +WebSocket 的消息都会使用 WebSocketPacket 结构,其结构如下: + +```json +{ + "type": "包类型", + "data": "包数据,可能为任何结构、类型", + "endpoint": "包需要请求的服务,在服务器传来的包不会携带此项;若客户端需要向服务器发送数据包,需要将此项填写为对应服务的 ID 以帮助网关转发此包", + "error_message": "服务器回传包的错误信息" +} +``` + +在访问 WebSocket 网关的时候,有两种授权方式,一种是通用的 `Authorization` 头。但是由于浏览器无法给 WebSocket 设置请求头,从而提供另一种兼容性选择,将访问令牌放置于 `?tk=` 查询参数中。 diff --git a/docs/en/solar-network/developers/site-templates.md b/docs/en/solar-network/developers/site-templates.md new file mode 100644 index 0000000..e447741 --- /dev/null +++ b/docs/en/solar-network/developers/site-templates.md @@ -0,0 +1,485 @@ +--- +title: 站点模版 +--- + +本指南说明如何为 DysonNetwork.Zone 的 `FullyManaged` 模式构建基于模版的站点。 + +## 概述 + +在 `FullyManaged` 模式下,Zone 会在请求时渲染来自站点文件存储的 `.liquid` 文件。 + +- `.liquid` 文件:由 DotLiquid 渲染 +- 非 `.liquid` 文件:作为静态文件提供(css/js/图片/字体等) + +## 上传和管理文件 + +使用以下文件 API: + +- `GET /zone/sites/{siteId}/files` +- `POST /zone/sites/{siteId}/files/upload` +- `POST /zone/sites/{siteId}/files/folder` +- `PUT /zone/sites/{siteId}/files/edit/{**relativePath}` +- `DELETE /zone/sites/{siteId}/files/delete/{**relativePath}` +- `POST /zone/sites/{siteId}/files/deploy`(zip 部署) + +创建文件夹示例: + +```http +POST /zone/sites/{siteId}/files/folder +Content-Type: application/json + +{ + "path": "templates/partials" +} +``` + +## 路由解析规则 + +Zone 按以下顺序解析路由: + +1. 约定查找 + +- `/` -> `index.html.liquid` +- `/foo` -> `foo.html.liquid` +- `/foo` -> `foo/index.html.liquid` +- 同样的检查也适用于 `templates/` 下 + +1. 可选清单查找 + +- `routes.json` 或 `templates/routes.json` + +1. 回退 404 模版 + +- `404.html.liquid` 或 `templates/404.html.liquid` + +如果没有任何模版/静态文件匹配,请求将回退到应用默认路由。 + +## 布局和局部模版 + +### 布局 + +如果当前模版不是 `layout.html.liquid`,Zone 将查找: + +- `layout.html.liquid`,然后 +- `templates/layout.html.liquid` + +如果找到,渲染的页面内容将被注入到 `content_for_layout` 中。 + +布局使用示例: + +```liquid + + + + {{ content_for_layout }} + + +``` + +### 局部模版 + +您的主题可以使用 Shopify 风格的 `render`: + +```liquid +{% render 'head' %} +{% render 'article', post: post %} +``` + +Zone 将 `render` 注册到 DotLiquid include 行为中,并解析如下候选文件: + +- `templates/head.html.liquid` +- `templates/head.liquid` +- `head.html.liquid` +- `head.liquid` + +## `routes.json` 格式 + +放置在根目录或 `templates/routes.json`。 + +```json +{ + "routes": [ + { + "path": "/", + "template": "templates/index.html.liquid", + "page_type": "home", + "data": { + "mode": "posts_list", + "order_by": "published_at", + "order_desc": true, + "page_size": 10, + "types": ["article"] + } + }, + { + "path": "/posts/{slug}", + "template": "templates/post.html.liquid", + "page_type": "post", + "data": { + "mode": "post_detail", + "slug_param": "slug" + } + }, + { + "path": "/github", + "redirect_to": "https://github.com/your-org/your-repo", + "redirect_status": 302 + } + ] +} +``` + +支持的路由字段: + +- `path`(支持 `{param}` 段占位符) +- `template` +- `redirect_to`(可选重定向目标 URL/路径) +- `redirect_status`(可选;支持 301、302、307、308;默认 302) +- `page_type`(可选) +- `data.mode`:`posts_list` | `post_detail` | `none` +- `data.order_by`、`data.order_desc`、`data.page_size`、`data.types`、`data.publisher_ids`、`data.categories`、`data.tags`、`data.query`、`data.include_replies`、`data.include_forwards`、`data.slug_param` +- `query_defaults` 在模式中被接受,但目前在运行时未应用。 + +## 可用的模版变量 + +Zone 注入的顶级变量: + +- `site` +- `publisher` +- `route` +- `page` +- `posts` +- `post` +- `page_type` +- `asset_url` +- `base_url` +- `config` +- `theme` +- `locale` +- `now` +- `open_graph_tags` +- `feed_tag` +- `favicon_tag` +- `content_for_layout`(仅在使用布局包装时) + +### `site` + +- `id`、`slug`、`name`、`description`、`mode`、`publisher_id`、`config` + +### `publisher` + +- `id`、`type`、`name`、`nick`、`bio` +- `picture_url`、`background_url` +- `picture`(`id`、`name`、`mime_type`、`size`、`url`)(如果存在) +- `background`(`id`、`name`、`mime_type`、`size`、`url`)(如果存在) + +### `route` + +- `path` +- `query`(字典) +- `params`(来自 `{param}` 的字典) +- `index`、`page` + +### `page`(列表页面) + +- `title`、`description`、`posts` +- `current`、`total`、`total_size` +- `prev_link`、`next_link`、`pagination_html` + +### `post` / `page.posts` 项 + +- `id`、`title`、`description`、`slug` +- `layout`、`content`、`excerpt` +- `path`、`url` +- `thumbnail_id`、`thumbnail_url` +- `photos`(图片 URL) +- `attachments`(包含 `id`、`name`、`url`、`mime_type`、`size`、`width`、`height`、`is_image` 的对象) +- `word_count`、`published_at` +- `categories[]`、`tags[]` + +`thumbnail_url` 解析顺序: + +1. `post.meta.thumbnail` 文件 id(`/drive/files/{id}`) +2. `photos` 中的第一张图片(仅限 `article`) +3. `null` + +## 最小启动结构 + +```text +/ + index.html.liquid + layout.html.liquid + 404.html.liquid + routes.json + css/style.css + js/site.js + templates/ + head.html.liquid + article.html.liquid +``` + +## 示例模版 + +### `index.html.liquid` + +```liquid +

{{ site.name }}

+ +{% for post in page.posts %} + {% render 'article', post: post %} +{% endfor %} +``` + +### `templates/article.html.liquid` + +```liquid + +``` + +## 主题提示 + +- 将所有主题局部模版保留在 `templates/` 下以获得可预测的查找。 +- 将 CSS/JS/字体/图片放在静态文件夹(`css/`、`js/`、`images/`)中,并使用根相对 URL 引用。 +- 使用 `site.config` 进行站点级别的样式切换/内容决策。 +- 优先使用路由清单处理文章详情页面(`/posts/{slug}`),而不是在模版中硬编码路径解析。 + +### 可选的资源最小化 + +要启用静态 CSS/JS 响应的运行时最小化: + +```json +{ + "auto_minify_assets": true +} +``` + +注意事项: + +- 适用于 `FullyManaged` 静态资源和 `SelfManaged` 文件服务。 +- 仅 `.css` 和 `.js` 被最小化。 +- `*.min.css` 和 `*.min.js` 文件不会被重新最小化。 + +## 渲染标签、分类和附件 + +### 分类 + +```liquid +{% if post.categories and post.categories.size > 0 %} + +{% endif %} +``` + +### 标签 + +```liquid +{% if post.tags and post.tags.size > 0 %} + +{% endif %} +``` + +### 附件(通用) + +```liquid +{% if post.attachments and post.attachments.size > 0 %} + +{% endif %} +``` + +### 附件(仅图片) + +```liquid +{% if post.attachments and post.attachments.size > 0 %} +
+ {% for file in post.attachments %} + {% if file.is_image %} + {{ file.name }} + {% endif %} + {% endfor %} +
+{% endif %} +``` + +### 现有快捷方式:`post.photos` + +如果您只需要图片 URL,`post.photos` 仍然可用: + +```liquid +{% for image in post.photos %} + +{% endfor %} +``` + +### 调试嵌套值(`photos`、`attachments`、`tags`、`categories`) + +当您直接输出整个对象时,DotLiquid 可能会打印 CLR 类型名称。 +使用以下内置过滤器获得可读输出: + +```liquid +{{ post | json }} +{{ post.attachments | json }} +{{ post.tags | json }} +{{ post.categories | inspect }} +``` + +### Markdown 内容中的 LaTeX + +Zone Markdown 渲染现在支持 LaTeX 语法: + +- 行内:`$E = mc^2$` +- 块级: + +```markdown +$$ +\int_0^1 x^2 \, dx +$$ +``` + +注意:这解析 Markdown 中的数学语法。您的主题仍然需要前端数学渲染器 +如 `layout.html.liquid` 中的 MathJax/KaTeX 来视觉排版公式。 + +## 当前限制 + +- `routes.json` 中的 `query_defaults` 尚未应用。 +- `asset_url` 目前默认为空字符串;对资源使用根相对路径。 +- `open_graph_tags`、`feed_tag` 和 `favicon_tag` 是占位符(默认为空)。 + +## 每个站点的 RSS 配置 + +RSS 通过 `site.config.rss`(在站点创建/更新 API 载荷中)配置。 +您可以选择性地设置顶级 `base_url` 来控制 RSS 和站点地图中生成的绝对 URL。 + +示例: + +```json +{ + "base_url": "https://blog.example.com", + "rss": { + "enabled": true, + "path": "/feed.xml", + "source_route_path": "/posts", + "title": "My Site Feed", + "description": "Latest updates", + "order_by": "published_at", + "order_desc": true, + "item_limit": 30, + "types": ["article", "moment"], + "publisher_ids": [ + "11111111-1111-1111-1111-111111111111", + "22222222-2222-2222-2222-222222222222" + ], + "include_replies": false, + "include_forwards": true, + "categories": ["tech"], + "tags": ["dotnet"], + "query": "release", + "content_mode": "excerpt", + "post_url_pattern": "/posts/{slug}" + } +} +``` + +字段: + +- `enabled`:为此站点打开/关闭 RSS +- `path`:提供 RSS 的请求路径(例如 `/feed.xml`) +- `source_route_path`:可选路由路径(来自 `routes.json`)以重用常规文章页面过滤器 +- `title`、`description`:feed 元数据覆盖 +- `order_by`、`order_desc`、`item_limit`:文章选择和排序 +- `types`:`article` 和/或 `moment` +- `publisher_ids`:feed 的自定义发布者范围(如果为空,仅使用站点发布者) +- `include_replies`:包含/排除回复文章 +- `include_forwards`:包含/排除转发文章 +- `categories`、`tags`、`query`:额外的文章过滤器 +- `content_mode`:`excerpt` | `html` | `none` +- `post_url_pattern`:支持 `{slug}` 和 `{id}` + +注意事项: + +- RSS 服务适用于 `FullyManaged` 站点(在站点中间件中解析)。 +- 请求仍必须针对站点上下文(例如在网关/内部路由流中使用 `X-SiteName`)。 +- 当设置 `source_route_path` 时,RSS 可以继承路由 `data` 过滤器(如 `types`、`categories`、`tags`、`query`、`publisher_ids`);显式 RSS 字段仍优先。 +- `base_url`(或 `site.config.base_url`)在构建 feed 链接时覆盖请求主机。 + +## 每个站点的站点地图配置 + +站点地图通过站点创建/更新载荷中的顶级 `sitemap` 配置(存储在 `site.config.sitemap` 中)。 + +示例: + +```json +{ + "base_url": "https://blog.example.com", + "sitemap": { + "enabled": true, + "path": "/sitemap.xml", + "source_route_path": "/posts", + "item_limit": 2000, + "order_by": "published_at", + "order_desc": true, + "types": ["article", "moment"], + "publisher_ids": [ + "11111111-1111-1111-1111-111111111111" + ], + "include_replies": false, + "include_forwards": true, + "categories": ["tech"], + "tags": ["dotnet"], + "query": "release", + "post_url_pattern": "/posts/{slug}", + "include_home": true, + "include_route_paths": true + } +} +``` + +字段: + +- `enabled`:为此站点打开/关闭站点地图 +- `path`:提供站点地图的请求路径(例如 `/sitemap.xml`) +- `source_route_path`:可选路由路径(来自 `routes.json`)以重用常规文章页面过滤器 +- `item_limit`、`order_by`、`order_desc`:文章选择和排序 +- `types`:`article` 和/或 `moment` +- `publisher_ids`:自定义发布者范围(如果为空,仅使用站点发布者) +- `include_replies`、`include_forwards`:包含/排除回复和转发文章 +- `categories`、`tags`、`query`:额外的文章过滤器 +- `post_url_pattern`:支持 `{slug}` 和 `{id}` +- `include_home`:在站点地图中包含站点根 URL +- `include_route_paths`:包含来自 `routes.json` 的静态路径(仅非参数路由) + +注意事项: + +- 站点地图服务适用于 `FullyManaged` 站点(在站点中间件中解析)。 +- 请求必须针对站点上下文(例如在网关/内部路由流中使用 `X-SiteName`)。 +- `base_url`(或 `site.config.base_url`)在构建站点地图 URL 时覆盖请求主机。 + +## 故障排除 + +### 模版未找到 + +- 验证路由约定和实际文件名。 +- 检查文件是否上传到站点根目录或 `templates/`。 + +### 未渲染文章 + +- 确认 `routes.json` 中的路由 `data.mode` 和 `types`。 +- 验证发布者有可用的文章。 diff --git a/docs/en/solar-network/fitness.md b/docs/en/solar-network/fitness.md new file mode 100644 index 0000000..b5b8a39 --- /dev/null +++ b/docs/en/solar-network/fitness.md @@ -0,0 +1,249 @@ +# Fitness Center + +Track your workouts, log body metrics, and set fitness goals. + +## Introduction + +The Fitness Center is Solar Network's fitness tracking service, designed to help you: + +- Log every workout +- Track body metrics (weight, steps, etc.) +- Set and achieve fitness goals +- Compare workout data with friends + +--- + +## Privacy Settings + +All fitness records (workouts, metrics, goals) have a visibility setting: + +| Setting | Description | +| :--- | :--- | +| Private | Only visible to you | +| Public | Visible to anyone when embedded in a post | + +By default, all records are **Private**. If you want others to see them, you need to set the visibility to "Public". + +--- + +## Logging Workouts + +### Workout Types + +| Type | Value | Description | +| :--- | :--- | :--- | +| Strength Training | 0 | Weightlifting, resistance training | +| Cardio | 1 | Running, cycling, swimming | +| HIIT | 2 | High-Intensity Interval Training | +| Yoga | 3 | Yoga, stretching | +| Other | 4 | Other types of exercise | + +### Log a Workout + +You can record the following information: + +| Field | Description | +| :--- | :--- | +| Name | Name of the workout, e.g., "Morning Run" | +| Type | Workout type | +| Start Time | When the workout started | +| End Time | When the workout ended | +| Duration | Total duration (automatically calculated if not provided) | +| Calories Burned | Estimated calories burned | +| Description | Brief description of the workout | +| Notes | Additional notes | +| Visibility | Private / Public | + +### Additional Data (Meta) + +In addition to basic info, you can also record: + +- **Distance:** Distance for running, cycling, etc. +- **Average Speed:** Average speed during the workout +- **Max Speed:** Highest speed reached +- **Average Heart Rate:** Average heart rate during the workout +- **Max Heart Rate:** Peak heart rate reached +- **Elevation Gain:** Total elevation climbed + +### Sharing Workouts in Posts + +Once you set a workout record to "Public", you can embed it in a post: +Use the fitness_reference field in your post: +workout:{WorkoutID} + + +When posting, an option will appear below the editor allowing you to attach fitness data (workouts, metrics, or goals). + +**Recommended Data Sync Methods** + +**iOS Devices:** We highly recommend using **HealthKit** to sync your workout data. Simply authorize Solar Network to access your health data in settings, and your workouts, steps, heart rate, and more will sync automatically. + +**Android Devices:** Since the Google Fit API has been replaced by Google Health Connect, Android data sync is not currently supported. We are working hard to add support in future updates. + +--- + +## Recording Body Metrics + +### Metric Types + +| Type | Value | Description | +| :--- | :--- | :--- | +| Weight | 0 | Body weight (kg/lbs) | +| Body Fat % | 1 | Body fat percentage | +| Steps | 2 | Daily step count | +| Distance | 3 | Walking/running distance | +| Heart Rate | 4 | Resting or active heart rate | +| Sleep | 5 | Sleep duration/quality | +| Custom | 6 | Other custom metrics | + +### How to Record + +Log your body metrics, including: +- Metric type +- Value +- Unit +- Recorded time +- Notes + +The system will automatically display the latest record for each type, making it easy for you to track changes. + +--- + +## Setting Fitness Goals + +### Goal Types + +| Type | Value | Description | +| :--- | :--- | :--- | +| Weight Loss | 0 | Weight reduction goal | +| Muscle Gain | 1 | Muscle building goal | +| Endurance | 2 | Endurance goals like running or cycling | +| Steps | 3 | Daily step count goal | +| Custom | 4 | Other custom goals | + +### Goal Status + +| Status | Value | Description | +| :--- | :--- | :--- | +| In Progress | 0 | Goal is currently active | +| Completed | 1 | Goal has been achieved | +| Paused | 2 | Goal is temporarily paused | +| Cancelled | 3 | Goal has been cancelled | + +### Automatic Progress Updates + +You can link goals to specific workout or metric types: + +- **Link Workout Type:** Automatically updates progress when you log a workout of the specified type. +- **Link Metric Type:** Automatically updates progress when you log a metric of the specified type. + +For example: +- Link a "Weight Loss" goal with the "Weight" metric. +- Link an "Endurance" goal with the "Cardio" workout type. + +### Recurring Goals + +Goals can be set to repeat: + +| Recurrence Type | Value | Description | +| :--- | :--- | :--- | +| None | 0 | No repetition | +| Daily | 1 | Repeats daily | +| Weekly | 2 | Repeats weekly | +| Monthly | 3 | Repeats monthly | + +You can set the recurrence interval and total number of repetitions. + +### Goal Completion + +When a goal status changes to "Completed": +- If it is a recurring goal, the system will automatically create the next cycle's goal. +- You will receive a notification. + +--- + +## Exercise Library + +The system provides a preset library of exercises, covering: + +### Exercise Categories + +| Category | Value | +| :--- | :--- | +| Chest | 0 | +| Back | 1 | +| Legs | 2 | +| Arms | 3 | +| Shoulders | 4 | +| Core | 5 | +| Cardio | 6 | +| Other | 7 | + +### Exercise Difficulty + +| Difficulty | Value | +| :--- | :--- | +| Beginner | 0 | +| Intermediate | 1 | +| Advanced | 2 | + +### Adding Exercises to Workouts + +You can add multiple exercises to each workout, recording: +- Exercise name +- Sets +- Reps +- Weight +- Notes + +--- + +## Leaderboards + +Compare your stats with friends! + +### Leaderboard Types + +| Type | Description | +| :--- | :--- | +| Calories | Total calories burned | +| Workouts | Total number of workouts completed | +| Goals Completed | Number of goals achieved | + +### Time Periods + +| Period | Description | +| :--- | :--- | +| Daily | Stats for today | +| Weekly | Stats for this week | +| Monthly | Stats for this month | +| All Time | Lifetime stats | + +### Viewing Leaderboards + +Leaderboards only include you and your friends, showing: +- Your rank +- Your value +- The gap between you and your friends + +--- + +## FAQ + +**Can I import data from other apps?** +Yes, the system supports bulk import via external IDs. You can use data from apps like Strava. + +**Do goals update progress automatically?** +Yes, if you link a workout type or metric type, the system will automatically update progress based on your logs. You can also update progress manually. + +**Can workout records be deleted?** +Yes, you can delete any workout, metric, or goal record. Once deleted, the data cannot be recovered. + +**Is my data secure?** +Yes, all private data is visible only to you. You can switch records between public and private at any time. + +**How do I sync my workout data?** + +**iOS Users:** We recommend using **HealthKit**. After authorizing Solar Network to access your health data in settings, your workouts, steps, heart rate, etc., will sync automatically. + +**Android Users:** Currently, Google Fit/Health Connect sync is not supported. We are working hard to add support in future updates. diff --git a/docs/en/solar-network/index.md b/docs/en/solar-network/index.md new file mode 100755 index 0000000..13c85f4 --- /dev/null +++ b/docs/en/solar-network/index.md @@ -0,0 +1,28 @@ +--- +title: Quick Start +--- + +Solar Network is an open-source social networking project by Solsynth. +Operated under the principles of openness, inclusivity, and friendliness, the project was initiated in 2023 and has been online since 2024, marking nearly 2 years of development. + +You can learn more about the Solar Network project via the links below: + +- [Solian](https://github.com/Solsynth/Solian): The main repository for the Solar Network project. +- [Dyson Network](https://github.com/Solsynth/DysonNetwork): The server-side code for Solar Network. +- [Capital](https://solsynth.dev/products/solar-network): The official website for Solar Network. + +### Before You Begin + +Before you start using Solar Network, there is some important information you should be aware of: + +- **Network Configuration:** If you are using proxy tools like Clash with automatic分流 (split tunneling) to access Solar Network from mainland China, you may experience high request latency. This is because our servers are located in Hong Kong and might be routed through a proxy node. We recommend using a direct connection for a better experience. +- **Patience is a Virtue:** Although Solar Network is nominally a project by the Solsynth dev group, in reality, Solsynth currently only has one student member. Therefore, you may encounter various design flaws, missing features, bugs, or errors. Your patience and understanding are greatly appreciated. +- **English Reading Skills:** Although most of Solar Network is localized, the majority of server-side error messages are in English. You may need to use a translator to read them or look up solutions in the documentation before seeking help. +- **Reporting Issues:** You can report problems in our GitHub main repository. Before creating a new Issue, please search to ensure that no one else has already reported the issue and that it hasn't been resolved. + +### Getting Started + +The official Solar Network client supports almost all platforms. +You can find download instructions on the [Solar Network product page](https://solsynth.dev/en/products/solar-network#download) of the Solsynth website. + +After downloading, you can continue reading the next chapters to learn about the Solar Network account system. diff --git a/docs/en/solar-network/livestream.md b/docs/en/solar-network/livestream.md new file mode 100644 index 0000000..070b609 --- /dev/null +++ b/docs/en/solar-network/livestream.md @@ -0,0 +1,80 @@ +--- +title: Livestreaming +description: Start your livestreaming journey on Solar Network. +--- + +The livestreaming feature allows you to create and broadcast video streams using LiveKit as the real-time infrastructure. + +## Feature Overview + +- RTMP streaming via OBS +- Viewers can watch via Web or Mobile +- Supports HLS playback for broader device compatibility +- Push streams to external platforms like YouTube and Bilibili +- Real-time chat for audience interaction +- Viewer tipping support + +## Getting Started + +### Using OBS (PC Streaming) + +1. **Create a Stream** + On the livestreaming page, click "Create Stream" and fill in details like the title and description. + +2. **Get Stream URL** + Click "Start Streaming" to obtain the RTMP URL and Stream Key. + +3. **Configure OBS** + - Service: Custom + - Server: Enter the RTMP URL you obtained + - Stream Key: Enter the Stream Key you obtained + +4. **Start Streaming** + Click "Start Streaming" in OBS to go live. + +### Mobile/Web Streaming + +1. After creating a stream, select the "In-App Streaming" mode. +2. Get the viewing link from the stream room. +3. Start broadcasting directly within the app. + +## Watching Streams + +1. Browse ongoing streams in the livestream list. +2. Click to enter a stream room. +3. Log in to participate in the chat and send tips. + +## Stream Chat + +- All viewers can send chat messages. +- Streamers can delete inappropriate messages or mute users. +- Messages are pushed in real-time to all viewers. + +## Stream Tipping + +Viewers can support streamers by sending tips: + +- Tips will boost the stream's ranking on the discovery page. +- Tips can include a message. +- Streamers can highlight tips (1 Credit = 2 seconds of highlight time). + +## Pushing to External Platforms + +You can push your stream to other platforms simultaneously: + +1. After starting your stream, click "Push to External Platform". +2. Enter the target platform's RTMP URL. +3. Your stream will broadcast on multiple platforms at once. + +## Frequently Asked Questions + +**Why does the stream say "Not Started"?** +- Confirm that OBS is connected and actively streaming. +- Check if the stream status is set to "Live". + +**Why can't I update stream info?** +- You cannot modify information while the stream is live. +- You need to end the stream first before making edits. + +**Why didn't the ranking change after tipping?** +- Ranking calculations have a slight delay. Please wait a moment and refresh. diff --git a/docs/en/solar-network/notification-preferences.md b/docs/en/solar-network/notification-preferences.md new file mode 100644 index 0000000..2ee7e5f --- /dev/null +++ b/docs/en/solar-network/notification-preferences.md @@ -0,0 +1,51 @@ +--- +title: Notification Preferences +--- + +Control how you receive different types of notifications. You can adjust these settings in your Account Settings. + +## Introduction + +You can set the delivery method for different notification types: + +- **Normal** - Receive fully (Stored + Push + Real-time notifications) +- **Silent** - Store only (No push, no real-time notifications) +- **Muted** - Completely off (Not stored, not sent) + +## Three Notification Levels + +| Level | Description | +|-------|-------------| +| Normal | You will receive push notifications and real-time alerts, and they will be saved in your notification history. | +| Silent | Notifications are only saved to the Notification Center without disturbing you (no push, no alerts). | +| Muted | You will not receive or save this type of notification at all. | + +## Common Notification Types + +| Notification Type | Description | +|-------------------|-------------| +| `posts.mentions.new` | Someone mentioned you in a post | +| `post.replies` | A post received a reply | +| `posts.reactions.new` | A post received a reaction (like, etc.) | +| `posts.awards.new` | A post received a badge/award | +| `subscriptions.discontinued_in_app` | A subscription has been canceled | +| `subscriptions.begun` | A subscription has started | +| `gifts.claimed` | A gift has been claimed | +| `wallets.transactions` | Wallet transaction records | +| `auth.verification` | Authentication verification | +| `invites.realms` | Realm invitations | +| `livestream.started` | Livestream has started | + +## How to Set Up + +You can manage these notification preferences in your settings: + +1. Go to **Account** > **Account Settings** > **Notification Preferences** +2. Find the notification type you want to adjust +3. Select the delivery level: **Normal**, **Silent**, or **Muted** + +For example: + +- If you don't want to miss any important messages, keep it **Normal** +- If you want to view them later but don't want to be disturbed, choose **Silent** +- If you don't care about a certain type of notification at all, choose **Muted** diff --git a/docs/en/solar-network/pages.md b/docs/en/solar-network/pages.md new file mode 100644 index 0000000..7b72305 --- /dev/null +++ b/docs/en/solar-network/pages.md @@ -0,0 +1,38 @@ +--- +title: Site Hosting +--- + +Solar Network Pages is the site hosting service provided by Solar Network. It conveniently helps you host static sites or utilize Solar Network's resources to dynamically render your site. + +## Hosting Modes + +Solar Network Pages offers two hosting modes: Fully Managed and Self-Hosted. + +### Fully Managed + +The Fully Managed mode has been updated in the new version to use a template engine for rendering. Instead of pre-made pages, you can now customize your site pages 100%. If you want to dive deeper into development on this topic, please visit the Site Templates page under the "Open Platform" section. + +### Self-Hosted + +Self-Hosted is a static page hosting mode. We do not process your files in any way; we simply host them directly. + +## Limits + +Solar Network Pages has certain restrictions on your sites. The most critical one is that the resource files for each site must not exceed **25MB** in size. + +Additionally, regular users and Stellar Program members have different quotas: + +- Regular Users: 1 site +- Stellar Members: 2 sites +- Nova Members: 3 sites +- Supernova Members: 5 sites + +## Domains + +We will automatically generate a `.solian.page` domain for your site to use. + +If you wish to use a custom domain, since we currently lack an automated configuration system, you will need to subscribe to the highest tier of the Stellar Program (excluding those obtained via redemption) and follow these steps: + +1. Set your domain's CNAME record to point to `api.solian.app` +2. Submit a support ticket explaining your domain change request +3. Wait 1-3 business days for it to take effect diff --git a/docs/en/solar-network/posts.md b/docs/en/solar-network/posts.md new file mode 100644 index 0000000..269a2e3 --- /dev/null +++ b/docs/en/solar-network/posts.md @@ -0,0 +1,164 @@ +--- +title: Posts, Publishing & Discovery +description: Learn about Posts, the primary community feature on Solar Network. +--- + +# Posts, Publishing & Discovery + +Posts are the main content type featured on the Explore page. Posts belong to Publishers and will respect the Publisher's specific settings. + +## Content + +Solar Network posts support full CommonMark, parts of GitHub Flavored Markdown, as well as some custom syntax. + +### Solar Network Flavored Markdown + +This section introduces Solar Network's special Markdown syntax. + +#### Highlight +Similar to Obsidian, use the `==Text==` syntax. + +#### Spoiler / Hidden Text +Use the `=!Text!=` syntax. Click to reveal. + +!!! warning + + If you use this syntax in a post, please include some introductory text at the beginning (around 80 characters). Otherwise, push notifications might accidentally "spoil" the hidden content! + +#### Stickers +You can use stickers in your posts as well—simply type the placeholder, for example: `:prefix+slug:`. + +#### Post Attachments +Use the standard Markdown syntax `![](url)`. +However, if you enter `solian://files/` as the URL, it will load our native attachment preview for a better display experience, including features like zooming. + +### Attachments + +Solar Network posts support a wide variety of file types, from images and audio to general files. All are supported, and you can mix and match them, adding up to 16 attachments at once! Way ahead of the competition. + +For posts containing only images, the client will use a featured carousel display. For posts with mixed attachments, the client will fall back to a standard horizontal scroll list to ensure usability. + +### Tags & Categories + +Adding tags and categories to your posts helps our recommendation algorithm suggest your content to interested users, increasing your initial exposure. + +However, abusing tags and categories violates the ToS and may result in warnings or suspension. Don't be naughty! + +## Feed Ranking Algorithm + +Solar Network's feed (timeline) ranking uses a multi-level scoring system to ensure we recommend the most relevant and interesting content to you. + +### Ranking Modes + +You can choose different browsing modes: + +| Mode | Description | +|------|-------------| +| Personalized | Recommends content based on your interests (Default) | +| Trending | Sorted by interaction热度 (popularity) | +| Latest | Sorted in reverse chronological order by publish time | + +### Scoring Factors + +#### Base Score +Posts are first assigned a base score calculated from the following factors: +- **Reaction Score**: Calculated based on the sentiment of reactions. + - Positive reaction: +2 points + - Neutral reaction: +1 point + - Negative reaction: -2 points +- **Reply Count**: The number of replies a post receives. +- **Appreciation Score**: Appreciation received on the post. +- **Article Bonus**: Article-type posts receive extra base points. +- **Time Decay**: As time passes, the score of older posts gradually decreases. + +#### Personalized Score +For logged-in users, the system further adjusts the ranking based on your interests: + +**Interest Sources**: +- Reactions you add to posts. +- Replies you publish. +- Posts you have viewed (limited per user per day). +- Content you explicitly mark as "Interested" or "Not Interested". + +**Interest Tags**: +The system tracks your interest in the following: +- Tags +- Categories +- Publishers + +Interests decay over time, so your recent behavior influences recommendations more than your past behavior. + +#### Publisher Bonus +A Publisher's Social Trust Score also provides an additional ranking bonus for their posts. Publishers with higher trust scores receive a slight boost in recommendations. + +#### Filtering & Diversity +In Personalized mode, the system also applies: +1. **Strength Filtering**: Filters out posts with low interest matching. +2. **Diversity Protection**: Ensures your feed isn't dominated by content from a single publisher. + +### Feedback & Adjustment + +You can influence recommendations in the following ways: +- Liking or Disliking posts. +- Choosing to "Hide" a publisher. +- Adjusting interest preferences in settings. + +These actions directly affect the recommended content you see subsequently. + +### Why wasn't my post recommended? + +If your post wasn't recommended, it could be due to the following reasons: +1. **Late Posting**: Time decay causes older posts to lose score. +2. **Low Interaction**: Reactions, replies, and appreciation all affect the score. +3. **Low Interest Match**: Your post might not match the interests of the target audience. +4. **Low Publisher Trust Score**: This affects the base ranking bonus. +5. **Marked as Not Interested**: If you or your followers have expressed disinterest in similar content. + +## Publishing Settings + +You can configure the default publisher used when posting, replying, and interacting with the Fediverse. + +### Where to Set +Go to **Account** > **Account Settings** > **Publishing Settings**. + +### Configurable Items + +| Setting | Description | +|---------|-------------| +| Default Post Publisher | The publisher used when creating new posts. | +| Default Reply Publisher | The publisher used when replying to posts. | +| Default Fediverse Publisher | The publisher used for Fediverse (e.g., Mastodon) interactions like following, boosting, and reacting. | + +### Setting Priority +When you perform an action, the system selects a publisher in the following order: +1. **Manual Selection**: If you explicitly choose a publisher while posting, your choice takes precedence. +2. **Default Settings**: If you have configured a default publisher, the system uses your set default. +3. **Auto Selection**: Uses the first eligible publisher you own. + +### Usage Scenarios + +**Scenario 1: Separating Personal and Organization Identities** +You have two publishers: +- `@MyPersonalAccount` (Individual Publisher) +- `@MyCompany` (Organization Publisher) + +You want to: +- Use `@MyPersonalAccount` for personal posts. +- Use `@MyCompany` when replying to others. +- Use `@MyCompany` for Fediverse interactions. + +Simply configure these three items separately in your publishing settings. + +**Scenario 2: Only One Publisher** +If you only have one publisher, the system will automatically use it, and no extra configuration is needed. + +**Scenario 3: Fediverse Publisher** +After configuring a default Fediverse publisher, it will be automatically used for the following actions on platforms like Mastodon: +- Following/Unfollowing users. +- Boosting posts. +- Adding reactions to posts. + +### Notes +- You must be a member of a publisher to set it as default. +- A Fediverse publisher must have Fediverse functionality enabled before it can be set as default. +- If your set Fediverse publisher later disables this feature, the system will automatically fall back to another available publisher. diff --git a/docs/en/solar-network/progression.md b/docs/en/solar-network/progression.md new file mode 100644 index 0000000..deb84bb --- /dev/null +++ b/docs/en/solar-network/progression.md @@ -0,0 +1,116 @@ +--- +title: Achievements and Quests System +description: Earn XP, points, and badges by completing quests and achievements! +--- + +# Achievements and Quests System + +Earn experience points, credits, and badges by completing quests and achievements! + +## Introduction + +Solar Network features a comprehensive achievements and quests system. When you perform specific actions (such as posting, sending messages, joining domains, etc.), the system automatically tracks your progress. Once you reach your goals, you'll automatically receive rewards. + +### Reward Types + +| Reward Type | Description | +|-------------|-------------| +| Experience Points (XP) | Level up your account | +| Credits | Spendable in your wallet | +| Badges | Displayed on your profile page | + +### Progress Types + +- **Standard Progress**: Accumulates with each completed action +- **Streak Progress**: Requires consecutive daily activity (breaking the streak resets the counter) + +--- + +## Achievement Catalog + +### First Steps + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Initial Commit | Publish your first post | 100 XP, 10 Credits | +| People Have Emotion | Add your first reaction to a post | 50 XP, 5 Credits | +| Gugugaga | Send your first chat message | 100 XP, 5 Credits | +| Horizons, Broadened | Join a domain | 100 XP, 10 Credits | +| Startup Company | Create a publisher | 100 XP, 10 Credits | + +### Posting Achievements + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Better than Lu You | Publish 9,362 posts | 9,362 XP, 100 Credits, "Better than Lu You" Badge | + +### Editor's Pick Series + +Achieve a certain number of posts being featured by editors: + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Editor's Pick | 1 post featured | 150 XP, 15 Credits, "Editor's Pick" Badge | +| Hall of Fame | 100 posts featured | 5,000 XP, 100 Credits, "Hall of Fame" Badge | + +### Publishing Streak Series + +Post consecutively for multiple days: + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Three-day Build | Post for 3 consecutive days | 200 XP, 20 Credits | +| One-week Columnist | Post for 7 consecutive days | 500 XP, 35 Credits | +| Serial Publisher | Post for 30 consecutive days | 3,000 XP, 90 Credits, "Serial Publisher" Badge | + +### Activity Streak Series + +Maintain daily active logins: + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Daily Standup | Active for 7 consecutive days | 250 XP, 20 Credits | +| Always Online | Active for 30 consecutive days | 1,200 XP, 60 Credits | +| Still Here | Active for 365 consecutive days | 12,000 XP, 365 Credits, "Still Here" Badge | + +### Stellar Supporter Series + +Support the Stellar Plan: + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Supporter | Subscribe for 1 month | 120 XP, 12 Credits | +| Backer | Subscribe for 3 months | 360 XP, 24 Credits | +| Patron | Subscribe for 6 months | 900 XP, 48 Credits | +| Celestial Patron | Subscribe for 9 months | 1,500 XP, 72 Credits | +| Mega Supporter | Subscribe for 12 months | 2,400 XP, 120 Credits, "Mega Supporter" Badge | + +### Chat Achievements + +| Achievement | Goal | Reward | +|-------------|------|--------| +| Never Touch Grass | Send 10,000 chat messages | 180 XP, 18 Credits, "No-life's Otaku" Badge | + +--- + +## Quest Catalog + +Quests refresh periodically and provide rewards upon completion. + +### Daily Quests + +| Quest | Goal | Reset Cycle | Reward | +|-------|------|-------------|--------| +| Daily Dispatch | Publish one post per day | Daily reset | 25 XP, 2 Credits | + +--- + +## How to Check Your Progress + +1. Go to your profile page +2. Click the **Achievements** or **Quests** tab +3. View your progress and completed items + +!!! tip + + Streak achievements (like consecutive posting or daily login) require consistent daily participation—missing just one day will reset your counter! diff --git a/docs/en/solar-network/publisher.md b/docs/en/solar-network/publisher.md new file mode 100755 index 0000000..dba780a --- /dev/null +++ b/docs/en/solar-network/publisher.md @@ -0,0 +1,106 @@ +--- +title: Publishers and Published Content +description: Learn about publishing content on Solar Network. +--- + +# Publishers and Published Content + +A Publisher is the primary organizational unit for most publicly shared content on Solar Network. A single account can own or join multiple Publishers. + +Before you can publish any content on Solar Network, you need a Publisher identity. + +## Creating a Publisher + +You can create a Publisher by clicking the empty Publisher avatar on the post creation page (available only if you haven't created one yet), or by navigating to the **Creator Center** through your **Account** page. On desktop, you can access the **Creator Center** directly from the sidebar. + +!!! note + + If you see a `Permission required` message when trying to create a Publisher, it means you're missing certain permissions. + Typically, users must confirm their registration via email. Accounts that haven't completed email verification cannot create Publishers. + +A user can own multiple Publishers and can also join different Publishers for collaborative management. + +When creating a Publisher, please note the following: + +1. **Name** — Like account names, Publisher names must be unique across the entire platform and URL-safe. This cannot be changed after creation. +2. **Avatar and Header Image** — When creating or editing a Publisher, uploaded avatars and header images are not applied immediately. You must click **"Save Changes"** to apply them officially. + +## Individual vs. Organization + +Publishers come in two types: **Individual** and **Organization**. Both types can invite others for collaborative management, but they differ in several key ways: + +- **Individual Publishers** will fall back to using the owner's account information (such as avatar and header) when Publisher-specific information is missing. They also display the owner's status and verification badge. +- **Organization Publishers** have a distinctive rounded rectangle avatar (not circular) and must be affiliated with a **Domain**. + +## Collaboration + +Since v3, Publishers can invite others for collaborative management with four default permission levels: + +1. **Owner** — Has all permissions +2. **Manager** — Has all Editor and Observer permissions, plus the ability to invite new collaborators (with permissions no higher than their own) +3. **Editor** — Can publish content under this Publisher +4. **Observer** — Can view internal statistics for this Publisher + +## Developer Publishers + +**Developer** is a special variant of Publisher. Through the **Developer Portal**, you can create a **Developer Identity** for a Publisher to access Solar Network's open APIs. + +See the **Developer Identity** section for more details. + +## Verification Badge + +If you're concerned about impersonation on Solar Network, want to publish authoritative information, or wish to showcase your uniqueness, you can apply for a verification badge. + +> You must have an active **Stellar Plan** subscription to apply for verification. Applications without this subscription will not be processed. The verification badge will remain even after your Stellar Plan subscription expires. + +The application process for Publisher verification is the same as for account verification: send an email explaining your request to `lily[at]solsynth.dev`. Our customer support team will respond within 7 business days. + +> For official verification requests, you'll need to provide supporting documentation. More complete documentation leads to higher approval chances. + +## Visibility Settings + +Visibility settings apply to posts, though different Publisher types have different visibility options available: + +- **Public**: Visible to everyone, including unauthenticated users and via API +- **Friends Only**: Only works for Individual Publishers. Makes posts visible only to the Publisher owner's friends. When used on Organization Publishers, this setting behaves like Private. +- **Private**: Only visible to the Publisher itself +- **Unlisted**: Posts won't appear in recommendation feeds or listings (except to the Publisher owner), but can still be accessed directly via post ID or alias + +### Domain Posts + +Whether Domain posts appear on the homepage depends on whether the user has joined the Domain and the Domain's settings: + +- If the Domain has **Public Mode** enabled, Domain posts will be visible to users who haven't joined the Domain +- If Public Mode is disabled, Domain posts will only appear in the homepage feeds of users who have joined the Domain + +!!! warning + + Domain posts still respect their original visibility settings. Domain settings only affect whether posts appear in recommendations (and general unsorted post listing APIs)—they don't affect the actual post visibility. Users can still access complete Domain post information when querying with the appropriate Domain parameters, including viewing Domain posts on the Publisher's page. + +### Gated Publishers + +**Follow Approval Required** and **Content Visible Only to Followers** are exclusive features available to Stellar Plan Nova tier and above, allowing more granular control over content visibility. + +- For Publishers with **Content Visible Only to Followers** enabled, users won't see this Publisher's content on their homepage unless they're followers. +- For Publishers with **Follow Approval Required** enabled, users can't follow directly from the Publisher page. Instead, they enter a **Pending** state until the Publisher approves their follow request. If a user later unfollows voluntarily, they'll need to reapply to follow again. + +Additionally, for **Follow Approval Required** Publishers, you can manually add users to your follower list. However, note that: + +- Users who have previously unfollowed cannot be added again manually +- Manually added followers don't automatically receive notifications + +!!! warning + + A post's individual visibility setting overrides **Follow Approval Required**. If your post is marked as Friends Only, friends who haven't followed you can still see it. + +## Subscribing to Publishers + +You can subscribe to Publishers to view content from those with **Content Visible Only to Followers** enabled. + +Since the introduction of the **Content Visible Only to Followers** feature, we've added notification level options for subscriptions. If you only want to maintain visibility access, you can disable notifications. + +## Federation Role + +Solar Network's federation operates at the Publisher level. Therefore, you need at least one federated Publisher to interact with federated posts/content. + +Only your public content is involved in federation. However, **Follow Approval Required** content is still sent to federation servers. If you don't want this behavior, please disable federation for the corresponding Publisher. diff --git a/docs/en/solar-network/punishment.md b/docs/en/solar-network/punishment.md new file mode 100644 index 0000000..b8ae726 --- /dev/null +++ b/docs/en/solar-network/punishment.md @@ -0,0 +1,102 @@ +--- +title: Moderation System +description: Learn about Solar Network's moderation policies and how to avoid violations. +--- + +# Moderation System + +Learn about Solar Network's moderation policies and how to avoid violations. + +## What is Moderation? + +Moderation refers to the administrative actions Solar Network takes against users who violate the Terms of Service or Community Guidelines. The purpose of moderation is to maintain community order and protect the rights of other users. + +## Types of Penalties + +| Penalty Type | Description | +|--------------|-------------| +| Warning | A notice to remind you about your behavior; accumulating too many may lead to escalation | +| Login Restriction | Temporary inability to log into your account | +| Account Suspension | Permanent disabling of your account | +| Feature Restrictions | Temporary prohibition from using specific features | + +### Feature Restrictions + +When you receive a feature restriction penalty, certain functionalities will be temporarily disabled, such as: + +- Unable to send chat messages +- Unable to upload files +- Unable to use wallet features + +Restrictions can be single-feature (e.g., chat only) or multi-feature (multiple functions restricted). + +## Relationship with Trust Score + +Solar Network uses a **Social Trust Score** system to evaluate user trustworthiness. Penalties directly impact your Trust Score: + +- **Warning**: Minor score deduction +- **Feature Restrictions**: Score deduction based on violation severity +- **Login Restriction**: Significant score deduction +- **Account Suspension**: Major score deduction, potentially leading to account trust freeze + +The lower your Trust Score, the more restrictions you'll face on the platform. Conversely, maintaining a good Trust Score allows you to enjoy more features and services. + +Learn more: [Social Trust Score System](./social-credit.md) + +## Penalty Notifications + +When you receive a penalty, the system will: + +1. **Send a Push Notification**: An in-app notification explaining the reason and duration of the penalty +2. **Send a Removal Notification**: You'll also be notified when the penalty expires and is lifted + +!!! tip + + Please ensure your notification settings are enabled to stay informed about your account status. + +## Common Reasons for Penalties + +- Posting prohibited content +- Harassing other users +- Spam or flooding messages +- Unauthorized use of automation tools +- Attempting to circumvent platform rules +- Low Trust Score leading to automatic feature restrictions + +## Frequently Asked Questions + +### How long will my penalty last? + +The duration depends on the severity of the violation and your history. Minor violations may result in warnings only, while serious violations could lead to longer restrictions. + +### Can I appeal? + +If you believe the penalty was issued in error, you can submit an appeal through our support channels. We will review your case again. + +### Will penalties be automatically lifted when they expire? + +Yes, most penalties are automatically lifted upon expiration, and your permissions will be restored. The system will send a notification confirming that your penalty has been removed. + +### Do warnings accumulate? + +Yes, multiple warnings may escalate to more severe penalties. Please follow community guidelines to avoid repeated violations. + +### Will penalties affect my Trust Score? + +Yes. Every penalty affects your Social Trust Score. A reduced score may result in: +- Restricted access to certain features +- Reduced visibility in recommendations and feeds +- Additional limitations + +## How to Avoid Penalties + +1. **Follow Terms of Service**: Understand and comply with Solar Network's Terms of Service +2. **Respect Others**: Treat other users kindly and avoid harassment +3. **Avoid Prohibited Content**: Don't post illegal, violent, or inappropriate content +4. **No Unauthorized Automation**: Don't use bots or automated scripts without permission +5. **Secure Your Account**: Don't share your account with others to prevent unauthorized use +6. **Maintain Good Standing**: Actively participate in the community and maintain a healthy Trust Score + +--- + +If you have any questions about penalties, please contact customer support. diff --git a/docs/en/solar-network/social-credit.md b/docs/en/solar-network/social-credit.md new file mode 100644 index 0000000..46b357c --- /dev/null +++ b/docs/en/solar-network/social-credit.md @@ -0,0 +1,95 @@ +--- +title: Social Credit +description: Learn about Solar Network's Social Credit. +--- + +# Social Trust Score + +![Social Credit Meme](https://api.solian.app/drive/files/EIy1WFPlrfP3qGtwb8Nuv) + +Learn how your Social Trust Score on Solar Network affects your experience. + +## What is the Social Trust Score? + +The Social Trust Score is Solar Network's way of measuring a user's trustworthiness. It's calculated based on your behavior and interactions, with a **baseline of 100 points**. A higher score indicates better reputation within the community. + +Your score changes over time to reflect your most recent activities. + +## Trust Tiers + +| Tier | Score Range | Description | +|------|-------------|-------------| +| Poor | < 100 | Low trust; some features may be restricted | +| Normal | 100 - 199 | Standard user; full feature access | +| Excellent | 200+ | High trust; enjoy additional benefits | + +## How Your Score Changes + +### Positive Actions (Score Increase) + +- **Active Engagement**: Posting high-quality content, receiving appreciation +- **Community Contribution**: Helping newcomers, participating in discussions +- **Consistent Activity**: Daily logins, ongoing interaction +- **Long-term good standing** + +### Negative Actions (Score Decrease) + +- **Receiving Penalties**: Warnings, permission restrictions, etc. +- **Policy Violations**: Posting inappropriate content, harassment +- **Expiration of temporary score boosts** + +### Time-Based Effects + +Some score changes are temporary: +- Points from certain positive actions expire after a period +- Minor violations may be forgiven over time + +## Impact of Your Trust Score + +### Benefits of a High Score + +- **Enhanced Visibility**: Your posts get better ranking in feeds +- **Greater Exposure**: High-quality content is more likely to be recommended +- **Community Trust**: Other users are more willing to interact with you +- **Feature Access**: Some advanced features may require a minimum trust tier + +### Consequences of a Low Score + +- **Reduced Visibility**: Posts appear lower in feeds +- **Feature Restrictions**: Certain features may become unavailable +- **Increased Scrutiny**: Low-trust accounts are monitored more closely by the system + +## How to Check Your Score + +1. Go to your profile page +2. View your trust tier in your profile information +3. Some clients display detailed score information in settings + +## Frequently Asked Questions + +### Will my score ever reset to zero? + +No, it won't reset to zero, but it continuously changes based on your behavior. Maintaining good conduct keeps your score at a healthy level. + +### Does my score recover immediately? + +If you consistently demonstrate good behavior, your score will gradually recover. Serious violations may take longer to fully recover from. + +### Why did my score decrease? + +Possible reasons: +- You received a penalty +- Temporary score boosts have expired +- System recalculation revealed historical issues + +### Is my score visible to everyone? + +Your trust tier is displayed on your profile, but the exact numerical score is not publicly shown. + +## How to Maintain a Good Trust Score + +1. **Follow Community Guidelines**: Understand and adhere to the terms of service +2. **Engage Positively**: Share valuable content and participate in community discussions +3. **Avoid Violations**: Don't post inappropriate content or harass others +4. **Stay Active**: Log in daily and remain engaged +5. **Secure Your Account**: Don't share your account with others to prevent unauthorized violations diff --git a/docs/en/solar-network/solarpass-factors.md b/docs/en/solar-network/solarpass-factors.md new file mode 100644 index 0000000..b9dd0b0 --- /dev/null +++ b/docs/en/solar-network/solarpass-factors.md @@ -0,0 +1,147 @@ +--- +title: Authentication Methods +description: Learn about Solar Network's Authentication Methods +--- + +# Authentication Methods + +Learn about the various authentication methods provided by Solar Network to keep your account secure. + +## What are Authentication Methods? + +Authentication methods (also known as "Two-Factor Authentication" or "2FA") add an extra layer of security to your account. Even if someone obtains your password, they cannot log in without your second verification step. + +!!! warning + + **Important**: Before adding any other authentication method, you **must first set up a Recovery Code**. This is your ultimate account recovery option. + +## Available Authentication Methods + +### Password + +The account password you set during registration. This is the basic method for logging in. + +### Email Verification Code + +A one-time code sent to your registered email address. A new code is sent each time you log in. + +### Time-based One-Time Password (TOTP) + +A one-time code generated by a TOTP authenticator app (such as Google Authenticator, Authy, etc.). This method works offline and is more secure and reliable. + +### In-App Notification + +A one-time code delivered via push notification through the Solar Network app. No manual input required—convenient and fast. + +### PIN Code + +A 6-digit numeric PIN. **Note: PIN codes cannot be used for login**, but the system will require you to enter your PIN to confirm high-risk actions (such as deleting your account or making large transfers). + +### Recovery Code + +A backup code for account recovery. If you lose access to your other verification methods (e.g., changing phones or losing email access), your Recovery Code can help you regain access to your account. + +!!! warning + + Your Recovery Code is displayed only once—please store it securely! We recommend using a password manager. + +### Physical Security Key + +You can obtain a physical security key as merchandise from our store or by participating in events. Use it to log in. + +### Passkey + +Verification using your device's biometric features (fingerprint, Face ID) or device passcode. This is the most modern and convenient method—simply use your fingerprint or face to log in. + +--- + +## Recommended Setup Order + +We strongly recommend setting up your authentication methods in the following order: + +1. **First**: Set up your Recovery Code (**required**) +2. **Second**: Set up a Passkey or TOTP (**recommended**) +3. **Optional**: Add Email Verification or In-App Notifications as backups + +--- + +## Managing Your Authentication Methods + +### Where to Set Up + +Go to **Account** > **Account Settings** > **Verification Factors** + +### Available Actions + +| Action | Description | +|--------|-------------| +| View | See your added authentication methods and their status | +| Add | Add a new authentication method | +| Enable | Activate an added but disabled method | +| Disable | Temporarily turn off a method (can be re-enabled) | +| Remove | Permanently delete a method | + +### Enabling an Authentication Method + +Some methods need to be enabled after being added. To enable, you'll need to enter a verification code generated by that method. + +### Removing an Authentication Method + +Once removed, the method can no longer be used for login. You can always add it again later. + +--- + +## Trust Levels + +Different authentication methods have different trust levels: + +| Authentication Method | Trust Level | Description | +|-----------------------|-------------|-------------| +| Recovery Code | 0 (Lowest) | For account recovery only | +| Password | 1 | Basic login method | +| PIN Code | 1 | For confirming high-risk actions | +| Email Verification Code | 2 | Sent via email | +| In-App Notification | 2 | Delivered via app push | +| Time-based One-Time Password (TOTP) | 3 (Highest) | The most secure method | + +During login, the system will require you to complete the appropriate number of verification steps based on your configured methods. + +--- + +## Frequently Asked Questions + +### Can I add multiple verification methods of the same type? + +No. You can only add one method per type. + +### What should I do if I lose my phone and can't receive verification codes? + +If you've already set up a Recovery Code, you can use it to log in and reset your other verification methods. + +### Can I log in with just one verification method? + +Yes, but we recommend setting up at least two methods for better security. A password is required, and adding any other method will enable two-factor authentication. + +### Will removing a verification method affect my ability to log in? + +No, but you'll lose the security protection that method provides. We recommend always keeping at least two active verification methods. + +### What counts as a high-risk action? + +High-risk actions include: +- Deleting your account +- Making large transfers +- Changing security settings +- Deleting important data + +These actions may require you to confirm with your PIN code. + +--- + +## Security Recommendations + +1. **Always set up your Recovery Code first**: It's your "lifeline" for account recovery. +2. **Set up at least two verification methods**: We recommend Password + Passkey/TOTP. +3. **Store your Recovery Code securely**: Use a password manager. +4. **Regularly review your verification methods**: Ensure they're all functional. +5. **Never share your verification codes**: Solar Network staff will never ask you for them. diff --git a/docs/en/solar-network/stellar-program.md b/docs/en/solar-network/stellar-program.md new file mode 100755 index 0000000..250320d --- /dev/null +++ b/docs/en/solar-network/stellar-program.md @@ -0,0 +1,65 @@ +--- +title: Stellar Program +description: The Patronage Plan for Solar Network +--- + +> *Because I am a star, Stellar Stellar —— [Stellar Stellar](https://youtu.be/a51VH9BYzZA?si=yuFvOGtfkuU80hwb)* + +The Stellar Program is the membership system for Solar Network. Subscribers gain access to special perks and privileges, including early access to new features. + +## Tiers + +The Stellar Program consists of three tiers: **Stellar**, **Nova**, and **Supernova**. +Respectively translated as "Stellar," "Nova," and "Supernova" (or informally as "Stellar," "Stellar+," "Stellar++"). + +Each higher tier includes all the features of the previous tier. + +## Perks + +1. **Stellar Tier** + - Colored username + - Early access to new features + - 1.5x Experience gain boost + uptos - Increased quotas for Realms, Bots, and Publishers + +2. **Nova Tier** + - All features of the **Stellar** tier + - Priority feature requests + - 2x Experience gain boost + - Publisher features: "Follow Approval Required" and "Content visible after following" + - Further increased quotas for Realms, Bots, and Publishers + +3. **Supernova Tier** + - All features of the **Nova** tier + - 2.5x Experience gain boost + - Priority customer support + - Source-level technical support + - Significantly increased quotas for Realms, Bots, and Publishers + +## Obtaining + +Users can obtain the Stellar Program via two methods: **In-app purchase using Source Points** or **as a gift when sponsoring the development of Solar Network**. + +!!! warning + + It is currently **not possible** to purchase the **Supernova** tier via in-app **Source Points**, either for gifting or self-use. + +### In-app Purchase + +In-app purchases via Solarpay require you to set up a PIN Code in advance for order payment. For details, please refer to the "Source Points Wallet" section. + +The in-app purchase prices are as follows: + +- **Stellar Tier**: 1200 Source Points/month (requires user level 30) +- **Nova Tier**: 2400 Source Points/month (requires user level 60) +- ~~**Supernova Tier**: 3600 Source Points/month (requires user level 90)~~ + +### Sponsorship (Gift with Support) + +Before obtaining the Stellar Program via sponsorship, you need to link your account from the sponsoring platform in the Account Settings to allow the system to automatically verify your order status. + +Although you may need to provide an order number as proof, linking the corresponding account is still mandatory to receive the subscription. + +You can sponsor LittleSheep to support the development of Solar Network and receive a Stellar Program subscription through the following method: + + diff --git a/docs/en/solar-network/sticker.md b/docs/en/solar-network/sticker.md new file mode 100755 index 0000000..3a0ca8a --- /dev/null +++ b/docs/en/solar-network/sticker.md @@ -0,0 +1,34 @@ +--- +title: Stickers & Sticker Packs +description: Learn about the sticker system on Solar Network +--- + +Stickers are custom emojis on Solar Network. +You can upload images to create emojis, which can be used in almost all places that support Markdown rich text. + +## Creation + +Stickers belong to Sticker Packs, and Sticker Packs belong to Publishers. Therefore, you need a Publisher to create stickers. +You can refer to the "Publishers" section to learn more. + +Sticker Packs support custom icons. If no icon is specified, the first sticker in the pack will be used as the Sticker Pack's icon. + +## Rendering Logic + +Stickers on Solar Network feature adaptive sizing. If a block of text contains only a single sticker, it will be rendered in an enlarged size; otherwise, it will use the normal size. + +- Enlarged: `80x80` +- Normal: `20x20` + +Therefore, you need to upload images that are at least `80x80` pixels to ensure they display clearly. + +## Usage + +In the Solian App, the chat box provides an expandable panel on the left, which helps you select and insert stickers into your messages. +For other places, you can use stickers by manually typing the sticker placeholder. The format is `:prefix+slug:`. +Here, `prefix` is the Sticker Pack prefix, and `slug` is the alias set when the sticker was created. + +In most input fields, as soon as you type the first colon and wait a moment, Solian will trigger autocomplete to help you complete the sticker placeholder. + +Stickers from all Sticker Packs will appear in autocomplete regardless of subscription status, while only subscribed Sticker Packs will be displayed in the sticker picker. +However, subscription status does not affect whether a sticker is rendered or not. diff --git a/docs/en/solar-network/wallet.md b/docs/en/solar-network/wallet.md new file mode 100755 index 0000000..431bd6c --- /dev/null +++ b/docs/en/solar-network/wallet.md @@ -0,0 +1,58 @@ +--- +title: Source Points Wallet +description: Learn about the Solar Network's Source Points Wallet. +--- + +Source Points (NSP) are the virtual currency on Solar Network, which can be used to cover various expenses within the ecosystem. + +## Wallet + +You can create a Source Points wallet by navigating to the "Wallet" page in Solian after logging in. Please note that you will not receive any passive Source Points income until you have created a wallet. + +On the "Wallet" page, you can also view your current balance and transaction details. + +## Payments + +Before making a payment, to ensure that the Source Points wallet is being used by you personally, you need to create a PIN Code authentication factor in "Account Settings". Its Trustworthy level is `0`, meaning it cannot be used for logging in and serves solely for identity verification. + +The Solian client will store an encrypted version of your PIN Code locally under secure conditions after the first entry, enabling quick biometric authentication. + +Source Points (NSP) are the basic monetary unit of Solar Network, which you can use to purchase certain virtual resources. + +**Note: Solar Network's Source Points cannot be topped up/purchased. Anyone selling Source Points is a scammer.** + +## Earning + +You can earn Source Points through daily check-ins. However, check-ins will not reward any Source Points until the user has created a wallet. + +*The content below has not been implemented yet.* + +Additionally, you can earn Source Points by actively participating in activities on Solar Network. After reaching a certain level, you can join the "Creator Reward Program" to earn Source Points when publishing posts, images, and other content. + +For details, please refer to the "Creator Reward Program" section. + +*End of unimplemented content section.* + +> **Did you know?** +> The in-game currency of GoatCraft, the Minecraft server operated by Solsynth, is interchangeable with Source Points. Although it falls under the scope of Solar Network, it is undoubtedly a great way to earn Source Points. + +## Spending + +There are many scenarios on Solar Network that require Source Points. Below are some examples: + +- Asking SN/MiChan questions +- Rewarding posts +- Redeeming Stellar Plans +- Tipping streamers +- Uploading larger files + +## Golden Sun Points + +Golden Points are a relatively rare currency on Solar Network. Currently, the only way to obtain them is by giving money to the LittleSheep purchasing a Stellar Plan. + +!!! note + + Stellar Plans redeemed within the app using Source Points will not grant Golden Point rewards. + +The amount of Golden Points you receive varies depending on the tier of the Stellar Plan subscription. Currently, the primary use of Golden Points is to provide Boosts to Realms, unlocking more features. +We are working hard to expand the capabilities of Golden Points. Stay tuned! diff --git a/docs/en/standards/index.md b/docs/en/standards/index.md new file mode 100755 index 0000000..5a6dbad --- /dev/null +++ b/docs/en/standards/index.md @@ -0,0 +1,17 @@ +--- +title: About standards +description: Understand the importance of Solsynth Works standards and their role in the ecosystem. +--- + +Solsynth Works is a set of principles that Solsynth follows when designing and developing products. It is used to standardize collaboration and improve efficiency. + +These standards cover all aspects from product design to coding and team collaboration, helping us build high-quality, user-friendly products. All Solsynth projects follow these standards to ensure consistency and maintainability. + +### Why should standards be followed? + +- **Increase Efficiency**: A unified process reduces communication costs and speeds up development. +- **Ensure Quality**: Standardized practices guarantee high-quality product output. +- **Easy to Maintain**: Consistent code and design styles make it easier for the team to maintain and expand. +- **Open Collaboration**: Clear standards make it easier for external contributors to participate in the project. + +For external contributors, reading these standards can increase the likelihood of your Pull Requests being accepted. It is recommended to familiarize yourself with these principles before starting contributions and ensure that your code and documentation meet the requirements. diff --git a/docs/en/standards/media-production.md b/docs/en/standards/media-production.md new file mode 100755 index 0000000..c120e87 --- /dev/null +++ b/docs/en/standards/media-production.md @@ -0,0 +1,6 @@ +--- +title: Media Production +description: Learn about Solsynth Works' standards and best practices in media production such as videos. +--- + +Since Yang is not familiar with Media Production now, Solsynthizers will write this part. diff --git a/docs/en/standards/programming.md b/docs/en/standards/programming.md new file mode 100755 index 0000000..b40f259 --- /dev/null +++ b/docs/en/standards/programming.md @@ -0,0 +1,54 @@ +--- +title: Coding Standards +description: Learn about Solsynth Works' coding standards and best practices. +--- + +Solsynth started as a software company, +making the coding standards at Solsynth Works particularly critical. +We have established a set of coding standards to ensure code quality, +consistency, and maintainability. + +## Code Style + +We adopt a unified code style guide to ensure consistency across the entire project. + +1. Braces + - Use K&R style braces. + - The opening brace for function definitions and control structures should be on the same line as the keyword. + - Example: + ```c + if (condition) { + // code + } else { + // code + } + ``` +2. Indentation + - Use 4 spaces for indentation. + - Do not use tabs. +3. Naming Conventions + - Use camelCase for variables and functions. + - Special languages (e.g., C#) follow their community conventions. + - Use ALL_CAPS with underscores for constants (e.g., `MAX_VALUE`). + - For C#, use PascalCase (e.g., `MaxValue`). + - For Dart, use camelCase with a leading k (e.g., `kMaxValue`). +4. Comments + - We Solsynthizers pride ourselves on having good coding habits and always writing Clean Code. + - Therefore, we refrain from adding comments to our code unless dealing with highly complex logic/algorithms or specific edge cases. + - Our philosophy is that our code is self-documenting. If an AI can understand our code but you can't, it simply means you need to improve your programming skills. + - For external contributors: If a Solsynthizer cannot understand your code during a PR review, and you haven't provided comments, we suggest you work on your programming skills, close that PR, and call it a night. + +## Implementation Style + +While over-engineering is discouraged, +Solsynthizers should still strive for elegant and efficient code implementation. +Furthermore, judging by our current Team Leader's preferences, +if you are building a system (e.g., an account activation module) that could be reused elsewhere (e.g., for password resets using the same data structure and logic), +you should ensure sufficient extensibility during development. This includes, but is not limited to: + +1. Neutral Naming + - Avoid overly specific names to ensure the code can be reused across different scenarios. +2. Modular Design + - Break functionality into independent modules for easier maintenance and reuse. +3. Sufficient Configurability + - Provide configuration options to adapt to various use cases. diff --git a/docs/en/standards/typography.md b/docs/en/standards/typography.md new file mode 100755 index 0000000..bc4dd8c --- /dev/null +++ b/docs/en/standards/typography.md @@ -0,0 +1,48 @@ +--- +title: Typography +description: Learn about Solsynth Works' typography standards and best practices. +--- + +Typography encompasses not only physical print publications but also the presentation of digital content. +Good typography enhances the readability and user experience of content. +Solsynth Works has established a set of typography standards to ensure that all products and documentation achieve a consistently high level of visual and functional quality. + +## Font Selection + +- Use readable sans-serif fonts as the primary typeface. For example, the iconic brand font for Solar Network is **Nunito**. For CJK (Chinese, Japanese, and Korean) languages, the **Noto Sans** series can be used as a supplement. +- Avoid using too many different fonts; typically, limit usage to no more than two fonts (one for headings and one for body text) to maintain consistency. +- For scenarios requiring serif fonts, do not mix sans-serif and serif fonts on the same visually observable plane. + +## Font Size and Line Height + +- The recommended body font size is between 16px and 18px to ensure good readability. +- Heading font sizes should be adjusted according to their hierarchy to ensure a clear structure. +- Line height should not be set excessively large to avoid wasting space with too much whitespace and creating visual disconnection. +- A margin of 2x to 3x the font size can be added between different paragraphs to distinguish content blocks. +- When using non-CJK characters within Chinese text, add a space before and after them. + - ✅ `这是一个 Solsynth Works 标准的例子。我们的稳定性达到了 100%,真是可喜可贺!` (This is an example of a Solsynth Works standard. Our stability has reached 100%, which is truly gratifying!) + - ❌ `这是一个Solsynth Works标准的例子。我们的稳定性达到了100%,真是可喜可贺!` +- Pay attention to languages with case distinctions (such as English) and use the correct capitalization for brand names. + - ✅ `Solian / Solar Network / iPhone / iOS / macOS` + - ❌ `solian / solar network / Iphone / ios / MacOS` + - For brand names without a clearly defined capitalization, use Title Case (capitalize the first letter of each word). + - ✅ `Open Source / Pull Request / Code Review / Android` + - ❌ `open source / pull request / code review / android` + +## Wording and Diction + +- Use concise and clear language, avoiding complex sentence structures. +- Avoid excessive jargon. Ensure content is accessible to a broad audience by providing clear explanations when technical terms are first mentioned. Additionally, it is recommended to include the original term in parentheses after the first mention of foreign words. +- If using honorifics (polite language), ensure they are used consistently throughout the entire article. If using non-honorifics, maintain that tone throughout; avoid mixing them. +- Use appropriate punctuation. Choose between Chinese and English punctuation based on the primary language of the sentence, and avoid mixing them. +- Since emojis display inconsistently across different platforms and devices—and most of them aren't particularly cute—it is recommended to avoid using emojis in formal documentation. Instead, use Kaomoji or ASCII art to express emotions or emphasize content. + +## Markdown Writing + +Most of Solsynth Works' documentation and content are written in Markdown format. +Due to the specific nature of Markdown, +an additional blank line is required to be treated as a new paragraph. +Therefore, without using soft breaks, +please ensure that each line in the source file does not exceed 80 characters, +and must not exceed the editor width of VS Code with default sidebars enabled on a 14-inch screen. +Additionally, please ensure there is a blank line between different syntax blocks to ensure correct rendering and clear readability of the source file.