2
0

3.0.0.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. const { v4: uuid } = require('uuid')
  2. const bcrypt = require('bcryptjs-then')
  3. const crypto = require('crypto')
  4. const pem2jwk = require('pem-jwk').pem2jwk
  5. /* global WIKI */
  6. exports.up = async knex => {
  7. WIKI.logger.info('Running 3.0.0 database migration...')
  8. // =====================================
  9. // PG EXTENSIONS
  10. // =====================================
  11. await knex.raw('CREATE EXTENSION IF NOT EXISTS pgcrypto;')
  12. await knex.schema
  13. // =====================================
  14. // MODEL TABLES
  15. // =====================================
  16. // ANALYTICS ---------------------------
  17. .createTable('analytics', table => {
  18. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  19. table.string('module').notNullable()
  20. table.boolean('isEnabled').notNullable().defaultTo(false)
  21. table.jsonb('config').notNullable()
  22. })
  23. // API KEYS ----------------------------
  24. .createTable('apiKeys', table => {
  25. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  26. table.string('name').notNullable()
  27. table.text('key').notNullable()
  28. table.timestamp('expiration').notNullable().defaultTo(knex.fn.now())
  29. table.boolean('isRevoked').notNullable().defaultTo(false)
  30. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  31. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  32. })
  33. // ASSETS ------------------------------
  34. .createTable('assets', table => {
  35. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  36. table.string('filename').notNullable()
  37. table.string('hash').notNullable().index()
  38. table.string('ext').notNullable()
  39. table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
  40. table.string('mime').notNullable().defaultTo('application/octet-stream')
  41. table.integer('fileSize').unsigned().comment('In kilobytes')
  42. table.jsonb('metadata')
  43. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  44. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  45. })
  46. // ASSET DATA --------------------------
  47. .createTable('assetData', table => {
  48. table.uuid('id').notNullable().index()
  49. table.binary('data').notNullable()
  50. })
  51. // ASSET FOLDERS -----------------------
  52. .createTable('assetFolders', table => {
  53. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  54. table.string('name').notNullable()
  55. table.string('slug').notNullable()
  56. })
  57. // AUTHENTICATION ----------------------
  58. .createTable('authentication', table => {
  59. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  60. table.string('module').notNullable()
  61. table.boolean('isEnabled').notNullable().defaultTo(false)
  62. table.string('displayName').notNullable().defaultTo('')
  63. table.jsonb('config').notNullable().defaultTo('{}')
  64. table.boolean('selfRegistration').notNullable().defaultTo(false)
  65. table.jsonb('domainWhitelist').notNullable().defaultTo('[]')
  66. table.jsonb('autoEnrollGroups').notNullable().defaultTo('[]')
  67. })
  68. .createTable('commentProviders', table => {
  69. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  70. table.string('module').notNullable()
  71. table.boolean('isEnabled').notNullable().defaultTo(false)
  72. table.json('config').notNullable()
  73. })
  74. // COMMENTS ----------------------------
  75. .createTable('comments', table => {
  76. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  77. table.uuid('replyTo')
  78. table.text('content').notNullable()
  79. table.text('render').notNullable().defaultTo('')
  80. table.string('name').notNullable().defaultTo('')
  81. table.string('email').notNullable().defaultTo('')
  82. table.string('ip').notNullable().defaultTo('')
  83. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  84. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  85. })
  86. // GROUPS ------------------------------
  87. .createTable('groups', table => {
  88. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  89. table.string('name').notNullable()
  90. table.jsonb('permissions').notNullable()
  91. table.jsonb('rules').notNullable()
  92. table.string('redirectOnLogin').notNullable().defaultTo('')
  93. table.string('redirectOnFirstLogin').notNullable().defaultTo('')
  94. table.string('redirectOnLogout').notNullable().defaultTo('')
  95. table.boolean('isSystem').notNullable().defaultTo(false)
  96. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  97. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  98. })
  99. // HOOKS -------------------------------
  100. .createTable('hooks', table => {
  101. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  102. table.string('name').notNullable()
  103. table.jsonb('events').notNullable().defaultTo('[]')
  104. table.string('url').notNullable()
  105. table.boolean('includeMetadata').notNullable().defaultTo(false)
  106. table.boolean('includeContent').notNullable().defaultTo(false)
  107. table.boolean('acceptUntrusted').notNullable().defaultTo(false)
  108. table.string('authHeader')
  109. table.enum('state', ['pending', 'error', 'success']).notNullable().defaultTo('pending')
  110. table.string('lastErrorMessage')
  111. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  112. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  113. })
  114. // LOCALES -----------------------------
  115. .createTable('locales', table => {
  116. table.string('code', 5).notNullable().primary()
  117. table.jsonb('strings')
  118. table.boolean('isRTL').notNullable().defaultTo(false)
  119. table.string('name').notNullable()
  120. table.string('nativeName').notNullable()
  121. table.integer('availability').notNullable().defaultTo(0)
  122. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  123. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  124. })
  125. // NAVIGATION ----------------------------
  126. .createTable('navigation', table => {
  127. table.string('key').notNullable().primary()
  128. table.jsonb('config')
  129. })
  130. // PAGE HISTORY ------------------------
  131. .createTable('pageHistory', table => {
  132. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  133. table.uuid('pageId').notNullable().index()
  134. table.string('path').notNullable()
  135. table.string('hash').notNullable()
  136. table.string('title').notNullable()
  137. table.string('description')
  138. table.enu('publishState', ['draft', 'published', 'scheduled']).notNullable().defaultTo('draft')
  139. table.timestamp('publishStartDate')
  140. table.timestamp('publishEndDate')
  141. table.string('action').defaultTo('updated')
  142. table.text('content')
  143. table.string('editor').notNullable()
  144. table.string('contentType').notNullable()
  145. table.jsonb('extra').notNullable().defaultTo('{}')
  146. table.jsonb('tags').defaultTo('[]')
  147. table.timestamp('versionDate').notNullable().defaultTo(knex.fn.now())
  148. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  149. })
  150. // PAGE LINKS --------------------------
  151. .createTable('pageLinks', table => {
  152. table.increments('id').primary()
  153. table.string('path').notNullable()
  154. table.string('localeCode', 5).notNullable()
  155. })
  156. // PAGES -------------------------------
  157. .createTable('pages', table => {
  158. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  159. table.string('slug')
  160. table.string('path').notNullable()
  161. table.string('hash').notNullable()
  162. table.string('title').notNullable()
  163. table.string('description')
  164. table.enu('publishState', ['draft', 'published', 'scheduled']).notNullable().defaultTo('draft')
  165. table.timestamp('publishStartDate')
  166. table.timestamp('publishEndDate')
  167. table.text('content')
  168. table.text('render')
  169. table.jsonb('toc')
  170. table.string('editor').notNullable()
  171. table.string('contentType').notNullable()
  172. table.jsonb('extra').notNullable().defaultTo('{}')
  173. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  174. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  175. })
  176. // PAGE TREE ---------------------------
  177. .createTable('pageTree', table => {
  178. table.integer('id').unsigned().primary()
  179. table.string('path').notNullable()
  180. table.integer('depth').unsigned().notNullable()
  181. table.string('title').notNullable()
  182. table.boolean('isFolder').notNullable().defaultTo(false)
  183. table.jsonb('ancestors')
  184. })
  185. // RENDERERS ---------------------------
  186. .createTable('renderers', table => {
  187. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  188. table.string('module').notNullable()
  189. table.boolean('isEnabled').notNullable().defaultTo(false)
  190. table.jsonb('config')
  191. })
  192. // SETTINGS ----------------------------
  193. .createTable('settings', table => {
  194. table.string('key').notNullable().primary()
  195. table.jsonb('value')
  196. })
  197. // SITES -------------------------------
  198. .createTable('sites', table => {
  199. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  200. table.string('hostname').notNullable()
  201. table.boolean('isEnabled').notNullable().defaultTo(false)
  202. table.jsonb('config').notNullable()
  203. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  204. })
  205. // STORAGE -----------------------------
  206. .createTable('storage', table => {
  207. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  208. table.string('module').notNullable()
  209. table.boolean('isEnabled').notNullable().defaultTo(false)
  210. table.jsonb('contentTypes')
  211. table.jsonb('assetDelivery')
  212. table.jsonb('versioning')
  213. table.jsonb('schedule')
  214. table.jsonb('config')
  215. table.jsonb('state')
  216. })
  217. // TAGS --------------------------------
  218. .createTable('tags', table => {
  219. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  220. table.string('tag').notNullable()
  221. table.jsonb('display').notNullable().defaultTo('{}')
  222. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  223. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  224. })
  225. // USER AVATARS ------------------------
  226. .createTable('userAvatars', table => {
  227. table.uuid('id').notNullable().primary()
  228. table.binary('data').notNullable()
  229. })
  230. // USER KEYS ---------------------------
  231. .createTable('userKeys', table => {
  232. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  233. table.string('kind').notNullable()
  234. table.string('token').notNullable()
  235. table.timestamp('validUntil').notNullable()
  236. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  237. })
  238. // USERS -------------------------------
  239. .createTable('users', table => {
  240. table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
  241. table.string('email').notNullable()
  242. table.string('name').notNullable()
  243. table.jsonb('auth')
  244. table.jsonb('tfa')
  245. table.jsonb('meta')
  246. table.jsonb('prefs')
  247. table.string('pictureUrl')
  248. table.boolean('isSystem').notNullable().defaultTo(false)
  249. table.boolean('isActive').notNullable().defaultTo(false)
  250. table.boolean('isVerified').notNullable().defaultTo(false)
  251. table.timestamp('lastLoginAt').index()
  252. table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
  253. table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
  254. })
  255. // =====================================
  256. // RELATION TABLES
  257. // =====================================
  258. // PAGE TAGS ---------------------------
  259. .createTable('pageTags', table => {
  260. table.increments('id').primary()
  261. table.uuid('pageId').references('id').inTable('pages').onDelete('CASCADE')
  262. table.uuid('tagId').references('id').inTable('tags').onDelete('CASCADE')
  263. })
  264. // USER GROUPS -------------------------
  265. .createTable('userGroups', table => {
  266. table.increments('id').primary()
  267. table.uuid('userId').references('id').inTable('users').onDelete('CASCADE')
  268. table.uuid('groupId').references('id').inTable('groups').onDelete('CASCADE')
  269. })
  270. // =====================================
  271. // REFERENCES
  272. // =====================================
  273. .table('analytics', table => {
  274. table.uuid('siteId').notNullable().references('id').inTable('sites')
  275. })
  276. .table('assets', table => {
  277. table.uuid('folderId').notNullable().references('id').inTable('assetFolders').index()
  278. table.uuid('authorId').notNullable().references('id').inTable('users')
  279. table.uuid('siteId').notNullable().references('id').inTable('sites').index()
  280. })
  281. .table('assetFolders', table => {
  282. table.uuid('parentId').references('id').inTable('assetFolders').index()
  283. })
  284. .table('commentProviders', table => {
  285. table.uuid('siteId').notNullable().references('id').inTable('sites')
  286. })
  287. .table('comments', table => {
  288. table.uuid('pageId').notNullable().references('id').inTable('pages').index()
  289. table.uuid('authorId').notNullable().references('id').inTable('users').index()
  290. })
  291. .table('navigation', table => {
  292. table.uuid('siteId').notNullable().references('id').inTable('sites').index()
  293. })
  294. .table('pageHistory', table => {
  295. table.string('localeCode', 5).references('code').inTable('locales')
  296. table.uuid('authorId').notNullable().references('id').inTable('users')
  297. table.uuid('siteId').notNullable().references('id').inTable('sites').index()
  298. })
  299. .table('pageLinks', table => {
  300. table.uuid('pageId').notNullable().references('id').inTable('pages').onDelete('CASCADE')
  301. table.index(['path', 'localeCode'])
  302. })
  303. .table('pages', table => {
  304. table.string('localeCode', 5).references('code').inTable('locales').index()
  305. table.uuid('authorId').notNullable().references('id').inTable('users').index()
  306. table.uuid('creatorId').notNullable().references('id').inTable('users').index()
  307. table.uuid('siteId').notNullable().references('id').inTable('sites').index()
  308. })
  309. .table('pageTree', table => {
  310. table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
  311. table.uuid('pageId').notNullable().references('id').inTable('pages').onDelete('CASCADE')
  312. table.string('localeCode', 5).references('code').inTable('locales')
  313. })
  314. .table('renderers', table => {
  315. table.uuid('siteId').notNullable().references('id').inTable('sites')
  316. })
  317. .table('storage', table => {
  318. table.uuid('siteId').notNullable().references('id').inTable('sites')
  319. })
  320. .table('tags', table => {
  321. table.uuid('siteId').notNullable().references('id').inTable('sites')
  322. table.unique(['siteId', 'tag'])
  323. })
  324. .table('userKeys', table => {
  325. table.uuid('userId').notNullable().references('id').inTable('users')
  326. })
  327. .table('users', table => {
  328. table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
  329. })
  330. // =====================================
  331. // DEFAULT DATA
  332. // =====================================
  333. // -> GENERATE IDS
  334. const groupAdminId = uuid()
  335. const groupGuestId = '10000000-0000-4000-8000-000000000001'
  336. const siteId = uuid()
  337. const authModuleId = uuid()
  338. const userAdminId = uuid()
  339. const userGuestId = uuid()
  340. // -> SYSTEM CONFIG
  341. WIKI.logger.info('Generating certificates...')
  342. const secret = crypto.randomBytes(32).toString('hex')
  343. const certs = crypto.generateKeyPairSync('rsa', {
  344. modulusLength: 2048,
  345. publicKeyEncoding: {
  346. type: 'pkcs1',
  347. format: 'pem'
  348. },
  349. privateKeyEncoding: {
  350. type: 'pkcs1',
  351. format: 'pem',
  352. cipher: 'aes-256-cbc',
  353. passphrase: secret
  354. }
  355. })
  356. await knex('settings').insert([
  357. {
  358. key: 'auth',
  359. value: {
  360. audience: 'urn:wiki.js',
  361. tokenExpiration: '30m',
  362. tokenRenewal: '14d',
  363. certs: {
  364. jwk: pem2jwk(certs.publicKey),
  365. public: certs.publicKey,
  366. private: certs.privateKey
  367. },
  368. secret,
  369. rootAdminUserId: userAdminId,
  370. guestUserId: userGuestId
  371. }
  372. },
  373. {
  374. key: 'mail',
  375. value: {
  376. senderName: '',
  377. senderEmail: '',
  378. host: '',
  379. port: 465,
  380. name: '',
  381. secure: true,
  382. verifySSL: true,
  383. user: '',
  384. pass: '',
  385. useDKIM: false,
  386. dkimDomainName: '',
  387. dkimKeySelector: '',
  388. dkimPrivateKey: ''
  389. }
  390. },
  391. {
  392. key: 'security',
  393. value: {
  394. corsConfig: '',
  395. corsMode: 'OFF',
  396. cspDirectives: '',
  397. disallowFloc: true,
  398. disallowIframe: true,
  399. disallowOpenRedirect: true,
  400. enforceCsp: false,
  401. enforceHsts: false,
  402. enforceSameOriginReferrerPolicy: true,
  403. forceAssetDownload: true,
  404. hstsDuration: 0,
  405. trustProxy: false,
  406. authJwtAudience: 'urn:wiki.js',
  407. authJwtExpiration: '30m',
  408. authJwtRenewablePeriod: '14d',
  409. uploadMaxFileSize: 10485760,
  410. uploadMaxFiles: 20,
  411. uploadScanSVG: true
  412. }
  413. },
  414. {
  415. key: 'update',
  416. value: {
  417. locales: true
  418. }
  419. }
  420. ])
  421. // -> DEFAULT LOCALE
  422. await knex('locales').insert({
  423. code: 'en',
  424. strings: {},
  425. isRTL: false,
  426. name: 'English',
  427. nativeName: 'English'
  428. })
  429. // -> DEFAULT SITE
  430. await knex('sites').insert({
  431. id: siteId,
  432. hostname: '*',
  433. isEnabled: true,
  434. config: {
  435. title: 'My Wiki Site',
  436. description: '',
  437. company: '',
  438. contentLicense: '',
  439. footerExtra: '',
  440. pageExtensions: ['md', 'html', 'txt'],
  441. defaults: {
  442. timezone: 'America/New_York',
  443. dateFormat: 'YYYY-MM-DD',
  444. timeFormat: '12h'
  445. },
  446. features: {
  447. ratings: false,
  448. ratingsMode: 'off',
  449. comments: false,
  450. contributions: false,
  451. profile: true,
  452. search: true
  453. },
  454. logoText: true,
  455. sitemap: true,
  456. robots: {
  457. index: true,
  458. follow: true
  459. },
  460. locale: 'en',
  461. localeNamespacing: false,
  462. localeNamespaces: [],
  463. theme: {
  464. dark: false,
  465. colorPrimary: '#1976D2',
  466. colorSecondary: '#02C39A',
  467. colorAccent: '#FF9800',
  468. colorHeader: '#000000',
  469. colorSidebar: '#1976D2',
  470. injectCSS: '',
  471. injectHead: '',
  472. injectBody: '',
  473. contentWidth: 'full',
  474. sidebarPosition: 'left',
  475. tocPosition: 'right',
  476. showSharingMenu: true,
  477. showPrintBtn: true,
  478. baseFont: 'roboto',
  479. contentFont: 'roboto'
  480. }
  481. }
  482. })
  483. // -> DEFAULT GROUPS
  484. await knex('groups').insert([
  485. {
  486. id: groupAdminId,
  487. name: 'Administrators',
  488. permissions: JSON.stringify(['manage:system']),
  489. rules: JSON.stringify([]),
  490. isSystem: true
  491. },
  492. {
  493. id: groupGuestId,
  494. name: 'Guests',
  495. permissions: JSON.stringify(['read:pages', 'read:assets', 'read:comments']),
  496. rules: JSON.stringify([
  497. {
  498. id: uuid(),
  499. name: 'Default Rule',
  500. roles: ['read:pages', 'read:assets', 'read:comments'],
  501. match: 'START',
  502. mode: 'DENY',
  503. path: '',
  504. locales: [],
  505. sites: []
  506. }
  507. ]),
  508. isSystem: true
  509. }
  510. ])
  511. // -> AUTHENTICATION MODULE
  512. await knex('authentication').insert({
  513. id: authModuleId,
  514. module: 'local',
  515. isEnabled: true,
  516. displayName: 'Local Authentication'
  517. })
  518. // -> USERS
  519. await knex('users').insert([
  520. {
  521. id: userAdminId,
  522. email: process.env.ADMIN_EMAIL ?? 'admin@example.com',
  523. auth: {
  524. [authModuleId]: {
  525. password: await bcrypt.hash(process.env.ADMIN_PASS || '12345678', 12),
  526. mustChangePwd: !process.env.ADMIN_PASS,
  527. restrictLogin: false,
  528. tfaRequired: false,
  529. tfaSecret: ''
  530. }
  531. },
  532. name: 'Administrator',
  533. isSystem: false,
  534. isActive: true,
  535. isVerified: true,
  536. meta: {
  537. location: '',
  538. jobTitle: '',
  539. pronouns: ''
  540. },
  541. prefs: {
  542. timezone: 'America/New_York',
  543. dateFormat: 'YYYY-MM-DD',
  544. timeFormat: '12h',
  545. darkMode: false
  546. },
  547. localeCode: 'en'
  548. },
  549. {
  550. id: userGuestId,
  551. email: 'guest@example.com',
  552. name: 'Guest',
  553. isSystem: true,
  554. isActive: true,
  555. isVerified: true,
  556. localeCode: 'en'
  557. }
  558. ])
  559. await knex('userGroups').insert([
  560. {
  561. userId: userAdminId,
  562. groupId: groupAdminId
  563. },
  564. {
  565. userId: userGuestId,
  566. groupId: groupGuestId
  567. }
  568. ])
  569. // -> STORAGE MODULE
  570. await knex('storage').insert({
  571. module: 'db',
  572. siteId,
  573. isEnabled: true,
  574. contentTypes: {
  575. activeTypes: ['pages', 'images', 'documents', 'others', 'large'],
  576. largeThreshold: '5MB'
  577. },
  578. assetDelivery: {
  579. streaming: true,
  580. directAccess: false
  581. },
  582. versioning: {
  583. enabled: false
  584. },
  585. state: {
  586. current: 'ok'
  587. }
  588. })
  589. WIKI.logger.info('Completed 3.0.0 database migration.')
  590. }
  591. exports.down = knex => { }