colloq 0.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of colloq might be problematic. Click here for more details.

Files changed (224) hide show
  1. colloq/__init__.py +12 -0
  2. colloq/__main__.py +315 -0
  3. colloq/_app/.colloq-dist.json +66 -0
  4. colloq/_app/LICENSE +21 -0
  5. colloq/_app/THIRD_PARTY_NOTICES.md +106 -0
  6. colloq/_app/cli/colloq.mjs +2517 -0
  7. colloq/_app/cli/launch.mjs +1572 -0
  8. colloq/_app/cli/package.json +5 -0
  9. colloq/_app/cli/public-url-lease.mjs +198 -0
  10. colloq/_app/kernel/Dockerfile +93 -0
  11. colloq/_app/kernel/environments/base-gpu.txt +21 -0
  12. colloq/_app/kernel/environments/base.txt +12 -0
  13. colloq/_app/kernel/environments/cv.txt +15 -0
  14. colloq/_app/kernel/environments/gpu.txt +17 -0
  15. colloq/_app/kernel/environments/kaggle-base.txt +20 -0
  16. colloq/_app/kernel/environments/pvz-geo.txt +6 -0
  17. colloq/_app/kernel/requirements.txt +18 -0
  18. colloq/_app/package.json +20 -0
  19. colloq/_app/scripts/backup-local.sh +165 -0
  20. colloq/_app/scripts/backup.sh +34 -0
  21. colloq/_app/scripts/cluster.sh +492 -0
  22. colloq/_app/scripts/dns.sh +251 -0
  23. colloq/_app/scripts/host.sh +1514 -0
  24. colloq/_app/scripts/lib.sh +62 -0
  25. colloq/_app/scripts/restore.sh +338 -0
  26. colloq/_app/scripts/runtime-backup.py +373 -0
  27. colloq/_app/scripts/state-lock.py +85 -0
  28. colloq/_app/server/assets/fonts/HSESans-Black.otf +0 -0
  29. colloq/_app/server/assets/fonts/HSESans-Bold.otf +0 -0
  30. colloq/_app/server/assets/fonts/HSESans-Regular.otf +0 -0
  31. colloq/_app/server/assets/fonts/JetBrainsMono-Medium.ttf +0 -0
  32. colloq/_app/server/assets/fonts/JetBrainsMono-OFL.txt +93 -0
  33. colloq/_app/server/dist/local-cleanup.js +15987 -0
  34. colloq/_app/server/dist/runtime-smoke.js +17026 -0
  35. colloq/_app/server/dist/server.js +54333 -0
  36. colloq/_app/server/package.json +6 -0
  37. colloq/_app/web/dist/assets/AdminScreen-C-cg0GlJ.css +1 -0
  38. colloq/_app/web/dist/assets/AdminScreen-C-cg0GlJ.css.br +0 -0
  39. colloq/_app/web/dist/assets/AdminScreen-C-cg0GlJ.css.gz +0 -0
  40. colloq/_app/web/dist/assets/AdminScreen-fu4cB6Ra.js +105 -0
  41. colloq/_app/web/dist/assets/AdminScreen-fu4cB6Ra.js.br +0 -0
  42. colloq/_app/web/dist/assets/AdminScreen-fu4cB6Ra.js.gz +0 -0
  43. colloq/_app/web/dist/assets/App-CMXNqOjp.css +1 -0
  44. colloq/_app/web/dist/assets/App-CMXNqOjp.css.br +0 -0
  45. colloq/_app/web/dist/assets/App-CMXNqOjp.css.gz +0 -0
  46. colloq/_app/web/dist/assets/App-DvCPzIRO.js +26 -0
  47. colloq/_app/web/dist/assets/App-DvCPzIRO.js.br +0 -0
  48. colloq/_app/web/dist/assets/App-DvCPzIRO.js.gz +0 -0
  49. colloq/_app/web/dist/assets/CellOutputs-Dl-AZL45.js +42 -0
  50. colloq/_app/web/dist/assets/CellOutputs-Dl-AZL45.js.br +0 -0
  51. colloq/_app/web/dist/assets/CellOutputs-Dl-AZL45.js.gz +0 -0
  52. colloq/_app/web/dist/assets/CommandPalette-DhBGho7_.js +3 -0
  53. colloq/_app/web/dist/assets/CommandPalette-DhBGho7_.js.br +0 -0
  54. colloq/_app/web/dist/assets/CommandPalette-DhBGho7_.js.gz +0 -0
  55. colloq/_app/web/dist/assets/CompetitionsScreen-Vu3RpHmf.js +3 -0
  56. colloq/_app/web/dist/assets/CompetitionsScreen-Vu3RpHmf.js.br +0 -0
  57. colloq/_app/web/dist/assets/CompetitionsScreen-Vu3RpHmf.js.gz +0 -0
  58. colloq/_app/web/dist/assets/ConsoleView-B2uyYvFh.js +1 -0
  59. colloq/_app/web/dist/assets/ConsoleView-B2uyYvFh.js.br +0 -0
  60. colloq/_app/web/dist/assets/ConsoleView-B2uyYvFh.js.gz +0 -0
  61. colloq/_app/web/dist/assets/ConsoleView-DLbp0Jul.css +1 -0
  62. colloq/_app/web/dist/assets/DependencyBundleCard-B392XMVZ.js +4 -0
  63. colloq/_app/web/dist/assets/DependencyBundleCard-B392XMVZ.js.br +0 -0
  64. colloq/_app/web/dist/assets/DependencyBundleCard-B392XMVZ.js.gz +0 -0
  65. colloq/_app/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  66. colloq/_app/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  67. colloq/_app/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  68. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  69. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  70. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  71. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  72. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  73. colloq/_app/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  74. colloq/_app/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  75. colloq/_app/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  76. colloq/_app/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  77. colloq/_app/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  78. colloq/_app/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  79. colloq/_app/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  80. colloq/_app/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  81. colloq/_app/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  82. colloq/_app/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  83. colloq/_app/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  84. colloq/_app/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  85. colloq/_app/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  86. colloq/_app/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  87. colloq/_app/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  88. colloq/_app/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  89. colloq/_app/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  90. colloq/_app/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  91. colloq/_app/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  92. colloq/_app/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  93. colloq/_app/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  94. colloq/_app/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  95. colloq/_app/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  96. colloq/_app/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  97. colloq/_app/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  98. colloq/_app/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  99. colloq/_app/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  100. colloq/_app/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  101. colloq/_app/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  102. colloq/_app/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  103. colloq/_app/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  104. colloq/_app/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  105. colloq/_app/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  106. colloq/_app/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  107. colloq/_app/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  108. colloq/_app/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  109. colloq/_app/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  110. colloq/_app/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  111. colloq/_app/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  112. colloq/_app/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  113. colloq/_app/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  114. colloq/_app/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  115. colloq/_app/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  116. colloq/_app/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  117. colloq/_app/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  118. colloq/_app/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  119. colloq/_app/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  120. colloq/_app/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  121. colloq/_app/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  122. colloq/_app/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  123. colloq/_app/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  124. colloq/_app/web/dist/assets/PultWindow-B3cc8537.css +1 -0
  125. colloq/_app/web/dist/assets/PultWindow-B3cc8537.css.br +0 -0
  126. colloq/_app/web/dist/assets/PultWindow-B3cc8537.css.gz +0 -0
  127. colloq/_app/web/dist/assets/PultWindow-DPOUhSqt.js +8 -0
  128. colloq/_app/web/dist/assets/PultWindow-DPOUhSqt.js.br +0 -0
  129. colloq/_app/web/dist/assets/PultWindow-DPOUhSqt.js.gz +0 -0
  130. colloq/_app/web/dist/assets/ReaderScreen-CV_0VAvW.js +9 -0
  131. colloq/_app/web/dist/assets/ReaderScreen-CV_0VAvW.js.br +0 -0
  132. colloq/_app/web/dist/assets/ReaderScreen-CV_0VAvW.js.gz +0 -0
  133. colloq/_app/web/dist/assets/SessionScreen-BrcB6CLH.js +132 -0
  134. colloq/_app/web/dist/assets/SessionScreen-BrcB6CLH.js.br +0 -0
  135. colloq/_app/web/dist/assets/SessionScreen-BrcB6CLH.js.gz +0 -0
  136. colloq/_app/web/dist/assets/SessionScreen-Dek9dK3C.css +1 -0
  137. colloq/_app/web/dist/assets/SessionScreen-Dek9dK3C.css.br +0 -0
  138. colloq/_app/web/dist/assets/SessionScreen-Dek9dK3C.css.gz +0 -0
  139. colloq/_app/web/dist/assets/actions-DLPyolgo.js +1 -0
  140. colloq/_app/web/dist/assets/admin-en-BHmR7Co0.js +9 -0
  141. colloq/_app/web/dist/assets/admin-en-BHmR7Co0.js.br +0 -0
  142. colloq/_app/web/dist/assets/admin-en-BHmR7Co0.js.gz +0 -0
  143. colloq/_app/web/dist/assets/admin-ru-Dr6Z36Zi.js +11 -0
  144. colloq/_app/web/dist/assets/admin-ru-Dr6Z36Zi.js.br +0 -0
  145. colloq/_app/web/dist/assets/admin-ru-Dr6Z36Zi.js.gz +0 -0
  146. colloq/_app/web/dist/assets/codemirror-6LUh0q1l.js +32 -0
  147. colloq/_app/web/dist/assets/codemirror-6LUh0q1l.js.br +0 -0
  148. colloq/_app/web/dist/assets/codemirror-6LUh0q1l.js.gz +0 -0
  149. colloq/_app/web/dist/assets/competitions-en-zw57uAGL.js +5 -0
  150. colloq/_app/web/dist/assets/competitions-en-zw57uAGL.js.br +0 -0
  151. colloq/_app/web/dist/assets/competitions-en-zw57uAGL.js.gz +0 -0
  152. colloq/_app/web/dist/assets/competitions-ru-BBQUjAx0.js +5 -0
  153. colloq/_app/web/dist/assets/competitions-ru-BBQUjAx0.js.br +0 -0
  154. colloq/_app/web/dist/assets/competitions-ru-BBQUjAx0.js.gz +0 -0
  155. colloq/_app/web/dist/assets/editor-theme-CVYtpZDy.js +1 -0
  156. colloq/_app/web/dist/assets/editor-theme-CVYtpZDy.js.br +0 -0
  157. colloq/_app/web/dist/assets/editor-theme-CVYtpZDy.js.gz +0 -0
  158. colloq/_app/web/dist/assets/index-CwbYK31L.css +1 -0
  159. colloq/_app/web/dist/assets/index-CwbYK31L.css.br +0 -0
  160. colloq/_app/web/dist/assets/index-CwbYK31L.css.gz +0 -0
  161. colloq/_app/web/dist/assets/index-Di16xdmz.js +2 -0
  162. colloq/_app/web/dist/assets/index-Di16xdmz.js.br +0 -0
  163. colloq/_app/web/dist/assets/index-Di16xdmz.js.gz +0 -0
  164. colloq/_app/web/dist/assets/katex-CS_ezkZp.css +1 -0
  165. colloq/_app/web/dist/assets/katex-CS_ezkZp.css.br +0 -0
  166. colloq/_app/web/dist/assets/katex-CS_ezkZp.css.gz +0 -0
  167. colloq/_app/web/dist/assets/katex-DLorvT8M.js +257 -0
  168. colloq/_app/web/dist/assets/katex-DLorvT8M.js.br +0 -0
  169. colloq/_app/web/dist/assets/katex-DLorvT8M.js.gz +0 -0
  170. colloq/_app/web/dist/assets/pdf-DEp6sk-T.js +55 -0
  171. colloq/_app/web/dist/assets/pdf-DEp6sk-T.js.br +0 -0
  172. colloq/_app/web/dist/assets/pdf-DEp6sk-T.js.gz +0 -0
  173. colloq/_app/web/dist/assets/persistence.svelte-vcuCBcT5.js +1 -0
  174. colloq/_app/web/dist/assets/persistence.svelte-vcuCBcT5.js.br +0 -0
  175. colloq/_app/web/dist/assets/persistence.svelte-vcuCBcT5.js.gz +0 -0
  176. colloq/_app/web/dist/assets/reader-en-UCGE3U6V.js +3 -0
  177. colloq/_app/web/dist/assets/reader-en-UCGE3U6V.js.br +0 -0
  178. colloq/_app/web/dist/assets/reader-en-UCGE3U6V.js.gz +0 -0
  179. colloq/_app/web/dist/assets/reader-ru-CaiNj0K8.js +3 -0
  180. colloq/_app/web/dist/assets/reader-ru-CaiNj0K8.js.br +0 -0
  181. colloq/_app/web/dist/assets/reader-ru-CaiNj0K8.js.gz +0 -0
  182. colloq/_app/web/dist/assets/render-CjP3RuRT.js +155 -0
  183. colloq/_app/web/dist/assets/render-CjP3RuRT.js.br +0 -0
  184. colloq/_app/web/dist/assets/render-CjP3RuRT.js.gz +0 -0
  185. colloq/_app/web/dist/assets/render.svelte-B4_hAJmX.js +0 -0
  186. colloq/_app/web/dist/assets/render.svelte-B4_hAJmX.js.br +0 -0
  187. colloq/_app/web/dist/assets/render.svelte-B4_hAJmX.js.gz +0 -0
  188. colloq/_app/web/dist/assets/room-en-Bh8L6NcO.js +3 -0
  189. colloq/_app/web/dist/assets/room-en-Bh8L6NcO.js.br +0 -0
  190. colloq/_app/web/dist/assets/room-en-Bh8L6NcO.js.gz +0 -0
  191. colloq/_app/web/dist/assets/room-ru-Bar2GgS0.js +3 -0
  192. colloq/_app/web/dist/assets/room-ru-Bar2GgS0.js.br +0 -0
  193. colloq/_app/web/dist/assets/room-ru-Bar2GgS0.js.gz +0 -0
  194. colloq/_app/web/dist/assets/seminar-link-CRaZl9Ku.js +1 -0
  195. colloq/_app/web/dist/assets/seminar-link-CRaZl9Ku.js.br +0 -0
  196. colloq/_app/web/dist/assets/seminar-link-CRaZl9Ku.js.gz +0 -0
  197. colloq/_app/web/dist/assets/seminar-link-DoTugxxA.css +1 -0
  198. colloq/_app/web/dist/assets/seminar-link-DoTugxxA.css.br +0 -0
  199. colloq/_app/web/dist/assets/seminar-link-DoTugxxA.css.gz +0 -0
  200. colloq/_app/web/dist/assets/yjs-rjB3hdnL.js +6 -0
  201. colloq/_app/web/dist/assets/yjs-rjB3hdnL.js.br +0 -0
  202. colloq/_app/web/dist/assets/yjs-rjB3hdnL.js.gz +0 -0
  203. colloq/_app/web/dist/fonts/JetBrainsMono-OFL.txt +93 -0
  204. colloq/_app/web/dist/fonts/hse-sans-400.woff2 +0 -0
  205. colloq/_app/web/dist/fonts/hse-sans-600.woff2 +0 -0
  206. colloq/_app/web/dist/fonts/hse-sans-700.woff2 +0 -0
  207. colloq/_app/web/dist/fonts/hse-sans-900.woff2 +0 -0
  208. colloq/_app/web/dist/fonts/jetbrains-mono-cyrillic-ext.woff2 +0 -0
  209. colloq/_app/web/dist/fonts/jetbrains-mono-cyrillic.woff2 +0 -0
  210. colloq/_app/web/dist/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
  211. colloq/_app/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  212. colloq/_app/web/dist/index.html +268 -0
  213. colloq/_app/web/dist/og/colloq.png +0 -0
  214. colloq/_app/web/dist/pdf/pdf.worker.min.mjs +29 -0
  215. colloq/_app/web/dist/pdf/pdf.worker.min.mjs.br +0 -0
  216. colloq/_app/web/dist/pdf/pdf.worker.min.mjs.gz +0 -0
  217. colloq/_app/web/dist/plotly/plotly.min.js +3620 -0
  218. colloq/_app/web/dist/plotly/plotly.min.js.br +0 -0
  219. colloq/_app/web/dist/plotly/plotly.min.js.gz +0 -0
  220. colloq/_version.py +5 -0
  221. colloq-0.2.0.dist-info/METADATA +124 -0
  222. colloq-0.2.0.dist-info/RECORD +224 -0
  223. colloq-0.2.0.dist-info/WHEEL +4 -0
  224. colloq-0.2.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Записи DNS для colloq.ru.
4
+ #
5
+ # Скрипт приводит зону к нужному виду, а не досыпает в неё записи. Разница не
6
+ # косметическая: домен приехал из Рег.ру с парковочными A-записями, и если
7
+ # просто добавить рядом адреса GitHub Pages, посетитель будет попадать то на
8
+ # сайт, то на заглушку — round-robin честно раздаст и то и другое. Поэтому для
9
+ # каждой пары «тип + имя» лишнее удаляется, недостающее создаётся, совпадающее
10
+ # не трогается, и запускать это можно сколько угодно раз подряд.
11
+ #
12
+ # Главное, ради чего всё: **серое облако везде**. Оранжевое означает, что
13
+ # посетитель идёт на пограничные адреса Cloudflare, а они из России не
14
+ # открываются — лендинг на colloq.sleep3r.ru не грузился ровно до того дня,
15
+ # когда с него сняли проксирование. Апекс colloq.ru приехал проксированным,
16
+ # и это здесь исправляется.
17
+ #
18
+ # Токен берётся из .env основного репозитория; нужны Zone:Read и DNS:Edit на
19
+ # зону colloq.ru.
20
+ #
21
+ # Входа два:
22
+ #
23
+ # scripts/dns.sh привести всю зону к нужному виду:
24
+ # лендинг, www и *.colloq.ru на ретранслятор
25
+ # scripts/dns.sh point <имя> <адрес> одна A-запись: имя → адрес машины
26
+ #
27
+ # Второй появился не ради удобства. Его зовёт прямой режим `make host-direct`:
28
+ # машина с белым адресом принимает пару сама, и её имя должно смотреть на неё,
29
+ # а не на ретранслятор. Своей копии этой логики у host.sh нет намеренно —
30
+ # «удалить лишнее, создать недостающее, никогда не оставлять проксирование»
31
+ # написано здесь один раз и здесь же чинится.
32
+ set -euo pipefail
33
+ cd "$(dirname "$0")"
34
+
35
+ # Режим разбирается до всего остального: в режиме point ни лендинг, ни www, ни
36
+ # звёздочка не трогаются вовсе — иначе `make host-direct` посреди пары
37
+ # переписывал бы записи, о которых его не просили.
38
+ MODE=zone
39
+ if [[ "${1:-}" == point ]]; then
40
+ MODE=point
41
+ POINT_NAME=${2:?scripts/dns.sh point <name> <address>}
42
+ POINT_ADDR=${3:?scripts/dns.sh point <name> <address>}
43
+ fi
44
+
45
+ # ../.env, а не ../colloq/.env: скрипт приехал из соседнего репозитория, где
46
+ # основной клон лежал рядом. Здесь он лежит выше, и прежний путь не существовал
47
+ # ни в одном клоне — вместо «нет файла» человек читал «нужен токен с Zone:Read»
48
+ # и шёл проверять права токена, который всё это время лежал в .env.
49
+ #
50
+ # Читает файл общий read_env (scripts/lib.sh) — тот же, которым живут host.sh,
51
+ # service.sh и vast.sh.
52
+ #
53
+ # Двумя строками, а не `ENV_FILE=../.env . ./lib.sh`: присваивание перед
54
+ # встроенной командой bash считает временным и после возврата снимает — а
55
+ # read_env зовут потом, и файл настроек ему нужен уже настоящий.
56
+ #
57
+ # COLLOQ_HOME сильнее «этажом выше»: у поставленного через pip colloq этажом
58
+ # выше лежит только каталог приложения (доступный на чтение и сносимый
59
+ # обновлением), а .env с CF_TOKEN — в каталоге состояния. Без переменной всё
60
+ # как было: ../.env, то есть корень репозитория.
61
+ ENV_FILE="${COLLOQ_HOME:-..}/.env"
62
+ . ./lib.sh
63
+
64
+ DOMAIN=${DOMAIN:-colloq.ru}
65
+ # Куда смотрят семинары. Тот же адрес, что в RELAY_ADDR у инстансов, и берётся
66
+ # он оттуда же — из .env, а не из прибитой строки.
67
+ #
68
+ # Прибитое умолчание здесь было ловушкой: RELAY_ADDR из .env скрипт не читал
69
+ # вовсе, и после переезда ретранслятора любой заход «привести зону в порядок»
70
+ # (а это единственный способ вернуть имя после host-direct) молча возвращал
71
+ # `*.colloq.ru` на старый адрес — то есть уводил все семинары на мёртвую
72
+ # машину. Нет адреса — нет и записи: об этом сказано вслух ниже.
73
+ RELAY="${RELAY_ADDR:-$(read_env RELAY_ADDR)}"
74
+ # Чья страница на GitHub Pages — цель CNAME для www.
75
+ PAGES_HOST=${PAGES_HOST:-colloq-edu.github.io}
76
+
77
+ if [[ -z "${CF_TOKEN_COLLOQ:-}" ]]; then
78
+ # `|| true` внутри read_env: файл есть, а строки в нём нет — это «нужен
79
+ # токен», а не молчаливый выход по set -e без единого слова на экране.
80
+ CF_TOKEN_COLLOQ=$(read_env CF_TOKEN)
81
+ fi
82
+ : "${CF_TOKEN_COLLOQ:?a token with Zone:Read and DNS:Edit on ${DOMAIN} is needed}"
83
+
84
+ # Идентификатор зоны лежит в том же .env соседней строкой с токеном, а читался
85
+ # только из окружения. Из-за этого он не использовался никогда: зона искалась
86
+ # запросом, и токену без права листать зоны скрипт отвечал «зона этому токену
87
+ # не видна» — при том что её id был у него под рукой.
88
+ if [[ -z "${CF_ZONE_COLLOQ:-}" ]]; then
89
+ CF_ZONE_COLLOQ=$(read_env CF_ZONE)
90
+ fi
91
+
92
+ API=https://api.cloudflare.com/client/v4
93
+ AUTH=(-H "Authorization: Bearer $CF_TOKEN_COLLOQ" -H "content-type: application/json")
94
+
95
+ RED=$'\033[31m'; OFF=$'\033[0m'
96
+ die() { printf '%s%s%s\n' "$RED" "$*" "$OFF" >&2; exit 1; }
97
+
98
+ # zone_id_for ИМЯ — в какой зоне живёт это имя.
99
+ #
100
+ # Спрашивать `?name=hse.colloq.ru` бесполезно: такой зоны нет, а Cloudflare
101
+ # отвечает на это не ошибкой, а пустым списком. Поэтому берётся список зон
102
+ # токена и из них самая длинная, которой запрошенное имя заканчивается:
103
+ # для hse.colloq.ru это colloq.ru, и то же правило работает для чужой зоны,
104
+ # если однажды инстанс встанет не под colloq.ru.
105
+ #
106
+ # И — без параметра status. Его допустимые значения это active, pending и
107
+ # подобные; «all» не из их числа, и Cloudflare на него опять же не ругается,
108
+ # а молча отдаёт пустой список. На этом можно потерять час, решив, что у
109
+ # токена нет доступа.
110
+ zone_id_for() {
111
+ local want=$1 id=""
112
+ # Явно названная зона сильнее поиска — но только если речь о ней же:
113
+ # CF_ZONE от colloq.ru для имени в чужой зоне это не подсказка, а ошибка,
114
+ # которая пишет запись не туда.
115
+ if [[ -n "${CF_ZONE_COLLOQ:-}" && ( "$want" == "$DOMAIN" || "$want" == *".$DOMAIN" ) ]]; then
116
+ printf '%s' "$CF_ZONE_COLLOQ"
117
+ return 0
118
+ fi
119
+ id=$(curl -s "${AUTH[@]}" "$API/zones?per_page=200" | WANT="$want" python3 -c '
120
+ import json, os, sys
121
+ want = os.environ["WANT"]
122
+ best = best_id = ""
123
+ try:
124
+ zones = json.load(sys.stdin).get("result") or []
125
+ except Exception:
126
+ zones = []
127
+ for z in zones:
128
+ name = z.get("name") or ""
129
+ if (want == name or want.endswith("." + name)) and len(name) > len(best):
130
+ best, best_id = name, z.get("id") or ""
131
+ print(best_id)' 2>/dev/null || true)
132
+ printf '%s' "$id"
133
+ }
134
+
135
+ fetch() { curl -s "${AUTH[@]}" "$API/zones/$zone/dns_records?per_page=200"; }
136
+
137
+ # reconcile ТИП ИМЯ [СОДЕРЖИМОЕ...]
138
+ #
139
+ # Приводит все записи данного типа с данным именем ровно к перечисленному
140
+ # списку. Пустой список означает «таких записей быть не должно» — так www
141
+ # избавляется от парковочной A, прежде чем стать CNAME: Cloudflare не даст
142
+ # держать CNAME рядом с A на одном имени.
143
+ reconcile() {
144
+ local type=$1 name=$2; shift 2
145
+ local want=("$@")
146
+ local records id content proxied keep w
147
+
148
+ records=$(fetch)
149
+ while read -r id content proxied; do
150
+ [[ -n "${id:-}" ]] || continue
151
+ keep=0
152
+ # Проксированную запись сохранять нельзя даже с верным адресом: оранжевое
153
+ # облако и есть то, из-за чего сайт не открывается.
154
+ for w in ${want[@]+"${want[@]}"}; do
155
+ [[ "$content" == "$w" && "$proxied" == "0" ]] && keep=1
156
+ done
157
+ if (( keep == 0 )); then
158
+ curl -s -X DELETE "${AUTH[@]}" "$API/zones/$zone/dns_records/$id" >/dev/null
159
+ printf ' removed %-5s %-16s -> %s%s\n' "$type" "$name" "$content" \
160
+ "$([[ $proxied == 1 ]] && echo ' (was proxied)')"
161
+ fi
162
+ done < <(printf '%s' "$records" | python3 -c "
163
+ import json,sys
164
+ for r in json.load(sys.stdin)['result']:
165
+ if r['type']=='$type' and r['name']=='$name':
166
+ print(r['id'], r['content'], int(bool(r.get('proxied'))))")
167
+
168
+ [[ ${#want[@]} -gt 0 ]] || return 0
169
+
170
+ local have
171
+ have=$(fetch | python3 -c "
172
+ import json,sys
173
+ print(' '.join(r['content'] for r in json.load(sys.stdin)['result']
174
+ if r['type']=='$type' and r['name']=='$name' and not r.get('proxied')))")
175
+
176
+ local resp
177
+ for w in "${want[@]}"; do
178
+ if [[ " $have " == *" $w "* ]]; then
179
+ printf ' in place %-5s %-16s -> %s\n' "$type" "$name" "$w"
180
+ continue
181
+ fi
182
+ resp=$(curl -s -X POST "${AUTH[@]}" "$API/zones/$zone/dns_records" \
183
+ --data "$(printf '{"type":"%s","name":"%s","content":"%s","ttl":300,"proxied":false}' \
184
+ "$type" "$name" "$w")")
185
+ # Ответ проверяется, а не выбрасывается. Раньше он уходил в /dev/null, и
186
+ # строка «создано» печаталась в том числе тогда, когда Cloudflare отказал —
187
+ # обычно из-за CNAME на том же имени или токена без DNS:Edit. Для зоны это
188
+ # означало «сайт почему-то не открылся», а для прямого режима — машину,
189
+ # которая ждёт сертификат на имя, никуда не указывающее.
190
+ if [[ "$resp" == *'"success":true'* || "$resp" == *'"success": true'* ]]; then
191
+ printf ' created %-5s %-16s -> %s\n' "$type" "$name" "$w"
192
+ else
193
+ printf '%s\n' "$resp" | head -c 400 >&2; printf '\n' >&2
194
+ die "Cloudflare did not create ${type} ${name} -> ${w}"
195
+ fi
196
+ done
197
+ }
198
+
199
+ # ------------------------------------------------------------- что делаем
200
+
201
+ # Одна запись: имя → адрес машины. Больше в зоне не трогается ничего.
202
+ if [[ "$MODE" == point ]]; then
203
+ zone=$(zone_id_for "$POINT_NAME")
204
+ [[ -n "$zone" ]] || die "this token cannot see the zone for ${POINT_NAME} (CF_TOKEN, CF_ZONE in .env)"
205
+ # Сначала снять CNAME с этого имени, потом ставить A — ровно та же причина,
206
+ # что у www ниже: Cloudflare не даёт держать их рядом. Случай не выдуманный:
207
+ # имя, которое когда-то заводил `make tunnel-setup`, держит CNAME на
208
+ # <id>.cfargotunnel.com, и без этой строки прямой режим на нём отказывал бы
209
+ # со ссылкой на конфликт записей.
210
+ reconcile CNAME "$POINT_NAME"
211
+ # Без проксирования — это здесь главное и единственное. Оранжевое облако
212
+ # увело бы студентов на пограничные адреса Cloudflare, а они из России не
213
+ # открываются: семинар стал бы недоступен ровно той аудитории, ради которой
214
+ # его и выставляют. Прямой режим тем и ценен, что между машиной и залом нет
215
+ # никого; проксирование вернуло бы посредника, да ещё и закрытого.
216
+ reconcile A "$POINT_NAME" "$POINT_ADDR"
217
+ exit 0
218
+ fi
219
+
220
+ # Адрес ретранслятора спрашивается до первой правки зоны, а не перед самой
221
+ # звёздочкой: лендинг и www приводятся в порядок раньше её, и отказ на середине
222
+ # оставил бы зону наполовину переписанной.
223
+ [[ -n "$RELAY" ]] || die "I do not know where to point *.${DOMAIN}: ../.env has no RELAY_ADDR.
224
+ That is the address of the relay (make relay-setup prints it). For one run it
225
+ can also be named like this: RELAY_ADDR=1.2.3.4 scripts/dns.sh"
226
+
227
+ zone=$(zone_id_for "$DOMAIN")
228
+ [[ -n "$zone" ]] || die "this token cannot see the zone ${DOMAIN}"
229
+ echo "zone ${DOMAIN}: $zone"
230
+
231
+ echo "landing page on GitHub Pages:"
232
+ reconcile A "$DOMAIN" 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153
233
+ # Без AAAA посетитель на чистом IPv6 не откроет сайт вовсе.
234
+ reconcile AAAA "$DOMAIN" 2606:50c0:8000::153 2606:50c0:8001::153 2606:50c0:8002::153 2606:50c0:8003::153
235
+ # Сначала снять парковочную A с www, потом ставить CNAME — иначе Cloudflare
236
+ # откажет: CNAME и A на одном имени не уживаются.
237
+ reconcile A "www.$DOMAIN"
238
+ reconcile CNAME "www.$DOMAIN" "$PAGES_HOST"
239
+
240
+ echo "classes on the relay:"
241
+ # Одной звёздочкой, а не именем на каждый университет: поддомены раздаёт frps
242
+ # по общему секрету, и запись в DNS на каждого означала бы самообслуживание
243
+ # на ручном приводе.
244
+ reconcile A "*.$DOMAIN" "$RELAY"
245
+
246
+ echo
247
+ echo "done. Next:"
248
+ echo " 1. push the landing page: CNAME in the repository already points at ${DOMAIN}"
249
+ echo " 2. wait for the Pages certificate on ${DOMAIN}"
250
+ echo " 3. check from a phone WITHOUT a VPN: https://${DOMAIN}"
251
+ echo " 4. publish a class: make host HOST=hse.${DOMAIN}"