3.0.0.js 26 KB

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