share-home 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-path-routes-manifest.json +19 -0
  3. package/.next/build-manifest.json +20 -0
  4. package/.next/dev/build-manifest.json +18 -0
  5. package/.next/dev/cache/webpack/client-development/0.pack.gz +0 -0
  6. package/.next/dev/cache/webpack/client-development/index.pack.gz +0 -0
  7. package/.next/dev/cache/webpack/server-development/0.pack.gz +0 -0
  8. package/.next/dev/cache/webpack/server-development/index.pack.gz +0 -0
  9. package/.next/dev/react-loadable-manifest.json +1 -0
  10. package/.next/dev/server/app/api/config/route_client-reference-manifest.js +1 -0
  11. package/.next/dev/server/app/api/documents/route_client-reference-manifest.js +1 -0
  12. package/.next/dev/server/app/api/init/route_client-reference-manifest.js +1 -0
  13. package/.next/dev/server/app/api/peers/route_client-reference-manifest.js +1 -0
  14. package/.next/dev/server/app/api/transfer/download/route_client-reference-manifest.js +1 -0
  15. package/.next/dev/server/app/api/transfer/prepare/route_client-reference-manifest.js +1 -0
  16. package/.next/dev/server/app/api/transfer/shared/download/route_client-reference-manifest.js +1 -0
  17. package/.next/dev/server/app/api/transfer/shared/route_client-reference-manifest.js +1 -0
  18. package/.next/dev/server/app/api/transfer/tasks/route_client-reference-manifest.js +1 -0
  19. package/.next/dev/server/app/page_client-reference-manifest.js +1 -0
  20. package/.next/dev/server/app-paths-manifest.json +3 -0
  21. package/.next/dev/server/middleware-build-manifest.js +18 -0
  22. package/.next/dev/server/middleware-react-loadable-manifest.js +1 -0
  23. package/.next/dev/server/next-font-manifest.js +1 -0
  24. package/.next/dev/server/next-font-manifest.json +1 -0
  25. package/.next/dev/server/pages-manifest.json +1 -0
  26. package/.next/dev/server/server-reference-manifest.js +1 -0
  27. package/.next/dev/server/server-reference-manifest.json +5 -0
  28. package/.next/dev/server/vendor-chunks/next@16.2.6_@babel+core@7.29.0_react-dom@19.2.4_react@19.2.4__react@19.2.4.js +3998 -0
  29. package/.next/dev/server/webpack-runtime.js +209 -0
  30. package/.next/dev/static/development/_buildManifest.js +1 -0
  31. package/.next/dev/static/development/_ssgManifest.js +1 -0
  32. package/.next/dev/types/app/layout.ts +87 -0
  33. package/.next/dev/types/app/page.ts +87 -0
  34. package/.next/dev/types/package.json +1 -0
  35. package/.next/diagnostics/build-diagnostics.json +6 -0
  36. package/.next/diagnostics/framework.json +1 -0
  37. package/.next/export-marker.json +6 -0
  38. package/.next/images-manifest.json +68 -0
  39. package/.next/next-minimal-server.js.nft.json +1 -0
  40. package/.next/next-server.js.nft.json +1 -0
  41. package/.next/package.json +1 -0
  42. package/.next/prerender-manifest.json +114 -0
  43. package/.next/react-loadable-manifest.json +1 -0
  44. package/.next/required-server-files.js +337 -0
  45. package/.next/required-server-files.json +337 -0
  46. package/.next/routes-manifest.json +147 -0
  47. package/.next/server/app/_global-error/page.js +32 -0
  48. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  49. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
  50. package/.next/server/app/_global-error.html +1 -0
  51. package/.next/server/app/_global-error.meta +16 -0
  52. package/.next/server/app/_global-error.rsc +14 -0
  53. package/.next/server/app/_global-error.segments/_full.segment.rsc +14 -0
  54. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +5 -0
  55. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +5 -0
  56. package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
  57. package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
  58. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  59. package/.next/server/app/_not-found/page.js +7 -0
  60. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  61. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  62. package/.next/server/app/_not-found.html +6 -0
  63. package/.next/server/app/_not-found.meta +16 -0
  64. package/.next/server/app/_not-found.rsc +18 -0
  65. package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -0
  66. package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
  67. package/.next/server/app/_not-found.segments/_index.segment.rsc +5 -0
  68. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
  69. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
  70. package/.next/server/app/_not-found.segments/_tree.segment.rsc +4 -0
  71. package/.next/server/app/api/board/sync/route.js +1 -0
  72. package/.next/server/app/api/board/sync/route.js.nft.json +1 -0
  73. package/.next/server/app/api/board/sync/route_client-reference-manifest.js +1 -0
  74. package/.next/server/app/api/config/route.js +1 -0
  75. package/.next/server/app/api/config/route.js.nft.json +1 -0
  76. package/.next/server/app/api/config/route_client-reference-manifest.js +1 -0
  77. package/.next/server/app/api/documents/route.js +1 -0
  78. package/.next/server/app/api/documents/route.js.nft.json +1 -0
  79. package/.next/server/app/api/documents/route_client-reference-manifest.js +1 -0
  80. package/.next/server/app/api/documents/sync/route.js +1 -0
  81. package/.next/server/app/api/documents/sync/route.js.nft.json +1 -0
  82. package/.next/server/app/api/documents/sync/route_client-reference-manifest.js +1 -0
  83. package/.next/server/app/api/init/route.js +1 -0
  84. package/.next/server/app/api/init/route.js.nft.json +1 -0
  85. package/.next/server/app/api/init/route_client-reference-manifest.js +1 -0
  86. package/.next/server/app/api/peers/route.js +1 -0
  87. package/.next/server/app/api/peers/route.js.nft.json +1 -0
  88. package/.next/server/app/api/peers/route_client-reference-manifest.js +1 -0
  89. package/.next/server/app/api/transfer/download/route.js +1 -0
  90. package/.next/server/app/api/transfer/download/route.js.nft.json +1 -0
  91. package/.next/server/app/api/transfer/download/route_client-reference-manifest.js +1 -0
  92. package/.next/server/app/api/transfer/prepare/route.js +1 -0
  93. package/.next/server/app/api/transfer/prepare/route.js.nft.json +1 -0
  94. package/.next/server/app/api/transfer/prepare/route_client-reference-manifest.js +1 -0
  95. package/.next/server/app/api/transfer/request/route.js +1 -0
  96. package/.next/server/app/api/transfer/request/route.js.nft.json +1 -0
  97. package/.next/server/app/api/transfer/request/route_client-reference-manifest.js +1 -0
  98. package/.next/server/app/api/transfer/shared/download/route.js +1 -0
  99. package/.next/server/app/api/transfer/shared/download/route.js.nft.json +1 -0
  100. package/.next/server/app/api/transfer/shared/download/route_client-reference-manifest.js +1 -0
  101. package/.next/server/app/api/transfer/shared/route.js +1 -0
  102. package/.next/server/app/api/transfer/shared/route.js.nft.json +1 -0
  103. package/.next/server/app/api/transfer/shared/route_client-reference-manifest.js +1 -0
  104. package/.next/server/app/api/transfer/start/route.js +1 -0
  105. package/.next/server/app/api/transfer/start/route.js.nft.json +1 -0
  106. package/.next/server/app/api/transfer/start/route_client-reference-manifest.js +1 -0
  107. package/.next/server/app/api/transfer/tasks/route.js +1 -0
  108. package/.next/server/app/api/transfer/tasks/route.js.nft.json +1 -0
  109. package/.next/server/app/api/transfer/tasks/route_client-reference-manifest.js +1 -0
  110. package/.next/server/app/favicon.ico/route.js +1 -0
  111. package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
  112. package/.next/server/app/favicon.ico.body +0 -0
  113. package/.next/server/app/favicon.ico.meta +1 -0
  114. package/.next/server/app/index.html +6 -0
  115. package/.next/server/app/index.meta +14 -0
  116. package/.next/server/app/index.rsc +21 -0
  117. package/.next/server/app/index.segments/__PAGE__.segment.rsc +10 -0
  118. package/.next/server/app/index.segments/_full.segment.rsc +21 -0
  119. package/.next/server/app/index.segments/_head.segment.rsc +6 -0
  120. package/.next/server/app/index.segments/_index.segment.rsc +5 -0
  121. package/.next/server/app/index.segments/_tree.segment.rsc +5 -0
  122. package/.next/server/app/page.js +115 -0
  123. package/.next/server/app/page.js.nft.json +1 -0
  124. package/.next/server/app/page_client-reference-manifest.js +1 -0
  125. package/.next/server/app-paths-manifest.json +19 -0
  126. package/.next/server/chunks/31.js +2 -0
  127. package/.next/server/chunks/4.js +1 -0
  128. package/.next/server/chunks/404.js +1 -0
  129. package/.next/server/chunks/516.js +1 -0
  130. package/.next/server/chunks/718.js +45 -0
  131. package/.next/server/chunks/887.js +1 -0
  132. package/.next/server/chunks/891.js +18 -0
  133. package/.next/server/functions-config-manifest.json +4 -0
  134. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  135. package/.next/server/middleware-build-manifest.js +1 -0
  136. package/.next/server/middleware-manifest.json +6 -0
  137. package/.next/server/middleware-react-loadable-manifest.js +1 -0
  138. package/.next/server/next-font-manifest.js +1 -0
  139. package/.next/server/next-font-manifest.json +1 -0
  140. package/.next/server/pages/404.html +6 -0
  141. package/.next/server/pages/500.html +1 -0
  142. package/.next/server/pages-manifest.json +4 -0
  143. package/.next/server/prefetch-hints.json +1 -0
  144. package/.next/server/server-reference-manifest.js +1 -0
  145. package/.next/server/server-reference-manifest.json +1 -0
  146. package/.next/server/webpack-runtime.js +1 -0
  147. package/.next/static/JAumJsHdasa7bQ7jxx37q/_buildManifest.js +1 -0
  148. package/.next/static/JAumJsHdasa7bQ7jxx37q/_ssgManifest.js +1 -0
  149. package/.next/static/chunks/148-2d58b90f6dc8cfaf.js +32 -0
  150. package/.next/static/chunks/5d8f0495-d87c92750ebe0885.js +82 -0
  151. package/.next/static/chunks/649-20578e0ca00dbde1.js +14 -0
  152. package/.next/static/chunks/991cd08a-0938e33045166413.js +1 -0
  153. package/.next/static/chunks/app/_global-error/page-a02f063e5aaa162f.js +1 -0
  154. package/.next/static/chunks/app/_not-found/page-5696c6e9b39a4885.js +1 -0
  155. package/.next/static/chunks/app/api/board/sync/route-a02f063e5aaa162f.js +1 -0
  156. package/.next/static/chunks/app/api/config/route-a02f063e5aaa162f.js +1 -0
  157. package/.next/static/chunks/app/api/documents/route-a02f063e5aaa162f.js +1 -0
  158. package/.next/static/chunks/app/api/documents/sync/route-a02f063e5aaa162f.js +1 -0
  159. package/.next/static/chunks/app/api/init/route-a02f063e5aaa162f.js +1 -0
  160. package/.next/static/chunks/app/api/peers/route-a02f063e5aaa162f.js +1 -0
  161. package/.next/static/chunks/app/api/transfer/download/route-a02f063e5aaa162f.js +1 -0
  162. package/.next/static/chunks/app/api/transfer/prepare/route-a02f063e5aaa162f.js +1 -0
  163. package/.next/static/chunks/app/api/transfer/request/route-a02f063e5aaa162f.js +1 -0
  164. package/.next/static/chunks/app/api/transfer/shared/download/route-a02f063e5aaa162f.js +1 -0
  165. package/.next/static/chunks/app/api/transfer/shared/route-a02f063e5aaa162f.js +1 -0
  166. package/.next/static/chunks/app/api/transfer/start/route-a02f063e5aaa162f.js +1 -0
  167. package/.next/static/chunks/app/api/transfer/tasks/route-a02f063e5aaa162f.js +1 -0
  168. package/.next/static/chunks/app/layout-f1c1109b37ee9a67.js +1 -0
  169. package/.next/static/chunks/app/page-d667dde9ae730a9e.js +15 -0
  170. package/.next/static/chunks/be838f7e-d7eb8d1a464523ea.js +1 -0
  171. package/.next/static/chunks/framework-1af2d653ea416252.js +1 -0
  172. package/.next/static/chunks/main-app-2475c374c5ac40b5.js +1 -0
  173. package/.next/static/chunks/main-c04fb4d60a5182d4.js +5 -0
  174. package/.next/static/chunks/next/dist/client/components/builtin/app-error-a02f063e5aaa162f.js +1 -0
  175. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-a02f063e5aaa162f.js +1 -0
  176. package/.next/static/chunks/next/dist/client/components/builtin/global-error-9771ee9f95e5e628.js +1 -0
  177. package/.next/static/chunks/next/dist/client/components/builtin/not-found-a02f063e5aaa162f.js +1 -0
  178. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-a02f063e5aaa162f.js +1 -0
  179. package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  180. package/.next/static/chunks/webpack-1bf2fae924e39757.js +1 -0
  181. package/.next/static/css/30baeec9f6889470.css +1 -0
  182. package/.next/static/css/30f58f83a0192172.css +1 -0
  183. package/.next/static/media/0f1bdadaf30e2d5f-s.woff2 +0 -0
  184. package/.next/static/media/22a5144ee8d83bca-s.p.woff2 +0 -0
  185. package/.next/static/media/2c34d62a75506231-s.woff2 +0 -0
  186. package/.next/static/media/601f5c280d60caca-s.woff2 +0 -0
  187. package/.next/static/media/9766a7e9e2e0ad5a-s.woff2 +0 -0
  188. package/.next/static/media/a115172161b307bb-s.woff2 +0 -0
  189. package/.next/static/media/aa016aab0e6d1295-s.woff2 +0 -0
  190. package/.next/static/media/b66cf8e69499582a-s.woff2 +0 -0
  191. package/.next/static/media/d100b2a099e34044-s.woff2 +0 -0
  192. package/.next/static/media/f5271587012faf78-s.p.woff2 +0 -0
  193. package/.next/static/media/f639721981034f88-s.woff2 +0 -0
  194. package/.next/trace +3 -0
  195. package/.next/trace-build +1 -0
  196. package/.next/types/app/api/board/sync/route.ts +351 -0
  197. package/.next/types/app/api/config/route.ts +351 -0
  198. package/.next/types/app/api/documents/route.ts +351 -0
  199. package/.next/types/app/api/documents/sync/route.ts +351 -0
  200. package/.next/types/app/api/init/route.ts +351 -0
  201. package/.next/types/app/api/peers/route.ts +351 -0
  202. package/.next/types/app/api/transfer/download/route.ts +351 -0
  203. package/.next/types/app/api/transfer/prepare/route.ts +351 -0
  204. package/.next/types/app/api/transfer/request/route.ts +351 -0
  205. package/.next/types/app/api/transfer/shared/download/route.ts +351 -0
  206. package/.next/types/app/api/transfer/shared/route.ts +351 -0
  207. package/.next/types/app/api/transfer/start/route.ts +351 -0
  208. package/.next/types/app/api/transfer/tasks/route.ts +351 -0
  209. package/.next/types/app/layout.ts +87 -0
  210. package/.next/types/app/page.ts +87 -0
  211. package/.next/types/cache-life.d.ts +145 -0
  212. package/.next/types/package.json +1 -0
  213. package/.next/types/routes.d.ts +85 -0
  214. package/.next/types/validator.ts +187 -0
  215. package/README.md +85 -0
  216. package/bin/cli.js +51 -0
  217. package/package.json +52 -0
  218. package/public/file.svg +1 -0
  219. package/public/globe.svg +1 -0
  220. package/public/next.svg +1 -0
  221. package/public/vercel.svg +1 -0
  222. package/public/window.svg +1 -0
  223. package/src/services/configService.ts +144 -0
  224. package/src/services/documentService.ts +210 -0
  225. package/src/services/fileService.ts +595 -0
  226. package/src/services/mdnsService.ts +284 -0
  227. package/src/services/socketService.ts +214 -0
  228. package/tsconfig.json +34 -0
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Share Home (局域网协作平台)
2
+
3
+ **Share Home** 是一款专为局域网(Lan)办公打造的**去中心化、私密极速、零配置**的高端协作办公平台。平台融合了极速点对点大文件收发、类飞书云文档知识库协作、以及动态持久化配置等功能,并采用前沿大厂极简 Zinc 中性冷灰视觉规范重塑,为团队局域网协作带来极致高效、安全的轻量化体验。
4
+
5
+ ---
6
+
7
+ ## ✨ 核心特性
8
+
9
+ ### 1. 📂 极速文件流 (File Transfer Hub)
10
+ - **免压极速**:支持局域网内任意大小大文件、文本文件的极速无损传输,不经过外网服务器,速度受限仅取决于局域网物理带宽。
11
+ - **自发现雷达**:基于 mDNS (多播 DNS) 协议,开启即自动捕获同一局域网内所有在线的协作节点,无需手动配置 IP。
12
+ - **断点续传**:基于流式写和 Range 请求,完美支持网络瞬断后的续传保护。
13
+ - **动态路径**:传输文件物理落盘路径支持在系统配置中动态修改。
14
+
15
+ ### 2. 📝 去中心化“飞书云文档”知识库 (Knowledge Base)
16
+ - **混合编写**:打破富文本与代码的壁垒,支持在一个编辑器内自由书写段落,并插入多语种标准 Markdown 代码块。
17
+ - **物理落盘**:文档在服务器端均以标准的 `.md` 格式文件物理存储(包含 Front Matter 元数据头部),对第三方工具(如 Obsidian、VS Code)完全透明且兼容。
18
+ - **防抖自动保存**:内置 `1000ms` 无感防抖自动落盘逻辑,打字即存,免去手动保存烦恼。
19
+ - **多端秒级同步**:采用 WebSocket + 局域网广播机制,本端编辑完成落盘后自动投递至所有在线 Peer 节点。对端接收后不仅刷新浏览器视图,还直接在各自的本地存储路径下物理落盘,实现去中心化的完美分布式备份。
20
+
21
+ ### 3. ⚙️ 动态参数持久化配置 (System Configuration)
22
+ - **零重启应用**:支持在页面中实时更改默认的文件和文档存储目录(默认相对路径为 `./storage`)。
23
+ - **写权限预检**:后端在保存配置时会自动对目标目录执行物理写权限预检测试(通过测试写入并销毁 `.write_test` 文件),校验通过方可应用,防错防灾。
24
+ - **硬核参数展示**:一站式展示本端设备 IP 地址、信道端口、以及系统各底层微服务运行状态。
25
+
26
+ ### 4. 🎨 科技大厂极简视觉 (Premium UI/UX)
27
+ - **Zinc 奢华灰度**:基于大厂高端 Zinc/Slate 冷灰色调进行全面重构,配合精致的毛玻璃材质(Backdrop Filter)与极细微光边框,信息层级清晰、视感大气耐看。
28
+ - **Sidebar 交互规范**:采用 `Sidebar(左侧边栏) + Workspace(右主工作区)` 的专业后台交互体系,配合贝塞尔曲线平滑淡入动效,让每一次切换都纵享丝滑。
29
+
30
+ ---
31
+
32
+ ## 🛠️ 技术栈
33
+
34
+ - **前端核心**:React 19, Next.js 16, TypeScript
35
+ - **视觉层**:Vanilla CSS (CSS Variables), Lucide Icons
36
+ - **通信/发现**:WebSocket (ws), mDNS (bonjour-service)
37
+ - **渲染与高亮**:Prism.js (Tomorrow Dark Theme)
38
+ - **底层存贮**:Node.js fs, path 流式文件系统
39
+
40
+ ---
41
+
42
+ ## 🚀 快速启动
43
+
44
+ 在开始之前,请确保本地已安装 [Node.js](https://nodejs.org/) 以及包管理器 [pnpm](https://pnpm.io/)。
45
+
46
+ ### 1. 克隆并安装依赖
47
+ ```bash
48
+ # 复制项目并进入目录
49
+ git clone https://github.com/18755120710/Share-Home.git
50
+ cd Share-Home
51
+
52
+ # 使用 pnpm 安装依赖
53
+ pnpm install
54
+ ```
55
+
56
+ ### 2. 启动开发服务器
57
+ ```bash
58
+ pnpm dev
59
+ ```
60
+ 启动后,控制台将输出本端设备在局域网中的监听端口及 Web 访问地址。打开浏览器访问对应的 `http://localhost:3000` 或本地 IP 即可进入平台。
61
+
62
+ ### 3. 系统持久化配置
63
+ 项目根目录下会生成一个 `config-settings.json` 隐藏参数配置文件:
64
+ ```json
65
+ {
66
+ "storagePath": "./storage"
67
+ }
68
+ ```
69
+ - **相对路径**:以 `./` 开头,将自动创建于项目根目录下。
70
+ - **绝对路径**:可以直接填写您本机的绝对物理目录路径(如 `D:/MySharedDocuments`)。
71
+
72
+ ---
73
+
74
+ ## 🌐 局域网协同要点
75
+
76
+ 为了让多台设备相互发现并进行极速协同,请确保:
77
+ 1. 所有协作设备均接入**同一局域网**(或连接同一 Wi-Fi)。
78
+ 2. 本机防火墙已为 Node.js / Next.js 放行相应的 TCP **网络端口端口**。
79
+ 3. 如果在协同编辑文档或收发大文件时遇到握手失败,建议手动重启一下各端开发服务。
80
+
81
+ ---
82
+
83
+ ## 📄 开源许可证
84
+
85
+ 本项目基于 MIT License 协议开源。
package/bin/cli.js ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { fork } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ console.log('\x1b[36m%s\x1b[0m', '🚀 正在启动 Share Home 局域网协作平台...');
8
+
9
+ const projectRoot = path.join(__dirname, '..');
10
+
11
+ // 1. 确保在用户当前运行命令的目录下(或宿主临时目录)创建存储空间,实现免侵入、免污染运行
12
+ const userStorageDir = path.join(process.cwd(), 'share-home-storage');
13
+ if (!fs.existsSync(userStorageDir)) {
14
+ try {
15
+ fs.mkdirSync(userStorageDir, { recursive: true });
16
+ fs.mkdirSync(path.join(userStorageDir, 'shared'), { recursive: true });
17
+ console.log(`[CLI] 已在当前目录下初始化物理存储舱: ${userStorageDir}`);
18
+ } catch (err) {
19
+ console.error(`[CLI] 创建本地存储目录失败: ${err.message}`);
20
+ }
21
+ }
22
+
23
+ // 2. 注入环境变量,使 Next.js 服务能够动态读取该路径
24
+ process.env.PORT = '8301';
25
+ process.env.HOSTNAME = '0.0.0.0';
26
+ process.env.NEXT_SHARP_PATH = 'none'; // 避免 sharp 依赖报错
27
+
28
+ // 3. 编程式寻找 Next.js 核心执行文件
29
+ const nextBin = path.join(projectRoot, 'node_modules', 'next', 'dist', 'bin', 'next');
30
+
31
+ if (!fs.existsSync(nextBin)) {
32
+ console.error('\x1b[31m%s\x1b[0m', '❌ 错误: 未能在包中找到 Next.js 核心执行文件,请确保已经完成 pnpm install!');
33
+ process.exit(1);
34
+ }
35
+
36
+ console.log(`[CLI] 正在极速拉起 Web 协同服务与信道,请稍候...`);
37
+
38
+ // 启动 Next.js 生产服务
39
+ const nextStart = fork(nextBin, ['start', '-p', '8301', '-H', '0.0.0.0'], {
40
+ cwd: projectRoot,
41
+ env: {
42
+ ...process.env,
43
+ // 强制指定项目的物理存储目录,解决宿主物理存储隔离问题
44
+ CUSTOM_STORAGE_PATH: userStorageDir
45
+ }
46
+ });
47
+
48
+ nextStart.on('exit', (code) => {
49
+ console.log(`[CLI] Next.js 服务进程已退出,代码: ${code}`);
50
+ process.exit(code || 0);
51
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "share-home",
3
+ "version": "1.0.0",
4
+ "description": "安全、无压缩的局域网零阻碍点对点极速传输与公共落盘共享空间",
5
+ "bin": {
6
+ "share-home": "bin/cli.js"
7
+ },
8
+ "preferGlobal": true,
9
+ "files": [
10
+ "bin/",
11
+ ".next/",
12
+ "public/",
13
+ "package.json",
14
+ "README.md",
15
+ "src/services/",
16
+ "tsconfig.json"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "scripts": {
22
+ "dev": "next dev --webpack -H 0.0.0.0 -p 8301",
23
+ "build": "next build --webpack",
24
+ "start": "next start -p 8301",
25
+ "lint": "eslint"
26
+ },
27
+ "dependencies": {
28
+ "@tiptap/core": "^3.23.6",
29
+ "@tiptap/extension-placeholder": "^3.23.6",
30
+ "@tiptap/pm": "^3.23.6",
31
+ "@tiptap/react": "^3.23.6",
32
+ "@tiptap/starter-kit": "^3.23.6",
33
+ "bonjour-service": "^1.4.0",
34
+ "lucide-react": "^1.16.0",
35
+ "next": "16.2.6",
36
+ "prismjs": "^1.30.0",
37
+ "react": "19.2.4",
38
+ "react-dom": "19.2.4",
39
+ "tiptap-markdown": "^0.9.0",
40
+ "ws": "^8.21.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20",
44
+ "@types/prismjs": "^1.26.6",
45
+ "@types/react": "^19",
46
+ "@types/react-dom": "^19",
47
+ "@types/ws": "^8.18.1",
48
+ "eslint": "^9",
49
+ "eslint-config-next": "16.2.6",
50
+ "typescript": "^5"
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,144 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ interface AppConfig {
5
+ storagePath: string;
6
+ }
7
+
8
+ export class ConfigService {
9
+ private configFilePath: string;
10
+ private currentConfig: AppConfig;
11
+
12
+ private constructor() {
13
+ this.configFilePath = path.join(process.cwd(), 'config-settings.json');
14
+ this.currentConfig = this.loadConfig();
15
+ }
16
+
17
+ public static getInstance(): ConfigService {
18
+ const globalSymbols = global as any;
19
+ if (!globalSymbols.__config_service_instance__) {
20
+ globalSymbols.__config_service_instance__ = new ConfigService();
21
+ }
22
+ return globalSymbols.__config_service_instance__;
23
+ }
24
+
25
+ /**
26
+ * 从文件载入配置,若无则初始化默认配置
27
+ */
28
+ private loadConfig(): AppConfig {
29
+ const defaultConfig: AppConfig = {
30
+ storagePath: './storage',
31
+ };
32
+
33
+ try {
34
+ if (fs.existsSync(this.configFilePath)) {
35
+ const fileContent = fs.readFileSync(this.configFilePath, 'utf-8');
36
+ const parsed = JSON.parse(fileContent);
37
+ return {
38
+ ...defaultConfig,
39
+ ...parsed,
40
+ };
41
+ }
42
+ } catch (err) {
43
+ console.error('[ConfigService] 载入配置文件失败,将使用默认配置:', err);
44
+ }
45
+
46
+ // 若无配置文件,创建默认的配置文件
47
+ this.saveConfig(defaultConfig);
48
+ return defaultConfig;
49
+ }
50
+
51
+ /**
52
+ * 保存配置到 config-settings.json 文件
53
+ */
54
+ private saveConfig(config: AppConfig): void {
55
+ try {
56
+ fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2), 'utf-8');
57
+ } catch (err) {
58
+ console.error('[ConfigService] 持久化保存配置文件失败:', err);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 获取当前配置
64
+ */
65
+ public getConfig(): AppConfig {
66
+ return { ...this.currentConfig };
67
+ }
68
+
69
+ /**
70
+ * 获取当前已解析的绝对存储路径,若目录不存在则自动创建
71
+ */
72
+ public getStoragePath(): string {
73
+ // 优先读取 CLI 或环境变量中强制指定的存储目录,解决全局发布环境下的宿主文件隔离
74
+ if (process.env.CUSTOM_STORAGE_PATH) {
75
+ const cliPath = process.env.CUSTOM_STORAGE_PATH;
76
+ try {
77
+ if (!fs.existsSync(cliPath)) {
78
+ fs.mkdirSync(cliPath, { recursive: true });
79
+ console.log(`[ConfigService] 已根据环境变量创建存储目录: ${cliPath}`);
80
+ }
81
+ } catch (err) {
82
+ console.error(`[ConfigService] 物理环境变量目录创建失败: ${cliPath}`, err);
83
+ }
84
+ return cliPath;
85
+ }
86
+
87
+ const rawPath = this.currentConfig.storagePath;
88
+ let resolvedPath = '';
89
+
90
+ if (path.isAbsolute(rawPath)) {
91
+ resolvedPath = rawPath;
92
+ } else {
93
+ resolvedPath = path.resolve(process.cwd(), rawPath);
94
+ }
95
+
96
+ // 确保物理目录存在
97
+ try {
98
+ if (!fs.existsSync(resolvedPath)) {
99
+ fs.mkdirSync(resolvedPath, { recursive: true });
100
+ console.log(`[ConfigService] 成功创建存储目录: ${resolvedPath}`);
101
+ }
102
+ } catch (err) {
103
+ console.error(`[ConfigService] 物理目录创建失败: ${resolvedPath}`, err);
104
+ }
105
+
106
+ return resolvedPath;
107
+ }
108
+
109
+ /**
110
+ * 动态更新存储路径配置
111
+ */
112
+ public updateStoragePath(newPath: string): boolean {
113
+ if (!newPath || !newPath.trim()) return false;
114
+
115
+ const sanitizedPath = newPath.trim();
116
+
117
+ // 预检该路径是否能够成功创建或写入
118
+ let testPath = '';
119
+ if (path.isAbsolute(sanitizedPath)) {
120
+ testPath = sanitizedPath;
121
+ } else {
122
+ testPath = path.resolve(process.cwd(), sanitizedPath);
123
+ }
124
+
125
+ try {
126
+ if (!fs.existsSync(testPath)) {
127
+ fs.mkdirSync(testPath, { recursive: true });
128
+ }
129
+ // 成功写入测试文件来验证写权限
130
+ const testFile = path.join(testPath, '.write_test');
131
+ fs.writeFileSync(testFile, 'test');
132
+ fs.unlinkSync(testFile);
133
+ } catch (err) {
134
+ console.error(`[ConfigService] 无法使用此存储路径(无写权限或路径无效): ${sanitizedPath}`, err);
135
+ return false;
136
+ }
137
+
138
+ // 更新内存及文件
139
+ this.currentConfig.storagePath = sanitizedPath;
140
+ this.saveConfig(this.currentConfig);
141
+ console.log(`[ConfigService] 存储路径成功修改为: ${sanitizedPath}`);
142
+ return true;
143
+ }
144
+ }
@@ -0,0 +1,210 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { KBDocument } from '@/types/document';
4
+ import { ConfigService } from './configService';
5
+
6
+ export class DocumentService {
7
+ private constructor() {}
8
+
9
+ public static getInstance(): DocumentService {
10
+ const globalSymbols = global as any;
11
+ if (!globalSymbols.__document_service_instance__) {
12
+ globalSymbols.__document_service_instance__ = new DocumentService();
13
+ }
14
+ return globalSymbols.__document_service_instance__;
15
+ }
16
+
17
+ /**
18
+ * 获取文档保存的 docs 子物理路径
19
+ */
20
+ private getDocsDir(): string {
21
+ const storagePath = ConfigService.getInstance().getStoragePath();
22
+ const docsDir = path.join(storagePath, 'docs');
23
+ if (!fs.existsSync(docsDir)) {
24
+ fs.mkdirSync(docsDir, { recursive: true });
25
+ }
26
+ return docsDir;
27
+ }
28
+
29
+ /**
30
+ * 将文档对象序列化为带 Front Matter 的标准 Markdown 字符串
31
+ */
32
+ private serialize(doc: KBDocument): string {
33
+ // 过滤掉内容里的 yaml 分割符,确保 Front Matter 解析稳健
34
+ return [
35
+ '---',
36
+ `id: ${doc.id}`,
37
+ `title: ${doc.title.replace(/:/g, ' ')}`,
38
+ `senderId: ${doc.senderId}`,
39
+ `senderName: ${doc.senderName}`,
40
+ `senderAvatar: ${doc.senderAvatar}`,
41
+ `updatedAt: ${doc.updatedAt}`,
42
+ `createdAt: ${doc.createdAt}`,
43
+ `type: ${doc.type || 'file'}`,
44
+ `parentId: ${doc.parentId || ''}`,
45
+ '---',
46
+ doc.content
47
+ ].join('\n');
48
+ }
49
+
50
+ /**
51
+ * 从带 Front Matter 的 Markdown 文本中解析出 KBDocument
52
+ */
53
+ private deserialize(fileContent: string): KBDocument | null {
54
+ try {
55
+ const lines = fileContent.split(/\r?\n/);
56
+ if (lines[0]?.trim() !== '---') return null;
57
+
58
+ let endIdx = -1;
59
+ for (let i = 1; i < lines.length; i++) {
60
+ if (lines[i]?.trim() === '---') {
61
+ endIdx = i;
62
+ break;
63
+ }
64
+ }
65
+
66
+ if (endIdx === -1) return null;
67
+
68
+ const metaLines = lines.slice(1, endIdx);
69
+ const content = lines.slice(endIdx + 1).join('\n');
70
+
71
+ const meta: any = {};
72
+ metaLines.forEach(line => {
73
+ const colonIndex = line.indexOf(':');
74
+ if (colonIndex !== -1) {
75
+ const key = line.substring(0, colonIndex).trim();
76
+ const value = line.substring(colonIndex + 1).trim();
77
+ meta[key] = value;
78
+ }
79
+ });
80
+
81
+ return {
82
+ id: meta.id || '',
83
+ title: meta.title || '无标题文档',
84
+ senderId: meta.senderId || 'unknown',
85
+ senderName: meta.senderName || '匿名',
86
+ senderAvatar: meta.senderAvatar || 'avatar-1',
87
+ updatedAt: parseInt(meta.updatedAt || '0', 10) || Date.now(),
88
+ createdAt: parseInt(meta.createdAt || '0', 10) || Date.now(),
89
+ type: (meta.type as 'file' | 'folder') || 'file',
90
+ parentId: meta.parentId || null,
91
+ content: content
92
+ };
93
+ } catch (err) {
94
+ console.error('[DocumentService] 反序列化 Markdown 失败:', err);
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 获取本地所有的文档列表(不包含大体量的 content)
101
+ */
102
+ public getDocuments(): KBDocument[] {
103
+ const docsDir = this.getDocsDir();
104
+ const docs: KBDocument[] = [];
105
+
106
+ try {
107
+ const files = fs.readdirSync(docsDir);
108
+ files.forEach(fileName => {
109
+ if (fileName.endsWith('.md')) {
110
+ const filePath = path.join(docsDir, fileName);
111
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
112
+ const doc = this.deserialize(fileContent);
113
+ if (doc) {
114
+ docs.push(doc);
115
+ }
116
+ }
117
+ });
118
+ } catch (err) {
119
+ console.error('[DocumentService] 读取文档列表异常:', err);
120
+ }
121
+
122
+ // 按最近更新时间倒序排列
123
+ return docs.sort((a, b) => b.updatedAt - a.updatedAt);
124
+ }
125
+
126
+ /**
127
+ * 读取单篇文档的详情
128
+ */
129
+ public getDocumentById(id: string): KBDocument | null {
130
+ const docsDir = this.getDocsDir();
131
+ try {
132
+ const files = fs.readdirSync(docsDir);
133
+ for (const fileName of files) {
134
+ if (fileName.endsWith('.md')) {
135
+ const filePath = path.join(docsDir, fileName);
136
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
137
+ const doc = this.deserialize(fileContent);
138
+ if (doc && doc.id === id) {
139
+ return doc;
140
+ }
141
+ }
142
+ }
143
+ } catch (err) {
144
+ console.error(`[DocumentService] 获取文档详情失败 id: ${id}`, err);
145
+ }
146
+ return null;
147
+ }
148
+
149
+ /**
150
+ * 保存或更新文档落盘
151
+ */
152
+ public saveDocument(doc: KBDocument): boolean {
153
+ const docsDir = this.getDocsDir();
154
+ try {
155
+ // 安全的文件名规范:文档ID_标题.md
156
+ const safeTitle = doc.title.replace(/[\\/:*?"<>|]/g, '_');
157
+
158
+ // 在写入新文件前,若有旧标题同 ID 的 md,先予删除,防止产生垃圾冗余文件
159
+ const files = fs.readdirSync(docsDir);
160
+ for (const fileName of files) {
161
+ if (fileName.endsWith('.md')) {
162
+ const filePath = path.join(docsDir, fileName);
163
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
164
+ const oldDoc = this.deserialize(fileContent);
165
+ if (oldDoc && oldDoc.id === doc.id) {
166
+ fs.unlinkSync(filePath);
167
+ break;
168
+ }
169
+ }
170
+ }
171
+
172
+ // 写入新文件
173
+ const finalFileName = `${doc.id}_${safeTitle}.md`;
174
+ const finalPath = path.join(docsDir, finalFileName);
175
+ const serialized = this.serialize(doc);
176
+
177
+ fs.writeFileSync(finalPath, serialized, 'utf-8');
178
+ console.log(`[DocumentService] 文档成功写入物理落盘: ${finalPath}`);
179
+ return true;
180
+ } catch (err) {
181
+ console.error(`[DocumentService] 文档落盘失败: ${doc.title}`, err);
182
+ return false;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * 删除本地文档
188
+ */
189
+ public deleteDocument(id: string): boolean {
190
+ const docsDir = this.getDocsDir();
191
+ try {
192
+ const files = fs.readdirSync(docsDir);
193
+ for (const fileName of files) {
194
+ if (fileName.endsWith('.md')) {
195
+ const filePath = path.join(docsDir, fileName);
196
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
197
+ const doc = this.deserialize(fileContent);
198
+ if (doc && doc.id === id) {
199
+ fs.unlinkSync(filePath);
200
+ console.log(`[DocumentService] 文档物理文件已删除: ${filePath}`);
201
+ return true;
202
+ }
203
+ }
204
+ }
205
+ } catch (err) {
206
+ console.error(`[DocumentService] 删除文档失败: ${id}`, err);
207
+ }
208
+ return false;
209
+ }
210
+ }