@fivepixels-js/lite-api 0.2.1

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 (372) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +537 -0
  3. package/dist/app.d.ts +10 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +70 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/auth/identity.repository.d.ts +62 -0
  8. package/dist/auth/identity.repository.d.ts.map +1 -0
  9. package/dist/auth/identity.repository.js +202 -0
  10. package/dist/auth/identity.repository.js.map +1 -0
  11. package/dist/auth/identity.routes.d.ts +5 -0
  12. package/dist/auth/identity.routes.d.ts.map +1 -0
  13. package/dist/auth/identity.routes.js +56 -0
  14. package/dist/auth/identity.routes.js.map +1 -0
  15. package/dist/auth/identity.schemas.d.ts +2711 -0
  16. package/dist/auth/identity.schemas.d.ts.map +1 -0
  17. package/dist/auth/identity.schemas.js +117 -0
  18. package/dist/auth/identity.schemas.js.map +1 -0
  19. package/dist/auth/identity.service.d.ts +37 -0
  20. package/dist/auth/identity.service.d.ts.map +1 -0
  21. package/dist/auth/identity.service.js +131 -0
  22. package/dist/auth/identity.service.js.map +1 -0
  23. package/dist/auth/password.service.d.ts +15 -0
  24. package/dist/auth/password.service.d.ts.map +1 -0
  25. package/dist/auth/password.service.js +116 -0
  26. package/dist/auth/password.service.js.map +1 -0
  27. package/dist/auth/token.service.d.ts +38 -0
  28. package/dist/auth/token.service.d.ts.map +1 -0
  29. package/dist/auth/token.service.js +54 -0
  30. package/dist/auth/token.service.js.map +1 -0
  31. package/dist/cli/cli.d.ts +3 -0
  32. package/dist/cli/cli.d.ts.map +1 -0
  33. package/dist/cli/cli.js +132 -0
  34. package/dist/cli/cli.js.map +1 -0
  35. package/dist/cli/commands/backup.d.ts +4 -0
  36. package/dist/cli/commands/backup.d.ts.map +1 -0
  37. package/dist/cli/commands/backup.js +24 -0
  38. package/dist/cli/commands/backup.js.map +1 -0
  39. package/dist/cli/commands/init.d.ts +4 -0
  40. package/dist/cli/commands/init.d.ts.map +1 -0
  41. package/dist/cli/commands/init.js +23 -0
  42. package/dist/cli/commands/init.js.map +1 -0
  43. package/dist/cli/commands/migrate.d.ts +4 -0
  44. package/dist/cli/commands/migrate.d.ts.map +1 -0
  45. package/dist/cli/commands/migrate.js +20 -0
  46. package/dist/cli/commands/migrate.js.map +1 -0
  47. package/dist/cli/commands/start.d.ts +4 -0
  48. package/dist/cli/commands/start.d.ts.map +1 -0
  49. package/dist/cli/commands/start.js +13 -0
  50. package/dist/cli/commands/start.js.map +1 -0
  51. package/dist/cli/commands/status.d.ts +4 -0
  52. package/dist/cli/commands/status.d.ts.map +1 -0
  53. package/dist/cli/commands/status.js +48 -0
  54. package/dist/cli/commands/status.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +4 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/io.d.ts +6 -0
  60. package/dist/cli/io.d.ts.map +1 -0
  61. package/dist/cli/io.js +9 -0
  62. package/dist/cli/io.js.map +1 -0
  63. package/dist/community/contracts.d.ts +29 -0
  64. package/dist/community/contracts.d.ts.map +1 -0
  65. package/dist/community/contracts.js +41 -0
  66. package/dist/community/contracts.js.map +1 -0
  67. package/dist/community/error-handler.d.ts +3 -0
  68. package/dist/community/error-handler.d.ts.map +1 -0
  69. package/dist/community/error-handler.js +66 -0
  70. package/dist/community/error-handler.js.map +1 -0
  71. package/dist/community/errors.d.ts +10 -0
  72. package/dist/community/errors.d.ts.map +1 -0
  73. package/dist/community/errors.js +39 -0
  74. package/dist/community/errors.js.map +1 -0
  75. package/dist/community/etag.d.ts +5 -0
  76. package/dist/community/etag.d.ts.map +1 -0
  77. package/dist/community/etag.js +31 -0
  78. package/dist/community/etag.js.map +1 -0
  79. package/dist/community/input.d.ts +10 -0
  80. package/dist/community/input.d.ts.map +1 -0
  81. package/dist/community/input.js +48 -0
  82. package/dist/community/input.js.map +1 -0
  83. package/dist/community/mutation-route.d.ts +7 -0
  84. package/dist/community/mutation-route.d.ts.map +1 -0
  85. package/dist/community/mutation-route.js +47 -0
  86. package/dist/community/mutation-route.js.map +1 -0
  87. package/dist/community/schemas.d.ts +807 -0
  88. package/dist/community/schemas.d.ts.map +1 -0
  89. package/dist/community/schemas.js +100 -0
  90. package/dist/community/schemas.js.map +1 -0
  91. package/dist/community/system.schemas.d.ts +40 -0
  92. package/dist/community/system.schemas.d.ts.map +1 -0
  93. package/dist/community/system.schemas.js +30 -0
  94. package/dist/community/system.schemas.js.map +1 -0
  95. package/dist/config/config.d.ts +43 -0
  96. package/dist/config/config.d.ts.map +1 -0
  97. package/dist/config/config.js +208 -0
  98. package/dist/config/config.js.map +1 -0
  99. package/dist/config/data-path.d.ts +2 -0
  100. package/dist/config/data-path.d.ts.map +1 -0
  101. package/dist/config/data-path.js +16 -0
  102. package/dist/config/data-path.js.map +1 -0
  103. package/dist/config/jwt-secret.d.ts +3 -0
  104. package/dist/config/jwt-secret.d.ts.map +1 -0
  105. package/dist/config/jwt-secret.js +67 -0
  106. package/dist/config/jwt-secret.js.map +1 -0
  107. package/dist/db/backup.d.ts +10 -0
  108. package/dist/db/backup.d.ts.map +1 -0
  109. package/dist/db/backup.js +51 -0
  110. package/dist/db/backup.js.map +1 -0
  111. package/dist/db/connection.d.ts +15 -0
  112. package/dist/db/connection.d.ts.map +1 -0
  113. package/dist/db/connection.js +82 -0
  114. package/dist/db/connection.js.map +1 -0
  115. package/dist/db/guid-order.d.ts +2 -0
  116. package/dist/db/guid-order.d.ts.map +1 -0
  117. package/dist/db/guid-order.js +9 -0
  118. package/dist/db/guid-order.js.map +1 -0
  119. package/dist/db/migrate.d.ts +37 -0
  120. package/dist/db/migrate.d.ts.map +1 -0
  121. package/dist/db/migrate.js +145 -0
  122. package/dist/db/migrate.js.map +1 -0
  123. package/dist/db/migrations/001_schema_migrations.sql +6 -0
  124. package/dist/db/migrations/002_identity.sql +95 -0
  125. package/dist/db/migrations/003_projects.sql +62 -0
  126. package/dist/db/migrations/004_feedback_core.sql +236 -0
  127. package/dist/db/migrations/005_project_member_management.sql +55 -0
  128. package/dist/db/migrations/006_align_credentials.sql +33 -0
  129. package/dist/db/migrations/007_align_storage_values.sql +78 -0
  130. package/dist/db/storage-migration.d.ts +9 -0
  131. package/dist/db/storage-migration.d.ts.map +1 -0
  132. package/dist/db/storage-migration.js +79 -0
  133. package/dist/db/storage-migration.js.map +1 -0
  134. package/dist/db/transaction.d.ts +9 -0
  135. package/dist/db/transaction.d.ts.map +1 -0
  136. package/dist/db/transaction.js +14 -0
  137. package/dist/db/transaction.js.map +1 -0
  138. package/dist/errors/error-handler.d.ts +3 -0
  139. package/dist/errors/error-handler.d.ts.map +1 -0
  140. package/dist/errors/error-handler.js +50 -0
  141. package/dist/errors/error-handler.js.map +1 -0
  142. package/dist/feedbacks/feedback-workflow.service.d.ts +59 -0
  143. package/dist/feedbacks/feedback-workflow.service.d.ts.map +1 -0
  144. package/dist/feedbacks/feedback-workflow.service.js +89 -0
  145. package/dist/feedbacks/feedback-workflow.service.js.map +1 -0
  146. package/dist/feedbacks/feedback.input.d.ts +4 -0
  147. package/dist/feedbacks/feedback.input.d.ts.map +1 -0
  148. package/dist/feedbacks/feedback.input.js +25 -0
  149. package/dist/feedbacks/feedback.input.js.map +1 -0
  150. package/dist/feedbacks/feedback.repository.d.ts +48 -0
  151. package/dist/feedbacks/feedback.repository.d.ts.map +1 -0
  152. package/dist/feedbacks/feedback.repository.js +238 -0
  153. package/dist/feedbacks/feedback.repository.js.map +1 -0
  154. package/dist/feedbacks/feedback.routes.d.ts +4 -0
  155. package/dist/feedbacks/feedback.routes.d.ts.map +1 -0
  156. package/dist/feedbacks/feedback.routes.js +78 -0
  157. package/dist/feedbacks/feedback.routes.js.map +1 -0
  158. package/dist/feedbacks/feedback.schemas.d.ts +642 -0
  159. package/dist/feedbacks/feedback.schemas.d.ts.map +1 -0
  160. package/dist/feedbacks/feedback.schemas.js +131 -0
  161. package/dist/feedbacks/feedback.schemas.js.map +1 -0
  162. package/dist/feedbacks/feedback.service.d.ts +23 -0
  163. package/dist/feedbacks/feedback.service.d.ts.map +1 -0
  164. package/dist/feedbacks/feedback.service.js +74 -0
  165. package/dist/feedbacks/feedback.service.js.map +1 -0
  166. package/dist/feedbacks/feedback.types.d.ts +64 -0
  167. package/dist/feedbacks/feedback.types.d.ts.map +1 -0
  168. package/dist/feedbacks/feedback.types.js +2 -0
  169. package/dist/feedbacks/feedback.types.js.map +1 -0
  170. package/dist/page-elements/page-element.input.d.ts +7 -0
  171. package/dist/page-elements/page-element.input.d.ts.map +1 -0
  172. package/dist/page-elements/page-element.input.js +59 -0
  173. package/dist/page-elements/page-element.input.js.map +1 -0
  174. package/dist/page-elements/page-element.repository.d.ts +21 -0
  175. package/dist/page-elements/page-element.repository.d.ts.map +1 -0
  176. package/dist/page-elements/page-element.repository.js +123 -0
  177. package/dist/page-elements/page-element.repository.js.map +1 -0
  178. package/dist/page-elements/page-element.schemas.d.ts +97 -0
  179. package/dist/page-elements/page-element.schemas.d.ts.map +1 -0
  180. package/dist/page-elements/page-element.schemas.js +40 -0
  181. package/dist/page-elements/page-element.schemas.js.map +1 -0
  182. package/dist/page-elements/page-element.types.d.ts +24 -0
  183. package/dist/page-elements/page-element.types.d.ts.map +1 -0
  184. package/dist/page-elements/page-element.types.js +2 -0
  185. package/dist/page-elements/page-element.types.js.map +1 -0
  186. package/dist/plugins/auth.plugin.d.ts +3 -0
  187. package/dist/plugins/auth.plugin.d.ts.map +1 -0
  188. package/dist/plugins/auth.plugin.js +28 -0
  189. package/dist/plugins/auth.plugin.js.map +1 -0
  190. package/dist/plugins/community-api.plugin.d.ts +5 -0
  191. package/dist/plugins/community-api.plugin.d.ts.map +1 -0
  192. package/dist/plugins/community-api.plugin.js +25 -0
  193. package/dist/plugins/community-api.plugin.js.map +1 -0
  194. package/dist/plugins/cors.plugin.d.ts +3 -0
  195. package/dist/plugins/cors.plugin.d.ts.map +1 -0
  196. package/dist/plugins/cors.plugin.js +19 -0
  197. package/dist/plugins/cors.plugin.js.map +1 -0
  198. package/dist/plugins/swagger.plugin.d.ts +3 -0
  199. package/dist/plugins/swagger.plugin.d.ts.map +1 -0
  200. package/dist/plugins/swagger.plugin.js +59 -0
  201. package/dist/plugins/swagger.plugin.js.map +1 -0
  202. package/dist/plugins/user-auth.plugin.d.ts +33 -0
  203. package/dist/plugins/user-auth.plugin.d.ts.map +1 -0
  204. package/dist/plugins/user-auth.plugin.js +62 -0
  205. package/dist/plugins/user-auth.plugin.js.map +1 -0
  206. package/dist/projects/members/project-member.repository.d.ts +49 -0
  207. package/dist/projects/members/project-member.repository.d.ts.map +1 -0
  208. package/dist/projects/members/project-member.repository.js +215 -0
  209. package/dist/projects/members/project-member.repository.js.map +1 -0
  210. package/dist/projects/members/project-member.routes.d.ts +4 -0
  211. package/dist/projects/members/project-member.routes.d.ts.map +1 -0
  212. package/dist/projects/members/project-member.routes.js +82 -0
  213. package/dist/projects/members/project-member.routes.js.map +1 -0
  214. package/dist/projects/members/project-member.schemas.d.ts +2735 -0
  215. package/dist/projects/members/project-member.schemas.d.ts.map +1 -0
  216. package/dist/projects/members/project-member.schemas.js +138 -0
  217. package/dist/projects/members/project-member.schemas.js.map +1 -0
  218. package/dist/projects/members/project-member.service.d.ts +29 -0
  219. package/dist/projects/members/project-member.service.d.ts.map +1 -0
  220. package/dist/projects/members/project-member.service.js +92 -0
  221. package/dist/projects/members/project-member.service.js.map +1 -0
  222. package/dist/projects/project-access.policy.d.ts +11 -0
  223. package/dist/projects/project-access.policy.d.ts.map +1 -0
  224. package/dist/projects/project-access.policy.js +30 -0
  225. package/dist/projects/project-access.policy.js.map +1 -0
  226. package/dist/projects/project-access.repository.d.ts +12 -0
  227. package/dist/projects/project-access.repository.d.ts.map +1 -0
  228. package/dist/projects/project-access.repository.js +45 -0
  229. package/dist/projects/project-access.repository.js.map +1 -0
  230. package/dist/projects/project-access.types.d.ts +9 -0
  231. package/dist/projects/project-access.types.d.ts.map +1 -0
  232. package/dist/projects/project-access.types.js +2 -0
  233. package/dist/projects/project-access.types.js.map +1 -0
  234. package/dist/projects/project-mutation.service.d.ts +15 -0
  235. package/dist/projects/project-mutation.service.d.ts.map +1 -0
  236. package/dist/projects/project-mutation.service.js +39 -0
  237. package/dist/projects/project-mutation.service.js.map +1 -0
  238. package/dist/projects/project.repository.d.ts +49 -0
  239. package/dist/projects/project.repository.d.ts.map +1 -0
  240. package/dist/projects/project.repository.js +155 -0
  241. package/dist/projects/project.repository.js.map +1 -0
  242. package/dist/projects/project.routes.d.ts +4 -0
  243. package/dist/projects/project.routes.d.ts.map +1 -0
  244. package/dist/projects/project.routes.js +45 -0
  245. package/dist/projects/project.routes.js.map +1 -0
  246. package/dist/projects/project.schemas.d.ts +2044 -0
  247. package/dist/projects/project.schemas.d.ts.map +1 -0
  248. package/dist/projects/project.schemas.js +90 -0
  249. package/dist/projects/project.schemas.js.map +1 -0
  250. package/dist/projects/project.service.d.ts +40 -0
  251. package/dist/projects/project.service.d.ts.map +1 -0
  252. package/dist/projects/project.service.js +128 -0
  253. package/dist/projects/project.service.js.map +1 -0
  254. package/dist/replies/reply-mutation.service.d.ts +19 -0
  255. package/dist/replies/reply-mutation.service.d.ts.map +1 -0
  256. package/dist/replies/reply-mutation.service.js +51 -0
  257. package/dist/replies/reply-mutation.service.js.map +1 -0
  258. package/dist/replies/reply.input.d.ts +4 -0
  259. package/dist/replies/reply.input.d.ts.map +1 -0
  260. package/dist/replies/reply.input.js +33 -0
  261. package/dist/replies/reply.input.js.map +1 -0
  262. package/dist/replies/reply.repository.d.ts +40 -0
  263. package/dist/replies/reply.repository.d.ts.map +1 -0
  264. package/dist/replies/reply.repository.js +173 -0
  265. package/dist/replies/reply.repository.js.map +1 -0
  266. package/dist/replies/reply.routes.d.ts +4 -0
  267. package/dist/replies/reply.routes.d.ts.map +1 -0
  268. package/dist/replies/reply.routes.js +64 -0
  269. package/dist/replies/reply.routes.js.map +1 -0
  270. package/dist/replies/reply.schemas.d.ts +251 -0
  271. package/dist/replies/reply.schemas.d.ts.map +1 -0
  272. package/dist/replies/reply.schemas.js +51 -0
  273. package/dist/replies/reply.schemas.js.map +1 -0
  274. package/dist/replies/reply.service.d.ts +19 -0
  275. package/dist/replies/reply.service.d.ts.map +1 -0
  276. package/dist/replies/reply.service.js +50 -0
  277. package/dist/replies/reply.service.js.map +1 -0
  278. package/dist/replies/reply.types.d.ts +31 -0
  279. package/dist/replies/reply.types.d.ts.map +1 -0
  280. package/dist/replies/reply.types.js +2 -0
  281. package/dist/replies/reply.types.js.map +1 -0
  282. package/dist/report-cases/report-case-workflow.service.d.ts +39 -0
  283. package/dist/report-cases/report-case-workflow.service.d.ts.map +1 -0
  284. package/dist/report-cases/report-case-workflow.service.js +103 -0
  285. package/dist/report-cases/report-case-workflow.service.js.map +1 -0
  286. package/dist/report-cases/report-case.input.d.ts +4 -0
  287. package/dist/report-cases/report-case.input.d.ts.map +1 -0
  288. package/dist/report-cases/report-case.input.js +29 -0
  289. package/dist/report-cases/report-case.input.js.map +1 -0
  290. package/dist/report-cases/report-case.repository.d.ts +45 -0
  291. package/dist/report-cases/report-case.repository.d.ts.map +1 -0
  292. package/dist/report-cases/report-case.repository.js +185 -0
  293. package/dist/report-cases/report-case.repository.js.map +1 -0
  294. package/dist/report-cases/report-case.routes.d.ts +4 -0
  295. package/dist/report-cases/report-case.routes.d.ts.map +1 -0
  296. package/dist/report-cases/report-case.routes.js +94 -0
  297. package/dist/report-cases/report-case.routes.js.map +1 -0
  298. package/dist/report-cases/report-case.schemas.d.ts +294 -0
  299. package/dist/report-cases/report-case.schemas.d.ts.map +1 -0
  300. package/dist/report-cases/report-case.schemas.js +77 -0
  301. package/dist/report-cases/report-case.schemas.js.map +1 -0
  302. package/dist/report-cases/report-case.service.d.ts +21 -0
  303. package/dist/report-cases/report-case.service.d.ts.map +1 -0
  304. package/dist/report-cases/report-case.service.js +60 -0
  305. package/dist/report-cases/report-case.service.js.map +1 -0
  306. package/dist/report-cases/report-case.types.d.ts +39 -0
  307. package/dist/report-cases/report-case.types.d.ts.map +1 -0
  308. package/dist/report-cases/report-case.types.js +2 -0
  309. package/dist/report-cases/report-case.types.js.map +1 -0
  310. package/dist/routes/health.route.d.ts +4 -0
  311. package/dist/routes/health.route.d.ts.map +1 -0
  312. package/dist/routes/health.route.js +39 -0
  313. package/dist/routes/health.route.js.map +1 -0
  314. package/dist/routes/version.route.d.ts +3 -0
  315. package/dist/routes/version.route.d.ts.map +1 -0
  316. package/dist/routes/version.route.js +22 -0
  317. package/dist/routes/version.route.js.map +1 -0
  318. package/dist/runtime/index.d.ts +8 -0
  319. package/dist/runtime/index.d.ts.map +1 -0
  320. package/dist/runtime/index.js +11 -0
  321. package/dist/runtime/index.js.map +1 -0
  322. package/dist/server.d.ts +11 -0
  323. package/dist/server.d.ts.map +1 -0
  324. package/dist/server.js +66 -0
  325. package/dist/server.js.map +1 -0
  326. package/dist/storage/values.d.ts +3 -0
  327. package/dist/storage/values.d.ts.map +1 -0
  328. package/dist/storage/values.js +30 -0
  329. package/dist/storage/values.js.map +1 -0
  330. package/dist/users/current-user.routes.d.ts +3 -0
  331. package/dist/users/current-user.routes.d.ts.map +1 -0
  332. package/dist/users/current-user.routes.js +9 -0
  333. package/dist/users/current-user.routes.js.map +1 -0
  334. package/dist/users/current-user.schemas.d.ts +641 -0
  335. package/dist/users/current-user.schemas.d.ts.map +1 -0
  336. package/dist/users/current-user.schemas.js +21 -0
  337. package/dist/users/current-user.schemas.js.map +1 -0
  338. package/dist/users/user-search.repository.d.ts +19 -0
  339. package/dist/users/user-search.repository.d.ts.map +1 -0
  340. package/dist/users/user-search.repository.js +55 -0
  341. package/dist/users/user-search.repository.js.map +1 -0
  342. package/dist/users/user-search.routes.d.ts +4 -0
  343. package/dist/users/user-search.routes.d.ts.map +1 -0
  344. package/dist/users/user-search.routes.js +19 -0
  345. package/dist/users/user-search.routes.js.map +1 -0
  346. package/dist/users/user-search.schemas.d.ts +693 -0
  347. package/dist/users/user-search.schemas.d.ts.map +1 -0
  348. package/dist/users/user-search.schemas.js +41 -0
  349. package/dist/users/user-search.schemas.js.map +1 -0
  350. package/dist/users/user-search.service.d.ts +20 -0
  351. package/dist/users/user-search.service.d.ts.map +1 -0
  352. package/dist/users/user-search.service.js +41 -0
  353. package/dist/users/user-search.service.js.map +1 -0
  354. package/dist/users/user.schemas.d.ts +47 -0
  355. package/dist/users/user.schemas.d.ts.map +1 -0
  356. package/dist/users/user.schemas.js +13 -0
  357. package/dist/users/user.schemas.js.map +1 -0
  358. package/dist/users/user.types.d.ts +6 -0
  359. package/dist/users/user.types.d.ts.map +1 -0
  360. package/dist/users/user.types.js +2 -0
  361. package/dist/users/user.types.js.map +1 -0
  362. package/dist/version.d.ts +3 -0
  363. package/dist/version.d.ts.map +1 -0
  364. package/dist/version.js +5 -0
  365. package/dist/version.js.map +1 -0
  366. package/docs/guides/community-api-quickstart.md +171 -0
  367. package/docs/guides/feature-structure.md +138 -0
  368. package/docs/guides/independent-cli.md +73 -0
  369. package/docs/guides/non-attachment-api-contract.md +138 -0
  370. package/docs/plans/selfdev-schema-alignment.md +61 -0
  371. package/package.json +76 -0
  372. package/sql/fivepixels-lite-init.sql +518 -0
@@ -0,0 +1,518 @@
1
+ -- Fivepixels Lite API: 최초 설치용 SQLite 통합 DDL
2
+ -- 기준: 현재 개발 소스 migration 001~007 (schema version 7).
3
+ -- SQL Server/selfdev용 SQL이 아닙니다. 기존 DB 업그레이드에 사용하지 마십시오.
4
+ --
5
+ -- 모든 테이블/인덱스/제약/마이그레이션 이력을 포함합니다.
6
+ -- Node.js, 앱 등록 함수, 확장 모듈이나 다른 SQL 파일이 필요하지 않습니다.
7
+ -- SQLite 3.38 이상(STRICT + 내장 JSON 함수), 쓰기 가능한 로컬 디스크가 필요합니다.
8
+ -- 최신 보안 패치 버전 사용을 권장합니다. API는 별도로 WAL 안전 버전을 검사합니다.
9
+ --
10
+ -- 실행: sqlite3 -bail /절대/신규/경로/data.db < /절대/경로/fivepixels-lite-init.sql
11
+ -- 새 연결에서 전체 파일을 실행하십시오. 기존 트랜잭션 안에서 실행하지 마십시오.
12
+ -- 오류 시 반드시 즉시 중단하고 ROLLBACK 또는 연결을 닫으십시오.
13
+ -- GUI 도구의 '오류 무시 후 계속 실행' 모드는 지원하지 않습니다.
14
+ -- 기존 스키마/데이터는 변경하거나 삭제하지 않습니다. 재실행도 오류로 거부합니다.
15
+ -- WAL/동기화 설정과 JWT 키 생성은 이후 같은 소스의 API init/start가 처리합니다.
16
+ -- DDL 자체는 관리자 계정, JWT 키, 비밀번호, 업무 샘플 데이터를 생성하지 않습니다.
17
+
18
+ PRAGMA foreign_keys = ON;
19
+ PRAGMA busy_timeout = 5000;
20
+
21
+ BEGIN IMMEDIATE;
22
+
23
+ -- main DB가 비었는지 쓰기 잠금 안에서 확인합니다. 임시 검사 객체는 커밋 전에 제거합니다.
24
+ CREATE TEMP TABLE fivepixels_init_guard (step TEXT NOT NULL) STRICT;
25
+
26
+ CREATE TEMP TRIGGER fivepixels_init_empty_database
27
+ BEFORE INSERT ON fivepixels_init_guard
28
+ WHEN NEW.step = 'empty' AND EXISTS (
29
+ SELECT 1 FROM main.sqlite_schema WHERE name NOT LIKE 'sqlite_%'
30
+ )
31
+ BEGIN
32
+ SELECT RAISE(ROLLBACK, 'fivepixels-init-requires-empty-database');
33
+ END;
34
+
35
+ CREATE TEMP TRIGGER fivepixels_init_foreign_keys
36
+ BEFORE INSERT ON fivepixels_init_guard
37
+ WHEN (SELECT foreign_keys FROM pragma_foreign_keys) <> 1
38
+ BEGIN
39
+ SELECT RAISE(ROLLBACK, 'fivepixels-init-requires-foreign-keys');
40
+ END;
41
+
42
+ CREATE TEMP TRIGGER fivepixels_init_integrity
43
+ BEFORE INSERT ON fivepixels_init_guard
44
+ WHEN NEW.step = 'verify' AND (
45
+ EXISTS (SELECT 1 FROM pragma_foreign_key_check)
46
+ OR EXISTS (SELECT 1 FROM pragma_integrity_check WHERE integrity_check <> 'ok')
47
+ )
48
+ BEGIN
49
+ SELECT RAISE(ROLLBACK, 'fivepixels-init-integrity-check-failed');
50
+ END;
51
+
52
+ INSERT INTO temp.fivepixels_init_guard (step) VALUES ('empty');
53
+
54
+ -- 최종 테이블 정의: 과거 버전의 임시 테이블 생성/변환 과정은 실행하지 않습니다.
55
+ CREATE TABLE schema_migrations (
56
+ version INTEGER PRIMARY KEY CHECK (version > 0),
57
+ name TEXT NOT NULL UNIQUE,
58
+ checksum TEXT NOT NULL CHECK (length(checksum) = 64),
59
+ applied_at TEXT NOT NULL
60
+ ) STRICT;
61
+
62
+ CREATE TABLE users (
63
+ user_id TEXT PRIMARY KEY
64
+ , status_code INTEGER NOT NULL DEFAULT 1
65
+ , user_name TEXT NOT NULL
66
+ , profile_image_url TEXT
67
+ , created_at TEXT NOT NULL
68
+ , updated_at TEXT NOT NULL
69
+ , CHECK (
70
+ length(user_id) = 36
71
+ AND user_id = lower(user_id)
72
+ AND substr(user_id, 9, 1) = '-'
73
+ AND substr(user_id, 14, 1) = '-'
74
+ AND substr(user_id, 19, 1) = '-'
75
+ AND substr(user_id, 24, 1) = '-'
76
+ AND length(replace(user_id, '-', '')) = 32
77
+ AND replace(user_id, '-', '') NOT GLOB '*[^0-9a-f]*'
78
+ )
79
+ , CHECK (status_code IN (1, 2, 3))
80
+ , CHECK (user_name = trim(user_name) AND length(user_name) BETWEEN 1 AND 100)
81
+ , CHECK (
82
+ profile_image_url IS NULL
83
+ OR (profile_image_url = trim(profile_image_url) AND length(profile_image_url) BETWEEN 1 AND 2048)
84
+ )
85
+ ) STRICT;
86
+
87
+ CREATE TABLE auth_refresh_tokens (
88
+ refresh_token_id TEXT PRIMARY KEY
89
+ , user_id TEXT NOT NULL
90
+ , token_hash BLOB NOT NULL UNIQUE
91
+ , expires_at TEXT NOT NULL
92
+ , last_used_at TEXT
93
+ , revoked_at TEXT
94
+ , replaced_by_token_id TEXT
95
+ , lock_version INTEGER NOT NULL DEFAULT 0
96
+ , created_at TEXT NOT NULL
97
+ , CONSTRAINT fk_auth_refresh_tokens_user FOREIGN KEY (user_id)
98
+ REFERENCES users (user_id) ON DELETE RESTRICT
99
+ , CONSTRAINT fk_auth_refresh_tokens_replacement FOREIGN KEY (replaced_by_token_id)
100
+ REFERENCES auth_refresh_tokens (refresh_token_id) ON DELETE RESTRICT
101
+ , CHECK (
102
+ length(refresh_token_id) = 36
103
+ AND refresh_token_id = lower(refresh_token_id)
104
+ AND substr(refresh_token_id, 9, 1) = '-'
105
+ AND substr(refresh_token_id, 14, 1) = '-'
106
+ AND substr(refresh_token_id, 19, 1) = '-'
107
+ AND substr(refresh_token_id, 24, 1) = '-'
108
+ AND length(replace(refresh_token_id, '-', '')) = 32
109
+ AND replace(refresh_token_id, '-', '') NOT GLOB '*[^0-9a-f]*'
110
+ )
111
+ , CHECK (length(token_hash) = 32)
112
+ , CHECK (expires_at > created_at)
113
+ , CHECK (
114
+ replaced_by_token_id IS NULL
115
+ OR (replaced_by_token_id <> refresh_token_id AND revoked_at IS NOT NULL)
116
+ )
117
+ , CHECK (lock_version >= 0)
118
+ ) STRICT;
119
+
120
+ CREATE TABLE projects (
121
+ project_id TEXT PRIMARY KEY
122
+ CHECK (
123
+ length(project_id) = 36
124
+ AND substr(project_id, 9, 1) = '-'
125
+ AND substr(project_id, 14, 1) = '-'
126
+ AND substr(project_id, 19, 1) = '-'
127
+ AND substr(project_id, 24, 1) = '-'
128
+ AND project_id = lower(project_id)
129
+ AND length(replace(project_id, '-', '')) = 32
130
+ AND replace(project_id, '-', '') NOT GLOB '*[^0-9a-f]*'
131
+ ),
132
+ creator_id TEXT NOT NULL,
133
+ status_code INTEGER NOT NULL DEFAULT 1
134
+ CHECK (status_code IN (1, 2)),
135
+ project_name TEXT NOT NULL
136
+ CHECK (
137
+ length(project_name) BETWEEN 1 AND 100
138
+ AND project_name = trim(project_name)
139
+ ),
140
+ lock_version INTEGER NOT NULL DEFAULT 0
141
+ CHECK (lock_version >= 0),
142
+ created_at TEXT NOT NULL,
143
+ updated_at TEXT NOT NULL,
144
+ CONSTRAINT fk_projects_creator
145
+ FOREIGN KEY (creator_id) REFERENCES users(user_id)
146
+ ON UPDATE RESTRICT ON DELETE RESTRICT
147
+ ) STRICT;
148
+
149
+ CREATE TABLE project_members (
150
+ project_id TEXT NOT NULL,
151
+ user_id TEXT NOT NULL,
152
+ role_code INTEGER NOT NULL
153
+ CHECK (role_code IN (1, 2)),
154
+ added_by TEXT NOT NULL,
155
+ lock_version INTEGER NOT NULL DEFAULT 0
156
+ CHECK (lock_version >= 0),
157
+ joined_at TEXT NOT NULL,
158
+ updated_at TEXT NOT NULL,
159
+ CONSTRAINT pk_project_members PRIMARY KEY (project_id, user_id),
160
+ CONSTRAINT fk_project_members_project
161
+ FOREIGN KEY (project_id) REFERENCES projects(project_id)
162
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
163
+ CONSTRAINT fk_project_members_user
164
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
165
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
166
+ CONSTRAINT fk_project_members_added_by
167
+ FOREIGN KEY (added_by) REFERENCES users(user_id)
168
+ ON UPDATE RESTRICT ON DELETE RESTRICT
169
+ ) STRICT;
170
+
171
+ CREATE TABLE feedbacks (
172
+ feedback_id TEXT PRIMARY KEY,
173
+ project_id TEXT NOT NULL,
174
+ environment TEXT NOT NULL
175
+ CHECK (length(environment) BETWEEN 1 AND 50 AND environment = lower(trim(environment))),
176
+ pathname TEXT NOT NULL
177
+ CHECK (length(pathname) BETWEEN 1 AND 2000 AND substr(pathname, 1, 1) = '/'),
178
+ pathname_hash TEXT NOT NULL
179
+ CHECK (length(pathname_hash) = 64 AND pathname_hash NOT GLOB '*[^0-9a-f]*'),
180
+ report_id TEXT NOT NULL
181
+ CHECK (length(report_id) BETWEEN 1 AND 255 AND report_id = trim(report_id)),
182
+ report_type TEXT NOT NULL
183
+ CHECK (length(report_type) BETWEEN 1 AND 50 AND report_type = trim(report_type)),
184
+ target_selector TEXT
185
+ CHECK (target_selector IS NULL OR (length(target_selector) BETWEEN 1 AND 2000 AND target_selector = trim(target_selector))),
186
+ status_code INTEGER NOT NULL DEFAULT 1
187
+ CHECK (status_code IN (1, 2)),
188
+ field_values TEXT NOT NULL
189
+ CHECK (json_valid(field_values) AND json_type(field_values) = 'object'),
190
+ position TEXT NOT NULL
191
+ CHECK (json_valid(position) AND json_type(position) = 'object'),
192
+ app_version TEXT NOT NULL
193
+ CHECK (length(app_version) BETWEEN 1 AND 100 AND app_version = trim(app_version)),
194
+ author_id TEXT NOT NULL,
195
+ assignee_id TEXT,
196
+ integrations TEXT NOT NULL
197
+ CHECK (json_valid(integrations) AND json_type(integrations) = 'array'),
198
+ lock_version INTEGER NOT NULL DEFAULT 0
199
+ CHECK (lock_version >= 0),
200
+ created_at TEXT NOT NULL,
201
+ updated_at TEXT NOT NULL,
202
+ CONSTRAINT fk_feedbacks_project
203
+ FOREIGN KEY (project_id) REFERENCES projects(project_id)
204
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
205
+ CONSTRAINT fk_feedbacks_author
206
+ FOREIGN KEY (author_id) REFERENCES users(user_id)
207
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
208
+ CONSTRAINT fk_feedbacks_assignee_membership
209
+ FOREIGN KEY (project_id, assignee_id) REFERENCES project_members(project_id, user_id)
210
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
211
+ CHECK (
212
+ length(feedback_id) = 36
213
+ AND feedback_id = lower(feedback_id)
214
+ AND substr(feedback_id, 9, 1) = '-'
215
+ AND substr(feedback_id, 14, 1) = '-'
216
+ AND substr(feedback_id, 19, 1) = '-'
217
+ AND substr(feedback_id, 24, 1) = '-'
218
+ AND replace(feedback_id, '-', '') NOT GLOB '*[^0-9a-f]*'
219
+ )
220
+ ) STRICT;
221
+
222
+ CREATE TABLE report_cases (
223
+ report_case_id TEXT PRIMARY KEY,
224
+ feedback_id TEXT NOT NULL,
225
+ type_code INTEGER NOT NULL
226
+ CHECK (type_code BETWEEN 1 AND 4),
227
+ status_code INTEGER NOT NULL DEFAULT 1
228
+ CHECK (status_code IN (1, 2)),
229
+ content TEXT NOT NULL
230
+ CHECK (length(content) >= 1 AND content = trim(content)),
231
+ creator_id TEXT NOT NULL,
232
+ assignee_id TEXT,
233
+ lock_version INTEGER NOT NULL DEFAULT 0
234
+ CHECK (lock_version >= 0),
235
+ resolved_at TEXT,
236
+ created_at TEXT NOT NULL,
237
+ updated_at TEXT NOT NULL,
238
+ CONSTRAINT fk_report_cases_feedback
239
+ FOREIGN KEY (feedback_id) REFERENCES feedbacks(feedback_id)
240
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
241
+ CONSTRAINT fk_report_cases_creator
242
+ FOREIGN KEY (creator_id) REFERENCES users(user_id)
243
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
244
+ CONSTRAINT fk_report_cases_assignee
245
+ FOREIGN KEY (assignee_id) REFERENCES users(user_id)
246
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
247
+ CHECK (
248
+ (status_code = 1 AND resolved_at IS NULL)
249
+ OR (status_code = 2 AND resolved_at IS NOT NULL)
250
+ ),
251
+ CHECK (
252
+ length(report_case_id) = 36
253
+ AND report_case_id = lower(report_case_id)
254
+ AND substr(report_case_id, 9, 1) = '-'
255
+ AND substr(report_case_id, 14, 1) = '-'
256
+ AND substr(report_case_id, 19, 1) = '-'
257
+ AND substr(report_case_id, 24, 1) = '-'
258
+ AND replace(report_case_id, '-', '') NOT GLOB '*[^0-9a-f]*'
259
+ )
260
+ ) STRICT;
261
+
262
+ CREATE TABLE replies (
263
+ reply_id TEXT PRIMARY KEY,
264
+ type_code INTEGER NOT NULL
265
+ CHECK (type_code BETWEEN 1 AND 9),
266
+ status_code INTEGER NOT NULL DEFAULT 1
267
+ CHECK (status_code IN (1, 2)),
268
+ report_case_id TEXT NOT NULL,
269
+ parent_reply_id TEXT,
270
+ content TEXT NOT NULL
271
+ CHECK (length(content) BETWEEN 1 AND 10000 AND content = trim(content)),
272
+ author_id TEXT NOT NULL,
273
+ assignee_id TEXT,
274
+ previous_assignee_id TEXT,
275
+ lock_version INTEGER NOT NULL DEFAULT 0
276
+ CHECK (lock_version >= 0),
277
+ deleted_at TEXT,
278
+ created_at TEXT NOT NULL,
279
+ updated_at TEXT NOT NULL,
280
+ CONSTRAINT uq_replies_report_case_reply UNIQUE (report_case_id, reply_id),
281
+ CONSTRAINT fk_replies_report_case
282
+ FOREIGN KEY (report_case_id) REFERENCES report_cases(report_case_id)
283
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
284
+ CONSTRAINT fk_replies_parent
285
+ FOREIGN KEY (report_case_id, parent_reply_id)
286
+ REFERENCES replies(report_case_id, reply_id)
287
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
288
+ CONSTRAINT fk_replies_author
289
+ FOREIGN KEY (author_id) REFERENCES users(user_id)
290
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
291
+ CONSTRAINT fk_replies_assignee
292
+ FOREIGN KEY (assignee_id) REFERENCES users(user_id)
293
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
294
+ CONSTRAINT fk_replies_previous_assignee
295
+ FOREIGN KEY (previous_assignee_id) REFERENCES users(user_id)
296
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
297
+ CHECK (parent_reply_id IS NULL OR parent_reply_id <> reply_id),
298
+ CHECK (
299
+ (status_code = 1 AND deleted_at IS NULL)
300
+ OR (status_code = 2 AND deleted_at IS NOT NULL)
301
+ ),
302
+ CHECK (
303
+ length(reply_id) = 36
304
+ AND reply_id = lower(reply_id)
305
+ AND substr(reply_id, 9, 1) = '-'
306
+ AND substr(reply_id, 14, 1) = '-'
307
+ AND substr(reply_id, 19, 1) = '-'
308
+ AND substr(reply_id, 24, 1) = '-'
309
+ AND replace(reply_id, '-', '') NOT GLOB '*[^0-9a-f]*'
310
+ )
311
+ ) STRICT;
312
+
313
+ CREATE TABLE page_elements (
314
+ page_element_id TEXT PRIMARY KEY,
315
+ mention_id TEXT NOT NULL UNIQUE,
316
+ label TEXT NOT NULL
317
+ CHECK (length(label) BETWEEN 1 AND 200 AND label = trim(label) AND instr(label, char(0)) = 0),
318
+ data_report_id TEXT
319
+ CHECK (data_report_id IS NULL OR (length(data_report_id) BETWEEN 1 AND 255 AND data_report_id = trim(data_report_id) AND instr(data_report_id, char(0)) = 0)),
320
+ selector TEXT NOT NULL
321
+ CHECK (length(selector) BETWEEN 1 AND 2000 AND selector = trim(selector) AND instr(selector, char(0)) = 0),
322
+ created_by TEXT NOT NULL,
323
+ created_at TEXT NOT NULL,
324
+ CONSTRAINT fk_page_elements_creator
325
+ FOREIGN KEY (created_by) REFERENCES users(user_id)
326
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
327
+ CHECK (
328
+ length(page_element_id) = 36
329
+ AND page_element_id = lower(page_element_id)
330
+ AND substr(page_element_id, 9, 1) = '-'
331
+ AND substr(page_element_id, 14, 1) = '-'
332
+ AND substr(page_element_id, 19, 1) = '-'
333
+ AND substr(page_element_id, 24, 1) = '-'
334
+ AND replace(page_element_id, '-', '') NOT GLOB '*[^0-9a-f]*'
335
+ ),
336
+ CHECK (
337
+ length(mention_id) = 36
338
+ AND mention_id = lower(mention_id)
339
+ AND substr(mention_id, 9, 1) = '-'
340
+ AND substr(mention_id, 14, 1) = '-'
341
+ AND substr(mention_id, 19, 1) = '-'
342
+ AND substr(mention_id, 24, 1) = '-'
343
+ AND replace(mention_id, '-', '') NOT GLOB '*[^0-9a-f]*'
344
+ )
345
+ ) STRICT;
346
+
347
+ CREATE TABLE report_case_page_elements (
348
+ report_case_id TEXT NOT NULL,
349
+ page_element_id TEXT NOT NULL,
350
+ CONSTRAINT pk_report_case_page_elements PRIMARY KEY (report_case_id, page_element_id),
351
+ CONSTRAINT fk_report_case_page_elements_case
352
+ FOREIGN KEY (report_case_id) REFERENCES report_cases(report_case_id)
353
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
354
+ CONSTRAINT fk_report_case_page_elements_element
355
+ FOREIGN KEY (page_element_id) REFERENCES page_elements(page_element_id)
356
+ ON UPDATE RESTRICT ON DELETE RESTRICT
357
+ ) STRICT;
358
+
359
+ CREATE TABLE reply_page_elements (
360
+ reply_id TEXT NOT NULL,
361
+ page_element_id TEXT NOT NULL,
362
+ CONSTRAINT pk_reply_page_elements PRIMARY KEY (reply_id, page_element_id),
363
+ CONSTRAINT fk_reply_page_elements_reply
364
+ FOREIGN KEY (reply_id) REFERENCES replies(reply_id)
365
+ ON UPDATE RESTRICT ON DELETE RESTRICT,
366
+ CONSTRAINT fk_reply_page_elements_element
367
+ FOREIGN KEY (page_element_id) REFERENCES page_elements(page_element_id)
368
+ ON UPDATE RESTRICT ON DELETE RESTRICT
369
+ ) STRICT;
370
+
371
+ CREATE TABLE "local_credentials" (
372
+ user_id TEXT PRIMARY KEY
373
+ , login_id TEXT COLLATE NOCASE NOT NULL
374
+ , email TEXT NOT NULL
375
+ , password_hash TEXT NOT NULL
376
+ , failed_login_count INTEGER NOT NULL DEFAULT 0
377
+ , locked_until TEXT
378
+ , email_verified_at TEXT
379
+ , password_changed_at TEXT NOT NULL
380
+ , last_login_at TEXT
381
+ , lock_version INTEGER NOT NULL DEFAULT 0
382
+ , created_at TEXT NOT NULL
383
+ , updated_at TEXT NOT NULL
384
+ , CONSTRAINT fk_local_credentials_user FOREIGN KEY (user_id)
385
+ REFERENCES users (user_id) ON DELETE RESTRICT
386
+ , CHECK (login_id = trim(login_id) AND length(login_id) BETWEEN 4 AND 50)
387
+ , CHECK (substr(login_id, 1, 1) GLOB '[A-Za-z0-9]')
388
+ , CHECK (login_id NOT GLOB '*[^A-Za-z0-9._-]*')
389
+ , CHECK (email = trim(email) AND length(email) BETWEEN 3 AND 320)
390
+ , CHECK (password_hash = trim(password_hash) AND length(password_hash) BETWEEN 20 AND 512)
391
+ , CHECK (failed_login_count >= 0)
392
+ , CHECK (lock_version >= 0)
393
+ ) STRICT;
394
+
395
+ -- 최종 인덱스: UNIQUE 및 SqlGuid 표현식 정렬을 포함합니다.
396
+ CREATE INDEX ix_auth_refresh_tokens_user_active
397
+ ON auth_refresh_tokens (user_id, expires_at, refresh_token_id)
398
+ WHERE revoked_at IS NULL;
399
+
400
+ CREATE INDEX ix_project_members_user_joined_at
401
+ ON project_members (user_id, joined_at DESC, project_id DESC);
402
+
403
+ CREATE INDEX ix_project_members_project_role
404
+ ON project_members (project_id, role_code, user_id);
405
+
406
+ CREATE UNIQUE INDEX uq_feedbacks_project_environment_report_id
407
+ ON feedbacks (project_id, environment, report_id);
408
+
409
+ CREATE INDEX ix_feedbacks_assignee_status
410
+ ON feedbacks (assignee_id, status_code, updated_at DESC, feedback_id DESC);
411
+
412
+ CREATE INDEX ix_report_cases_assignee_status
413
+ ON report_cases (assignee_id, status_code, updated_at DESC, report_case_id DESC);
414
+
415
+ CREATE INDEX ix_replies_parent
416
+ ON replies (parent_reply_id, created_at, reply_id);
417
+
418
+ CREATE INDEX ix_report_case_page_elements_element
419
+ ON report_case_page_elements (page_element_id, report_case_id);
420
+
421
+ CREATE INDEX ix_reply_page_elements_element
422
+ ON reply_page_elements (page_element_id, reply_id);
423
+
424
+ CREATE UNIQUE INDEX uq_local_credentials_login_id ON local_credentials (login_id);
425
+
426
+ CREATE UNIQUE INDEX uq_local_credentials_email ON local_credentials (email);
427
+
428
+ CREATE INDEX ix_projects_status_created_at ON projects (status_code, created_at DESC, (substr(project_id, 25, 12) || substr(project_id, 20, 4) || substr(project_id, 17, 2) || substr(project_id, 15, 2) || substr(project_id, 12, 2) || substr(project_id, 10, 2) || substr(project_id, 7, 2) || substr(project_id, 5, 2) || substr(project_id, 3, 2) || substr(project_id, 1, 2)) DESC);
429
+
430
+ CREATE INDEX ix_projects_creator_created_at ON projects (creator_id, created_at DESC, (substr(project_id, 25, 12) || substr(project_id, 20, 4) || substr(project_id, 17, 2) || substr(project_id, 15, 2) || substr(project_id, 12, 2) || substr(project_id, 10, 2) || substr(project_id, 7, 2) || substr(project_id, 5, 2) || substr(project_id, 3, 2) || substr(project_id, 1, 2)) DESC);
431
+
432
+ CREATE INDEX ix_project_members_project_joined_at ON project_members (project_id, joined_at, (substr(user_id, 25, 12) || substr(user_id, 20, 4) || substr(user_id, 17, 2) || substr(user_id, 15, 2) || substr(user_id, 12, 2) || substr(user_id, 10, 2) || substr(user_id, 7, 2) || substr(user_id, 5, 2) || substr(user_id, 3, 2) || substr(user_id, 1, 2)));
433
+
434
+ CREATE INDEX ix_feedbacks_markers ON feedbacks (project_id, environment, pathname_hash, status_code, created_at DESC, (substr(feedback_id, 25, 12) || substr(feedback_id, 20, 4) || substr(feedback_id, 17, 2) || substr(feedback_id, 15, 2) || substr(feedback_id, 12, 2) || substr(feedback_id, 10, 2) || substr(feedback_id, 7, 2) || substr(feedback_id, 5, 2) || substr(feedback_id, 3, 2) || substr(feedback_id, 1, 2)) DESC);
435
+
436
+ CREATE INDEX ix_feedbacks_author_created_at ON feedbacks (author_id, created_at DESC, (substr(feedback_id, 25, 12) || substr(feedback_id, 20, 4) || substr(feedback_id, 17, 2) || substr(feedback_id, 15, 2) || substr(feedback_id, 12, 2) || substr(feedback_id, 10, 2) || substr(feedback_id, 7, 2) || substr(feedback_id, 5, 2) || substr(feedback_id, 3, 2) || substr(feedback_id, 1, 2)) DESC);
437
+
438
+ CREATE INDEX ix_report_cases_feedback_status_created_at ON report_cases (feedback_id, status_code, created_at DESC, (substr(report_case_id, 25, 12) || substr(report_case_id, 20, 4) || substr(report_case_id, 17, 2) || substr(report_case_id, 15, 2) || substr(report_case_id, 12, 2) || substr(report_case_id, 10, 2) || substr(report_case_id, 7, 2) || substr(report_case_id, 5, 2) || substr(report_case_id, 3, 2) || substr(report_case_id, 1, 2)) DESC);
439
+
440
+ CREATE INDEX ix_replies_report_case_created_at ON replies (report_case_id, created_at, (substr(reply_id, 25, 12) || substr(reply_id, 20, 4) || substr(reply_id, 17, 2) || substr(reply_id, 15, 2) || substr(reply_id, 12, 2) || substr(reply_id, 10, 2) || substr(reply_id, 7, 2) || substr(reply_id, 5, 2) || substr(reply_id, 3, 2) || substr(reply_id, 1, 2)));
441
+
442
+ CREATE INDEX ix_users_status_user_name ON users (status_code, user_name, (substr(user_id, 25, 12) || substr(user_id, 20, 4) || substr(user_id, 17, 2) || substr(user_id, 15, 2) || substr(user_id, 12, 2) || substr(user_id, 10, 2) || substr(user_id, 7, 2) || substr(user_id, 5, 2) || substr(user_id, 3, 2) || substr(user_id, 1, 2)));
443
+
444
+ CREATE INDEX ix_feedbacks_path_created_at ON feedbacks (project_id, environment, pathname_hash, created_at DESC, (substr(feedback_id, 25, 12) || substr(feedback_id, 20, 4) || substr(feedback_id, 17, 2) || substr(feedback_id, 15, 2) || substr(feedback_id, 12, 2) || substr(feedback_id, 10, 2) || substr(feedback_id, 7, 2) || substr(feedback_id, 5, 2) || substr(feedback_id, 3, 2) || substr(feedback_id, 1, 2)) DESC);
445
+
446
+ CREATE INDEX ix_report_cases_feedback_created_at ON report_cases (feedback_id, created_at DESC, (substr(report_case_id, 25, 12) || substr(report_case_id, 20, 4) || substr(report_case_id, 17, 2) || substr(report_case_id, 15, 2) || substr(report_case_id, 12, 2) || substr(report_case_id, 10, 2) || substr(report_case_id, 7, 2) || substr(report_case_id, 5, 2) || substr(report_case_id, 3, 2) || substr(report_case_id, 1, 2)) DESC);
447
+
448
+ -- 마지막 프로젝트 OWNER를 삭제/강등/교체하지 못하도록 보호하는 업무 트리거입니다.
449
+ CREATE TRIGGER tr_project_members_keep_owner_on_update
450
+ BEFORE UPDATE OF project_id, role_code ON project_members
451
+ FOR EACH ROW
452
+ WHEN OLD.role_code = 1
453
+ AND (NEW.project_id <> OLD.project_id OR NEW.role_code <> 1)
454
+ AND (
455
+ SELECT COUNT(*)
456
+ FROM project_members
457
+ WHERE project_id = OLD.project_id
458
+ AND role_code = 1
459
+ ) <= 1
460
+ BEGIN
461
+ SELECT RAISE(ABORT, 'project-owner-required');
462
+ END;
463
+
464
+ CREATE TRIGGER tr_project_members_keep_owner_on_replace
465
+ BEFORE INSERT ON project_members
466
+ FOR EACH ROW
467
+ WHEN NEW.role_code <> 1
468
+ AND EXISTS (
469
+ SELECT 1
470
+ FROM project_members
471
+ WHERE project_id = NEW.project_id
472
+ AND user_id = NEW.user_id
473
+ AND role_code = 1
474
+ )
475
+ AND (
476
+ SELECT COUNT(*)
477
+ FROM project_members
478
+ WHERE project_id = NEW.project_id
479
+ AND role_code = 1
480
+ ) <= 1
481
+ BEGIN
482
+ SELECT RAISE(ABORT, 'project-owner-required');
483
+ END;
484
+
485
+ CREATE TRIGGER tr_project_members_keep_owner_on_delete
486
+ BEFORE DELETE ON project_members
487
+ FOR EACH ROW
488
+ WHEN OLD.role_code = 1
489
+ AND (
490
+ SELECT COUNT(*)
491
+ FROM project_members
492
+ WHERE project_id = OLD.project_id
493
+ AND role_code = 1
494
+ ) <= 1
495
+ BEGIN
496
+ SELECT RAISE(ABORT, 'project-owner-required');
497
+ END;
498
+
499
+ -- 최종 구조가 001~007 적용 결과와 같으므로 해당 이력도 같은 트랜잭션으로 등록합니다.
500
+ -- checksum은 원본 migration SQL 바이트의 SHA-256입니다. 임의 변경하지 마십시오.
501
+ INSERT INTO schema_migrations (version, name, checksum, applied_at) VALUES
502
+ (1, 'schema_migrations', '4b87d3d340de3ccf5fc49c58af4ee77c9b97829f63b5d6738f62536e1ff5511e', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
503
+ (2, 'identity', 'b3a254a2dbfcd45c45639e09bf7a7a20b299d23ec0ce625454a49a30c3a7f187', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
504
+ (3, 'projects', 'e43e59cef1429089ab64145712d225e681955d63ed0b48b433f452730d12c62e', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
505
+ (4, 'feedback_core', '7f3d6fabd427094438e05103f6137f6553a76ff0f8ed1a8ce41c38e708d764af', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
506
+ (5, 'project_member_management', 'b962aecbe7e5b8fbced8fcc756048832f44e465eafc1724528087f68cf0657e9', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
507
+ (6, 'align_credentials', '9219ee1d2355a21dff10089e20ccb1a746562a8d23dd669be6a53c4bd5edfc5b', strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
508
+ (7, 'align_storage_values', '593cc57d8563707b1755a92173db44957c45765d36c4d2f60f3f9373effed367', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
509
+
510
+ INSERT INTO temp.fivepixels_init_guard (step) VALUES ('verify');
511
+ DROP TABLE temp.fivepixels_init_guard;
512
+
513
+ COMMIT;
514
+
515
+ -- 성공 확인: schema_version=7, integrity_check=ok, foreign_key_check 결과 없음.
516
+ SELECT MAX(version) AS schema_version FROM schema_migrations;
517
+ PRAGMA integrity_check;
518
+ PRAGMA foreign_key_check;