@weibaohui/dsh-git-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/README.md +43 -0
  2. package/client/bundle.js +2356 -0
  3. package/client/index.js +2342 -0
  4. package/cordis.patch.yml +11 -0
  5. package/package.json +70 -0
  6. package/server/dist/api/v1.js +2029 -0
  7. package/server/dist/authx/password.js +43 -0
  8. package/server/dist/authx/um.js +157 -0
  9. package/server/dist/conf.js +323 -0
  10. package/server/dist/context.js +701 -0
  11. package/server/dist/db/actions.js +202 -0
  12. package/server/dist/db/db.js +838 -0
  13. package/server/dist/dshapi.js +1416 -0
  14. package/server/dist/editorconfig.js +104 -0
  15. package/server/dist/gitx/git.js +580 -0
  16. package/server/dist/gitx/http.js +232 -0
  17. package/server/dist/gitx/paths.js +28 -0
  18. package/server/dist/gitx/service.js +251 -0
  19. package/server/dist/gosprintf.js +167 -0
  20. package/server/dist/hook.js +53 -0
  21. package/server/dist/index.js +112 -0
  22. package/server/dist/lfsx.js +265 -0
  23. package/server/dist/mailer.js +127 -0
  24. package/server/dist/markup.js +317 -0
  25. package/server/dist/metrics.js +18 -0
  26. package/server/dist/mirror.js +203 -0
  27. package/server/dist/multipart.js +33 -0
  28. package/server/dist/repox.js +99 -0
  29. package/server/dist/router.js +185 -0
  30. package/server/dist/serv.js +43 -0
  31. package/server/dist/server.js +382 -0
  32. package/server/dist/sshx/dispatch.js +160 -0
  33. package/server/dist/sshx/lfs-transfer.js +289 -0
  34. package/server/dist/sshx/server.js +107 -0
  35. package/server/dist/toolx.js +108 -0
  36. package/server/dist/twofactor.js +160 -0
  37. package/server/dist/webapi.js +377 -0
  38. package/server/dist/webhook.js +128 -0
  39. package/server/package.json +34 -0
  40. package/server/public/img/avatar_default.png +0 -0
  41. package/server/tsconfig.json +19 -0
  42. package/server/vendored-conf/app.ini +538 -0
  43. package/server/vendored-conf/gitignore/Actionscript +11 -0
  44. package/server/vendored-conf/gitignore/Ada +5 -0
  45. package/server/vendored-conf/gitignore/Agda +1 -0
  46. package/server/vendored-conf/gitignore/Android +32 -0
  47. package/server/vendored-conf/gitignore/Anjuta +3 -0
  48. package/server/vendored-conf/gitignore/AppEngine +2 -0
  49. package/server/vendored-conf/gitignore/AppceleratorTitanium +3 -0
  50. package/server/vendored-conf/gitignore/ArchLinuxPackages +13 -0
  51. package/server/vendored-conf/gitignore/Archives +27 -0
  52. package/server/vendored-conf/gitignore/Autotools +14 -0
  53. package/server/vendored-conf/gitignore/BricxCC +4 -0
  54. package/server/vendored-conf/gitignore/C +32 -0
  55. package/server/vendored-conf/gitignore/C Sharp +108 -0
  56. package/server/vendored-conf/gitignore/C++ +28 -0
  57. package/server/vendored-conf/gitignore/CFWheels +12 -0
  58. package/server/vendored-conf/gitignore/CMake +6 -0
  59. package/server/vendored-conf/gitignore/CUDA +6 -0
  60. package/server/vendored-conf/gitignore/CakePHP +13 -0
  61. package/server/vendored-conf/gitignore/ChefCookbook +9 -0
  62. package/server/vendored-conf/gitignore/Cloud9 +3 -0
  63. package/server/vendored-conf/gitignore/CodeIgniter +6 -0
  64. package/server/vendored-conf/gitignore/CodeKit +3 -0
  65. package/server/vendored-conf/gitignore/CommonLisp +3 -0
  66. package/server/vendored-conf/gitignore/Composer +6 -0
  67. package/server/vendored-conf/gitignore/Concrete5 +3 -0
  68. package/server/vendored-conf/gitignore/Coq +3 -0
  69. package/server/vendored-conf/gitignore/CraftCMS +3 -0
  70. package/server/vendored-conf/gitignore/DM +5 -0
  71. package/server/vendored-conf/gitignore/Dart +15 -0
  72. package/server/vendored-conf/gitignore/DartEditor +2 -0
  73. package/server/vendored-conf/gitignore/Delphi +57 -0
  74. package/server/vendored-conf/gitignore/Dreamweaver +3 -0
  75. package/server/vendored-conf/gitignore/Drupal +36 -0
  76. package/server/vendored-conf/gitignore/EPiServer +4 -0
  77. package/server/vendored-conf/gitignore/Eagle +36 -0
  78. package/server/vendored-conf/gitignore/Eclipse +39 -0
  79. package/server/vendored-conf/gitignore/EiffelStudio +2 -0
  80. package/server/vendored-conf/gitignore/Elisp +5 -0
  81. package/server/vendored-conf/gitignore/Elixir +4 -0
  82. package/server/vendored-conf/gitignore/Emacs +32 -0
  83. package/server/vendored-conf/gitignore/Ensime +4 -0
  84. package/server/vendored-conf/gitignore/Erlang +10 -0
  85. package/server/vendored-conf/gitignore/Espresso +1 -0
  86. package/server/vendored-conf/gitignore/ExpressionEngine +19 -0
  87. package/server/vendored-conf/gitignore/ExtJs +4 -0
  88. package/server/vendored-conf/gitignore/Fancy +2 -0
  89. package/server/vendored-conf/gitignore/Finale +13 -0
  90. package/server/vendored-conf/gitignore/FlexBuilder +3 -0
  91. package/server/vendored-conf/gitignore/ForceDotCom +4 -0
  92. package/server/vendored-conf/gitignore/FuelPHP +2 -0
  93. package/server/vendored-conf/gitignore/GWT +28 -0
  94. package/server/vendored-conf/gitignore/Gcov +5 -0
  95. package/server/vendored-conf/gitignore/GitBook +16 -0
  96. package/server/vendored-conf/gitignore/Go +24 -0
  97. package/server/vendored-conf/gitignore/Gradle +8 -0
  98. package/server/vendored-conf/gitignore/Grails +33 -0
  99. package/server/vendored-conf/gitignore/Haskell +16 -0
  100. package/server/vendored-conf/gitignore/IGORPro +5 -0
  101. package/server/vendored-conf/gitignore/IPythonNotebook +2 -0
  102. package/server/vendored-conf/gitignore/Idris +2 -0
  103. package/server/vendored-conf/gitignore/JDeveloper +13 -0
  104. package/server/vendored-conf/gitignore/Java +12 -0
  105. package/server/vendored-conf/gitignore/Jboss +19 -0
  106. package/server/vendored-conf/gitignore/Jekyll +3 -0
  107. package/server/vendored-conf/gitignore/JetBrains +46 -0
  108. package/server/vendored-conf/gitignore/Joomla +525 -0
  109. package/server/vendored-conf/gitignore/KDevelop4 +2 -0
  110. package/server/vendored-conf/gitignore/Kate +3 -0
  111. package/server/vendored-conf/gitignore/KiCAD +13 -0
  112. package/server/vendored-conf/gitignore/Kohana +2 -0
  113. package/server/vendored-conf/gitignore/LabVIEW +16 -0
  114. package/server/vendored-conf/gitignore/Laravel +4 -0
  115. package/server/vendored-conf/gitignore/Lazarus +30 -0
  116. package/server/vendored-conf/gitignore/Leiningen +12 -0
  117. package/server/vendored-conf/gitignore/LemonStand +21 -0
  118. package/server/vendored-conf/gitignore/LibreOffice +2 -0
  119. package/server/vendored-conf/gitignore/Lilypond +6 -0
  120. package/server/vendored-conf/gitignore/Linux +7 -0
  121. package/server/vendored-conf/gitignore/Lithium +2 -0
  122. package/server/vendored-conf/gitignore/Lua +41 -0
  123. package/server/vendored-conf/gitignore/LyX +4 -0
  124. package/server/vendored-conf/gitignore/Magento +126 -0
  125. package/server/vendored-conf/gitignore/Matlab +16 -0
  126. package/server/vendored-conf/gitignore/Maven +9 -0
  127. package/server/vendored-conf/gitignore/Mercurial +6 -0
  128. package/server/vendored-conf/gitignore/Mercury +13 -0
  129. package/server/vendored-conf/gitignore/MetaProgrammingSystem +16 -0
  130. package/server/vendored-conf/gitignore/MicrosoftOffice +10 -0
  131. package/server/vendored-conf/gitignore/ModelSim +23 -0
  132. package/server/vendored-conf/gitignore/Momentics +8 -0
  133. package/server/vendored-conf/gitignore/MonoDevelop +8 -0
  134. package/server/vendored-conf/gitignore/Nanoc +10 -0
  135. package/server/vendored-conf/gitignore/NetBeans +8 -0
  136. package/server/vendored-conf/gitignore/Nim +1 -0
  137. package/server/vendored-conf/gitignore/Ninja +2 -0
  138. package/server/vendored-conf/gitignore/Node +28 -0
  139. package/server/vendored-conf/gitignore/NotepadPP +2 -0
  140. package/server/vendored-conf/gitignore/OCaml +20 -0
  141. package/server/vendored-conf/gitignore/Objective-C +43 -0
  142. package/server/vendored-conf/gitignore/Opa +13 -0
  143. package/server/vendored-conf/gitignore/OpenCart +11 -0
  144. package/server/vendored-conf/gitignore/OracleForms +8 -0
  145. package/server/vendored-conf/gitignore/Packer +5 -0
  146. package/server/vendored-conf/gitignore/Perl +20 -0
  147. package/server/vendored-conf/gitignore/Phalcon +2 -0
  148. package/server/vendored-conf/gitignore/PhpStorm +63 -0
  149. package/server/vendored-conf/gitignore/PlayFramework +17 -0
  150. package/server/vendored-conf/gitignore/Plone +18 -0
  151. package/server/vendored-conf/gitignore/Prestashop +25 -0
  152. package/server/vendored-conf/gitignore/Processing +7 -0
  153. package/server/vendored-conf/gitignore/Python +58 -0
  154. package/server/vendored-conf/gitignore/Qooxdoo +5 -0
  155. package/server/vendored-conf/gitignore/Qt +34 -0
  156. package/server/vendored-conf/gitignore/R +16 -0
  157. package/server/vendored-conf/gitignore/ROS +47 -0
  158. package/server/vendored-conf/gitignore/Rails +35 -0
  159. package/server/vendored-conf/gitignore/Redcar +1 -0
  160. package/server/vendored-conf/gitignore/Redis +3 -0
  161. package/server/vendored-conf/gitignore/RhodesRhomobile +9 -0
  162. package/server/vendored-conf/gitignore/Ruby +36 -0
  163. package/server/vendored-conf/gitignore/Rust +11 -0
  164. package/server/vendored-conf/gitignore/SBT +9 -0
  165. package/server/vendored-conf/gitignore/SCons +2 -0
  166. package/server/vendored-conf/gitignore/SVN +1 -0
  167. package/server/vendored-conf/gitignore/Sass +2 -0
  168. package/server/vendored-conf/gitignore/Scala +17 -0
  169. package/server/vendored-conf/gitignore/Scrivener +7 -0
  170. package/server/vendored-conf/gitignore/Sdcc +8 -0
  171. package/server/vendored-conf/gitignore/SeamGen +26 -0
  172. package/server/vendored-conf/gitignore/SketchUp +1 -0
  173. package/server/vendored-conf/gitignore/SlickEdit +11 -0
  174. package/server/vendored-conf/gitignore/Stella +12 -0
  175. package/server/vendored-conf/gitignore/SublimeText +14 -0
  176. package/server/vendored-conf/gitignore/SugarCRM +25 -0
  177. package/server/vendored-conf/gitignore/Swift +43 -0
  178. package/server/vendored-conf/gitignore/Symfony +37 -0
  179. package/server/vendored-conf/gitignore/SymphonyCMS +6 -0
  180. package/server/vendored-conf/gitignore/SynopsysVCS +36 -0
  181. package/server/vendored-conf/gitignore/Tags +14 -0
  182. package/server/vendored-conf/gitignore/TeX +136 -0
  183. package/server/vendored-conf/gitignore/TextMate +3 -0
  184. package/server/vendored-conf/gitignore/Textpattern +11 -0
  185. package/server/vendored-conf/gitignore/TortoiseGit +2 -0
  186. package/server/vendored-conf/gitignore/TurboGears2 +20 -0
  187. package/server/vendored-conf/gitignore/Typo3 +20 -0
  188. package/server/vendored-conf/gitignore/Umbraco +17 -0
  189. package/server/vendored-conf/gitignore/Unity +21 -0
  190. package/server/vendored-conf/gitignore/UnrealEngine +76 -0
  191. package/server/vendored-conf/gitignore/VVVV +6 -0
  192. package/server/vendored-conf/gitignore/Vagrant +1 -0
  193. package/server/vendored-conf/gitignore/Vim +6 -0
  194. package/server/vendored-conf/gitignore/VirtualEnv +10 -0
  195. package/server/vendored-conf/gitignore/VisualStudio +217 -0
  196. package/server/vendored-conf/gitignore/VisualStudioCode +2 -0
  197. package/server/vendored-conf/gitignore/Waf +3 -0
  198. package/server/vendored-conf/gitignore/WebMethods +14 -0
  199. package/server/vendored-conf/gitignore/WebStorm +63 -0
  200. package/server/vendored-conf/gitignore/Windows +18 -0
  201. package/server/vendored-conf/gitignore/WordPress +18 -0
  202. package/server/vendored-conf/gitignore/Xcode +23 -0
  203. package/server/vendored-conf/gitignore/XilinxISE +57 -0
  204. package/server/vendored-conf/gitignore/Xojo +11 -0
  205. package/server/vendored-conf/gitignore/Yeoman +6 -0
  206. package/server/vendored-conf/gitignore/Yii +6 -0
  207. package/server/vendored-conf/gitignore/ZendFramework +20 -0
  208. package/server/vendored-conf/gitignore/Zephir +26 -0
  209. package/server/vendored-conf/gitignore/macOS +25 -0
  210. package/server/vendored-conf/label/Default +7 -0
  211. package/server/vendored-conf/license/Abstyles License +10 -0
  212. package/server/vendored-conf/license/Academic Free License v1.1 +22 -0
  213. package/server/vendored-conf/license/Academic Free License v1.2 +27 -0
  214. package/server/vendored-conf/license/Academic Free License v2.0 +43 -0
  215. package/server/vendored-conf/license/Academic Free License v2.1 +44 -0
  216. package/server/vendored-conf/license/Academic Free License v3.0 +42 -0
  217. package/server/vendored-conf/license/Affero General Public License v1.0 +84 -0
  218. package/server/vendored-conf/license/Apache License 1.0 +19 -0
  219. package/server/vendored-conf/license/Apache License 1.1 +20 -0
  220. package/server/vendored-conf/license/Apache License 2.0 +72 -0
  221. package/server/vendored-conf/license/Artistic License 1.0 +48 -0
  222. package/server/vendored-conf/license/Artistic License 2.0 +84 -0
  223. package/server/vendored-conf/license/BSD 2-clause License +8 -0
  224. package/server/vendored-conf/license/BSD 3-clause License +10 -0
  225. package/server/vendored-conf/license/BSD 4-clause License +13 -0
  226. package/server/vendored-conf/license/Creative Commons CC0 1.0 Universal +39 -0
  227. package/server/vendored-conf/license/Eclipse Public License 1.0 +72 -0
  228. package/server/vendored-conf/license/Educational Community License v1.0 +22 -0
  229. package/server/vendored-conf/license/Educational Community License v2.0 +97 -0
  230. package/server/vendored-conf/license/GNU Affero General Public License v3.0 +241 -0
  231. package/server/vendored-conf/license/GNU Free Documentation License v1.1 +118 -0
  232. package/server/vendored-conf/license/GNU Free Documentation License v1.2 +129 -0
  233. package/server/vendored-conf/license/GNU Free Documentation License v1.3 +148 -0
  234. package/server/vendored-conf/license/GNU General Public License v1.0 +99 -0
  235. package/server/vendored-conf/license/GNU General Public License v2.0 +116 -0
  236. package/server/vendored-conf/license/GNU General Public License v3.0 +231 -0
  237. package/server/vendored-conf/license/GNU Lesser General Public License v2.1 +174 -0
  238. package/server/vendored-conf/license/GNU Lesser General Public License v3.0 +70 -0
  239. package/server/vendored-conf/license/GNU Library General Public License v2.0 +173 -0
  240. package/server/vendored-conf/license/ISC license +6 -0
  241. package/server/vendored-conf/license/MIT License +8 -0
  242. package/server/vendored-conf/license/Mozilla Public License 1.0 +122 -0
  243. package/server/vendored-conf/license/Mozilla Public License 1.1 +142 -0
  244. package/server/vendored-conf/license/Mozilla Public License 2.0 +143 -0
  245. package/server/vendored-conf/readme/Default +3 -0
  246. package/src/dsh-host.js +168 -0
  247. package/src/index.js +682 -0
  248. package/src/runner.js +364 -0
@@ -0,0 +1,43 @@
1
+ // Password hashing compatible with gogs: PBKDF2-HMAC-SHA256, 10000 iterations,
2
+ // 50-byte key, hex encoded. Salt: 10 random alphanumeric chars.
3
+ import * as crypto from 'node:crypto';
4
+ export function encodePassword(password, salt) {
5
+ return crypto.pbkdf2Sync(password, salt, 10000, 50, 'sha256').toString('hex');
6
+ }
7
+ export function verifyPassword(password, salt, encoded) {
8
+ const candidate = encodePassword(password, salt);
9
+ const a = Buffer.from(candidate, 'hex');
10
+ const b = Buffer.from(encoded, 'hex');
11
+ return a.length === b.length && crypto.timingSafeEqual(a, b);
12
+ }
13
+ const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
14
+ export function randomSalt() {
15
+ const bytes = crypto.randomBytes(10);
16
+ let out = '';
17
+ for (let i = 0; i < 10; i++)
18
+ out += ALPHABET[bytes[i] % ALPHABET.length];
19
+ return out;
20
+ }
21
+ export function md5(input) {
22
+ return crypto.createHash('md5').update(input).digest('hex');
23
+ }
24
+ export function sha1(input) {
25
+ return crypto.createHash('sha1').update(input).digest('hex');
26
+ }
27
+ export function sha256(input) {
28
+ return crypto.createHash('sha256').update(input).digest('hex');
29
+ }
30
+ export function newTokenSHA1() {
31
+ return crypto.randomBytes(20).toString('hex');
32
+ }
33
+ export function basicAuthDecode(encoded) {
34
+ const decoded = Buffer.from(encoded, 'base64').toString('utf8');
35
+ const idx = decoded.indexOf(':');
36
+ if (idx < 0)
37
+ return [decoded, ''];
38
+ return [decoded.slice(0, idx), decoded.slice(idx + 1)];
39
+ }
40
+ export function basicAuthEncode(username, password) {
41
+ return Buffer.from(`${username}:${password}`).toString('base64');
42
+ }
43
+ //# sourceMappingURL=password.js.map
@@ -0,0 +1,157 @@
1
+ // dsh 服务桥:登录认证直接走 user-management 插件的 service。
2
+ //
3
+ // 注入方式(dsh 插件 service 模式):本模块按 DSH_UM_STORE_PATH 加载
4
+ // user-management 插件的 store 模块(src/store.js,CJS),createStore 后
5
+ // 调用其公开 API(checkLogin / findUserByUsername)——与 UM 网关进程
6
+ // 完全同一代码路径。不再复刻 scrypt 校验,也不把密码同步进本库。
7
+ //
8
+ // 本库 user 表只承载「影子账号」:身份/仓库所有权/令牌归属。passwd 字段
9
+ // 永远是不可用占位(随机盐+随机哈希),任何登录路径都不校验它。
10
+ //
11
+ // 一致性:UM 网关是另一进程,users.json 的外部写入(建号/改密/禁用)
12
+ // 通过 mtime+size 指纹检测——变化即重建 store 实例(load 重读文件),
13
+ // 未变化时复用实例(checkLogin 仅付 scrypt 成本,约几十毫秒)。
14
+ import * as fs from 'node:fs';
15
+ import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+ import { createRequire } from 'node:module';
18
+ import { createHash, randomBytes } from 'node:crypto';
19
+ import * as dbm from '../db/db.js';
20
+ const requireCjs = createRequire(import.meta.url);
21
+ let storeMod = null; // user-management store 模块(一次性加载)
22
+ let storeInst = null; // 当前 store 实例(users.json 指纹未变时复用)
23
+ let storeStamp = null;
24
+ let storeHome = null;
25
+ export function umStorePath() {
26
+ return process.env.DSH_UM_STORE_PATH || '';
27
+ }
28
+ export function umServiceEnabled() {
29
+ return !!umStorePath();
30
+ }
31
+ /** dsh 数据根(store 的 <home>/user-management)。 */
32
+ function dshHome() {
33
+ return process.env.DSH_UM_HOME ? path.resolve(process.env.DSH_UM_HOME) : path.join(os.homedir(), '.dsh');
34
+ }
35
+ /** 取可用的 store 实例;users.json 指纹变化时重建。 */
36
+ async function getStore() {
37
+ const modPath = umStorePath();
38
+ if (!modPath)
39
+ return null;
40
+ try {
41
+ if (!storeMod)
42
+ storeMod = requireCjs(modPath);
43
+ if (!storeHome)
44
+ storeHome = dshHome();
45
+ const usersFile = path.join(storeHome, 'user-management', 'users.json');
46
+ let stamp = 'missing';
47
+ try {
48
+ const st = fs.statSync(usersFile);
49
+ stamp = `${st.mtimeMs}:${st.size}`;
50
+ }
51
+ catch { }
52
+ if (!storeInst || stamp !== storeStamp) {
53
+ const inst = storeMod.createStore({ home: storeHome });
54
+ await inst.load();
55
+ storeInst = inst;
56
+ storeStamp = stamp;
57
+ }
58
+ return storeInst;
59
+ }
60
+ catch (e) {
61
+ storeInst = null;
62
+ storeStamp = null;
63
+ return null;
64
+ }
65
+ }
66
+ /** 服务可用性:'ok'(store 模块可加载)| 'missing'(模块缺失)| 'disabled'。 */
67
+ export function umAvailability() {
68
+ if (!umServiceEnabled())
69
+ return 'disabled';
70
+ try {
71
+ fs.accessSync(umStorePath());
72
+ return 'ok';
73
+ }
74
+ catch {
75
+ return 'missing';
76
+ }
77
+ }
78
+ /**
79
+ * 登录校验(直接调 UM service 的 checkLogin):
80
+ * result 'ok' → { ok: true, user }(UM publicUser:id/username/role/disabled/totpEnabled…)
81
+ * TOTP 账号明确拒绝(Basic/网页直登无处输入动态码)
82
+ * 'disabled' → reason 'disabled';'invalid' → reason 'bad-password'
83
+ * store 模块/用户库不可用 → unavailable(上层回退本地兜底账号)
84
+ */
85
+ export async function umCheckLogin(username, password) {
86
+ if (typeof username !== 'string' || !username || typeof password !== 'string' || !password) {
87
+ return { ok: false };
88
+ }
89
+ const store = await getStore();
90
+ if (!store)
91
+ return { ok: false, unavailable: true };
92
+ try {
93
+ const outcome = await store.checkLogin(username, password);
94
+ if (outcome.result === 'ok' && outcome.user) {
95
+ if (outcome.user.totpEnabled)
96
+ return { ok: false, reason: 'totp' };
97
+ return { ok: true, user: outcome.user };
98
+ }
99
+ return { ok: false, reason: outcome.result === 'disabled' ? 'disabled' : 'bad-password' };
100
+ }
101
+ catch {
102
+ return { ok: false, unavailable: true };
103
+ }
104
+ }
105
+ /** 按 username 查 UM 用户(publicUser 形态);服务不可用返回 null。 */
106
+ export async function umFindUser(username) {
107
+ const store = await getStore();
108
+ if (!store || typeof store.findUserByUsername !== 'function')
109
+ return null;
110
+ try {
111
+ const u = store.findUserByUsername(username);
112
+ return u ? { id: u.id, username: u.username, role: u.role, disabled: !!u.disabled, totpEnabled: !!u.totpSecret } : null;
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ /**
119
+ * 影子开户/对齐:按 UM profile 维护本库同名行(仓库所有权/令牌归属的载体)。
120
+ * 只同步身份与角色(UM admin → 本库管理员);密码永不同步——passwd 为
121
+ * 随机不可用占位,任何登录路径都不校验它(校验永远走 UM service)。
122
+ */
123
+ export function ensureShadowUser(profile) {
124
+ if (!profile || !profile.username)
125
+ return null;
126
+ if (profile.disabled)
127
+ return null;
128
+ const username = String(profile.username);
129
+ const isAdmin = profile.role === 'admin' ? 1 : 0;
130
+ const now = Math.floor(Date.now() / 1000);
131
+ const bootstrapName = (process.env.DSH_BOOTSTRAP_ADMIN || '').split(':')[0] || 'root';
132
+ let user = dbm.getUserByUsername(username);
133
+ if (!user) {
134
+ const salt = randomBytes(16).toString('hex');
135
+ const unusable = createHash('sha256').update(randomBytes(32).toString('hex') + username).digest('hex');
136
+ dbm.db().prepare('INSERT INTO user (name, lower_name, email, passwd, salt, type, is_admin, created_unix, updated_unix) VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?)').run(username, username.toLowerCase(), `${username.toLowerCase()}@users.dsh.local`, unusable, salt, isAdmin, now, now);
137
+ }
138
+ else {
139
+ // 既有影子账号:passwd 重置为随机不可用占位(清除历史版本同步过的密码;
140
+ // 兜底管理员除外——它是 service 不可用时的紧急登录通道)
141
+ const patch = [];
142
+ const args = [];
143
+ if ((user.is_admin === 1 ? 1 : 0) !== isAdmin) {
144
+ patch.push('is_admin = ?');
145
+ args.push(isAdmin);
146
+ }
147
+ if (username !== bootstrapName) {
148
+ patch.push('passwd = ?', 'salt = ?');
149
+ args.push(createHash('sha256').update(randomBytes(32).toString('hex') + username).digest('hex'), randomBytes(16).toString('hex'));
150
+ }
151
+ if (patch.length) {
152
+ dbm.db().prepare(`UPDATE user SET ${patch.join(', ')}, updated_unix = ? WHERE id = ?`).run(...args, now, user.id);
153
+ }
154
+ }
155
+ return dbm.getUserByUsername(username) ?? null;
156
+ }
157
+ //# sourceMappingURL=um.js.map
@@ -0,0 +1,323 @@
1
+ // Configuration loading, mirroring gogs internal/conf semantics:
2
+ // vendored defaults (vendored-conf/app.ini) overlaid by custom/conf/app.ini,
3
+ // %(VAR)s interpolation, ExternalURL/Subpath normalization, absolute paths.
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import * as os from 'node:os';
7
+ import * as crypto from 'node:crypto';
8
+ import { parse as parseIni } from 'ini';
9
+ function toBool(v, def) {
10
+ if (v === undefined || v === '')
11
+ return def;
12
+ return v.toLowerCase() === 'true' || v === '1';
13
+ }
14
+ function toInt(v, def) {
15
+ if (v === undefined || v === '')
16
+ return def;
17
+ const n = Number(v);
18
+ return Number.isFinite(n) ? n : def;
19
+ }
20
+ export class Conf {
21
+ /** raw merged ini sections (uppercase keys) */
22
+ raw = {};
23
+ // [server]
24
+ protocol = 'http';
25
+ domain = 'localhost';
26
+ httpAddr = '0.0.0.0';
27
+ httpPort = 3000;
28
+ externalURL = 'http://localhost:3000/';
29
+ url;
30
+ subpath = '';
31
+ subpathDepth = 0;
32
+ localRootURL = '';
33
+ appDataPath = 'data';
34
+ disableRouterLog = true;
35
+ enableGzip = false;
36
+ loadAssetsFromDisk = false;
37
+ landingURL = '/';
38
+ offlineMode = false;
39
+ staticRootPath = '';
40
+ // ssh
41
+ disableSSH = false;
42
+ sshDomain = 'localhost';
43
+ sshPort = 22;
44
+ startSSHServer = false;
45
+ rewriteAuthorizedKeysAtStart = false;
46
+ // [app]
47
+ brandName = 'dsh Git';
48
+ runUser = '';
49
+ runMode = 'prod';
50
+ version = '0.13.0+dev';
51
+ buildCommit = 'unknown';
52
+ buildTime = 'unknown';
53
+ // [database]
54
+ dbType = 'sqlite3';
55
+ dbPath = 'data/gogs.db';
56
+ dbHost = '127.0.0.1:5432';
57
+ dbName = 'gogs';
58
+ dbUser = 'gogs';
59
+ dbPassword = '';
60
+ // [security]
61
+ installLock = false;
62
+ secretKey = '';
63
+ loginRememberDays = 7;
64
+ cookieRememberName = 'gogs_incredible';
65
+ cookieUserName = 'i_like_gogs';
66
+ reverseProxyAuthUserHeader = 'X-Forwarded-User';
67
+ reverseProxyAuthEmailHeader = '';
68
+ enableLoginStatusCookie = false;
69
+ loginStatusCookieName = 'login_status';
70
+ // [auth]
71
+ requireSigninView = false;
72
+ disableRegistration = false;
73
+ enableRegistrationCaptcha = true;
74
+ activateCodeLives = 180;
75
+ resetPwdCodeLives = 180;
76
+ requireEmailConfirmation = false;
77
+ enableNotifyMail = false;
78
+ enableReverseProxyAuthentication = false;
79
+ enableReverseProxyAutoRegistration = false;
80
+ showRegistrationButton = true;
81
+ // [session]
82
+ sessionProvider = 'memory';
83
+ sessionConfig = 'data/sessions';
84
+ sessionCookieSecure = false;
85
+ // [repository]
86
+ repositoryRoot = '';
87
+ scriptType = 'bash';
88
+ forcePrivate = false;
89
+ maxCreationLimit = -1;
90
+ preferredLicenses = [];
91
+ disableHTTPGit = false;
92
+ defaultBranch = 'master';
93
+ uploadEnabled = true;
94
+ uploadTempPath = 'data/tmp/uploads';
95
+ uploadFileMaxSize = 3;
96
+ uploadMaxFiles = 5;
97
+ // [ui]
98
+ explorePagingNum = 20;
99
+ issuePagingNum = 10;
100
+ feedsPagingNum = 20;
101
+ userRepoPagingNum = 15;
102
+ themeColorMetaTag = '#6cc644';
103
+ maxDisplayFileSize = 8388608;
104
+ // [api]
105
+ maxResponseItems = 50;
106
+ defaultGitTreesPerPage = 1000;
107
+ // [picture]
108
+ gravatarSource = 'https://gravatar.com/avatar/';
109
+ disableGravatar = false;
110
+ enableFederatedAvatar = false;
111
+ avatarUploadPath = 'data/avatars';
112
+ repositoryAvatarUploadPath = 'data/repo-avatars';
113
+ // [time]
114
+ timeFormatLayout = '2006-01-02 15:04:05';
115
+ timeFormatJS = 'YYYY-MM-DD HH:mm:ss';
116
+ timeZone = '';
117
+ // [markdown]
118
+ markdownFileExtensions = ['.md', '.markdown', '.mdown', '.mkd'];
119
+ enableHardLineBreak = false;
120
+ customURLSchemes = [];
121
+ // [webhook]
122
+ webhookTypes = ['gogs', 'slack', 'discord', 'dingtalk'];
123
+ deliverTimeout = 5;
124
+ skipVerify = false;
125
+ pagingNum = 10;
126
+ // [mirror]
127
+ defaultMirrorInterval = 8;
128
+ // [git]
129
+ maxDiffFiles = 1000;
130
+ maxDiffLines = 5000;
131
+ maxDiffLineCharacters = 2000;
132
+ gitTimeoutMigrate = 600;
133
+ gitTimeoutMirror = 300;
134
+ gitTimeoutClone = 300;
135
+ gitTimeoutPull = 300;
136
+ gitTimeoutGC = 60;
137
+ // [i18n](已随 /api/web 裁撤,不再加载语言)
138
+ // [prometheus]
139
+ prometheusEnabled = false;
140
+ prometheusEnableBasicAuth = false;
141
+ // [mailer]
142
+ emailEnabled = false;
143
+ emailHost = '';
144
+ emailPort = 587;
145
+ emailFrom = '';
146
+ emailUser = '';
147
+ emailPasswd = '';
148
+ emailSkipVerify = false;
149
+ emailUseTLS = true;
150
+ emailSubjectPrefix = '[Gogs] ';
151
+ // [other]
152
+ showFooterBranding = false;
153
+ showFooterTemplateLoadTime = true;
154
+ /** resolved absolute paths */
155
+ workDir;
156
+ customDir;
157
+ customConf;
158
+ interpolate(value) {
159
+ return String(value ?? '').replace(/%\((\w+)\)s/g, (_m, key) => {
160
+ const section = this.raw['DEFAULT'] ?? {};
161
+ const server = this.raw['server'] ?? {};
162
+ const v = section[key] ?? server[key];
163
+ return typeof v === 'string' ? v : '';
164
+ });
165
+ }
166
+ sect(name) {
167
+ return this.raw[name] ?? {};
168
+ }
169
+ get(name, key) {
170
+ const v = this.sect(name)[key];
171
+ return v === undefined ? undefined : this.interpolate(v);
172
+ }
173
+ load(workDir, customDir, customConf) {
174
+ this.workDir = workDir;
175
+ this.customDir = customDir;
176
+ this.customConf = customConf ?? path.join(customDir, 'conf', 'app.ini');
177
+ // 1. vendored defaults
178
+ const defaultIni = fs.readFileSync(path.join(workDir, 'vendored-conf', 'app.ini'), 'utf8');
179
+ this.raw = parseIni(defaultIni);
180
+ // 2. custom overrides
181
+ if (fs.existsSync(this.customConf)) {
182
+ const custom = parseIni(fs.readFileSync(this.customConf, 'utf8'));
183
+ for (const [sect, kv] of Object.entries(custom)) {
184
+ if (typeof kv !== 'object' || kv === null) {
185
+ // top-level key → DEFAULT section
186
+ this.raw['DEFAULT'] = this.raw['DEFAULT'] ?? {};
187
+ this.raw['DEFAULT'][sect] = String(kv);
188
+ continue;
189
+ }
190
+ if (!this.raw[sect] || typeof this.raw[sect] !== 'object')
191
+ this.raw[sect] = {};
192
+ Object.assign(this.raw[sect], kv);
193
+ }
194
+ }
195
+ // 3. programmatic defaults & computed values
196
+ this.brandName = this.get('app', 'BRAND_NAME') ?? 'dsh Git';
197
+ this.runUser = this.raw['DEFAULT']?.RUN_USER ?? this.get('server', 'RUN_USER') ?? process.env.USER ?? '';
198
+ this.runMode = this.get('app', 'RUN_MODE') ?? 'prod';
199
+ this.protocol = (this.get('server', 'PROTOCOL') ?? 'http').toLowerCase();
200
+ this.domain = this.get('server', 'DOMAIN') ?? 'localhost';
201
+ this.httpAddr = this.get('server', 'HTTP_ADDR') ?? '0.0.0.0';
202
+ this.httpPort = toInt(this.get('server', 'HTTP_PORT'), 3000);
203
+ let ext = this.get('server', 'EXTERNAL_URL') ?? '';
204
+ if (!ext)
205
+ ext = `${this.protocol}://${this.domain}:${this.httpPort}/`;
206
+ if (!ext.endsWith('/'))
207
+ ext += '/';
208
+ this.externalURL = ext;
209
+ this.url = new URL(this.externalURL);
210
+ this.subpath = this.url.pathname.replace(/\/+$/, '');
211
+ this.subpathDepth = this.subpath === '' ? 0 : this.subpath.split('/').length - 1;
212
+ this.localRootURL = this.get('server', 'LOCAL_ROOT_URL') ?? `${this.protocol}://${this.httpAddr}:${this.httpPort}/`;
213
+ this.appDataPath = this.ensureAbs(this.get('server', 'APP_DATA_PATH') ?? 'data');
214
+ this.disableRouterLog = toBool(this.get('server', 'DISABLE_ROUTER_LOG'), true);
215
+ this.enableGzip = toBool(this.get('server', 'ENABLE_GZIP'), false);
216
+ this.loadAssetsFromDisk = toBool(this.get('server', 'LOAD_ASSETS_FROM_DISK'), false);
217
+ this.landingURL = this.get('server', 'LANDING_URL') ?? '/';
218
+ this.offlineMode = toBool(this.get('server', 'OFFLINE_MODE'), false);
219
+ this.staticRootPath = path.join(this.workDir, 'public');
220
+ this.disableSSH = toBool(this.get('server', 'DISABLE_SSH'), false);
221
+ this.sshDomain = this.get('server', 'SSH_DOMAIN') ?? this.domain;
222
+ this.sshPort = toInt(this.get('server', 'SSH_PORT'), 22);
223
+ this.startSSHServer = toBool(this.get('server', 'START_SSH_SERVER'), false);
224
+ this.rewriteAuthorizedKeysAtStart = toBool(this.get('server', 'REWRITE_AUTHORIZED_KEYS_AT_START'), false);
225
+ this.dbType = (this.get('database', 'TYPE') ?? 'sqlite3').toLowerCase();
226
+ this.dbPath = this.ensureAbs(this.get('database', 'PATH') ?? 'data/gogs.db');
227
+ this.dbHost = this.get('database', 'HOST') ?? '127.0.0.1:5432';
228
+ this.dbName = this.get('database', 'NAME') ?? 'gogs';
229
+ this.dbUser = this.get('database', 'USER') ?? 'gogs';
230
+ this.dbPassword = this.get('database', 'PASSWORD') ?? '';
231
+ this.installLock = toBool(this.get('security', 'INSTALL_LOCK'), false);
232
+ this.secretKey = this.get('security', 'SECRET_KEY') ?? '';
233
+ this.loginRememberDays = toInt(this.get('security', 'LOGIN_REMEMBER_DAYS'), 7);
234
+ this.cookieRememberName = this.get('security', 'COOKIE_REMEMBER_NAME') ?? 'gogs_incredible';
235
+ this.cookieUserName = this.get('security', 'COOKIE_USERNAME') ?? 'i_like_gogs';
236
+ this.enableLoginStatusCookie = toBool(this.get('security', 'ENABLE_LOGIN_STATUS_COOKIE'), false);
237
+ this.loginStatusCookieName = this.get('security', 'LOGIN_STATUS_COOKIE_NAME') ?? 'login_status';
238
+ this.requireSigninView = toBool(this.get('auth', 'REQUIRE_SIGNIN_VIEW'), false);
239
+ this.disableRegistration = toBool(this.get('auth', 'DISABLE_REGISTRATION'), false);
240
+ this.enableRegistrationCaptcha = toBool(this.get('auth', 'ENABLE_REGISTRATION_CAPTCHA'), true);
241
+ this.requireEmailConfirmation = toBool(this.get('auth', 'REQUIRE_EMAIL_CONFIRMATION'), false);
242
+ this.enableNotifyMail = toBool(this.get('auth', 'ENABLE_NOTIFY_MAIL'), false);
243
+ this.sessionProvider = this.get('session', 'PROVIDER') ?? 'memory';
244
+ this.sessionConfig = this.get('session', 'PROVIDER_CONFIG') ?? 'data/sessions';
245
+ this.sessionCookieSecure = toBool(this.get('session', 'COOKIE_SECURE'), false);
246
+ const root = this.get('repository', 'ROOT') ?? '';
247
+ this.repositoryRoot = this.ensureAbs(root || path.join(os.homedir(), 'gogs-repositories'));
248
+ this.scriptType = this.get('repository', 'SCRIPT_TYPE') ?? 'bash';
249
+ this.forcePrivate = toBool(this.get('repository', 'FORCE_PRIVATE'), false);
250
+ this.maxCreationLimit = toInt(this.get('repository', 'MAX_CREATION_LIMIT'), -1);
251
+ this.preferredLicenses = (this.get('repository', 'PREFERRED_LICENSES') ?? 'Apache License 2.0, MIT License').split(',').map((s) => s.trim());
252
+ this.disableHTTPGit = toBool(this.get('repository', 'DISABLE_HTTP_GIT'), false);
253
+ this.defaultBranch = this.get('repository', 'DEFAULT_BRANCH') ?? 'master';
254
+ this.uploadEnabled = toBool(this.get('repository.upload', 'ENABLED'), true);
255
+ this.uploadTempPath = this.ensureAbs(this.get('repository.upload', 'TEMP_PATH') ?? 'data/tmp/uploads');
256
+ this.uploadFileMaxSize = toInt(this.get('repository.upload', 'FILE_MAX_SIZE'), 3);
257
+ this.uploadMaxFiles = toInt(this.get('repository.upload', 'MAX_FILES'), 5);
258
+ this.explorePagingNum = toInt(this.get('ui', 'EXPLORE_PAGING_NUM'), 20);
259
+ this.issuePagingNum = toInt(this.get('ui', 'ISSUE_PAGING_NUM'), 10);
260
+ this.feedsPagingNum = toInt(this.get('ui', 'FEEDS_PAGING_NUM'), 20);
261
+ this.userRepoPagingNum = toInt(this.get('ui.user', 'REPO_PAGING_NUM'), 15);
262
+ this.themeColorMetaTag = this.get('ui', 'THEME_COLOR_META_TAG') ?? '#6cc644';
263
+ this.maxDisplayFileSize = toInt(this.get('ui', 'MAX_DISPLAY_FILE_SIZE'), 8388608);
264
+ this.maxResponseItems = toInt(this.get('api', 'MAX_RESPONSE_ITEMS'), 50);
265
+ this.defaultGitTreesPerPage = toInt(this.get('api', 'DEFAULT_GIT_TREES_PER_PAGE'), 1000);
266
+ this.gravatarSource = this.get('picture', 'GRAVATAR_SOURCE') ?? 'https://gravatar.com/avatar/';
267
+ this.disableGravatar = toBool(this.get('picture', 'DISABLE_GRAVATAR'), false);
268
+ this.enableFederatedAvatar = toBool(this.get('picture', 'ENABLE_FEDERATED_AVATAR'), false);
269
+ this.avatarUploadPath = this.ensureAbs(this.get('picture', 'AVATAR_UPLOAD_PATH') ?? 'data/avatars');
270
+ this.repositoryAvatarUploadPath = this.ensureAbs(this.get('picture', 'REPOSITORY_AVATAR_UPLOAD_PATH') ?? 'data/repo-avatars');
271
+ this.timeFormatLayout = this.get('time', 'FORMAT') ?? '2006-01-02 15:04:05';
272
+ this.timeFormatJS = this.get('time', 'FORMAT_JS') ?? 'YYYY-MM-DD HH:mm:ss';
273
+ const exts = this.get('markdown', 'FILE_EXTENSIONS');
274
+ if (exts) {
275
+ this.markdownFileExtensions = exts.split(',').map((s) => s.trim());
276
+ }
277
+ this.enableHardLineBreak = toBool(this.get('markdown', 'ENABLE_HARD_LINE_BREAK'), false);
278
+ const schemes = this.get('markdown', 'CUSTOM_URL_SCHEMES');
279
+ this.customURLSchemes = schemes ? schemes.split(',').map((s) => s.trim()) : [];
280
+ this.deliverTimeout = toInt(this.get('webhook', 'DELIVER_TIMEOUT'), 5);
281
+ this.skipVerify = toBool(this.get('webhook', 'SKIP_VERIFY'), false);
282
+ this.pagingNum = toInt(this.get('webhook', 'PAGING_NUM'), 10);
283
+ this.defaultMirrorInterval = toInt(this.get('mirror', 'DEFAULT_INTERVAL'), 8);
284
+ this.maxDiffFiles = toInt(this.get('git', 'MAX_GIT_DIFF_FILES'), 1000);
285
+ this.maxDiffLines = toInt(this.get('git', 'MAX_GIT_DIFF_LINES'), 5000);
286
+ this.maxDiffLineCharacters = toInt(this.get('git', 'MAX_GIT_DIFF_LINE_CHARACTERS'), 2000);
287
+ this.gitTimeoutMigrate = toInt(this.get('git.timeout', 'MIGRATE'), 600);
288
+ this.gitTimeoutMirror = toInt(this.get('git.timeout', 'MIRROR'), 300);
289
+ this.gitTimeoutClone = toInt(this.get('git.timeout', 'CLONE'), 300);
290
+ this.gitTimeoutPull = toInt(this.get('git.timeout', 'PULL'), 300);
291
+ this.gitTimeoutGC = toInt(this.get('git.timeout', 'GC'), 60);
292
+ this.prometheusEnabled = toBool(this.get('prometheus', 'ENABLED'), false);
293
+ this.prometheusEnableBasicAuth = toBool(this.get('prometheus', 'ENABLE_BASIC_AUTH'), false);
294
+ this.emailEnabled = toBool(this.get('mailer', 'ENABLED'), false);
295
+ this.emailHost = this.get('mailer', 'HOST') ?? '';
296
+ this.emailPort = toInt(this.get('mailer', 'PORT'), 587);
297
+ this.emailFrom = this.get('mailer', 'FROM') ?? '';
298
+ this.emailUser = this.get('mailer', 'USER') ?? '';
299
+ this.emailPasswd = this.get('mailer', 'PASSWD') ?? '';
300
+ this.emailSkipVerify = toBool(this.get('mailer', 'SKIP_VERIFY'), false);
301
+ this.emailUseTLS = toBool(this.get('mailer', 'USE_TLS'), true);
302
+ this.emailSubjectPrefix = this.get('mailer', 'SUBJECT_PREFIX') ?? '[Gogs] ';
303
+ this.showFooterBranding = toBool(this.get('other', 'SHOW_FOOTER_BRANDING'), false);
304
+ this.showFooterTemplateLoadTime = toBool(this.get('other', 'SHOW_FOOTER_TEMPLATE_LOAD_TIME'), true);
305
+ // generate a secret key for a fresh install if not present
306
+ if (!this.secretKey) {
307
+ this.secretKey = crypto.randomBytes(15).toString('hex');
308
+ }
309
+ }
310
+ isProdMode() {
311
+ return this.runMode === 'prod';
312
+ }
313
+ ensureAbs(p) {
314
+ if (path.isAbsolute(p))
315
+ return p;
316
+ return path.join(this.workDir, p);
317
+ }
318
+ homeDir() {
319
+ return os.homedir();
320
+ }
321
+ }
322
+ export const conf = new Conf();
323
+ //# sourceMappingURL=conf.js.map