ニジカ投稿局 https://tv.nizika.tv
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

update-object-storage-url.ts 3.7 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /* eslint-disable max-len */
  2. import { InvalidArgumentError, createCommand } from '@commander-js/extra-typings'
  3. import { FileStorage } from '@peertube/peertube-models'
  4. import { escapeForRegex } from '@server/helpers/regexp.js'
  5. import { initDatabaseModels, sequelizeTypescript } from '@server/initializers/database.js'
  6. import { QueryTypes } from 'sequelize'
  7. import { askConfirmation, displayPeerTubeMustBeStoppedWarning } from './shared/common.js'
  8. const program = createCommand()
  9. .description('Update PeerTube object file URLs after an object storage migration.')
  10. .requiredOption('-f, --from <url>', 'Previous object storage base URL', parseUrl)
  11. .requiredOption('-t, --to <url>', 'New object storage base URL', parseUrl)
  12. .parse(process.argv)
  13. const options = program.opts()
  14. run()
  15. .then(() => process.exit(0))
  16. .catch(err => {
  17. console.error(err)
  18. process.exit(-1)
  19. })
  20. async function run () {
  21. await initDatabaseModels(true)
  22. displayPeerTubeMustBeStoppedWarning()
  23. const fromRegexp = `^${escapeForRegex(options.from)}`
  24. const to = options.to
  25. const replacements = { fromRegexp, to, storage: FileStorage.OBJECT_STORAGE }
  26. // Candidates
  27. {
  28. const queries = [
  29. `SELECT COUNT(*) AS "c", 'videoFile->fileUrl: ' || COUNT(*) AS "t" FROM "videoFile" WHERE "fileUrl" ~ :fromRegexp AND "storage" = :storage`,
  30. `SELECT COUNT(*) AS "c", 'videoStreamingPlaylist->playlistUrl: ' || COUNT(*) AS "t" FROM "videoStreamingPlaylist" WHERE "playlistUrl" ~ :fromRegexp AND "storage" = :storage`,
  31. `SELECT COUNT(*) AS "c", 'videoStreamingPlaylist->segmentsSha256Url: ' || COUNT(*) AS "t" FROM "videoStreamingPlaylist" WHERE "segmentsSha256Url" ~ :fromRegexp AND "storage" = :storage`,
  32. `SELECT COUNT(*) AS "c", 'userExport->fileUrl: ' || COUNT(*) AS "t" FROM "userExport" WHERE "fileUrl" ~ :fromRegexp AND "storage" = :storage`,
  33. `SELECT COUNT(*) AS "c", 'videoSource->fileUrl: ' || COUNT(*) AS "t" FROM "videoSource" WHERE "fileUrl" ~ :fromRegexp AND "storage" = :storage`
  34. ]
  35. let hasResults = false
  36. console.log('Candidate URLs to update:')
  37. for (const query of queries) {
  38. const [ row ] = await sequelizeTypescript.query(query, { replacements, type: QueryTypes.SELECT as QueryTypes.SELECT })
  39. if (row['c'] !== 0) hasResults = true
  40. console.log(` ${row['t']}`)
  41. }
  42. console.log('\n')
  43. if (!hasResults) {
  44. console.log('No candidate URLs found, exiting.')
  45. process.exit(0)
  46. }
  47. }
  48. const res = await askUpdateConfirmation()
  49. if (res !== true) {
  50. console.log('Exiting without updating URLs.')
  51. process.exit(0)
  52. }
  53. // Execute
  54. {
  55. const queries = [
  56. `UPDATE "videoFile" SET "fileUrl" = regexp_replace("fileUrl", :fromRegexp, :to) WHERE "storage" = :storage`,
  57. `UPDATE "videoStreamingPlaylist" SET "playlistUrl" = regexp_replace("playlistUrl", :fromRegexp, :to) WHERE "storage" = :storage`,
  58. `UPDATE "videoStreamingPlaylist" SET "segmentsSha256Url" = regexp_replace("segmentsSha256Url", :fromRegexp, :to) WHERE "storage" = :storage`,
  59. `UPDATE "userExport" SET "fileUrl" = regexp_replace("fileUrl", :fromRegexp, :to) WHERE "storage" = :storage`,
  60. `UPDATE "videoSource" SET "fileUrl" = regexp_replace("fileUrl", :fromRegexp, :to) WHERE "storage" = :storage`
  61. ]
  62. for (const query of queries) {
  63. await sequelizeTypescript.query(query, { replacements })
  64. }
  65. console.log('URLs updated.')
  66. }
  67. }
  68. function parseUrl (value: string) {
  69. if (!value || /^https?:\/\//.test(value) !== true) {
  70. throw new InvalidArgumentError('Must be a valid URL (starting with http:// or https://).')
  71. }
  72. return value
  73. }
  74. async function askUpdateConfirmation () {
  75. return askConfirmation(
  76. 'These URLs can be updated, but please check your backups first (bugs happen). ' +
  77. 'Can we update these URLs?'
  78. )
  79. }