3.0.0.js 25 KB

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