You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
14 KiB

6 years ago
6 years ago
6 years ago
  1. /* global WIKI */
  2. exports.up = knex => {
  3. const dbCompat = {
  4. blobLength: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
  5. charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
  6. selfCascadeDelete: WIKI.config.db.type !== 'mssql'
  7. }
  8. return knex.schema
  9. // =====================================
  10. // MODEL TABLES
  11. // =====================================
  12. // ANALYTICS ---------------------------
  13. .createTable('analytics', table => {
  14. if (dbCompat.charset) { table.charset('utf8mb4') }
  15. table.string('key').notNullable().primary()
  16. table.boolean('isEnabled').notNullable().defaultTo(false)
  17. table.json('config').notNullable()
  18. })
  19. // ASSETS ------------------------------
  20. .createTable('assets', table => {
  21. if (dbCompat.charset) { table.charset('utf8mb4') }
  22. table.increments('id').primary()
  23. table.string('filename').notNullable()
  24. table.string('hash').notNullable()
  25. table.string('ext').notNullable()
  26. table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
  27. table.string('mime').notNullable().defaultTo('application/octet-stream')
  28. table.integer('fileSize').unsigned().comment('In kilobytes')
  29. table.json('metadata')
  30. table.string('createdAt').notNullable()
  31. table.string('updatedAt').notNullable()
  32. })
  33. // ASSET DATA --------------------------
  34. .createTable('assetData', table => {
  35. if (dbCompat.charset) { table.charset('utf8mb4') }
  36. table.integer('id').primary()
  37. if (dbCompat.blobLength) {
  38. table.specificType('data', 'LONGBLOB').notNullable()
  39. } else {
  40. table.binary('data').notNullable()
  41. }
  42. })
  43. // ASSET FOLDERS -----------------------
  44. .createTable('assetFolders', table => {
  45. if (dbCompat.charset) { table.charset('utf8mb4') }
  46. table.increments('id').primary()
  47. table.string('name').notNullable()
  48. table.string('slug').notNullable()
  49. table.integer('parentId').unsigned().references('id').inTable('assetFolders')
  50. })
  51. // AUTHENTICATION ----------------------
  52. .createTable('authentication', table => {
  53. if (dbCompat.charset) { table.charset('utf8mb4') }
  54. table.string('key').notNullable().primary()
  55. table.boolean('isEnabled').notNullable().defaultTo(false)
  56. table.json('config').notNullable()
  57. table.boolean('selfRegistration').notNullable().defaultTo(false)
  58. table.json('domainWhitelist').notNullable()
  59. table.json('autoEnrollGroups').notNullable()
  60. })
  61. // COMMENTS ----------------------------
  62. .createTable('comments', table => {
  63. if (dbCompat.charset) { table.charset('utf8mb4') }
  64. table.increments('id').primary()
  65. table.text('content').notNullable()
  66. table.string('createdAt').notNullable()
  67. table.string('updatedAt').notNullable()
  68. })
  69. // EDITORS -----------------------------
  70. .createTable('editors', table => {
  71. if (dbCompat.charset) { table.charset('utf8mb4') }
  72. table.string('key').notNullable().primary()
  73. table.boolean('isEnabled').notNullable().defaultTo(false)
  74. table.json('config').notNullable()
  75. })
  76. // GROUPS ------------------------------
  77. .createTable('groups', table => {
  78. if (dbCompat.charset) { table.charset('utf8mb4') }
  79. table.increments('id').primary()
  80. table.string('name').notNullable()
  81. table.json('permissions').notNullable()
  82. table.json('pageRules').notNullable()
  83. table.boolean('isSystem').notNullable().defaultTo(false)
  84. table.string('createdAt').notNullable()
  85. table.string('updatedAt').notNullable()
  86. })
  87. // LOCALES -----------------------------
  88. .createTable('locales', table => {
  89. if (dbCompat.charset) { table.charset('utf8mb4') }
  90. table.string('code', 5).notNullable().primary()
  91. table.json('strings')
  92. table.boolean('isRTL').notNullable().defaultTo(false)
  93. table.string('name').notNullable()
  94. table.string('nativeName').notNullable()
  95. table.integer('availability').notNullable().defaultTo(0)
  96. table.string('createdAt').notNullable()
  97. table.string('updatedAt').notNullable()
  98. })
  99. // LOGGING ----------------------------
  100. .createTable('loggers', table => {
  101. if (dbCompat.charset) { table.charset('utf8mb4') }
  102. table.string('key').notNullable().primary()
  103. table.boolean('isEnabled').notNullable().defaultTo(false)
  104. table.string('level').notNullable().defaultTo('warn')
  105. table.json('config')
  106. })
  107. // NAVIGATION ----------------------------
  108. .createTable('navigation', table => {
  109. if (dbCompat.charset) { table.charset('utf8mb4') }
  110. table.string('key').notNullable().primary()
  111. table.json('config')
  112. })
  113. // PAGE HISTORY ------------------------
  114. .createTable('pageHistory', table => {
  115. if (dbCompat.charset) { table.charset('utf8mb4') }
  116. table.increments('id').primary()
  117. table.string('path').notNullable()
  118. table.string('hash').notNullable()
  119. table.string('title').notNullable()
  120. table.string('description')
  121. table.boolean('isPrivate').notNullable().defaultTo(false)
  122. table.boolean('isPublished').notNullable().defaultTo(false)
  123. table.string('publishStartDate')
  124. table.string('publishEndDate')
  125. table.string('action').defaultTo('updated')
  126. table.integer('pageId').unsigned()
  127. table.text('content')
  128. table.string('contentType').notNullable()
  129. table.string('createdAt').notNullable()
  130. })
  131. // PAGE LINKS --------------------------
  132. .createTable('pageLinks', table => {
  133. if (dbCompat.charset) { table.charset('utf8mb4') }
  134. table.increments('id').primary()
  135. table.string('path').notNullable()
  136. table.string('localeCode', 5).notNullable()
  137. })
  138. // PAGES -------------------------------
  139. .createTable('pages', table => {
  140. if (dbCompat.charset) { table.charset('utf8mb4') }
  141. table.increments('id').primary()
  142. table.string('path').notNullable()
  143. table.string('hash').notNullable()
  144. table.string('title').notNullable()
  145. table.string('description')
  146. table.boolean('isPrivate').notNullable().defaultTo(false)
  147. table.boolean('isPublished').notNullable().defaultTo(false)
  148. table.string('privateNS')
  149. table.string('publishStartDate')
  150. table.string('publishEndDate')
  151. switch (WIKI.config.db.type) {
  152. case 'postgres':
  153. case 'sqlite':
  154. table.text('content')
  155. table.text('render')
  156. break
  157. case 'mariadb':
  158. case 'mysql':
  159. table.specificType('content', 'LONGTEXT')
  160. table.specificType('render', 'LONGTEXT')
  161. break
  162. case 'mssql':
  163. table.specificType('content', 'VARCHAR(max)')
  164. table.specificType('render', 'VARCHAR(max)')
  165. break
  166. }
  167. table.json('toc')
  168. table.string('contentType').notNullable()
  169. table.string('createdAt').notNullable()
  170. table.string('updatedAt').notNullable()
  171. })
  172. // PAGE TREE ---------------------------
  173. .createTable('pageTree', table => {
  174. if (dbCompat.charset) { table.charset('utf8mb4') }
  175. table.integer('id').unsigned().primary()
  176. table.string('path').notNullable()
  177. table.integer('depth').unsigned().notNullable()
  178. table.string('title').notNullable()
  179. table.boolean('isPrivate').notNullable().defaultTo(false)
  180. table.boolean('isFolder').notNullable().defaultTo(false)
  181. table.string('privateNS')
  182. })
  183. // RENDERERS ---------------------------
  184. .createTable('renderers', table => {
  185. if (dbCompat.charset) { table.charset('utf8mb4') }
  186. table.string('key').notNullable().primary()
  187. table.boolean('isEnabled').notNullable().defaultTo(false)
  188. table.json('config')
  189. })
  190. // SEARCH ------------------------------
  191. .createTable('searchEngines', table => {
  192. if (dbCompat.charset) { table.charset('utf8mb4') }
  193. table.string('key').notNullable().primary()
  194. table.boolean('isEnabled').notNullable().defaultTo(false)
  195. table.json('config')
  196. })
  197. // SETTINGS ----------------------------
  198. .createTable('settings', table => {
  199. if (dbCompat.charset) { table.charset('utf8mb4') }
  200. table.string('key').notNullable().primary()
  201. table.json('value')
  202. table.string('updatedAt').notNullable()
  203. })
  204. // STORAGE -----------------------------
  205. .createTable('storage', table => {
  206. if (dbCompat.charset) { table.charset('utf8mb4') }
  207. table.string('key').notNullable().primary()
  208. table.boolean('isEnabled').notNullable().defaultTo(false)
  209. table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
  210. table.json('config')
  211. table.string('syncInterval')
  212. table.json('state')
  213. })
  214. // TAGS --------------------------------
  215. .createTable('tags', table => {
  216. if (dbCompat.charset) { table.charset('utf8mb4') }
  217. table.increments('id').primary()
  218. table.string('tag').notNullable().unique()
  219. table.string('title')
  220. table.string('createdAt').notNullable()
  221. table.string('updatedAt').notNullable()
  222. })
  223. // USER KEYS ---------------------------
  224. .createTable('userKeys', table => {
  225. if (dbCompat.charset) { table.charset('utf8mb4') }
  226. table.increments('id').primary()
  227. table.string('kind').notNullable()
  228. table.string('token').notNullable()
  229. table.string('createdAt').notNullable()
  230. table.string('validUntil').notNullable()
  231. })
  232. // USERS -------------------------------
  233. .createTable('users', table => {
  234. if (dbCompat.charset) { table.charset('utf8mb4') }
  235. table.increments('id').primary()
  236. table.string('email').notNullable()
  237. table.string('name').notNullable()
  238. table.string('providerId')
  239. table.string('password')
  240. table.boolean('tfaIsActive').notNullable().defaultTo(false)
  241. table.string('tfaSecret')
  242. table.string('jobTitle').defaultTo('')
  243. table.string('location').defaultTo('')
  244. table.string('pictureUrl')
  245. table.string('timezone').notNullable().defaultTo('America/New_York')
  246. table.boolean('isSystem').notNullable().defaultTo(false)
  247. table.boolean('isActive').notNullable().defaultTo(false)
  248. table.boolean('isVerified').notNullable().defaultTo(false)
  249. table.boolean('mustChangePwd').notNullable().defaultTo(false)
  250. table.string('createdAt').notNullable()
  251. table.string('updatedAt').notNullable()
  252. })
  253. // =====================================
  254. // RELATION TABLES
  255. // =====================================
  256. // PAGE HISTORY TAGS ---------------------------
  257. .createTable('pageHistoryTags', table => {
  258. if (dbCompat.charset) { table.charset('utf8mb4') }
  259. table.increments('id').primary()
  260. table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
  261. table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
  262. })
  263. // PAGE TAGS ---------------------------
  264. .createTable('pageTags', table => {
  265. if (dbCompat.charset) { table.charset('utf8mb4') }
  266. table.increments('id').primary()
  267. table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
  268. table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
  269. })
  270. // USER GROUPS -------------------------
  271. .createTable('userGroups', table => {
  272. if (dbCompat.charset) { table.charset('utf8mb4') }
  273. table.increments('id').primary()
  274. table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
  275. table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
  276. })
  277. // =====================================
  278. // REFERENCES
  279. // =====================================
  280. .table('assets', table => {
  281. table.integer('folderId').unsigned().references('id').inTable('assetFolders')
  282. table.integer('authorId').unsigned().references('id').inTable('users')
  283. })
  284. .table('comments', table => {
  285. table.integer('pageId').unsigned().references('id').inTable('pages')
  286. table.integer('authorId').unsigned().references('id').inTable('users')
  287. })
  288. .table('pageHistory', table => {
  289. table.string('editorKey').references('key').inTable('editors')
  290. table.string('localeCode', 5).references('code').inTable('locales')
  291. table.integer('authorId').unsigned().references('id').inTable('users')
  292. })
  293. .table('pageLinks', table => {
  294. table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
  295. table.index(['path', 'localeCode'])
  296. })
  297. .table('pages', table => {
  298. table.string('editorKey').references('key').inTable('editors')
  299. table.string('localeCode', 5).references('code').inTable('locales')
  300. table.integer('authorId').unsigned().references('id').inTable('users')
  301. table.integer('creatorId').unsigned().references('id').inTable('users')
  302. })
  303. .table('pageTree', table => {
  304. if (dbCompat.selfCascadeDelete) {
  305. table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
  306. } else {
  307. table.integer('parent').unsigned()
  308. }
  309. table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
  310. table.string('localeCode', 5).references('code').inTable('locales')
  311. })
  312. .table('userKeys', table => {
  313. table.integer('userId').unsigned().references('id').inTable('users')
  314. })
  315. .table('users', table => {
  316. table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
  317. table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
  318. table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
  319. table.unique(['providerKey', 'email'])
  320. })
  321. }
  322. exports.down = knex => { }