Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion models/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ module.exports = function PostModel(sequelize, DataTypes) {
allowNull: false,
},
status: { type: DataTypes.ENUM('draft', 'published', 'deleted'), allowNull: false, defaultValue: 'draft' },
slug: { type: DataTypes.STRING, allowNull: false, unique: true },
// Nullable since the new app creates drafts with slug NULL until publish
slug: { type: DataTypes.STRING, allowNull: true, unique: true },
group_id: {
type: DataTypes.INTEGER.UNSIGNED,
references: {
Expand Down
22 changes: 20 additions & 2 deletions routes/graphql/mutations/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ const { Post } = require('../../../models')
const { cleanContent } = require('../../../utils/content')


function timestampSlug(slug) {
// Keep room for the suffix — truncating after appending would return a
// 200-char base unchanged and keep the collision
const suffix = `-${Date.now()}`
return slug.substr(0, 200 - suffix.length) + suffix
}

async function generateSlug(Model, name) {
const slug = slugify(name, { lower: true }).substr(0, 200)
const slugExist = await Model.findOne({ where: { slug } })
if (slugExist) return `${slug}-${Date.now()}`.substr(0, 200)
if (slugExist) return timestampSlug(slug)
return slug
}

Expand Down Expand Up @@ -41,8 +48,19 @@ const postMutations = {
post.content = cleanContent(args.content)
post.status = args.status
post.group_id = args.groupId
// Drafts from the new app have no slug until published
const mintedSlug = !post.slug && post.status === 'published'
if (mintedSlug) post.slug = await generateSlug(Post, post.title)

await post.save()
try {
await post.save()
} catch (err) {
// slug is the only unique key — a concurrent publish won the
// check-then-save race, so retry once with a timestamped slug
if (!mintedSlug || err.name !== 'SequelizeUniqueConstraintError') throw err
post.slug = timestampSlug(post.slug)
await post.save()
}
return post
},

Expand Down
2 changes: 1 addition & 1 deletion routes/graphql/typeDefs/types.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type PostsList {
type Post {
id: ID!
title: String!
slug: String!
slug: String
status: PostStatus
content: String
htmlContent: String
Expand Down