3.0.0.js 25 KB

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