diff --git a/lib/models/note.js b/lib/models/note.js
index 628eb254..c5f162ce 100644
--- a/lib/models/note.js
+++ b/lib/models/note.js
@@ -7,7 +7,7 @@ const base64url = require('base64url')
const md = require('markdown-it')()
const metaMarked = require('@hedgedoc/meta-marked')
const cheerio = require('cheerio')
-const shortId = require('shortid')
+const nanoid = require('nanoid')
const Sequelize = require('sequelize')
const async = require('async')
const moment = require('moment')
@@ -37,7 +37,7 @@ module.exports = function (sequelize, DataTypes) {
type: DataTypes.STRING,
unique: true,
allowNull: false,
- defaultValue: shortId.generate
+ defaultValue: () => nanoid.nanoid(10)
},
alias: {
type: DataTypes.STRING,
@@ -297,8 +297,12 @@ module.exports = function (sequelize, DataTypes) {
parseNoteIdByShortId: function (_callback) {
// try to parse note id by shortId
try {
- if (shortId.isValid(noteId)) {
+ // old short ids generated by the `shortid` package could be from 7 to 14 characters long
+ // new ones generated by the `nanoid` package are always 10 characters long
+ if (noteId && noteId.length >= 7 && noteId.length <= 14) {
Note.findOne({
+ // MariaDB and MySQL do case-insensitive comparison by default (unless a collation charset like utf8mb4 is used)
+ // The binary conversion ensures, case-sensitive comparison.
where: utils.isMySQL(sequelize)
? sequelize.where(sequelize.fn('BINARY', sequelize.col('shortid')), noteId)
: {
diff --git a/lib/models/revision.js b/lib/models/revision.js
index 0a3cfa60..2f7399ff 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -4,7 +4,7 @@ const Sequelize = require('sequelize')
const async = require('async')
const moment = require('moment')
const childProcess = require('child_process')
-const shortId = require('shortid')
+const nanoid = require('nanoid')
const path = require('path')
const Op = Sequelize.Op
@@ -44,7 +44,7 @@ function createDmpWorker () {
function sendDmpWorker (data, callback) {
if (!dmpWorker) dmpWorker = createDmpWorker()
- const cacheKey = Date.now() + '_' + shortId.generate()
+ const cacheKey = Date.now() + '_' + nanoid.nanoid()
dmpCallbackCache[cacheKey] = callback
data = Object.assign(data, {
cacheKey
diff --git a/lib/models/temp.js b/lib/models/temp.js
index dee6c573..bfc88a3f 100644
--- a/lib/models/temp.js
+++ b/lib/models/temp.js
@@ -1,13 +1,13 @@
'use strict'
// external modules
-const shortId = require('shortid')
+const nanoid = require('nanoid')
module.exports = function (sequelize, DataTypes) {
const Temp = sequelize.define('Temp', {
id: {
type: DataTypes.STRING,
primaryKey: true,
- defaultValue: shortId.generate
+ defaultValue: nanoid.nanoid
},
data: {
type: DataTypes.TEXT
diff --git a/lib/web/note/actions.js b/lib/web/note/actions.js
index 3c10887d..61eab0ee 100644
--- a/lib/web/note/actions.js
+++ b/lib/web/note/actions.js
@@ -2,7 +2,7 @@ const models = require('../../models')
const logger = require('../../logger')
const config = require('../../config')
const errors = require('../../errors')
-const shortId = require('shortid')
+const nanoid = require('nanoid')
const moment = require('moment')
const querystring = require('querystring')
@@ -36,7 +36,7 @@ exports.createGist = function createGist (req, res, note) {
client_id: config.github.clientID,
redirect_uri: config.serverURL + '/auth/github/callback/' + models.Note.encodeNoteId(note.id) + '/gist',
scope: 'gist',
- state: shortId.generate()
+ state: nanoid.nanoid()
}
const query = querystring.stringify(data)
res.redirect('https://github.com/login/oauth/authorize?' + query)
diff --git a/package.json b/package.json
index 1d4c0b13..c7083b29 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"moment": "2.30.1",
"morgan": "1.10.0",
"mysql2": "3.14.0",
+ "nanoid": "3.3.11",
"node-fetch": "2.7.0",
"passport": "patch:passport@npm%3A0.7.0#~/.yarn/patches/passport-npm-0.7.0-df02531736.patch",
"passport-dropbox-oauth2": "1.1.0",
@@ -95,7 +96,6 @@
"sanitize-filename": "1.6.3",
"scrypt-kdf": "2.0.1",
"sequelize": "5.22.5",
- "shortid": "2.2.17",
"socket.io": "2.5.1",
"sqlite3": "5.1.7",
"store": "2.0.12",
diff --git a/public/docs/release-notes.md b/public/docs/release-notes.md
index abb29bc2..9e46a404 100644
--- a/public/docs/release-notes.md
+++ b/public/docs/release-notes.md
@@ -3,16 +3,14 @@
## 1.x.x UNRELEASED
### Enhancements
-
- Add `enableUploads` (`CMD_ENABLE_UPLOADS`) config option to restrict uploads to `registered` users, `all` users or
`none` to completely disable uploads.
+- Allow links to protocols such as xmpp, webcal or geo
+- Switch from deprecated shortid to nanoid module, with 10 character long aliases in "public" links
### Bugfixes
- Ignore the healthcheck endpoint in the "too busy" limiter
-### Enhancements
-- Allow links to protocols such as xmpp, webcal or geo
-
## 1.10.3 2025-04-09
### Security fixes
diff --git a/yarn.lock b/yarn.lock
index e44372be..d894317b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1461,6 +1461,7 @@ __metadata:
moment: "npm:2.30.1"
morgan: "npm:1.10.0"
mysql2: "npm:3.14.0"
+ nanoid: "npm:3.3.11"
node-fetch: "npm:2.7.0"
optimize-css-assets-webpack-plugin: "npm:6.0.1"
passport: "patch:passport@npm%3A0.7.0#~/.yarn/patches/passport-npm-0.7.0-df02531736.patch"
@@ -1491,7 +1492,6 @@ __metadata:
scrypt-kdf: "npm:2.0.1"
select2: "npm:3.5.2-browserify"
sequelize: "npm:5.22.5"
- shortid: "npm:2.2.17"
socket.io: "npm:2.5.1"
socket.io-client: "npm:2.5.0"
spin.js: "npm:4.1.2"
@@ -11233,7 +11233,7 @@ __metadata:
languageName: node
linkType: hard
-"nanoid@npm:^3.3.8":
+"nanoid@npm:3.3.11, nanoid@npm:^3.3.8":
version: 3.3.11
resolution: "nanoid@npm:3.3.11"
bin:
@@ -14963,15 +14963,6 @@ __metadata:
languageName: node
linkType: hard
-"shortid@npm:2.2.17":
- version: 2.2.17
- resolution: "shortid@npm:2.2.17"
- dependencies:
- nanoid: "npm:^3.3.8"
- checksum: 10/5c85635e31c08f8c6824b1802a0abb4cd26b39a5c84498dacc91b865f9a860979b010420423e5a4c0abf966aedf197a664a610e813745a6df1497f1376a72350
- languageName: node
- linkType: hard
-
"side-channel-list@npm:^1.0.0":
version: 1.0.0
resolution: "side-channel-list@npm:1.0.0"