diff --git a/bin/lib/cli.js b/bin/lib/cli.js index 8ac5e70c8..85dcb789a 100644 --- a/bin/lib/cli.js +++ b/bin/lib/cli.js @@ -2,6 +2,7 @@ const program = require('commander') const loadInit = require('./init') const loadStart = require('./start') const loadInvalidUsernames = require('./invalidUsernames') +const loadMigrateLegacyResources = require('./migrateLegacyResources') const { spawnSync } = require('child_process') const path = require('path') @@ -11,6 +12,7 @@ module.exports = function startCli (server) { loadInit(program) loadStart(program, server) loadInvalidUsernames(program) + loadMigrateLegacyResources(program) program.parse(process.argv) if (program.args.length === 0) program.help() diff --git a/bin/lib/migrateLegacyResources.js b/bin/lib/migrateLegacyResources.js new file mode 100644 index 000000000..941880b17 --- /dev/null +++ b/bin/lib/migrateLegacyResources.js @@ -0,0 +1,67 @@ +const fs = require('fs') +const Path = require('path') +const promisify = require('util').promisify +const readdir = promisify(fs.readdir) +const lstat = promisify(fs.lstat) +const rename = promisify(fs.rename) + +/* Converts the old (pre-5.0.0) extensionless files to $-based files _with_ extensions + * to make them work in the new resource mapper (post-5.0.0). + * By default, all extensionless files (that used to be interpreted as Turtle) will now receive a '$.ttl' suffix. */ +/* https://www.w3.org/DesignIssues/HTTPFilenameMapping.html */ + +module.exports = function (program) { + program + .command('migrate-legacy-resources') + .option('-p, --path ', 'Path to the data folder, defaults to \'data/\'') + .option('-s, --suffix ', 'The suffix to add to extensionless files, defaults to \'$.ttl\'') + .option('-v, --verbose', 'Path to the data folder') + .description('Migrate the data folder from node-solid-server 4 to node-solid-server 5') + .action(async (opts) => { + const verbose = opts.verbose + const suffix = opts.suffix || '$.ttl' + let path = opts.path || 'data' + path = path.startsWith(Path.sep) ? path : Path.join(process.cwd(), path) + if (verbose) { + console.log(`Migrating files in ${path}`) + } + try { + await migrate(path, suffix, verbose) + } catch (err) { + console.error(err) + } + }) +} + +async function migrate (path, suffix, verbose) { + const files = await readdir(path) + for (const file of files) { + const fullFilePath = Path.join(path, file) + const stat = await lstat(fullFilePath) + if (stat.isFile()) { + if (shouldMigrateFile(file)) { + const newFullFilePath = getNewFileName(fullFilePath, suffix) + if (verbose) { + console.log(`${fullFilePath}\n => ${newFullFilePath}`) + } + await rename(fullFilePath, newFullFilePath) + } + } else { + if (shouldMigrateFolder(file)) { + await migrate(fullFilePath, suffix, verbose) + } + } + } +} + +function getNewFileName (fullFilePath, suffix) { + return fullFilePath + suffix +} + +function shouldMigrateFile (filename) { + return filename.indexOf('.') < 0 +} + +function shouldMigrateFolder (foldername) { + return foldername[0] !== '.' +} diff --git a/default-templates/new-account/profile/card b/default-templates/new-account/profile/card$.ttl similarity index 100% rename from default-templates/new-account/profile/card rename to default-templates/new-account/profile/card$.ttl diff --git a/test/integration/account-creation-oidc-test.js b/test/integration/account-creation-oidc-test.js index 9632b93a8..a7fcb4078 100644 --- a/test/integration/account-creation-oidc-test.js +++ b/test/integration/account-creation-oidc-test.js @@ -129,7 +129,7 @@ describe('AccountManager (OIDC account creation tests)', function () { } var domain = host.split(':')[0] var card = read(path.join('accounts/nicola.' + domain, - 'profile/card')) + 'profile/card$.ttl')) var cardAcl = read(path.join('accounts/nicola.' + domain, 'profile/card.acl')) var prefs = read(path.join('accounts/nicola.' + domain, diff --git a/test/integration/account-manager-test.js b/test/integration/account-manager-test.js index 215e21339..8d62b1223 100644 --- a/test/integration/account-manager-test.js +++ b/test/integration/account-manager-test.js @@ -107,7 +107,7 @@ describe('AccountManager', () => { expect(found).to.be.true }) .then(() => { - let profile = fs.readFileSync(path.join(accountDir, '/profile/card'), 'utf8') + let profile = fs.readFileSync(path.join(accountDir, '/profile/card$.ttl'), 'utf8') expect(profile).to.include('"Alice Q."') let rootAcl = fs.readFileSync(path.join(accountDir, '.acl'), 'utf8') diff --git a/test/integration/account-template-test.js b/test/integration/account-template-test.js index ff5cdcc94..930b97d28 100644 --- a/test/integration/account-template-test.js +++ b/test/integration/account-template-test.js @@ -46,7 +46,7 @@ describe('AccountTemplate', () => { return template.processAccount(accountPath) }) .then(() => { - let profile = fs.readFileSync(path.join(accountPath, '/profile/card'), 'utf8') + let profile = fs.readFileSync(path.join(accountPath, '/profile/card$.ttl'), 'utf8') expect(profile).to.include('"Alice Q."') let rootAcl = fs.readFileSync(path.join(accountPath, '.acl'), 'utf8')