From b9a03b9b8ee18829b0d48df0b11619202ad33377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 24 Mar 2019 16:58:54 +0100 Subject: [PATCH 01/25] Expose #238 --- .../src/middleware/dateTimeMiddleware.spec.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 backend/src/middleware/dateTimeMiddleware.spec.js diff --git a/backend/src/middleware/dateTimeMiddleware.spec.js b/backend/src/middleware/dateTimeMiddleware.spec.js new file mode 100644 index 000000000..cae03db1f --- /dev/null +++ b/backend/src/middleware/dateTimeMiddleware.spec.js @@ -0,0 +1,42 @@ +import Factory from '../seed/factories' +import { host, login } from '../jest/helpers' +import { GraphQLClient } from 'graphql-request' + +let client +let headers +let query +const factory = Factory() + +beforeEach(async () => { + const userParams = { name: 'Author', email: 'author@example.org', password: '1234' } + await factory.create('User', userParams) + await factory.authenticateAs(userParams) + await factory.create('Post', { title: 'first' }) + await factory.create('Post', { title: 'second' }) + await factory.create('Post', { title: 'third' }) + await factory.create('Post', { title: 'last' }) + headers = {} + client = new GraphQLClient(host, { headers }) +}) + +afterEach(async () => { + await factory.cleanDatabase() +}) + +describe('Query', () => { + describe('Post', () => { + describe('orderBy', () => { + it('createdAt descending is default', async () => { + query = '{ Post { title } }' + const posts = [ + { title: 'last' }, + { title: 'third' }, + { title: 'second' }, + { title: 'first' } + ] + const expected = { data: { Post: posts } } + await expect(client.request(query)).resolves.toEqual(expected) + }) + }) + }) +}) From 1cbab2a5c42fa935fed0dbd1c0d992c0266fcefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 24 Mar 2019 19:58:17 +0100 Subject: [PATCH 02/25] Implement #238 --- backend/src/middleware/dateTimeMiddleware.js | 57 ++++++------------- .../middleware/includedFieldsMiddleware.js | 4 +- backend/src/middleware/index.js | 4 +- backend/src/middleware/orderByMiddleware.js | 17 ++++++ ...ware.spec.js => orderByMiddleware.spec.js} | 24 +++++++- 5 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 backend/src/middleware/orderByMiddleware.js rename backend/src/middleware/{dateTimeMiddleware.spec.js => orderByMiddleware.spec.js} (64%) diff --git a/backend/src/middleware/dateTimeMiddleware.js b/backend/src/middleware/dateTimeMiddleware.js index 473dbf444..58b18368e 100644 --- a/backend/src/middleware/dateTimeMiddleware.js +++ b/backend/src/middleware/dateTimeMiddleware.js @@ -1,44 +1,21 @@ +const setCreatedAt = (resolve, root, args, context, info) => { + args.createdAt = (new Date()).toISOString() + return resolve(root, args, context, info) +} +const setUpdatedAt = (resolve, root, args, context, info) => { + args.updatedAt = (new Date()).toISOString() + return resolve(root, args, context, info) +} + export default { Mutation: { - CreateUser: async (resolve, root, args, context, info) => { - args.createdAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - CreatePost: async (resolve, root, args, context, info) => { - args.createdAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - CreateComment: async (resolve, root, args, context, info) => { - args.createdAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - CreateOrganization: async (resolve, root, args, context, info) => { - args.createdAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - UpdateUser: async (resolve, root, args, context, info) => { - args.updatedAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - UpdatePost: async (resolve, root, args, context, info) => { - args.updatedAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - UpdateComment: async (resolve, root, args, context, info) => { - args.updatedAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - }, - UpdateOrganization: async (resolve, root, args, context, info) => { - args.updatedAt = (new Date()).toISOString() - const result = await resolve(root, args, context, info) - return result - } + CreateUser: setCreatedAt, + CreatePost: setCreatedAt, + CreateComment: setCreatedAt, + CreateOrganization: setCreatedAt, + UpdateUser: setUpdatedAt, + UpdatePost: setUpdatedAt, + UpdateComment: setUpdatedAt, + UpdateOrganization: setUpdatedAt, } } diff --git a/backend/src/middleware/includedFieldsMiddleware.js b/backend/src/middleware/includedFieldsMiddleware.js index 6d8ccf8f1..5dd63cd3c 100644 --- a/backend/src/middleware/includedFieldsMiddleware.js +++ b/backend/src/middleware/includedFieldsMiddleware.js @@ -24,6 +24,6 @@ const includeFieldsRecursively = (includedFields) => { } export default { - Query: includeFieldsRecursively(['id', 'disabled', 'deleted']), - Mutation: includeFieldsRecursively(['id', 'disabled', 'deleted']) + Query: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted']), + Mutation: includeFieldsRecursively(['id', 'createdAt', 'disabled', 'deleted']) } diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js index 6ed0955e8..57dd82025 100644 --- a/backend/src/middleware/index.js +++ b/backend/src/middleware/index.js @@ -9,6 +9,7 @@ import xssMiddleware from './xssMiddleware' import permissionsMiddleware from './permissionsMiddleware' import userMiddleware from './userMiddleware' import includedFieldsMiddleware from './includedFieldsMiddleware' +import orderByMiddleware from './orderByMiddleware' export default schema => { let middleware = [ @@ -20,7 +21,8 @@ export default schema => { fixImageUrlsMiddleware, softDeleteMiddleware, userMiddleware, - includedFieldsMiddleware + includedFieldsMiddleware, + orderByMiddleware ] // add permisions middleware at the first position (unless we're seeding) diff --git a/backend/src/middleware/orderByMiddleware.js b/backend/src/middleware/orderByMiddleware.js new file mode 100644 index 000000000..d3ecf5b2b --- /dev/null +++ b/backend/src/middleware/orderByMiddleware.js @@ -0,0 +1,17 @@ +import cloneDeep from 'lodash/cloneDeep' + +const defaultOrderBy = (resolve, root, args, context, resolveInfo) => { + const copy = cloneDeep(resolveInfo) + const newestFirst = { + kind: "Argument", + name: { kind: "Name", value: "orderBy" }, + value: { kind: "EnumValue", value: "createdAt_desc" } + } + const [fieldNode] = copy.fieldNodes + if(fieldNode) fieldNode.arguments.push(newestFirst) + return resolve(root, args, context, copy) +} + +export default { + Query: defaultOrderBy +} diff --git a/backend/src/middleware/dateTimeMiddleware.spec.js b/backend/src/middleware/orderByMiddleware.spec.js similarity index 64% rename from backend/src/middleware/dateTimeMiddleware.spec.js rename to backend/src/middleware/orderByMiddleware.spec.js index cae03db1f..0e2a1eace 100644 --- a/backend/src/middleware/dateTimeMiddleware.spec.js +++ b/backend/src/middleware/orderByMiddleware.spec.js @@ -25,18 +25,38 @@ afterEach(async () => { describe('Query', () => { describe('Post', () => { + beforeEach(() => { + query = '{ Post { title } }' + }) + describe('orderBy', () => { it('createdAt descending is default', async () => { - query = '{ Post { title } }' const posts = [ { title: 'last' }, { title: 'third' }, { title: 'second' }, { title: 'first' } ] - const expected = { data: { Post: posts } } + const expected = { Post: posts } await expect(client.request(query)).resolves.toEqual(expected) }) + + describe('(orderBy: createdAt_asc)', () => { + beforeEach(() => { + query = '{ Post(orderBy: createdAt_asc) { title } }' + }) + + it('orders by createdAt ascending', async () => { + const posts = [ + { title: 'first' }, + { title: 'second' }, + { title: 'third' }, + { title: 'last' } + ] + const expected = { Post: posts } + await expect(client.request(query)).resolves.toEqual(expected) + }) + }) }) }) }) From 4d70ba68c56660579233220216b427f1feb43cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 24 Mar 2019 22:48:44 +0100 Subject: [PATCH 03/25] Fix lint --- backend/src/middleware/dateTimeMiddleware.js | 2 +- backend/src/middleware/orderByMiddleware.js | 8 ++++---- backend/src/middleware/orderByMiddleware.spec.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/middleware/dateTimeMiddleware.js b/backend/src/middleware/dateTimeMiddleware.js index 58b18368e..73b75070c 100644 --- a/backend/src/middleware/dateTimeMiddleware.js +++ b/backend/src/middleware/dateTimeMiddleware.js @@ -16,6 +16,6 @@ export default { UpdateUser: setUpdatedAt, UpdatePost: setUpdatedAt, UpdateComment: setUpdatedAt, - UpdateOrganization: setUpdatedAt, + UpdateOrganization: setUpdatedAt } } diff --git a/backend/src/middleware/orderByMiddleware.js b/backend/src/middleware/orderByMiddleware.js index d3ecf5b2b..d4226425e 100644 --- a/backend/src/middleware/orderByMiddleware.js +++ b/backend/src/middleware/orderByMiddleware.js @@ -3,12 +3,12 @@ import cloneDeep from 'lodash/cloneDeep' const defaultOrderBy = (resolve, root, args, context, resolveInfo) => { const copy = cloneDeep(resolveInfo) const newestFirst = { - kind: "Argument", - name: { kind: "Name", value: "orderBy" }, - value: { kind: "EnumValue", value: "createdAt_desc" } + kind: 'Argument', + name: { kind: 'Name', value: 'orderBy' }, + value: { kind: 'EnumValue', value: 'createdAt_desc' } } const [fieldNode] = copy.fieldNodes - if(fieldNode) fieldNode.arguments.push(newestFirst) + if (fieldNode) fieldNode.arguments.push(newestFirst) return resolve(root, args, context, copy) } diff --git a/backend/src/middleware/orderByMiddleware.spec.js b/backend/src/middleware/orderByMiddleware.spec.js index 0e2a1eace..2d85452e5 100644 --- a/backend/src/middleware/orderByMiddleware.spec.js +++ b/backend/src/middleware/orderByMiddleware.spec.js @@ -1,5 +1,5 @@ import Factory from '../seed/factories' -import { host, login } from '../jest/helpers' +import { host } from '../jest/helpers' import { GraphQLClient } from 'graphql-request' let client From dce9a3eea21f8d01e0fa47f580ff7677e8fd6769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 24 Mar 2019 22:50:29 +0100 Subject: [PATCH 04/25] Better use defaultOrder only for posts E.g. comments should be ordered exactly the opposite way. --- backend/src/middleware/orderByMiddleware.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/orderByMiddleware.js b/backend/src/middleware/orderByMiddleware.js index d4226425e..5f8aabb9e 100644 --- a/backend/src/middleware/orderByMiddleware.js +++ b/backend/src/middleware/orderByMiddleware.js @@ -13,5 +13,7 @@ const defaultOrderBy = (resolve, root, args, context, resolveInfo) => { } export default { - Query: defaultOrderBy + Query: { + Post: defaultOrderBy + } } From f73198f9f28ed40954f0d7b82ac96357cbb613df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 23:53:26 +0100 Subject: [PATCH 05/25] Try/error: Add headers back `git diff b32c85b2de014770d07df8a642616b016cf69b50..46aecd612018db490fb3d6f91572be848371b32e -- webapp/nuxt.config.js` showed me that I removed headers because I didn't thought they are relevant. Now the proxy route `/activityPub` is not reachable anymore. --- webapp/nuxt.config.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 80b17a26c..769b0fca0 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -123,13 +123,23 @@ module.exports = { proxy: { '/.well-known/webfinger': { target: process.env.GRAPHQL_URI || 'http://localhost:4000', - toProxy: true // cloudflare needs that + toProxy: true, // cloudflare needs that + headers: { + Accept: 'application/json', + 'X-UI-Request': true, + 'X-API-TOKEN': process.env.BACKEND_TOKEN || 'NULL' + } }, - '/activityPub': { + '/activitypub': { // make this configurable (nuxt-dotenv) target: process.env.GRAPHQL_URI || 'http://localhost:4000', - pathRewrite: { '^/activityPub': '' }, - toProxy: true // cloudflare needs that + pathRewrite: { '^/activitypub': '' }, + toProxy: true, // cloudflare needs that + headers: { + Accept: 'application/json', + 'X-UI-Request': true, + 'X-API-TOKEN': process.env.BACKEND_TOKEN || 'NULL' + } }, '/api': { // make this configurable (nuxt-dotenv) From 17fdbf4389c49a2d0d1cbaca17ca2ff1acaea6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 00:21:14 +0100 Subject: [PATCH 06/25] Send status 500 instead of not-responsiveness --- backend/src/activitypub/routes/webFinger.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/src/activitypub/routes/webFinger.js b/backend/src/activitypub/routes/webFinger.js index ad1c806ad..8def32328 100644 --- a/backend/src/activitypub/routes/webFinger.js +++ b/backend/src/activitypub/routes/webFinger.js @@ -12,15 +12,20 @@ router.get('/', async function (req, res) { const nameAndDomain = resource.replace('acct:', '') const name = nameAndDomain.split('@')[0] - const result = await req.app.get('ap').dataSource.client.query({ - query: gql` + let result + try { + result = await req.app.get('ap').dataSource.client.query({ + query: gql` query { User(slug: "${name}") { slug } } ` - }) + }) + } catch (error) { + return res.status(500).json({ error }) + } if (result.data && result.data.User.length > 0) { const webFinger = createWebFinger(name) From 46c0d0659a5ccc0810b1c00bfba4eff4ce3402fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 01:01:30 +0100 Subject: [PATCH 07/25] Fix routing/proxying of AP, works locally RSA keys get seeded but not for tests --- backend/package.json | 4 ++-- backend/src/activitypub/ActivityPub.js | 10 ++++----- backend/src/activitypub/utils/activity.js | 16 +++++++------- backend/src/activitypub/utils/actor.js | 23 +++++++++++---------- backend/src/activitypub/utils/collection.js | 8 +++---- backend/src/activitypub/utils/index.js | 6 +++--- backend/src/middleware/index.js | 8 ++++--- backend/test/features/webfinger.feature | 2 +- webapp/nuxt.config.js | 1 - 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/backend/package.json b/backend/package.json index 6bd5db20f..46c228485 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,7 @@ "description": "GraphQL Backend for Human Connection", "main": "src/index.js", "config": { - "no_auth": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 PERMISSIONS=disabled" + "no_auth": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions" }, "scripts": { "build": "babel src/ -d dist/ --copy-files", @@ -15,7 +15,7 @@ "test": "nyc --reporter=text-lcov yarn test:jest", "test:cypress": "run-p --race test:before:*", "test:before:server": "cross-env CLIENT_URI=http://localhost:4123 GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 babel-node src/ 2> /dev/null", - "test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 PERMISSIONS=disabled babel-node src/ 2> /dev/null", + "test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub babel-node src/ 2> /dev/null", "test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand", "test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/", "test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand", diff --git a/backend/src/activitypub/ActivityPub.js b/backend/src/activitypub/ActivityPub.js index 6c3fb4082..9708b33c0 100644 --- a/backend/src/activitypub/ActivityPub.js +++ b/backend/src/activitypub/ActivityPub.js @@ -22,21 +22,19 @@ let activityPub = null export { activityPub } export default class ActivityPub { - constructor (host, uri) { - this.host = host - this.dataSource = new NitroDataSource(uri) + constructor (activity_pub_endpoint_uri, internal_graphql_uri) { + this.endpoint = activity_pub_endpoint_uri + this.dataSource = new NitroDataSource(internal_graphql_uri) this.collections = new Collections(this.dataSource) } static init (server) { if (!activityPub) { dotenv.config() - const url = new URL(process.env.CLIENT_URI) - activityPub = new ActivityPub(url.host || 'localhost:4000', url.origin) + activityPub = new ActivityPub(process.env.CLIENT_URI || 'http://localhost:3000', process.env.GRAPHQL_URI || 'http://localhost:4000') // integrate into running graphql express server server.express.set('ap', activityPub) - server.express.set('port', url.port) server.express.use(router) console.log('-> ActivityPub middleware added to the graphql express server') } else { diff --git a/backend/src/activitypub/utils/activity.js b/backend/src/activitypub/utils/activity.js index a8c525302..57b6dfb83 100644 --- a/backend/src/activitypub/utils/activity.js +++ b/backend/src/activitypub/utils/activity.js @@ -11,14 +11,14 @@ export function createNoteObject (text, name, id, published) { return { '@context': 'https://www.w3.org/ns/activitystreams', - 'id': `https://${activityPub.host}/activitypub/users/${name}/status/${createUuid}`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}/status/${createUuid}`, 'type': 'Create', - 'actor': `https://${activityPub.host}/activitypub/users/${name}`, + 'actor': `${activityPub.endpoint}/activitypub/users/${name}`, 'object': { - 'id': `https://${activityPub.host}/activitypub/users/${name}/status/${id}`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}/status/${id}`, 'type': 'Note', 'published': published, - 'attributedTo': `https://${activityPub.host}/activitypub/users/${name}`, + 'attributedTo': `${activityPub.endpoint}/activitypub/users/${name}`, 'content': text, 'to': 'https://www.w3.org/ns/activitystreams#Public' } @@ -64,8 +64,8 @@ export async function getActorId (name) { export function sendAcceptActivity (theBody, name, targetDomain, url) { as.accept() - .id(`https://${activityPub.host}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex')) - .actor(`https://${activityPub.host}/activitypub/users/${name}`) + .id(`${activityPub.endpoint}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex')) + .actor(`${activityPub.endpoint}/activitypub/users/${name}`) .object(theBody) .prettyWrite((err, doc) => { if (!err) { @@ -79,8 +79,8 @@ export function sendAcceptActivity (theBody, name, targetDomain, url) { export function sendRejectActivity (theBody, name, targetDomain, url) { as.reject() - .id(`https://${activityPub.host}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex')) - .actor(`https://${activityPub.host}/activitypub/users/${name}`) + .id(`${activityPub.endpoint}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex')) + .actor(`${activityPub.endpoint}/activitypub/users/${name}`) .object(theBody) .prettyWrite((err, doc) => { if (!err) { diff --git a/backend/src/activitypub/utils/actor.js b/backend/src/activitypub/utils/actor.js index 0a3884023..27612517b 100644 --- a/backend/src/activitypub/utils/actor.js +++ b/backend/src/activitypub/utils/actor.js @@ -6,34 +6,35 @@ export function createActor (name, pubkey) { 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1' ], - 'id': `https://${activityPub.host}/activitypub/users/${name}`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}`, 'type': 'Person', 'preferredUsername': `${name}`, 'name': `${name}`, - 'following': `https://${activityPub.host}/activitypub/users/${name}/following`, - 'followers': `https://${activityPub.host}/activitypub/users/${name}/followers`, - 'inbox': `https://${activityPub.host}/activitypub/users/${name}/inbox`, - 'outbox': `https://${activityPub.host}/activitypub/users/${name}/outbox`, - 'url': `https://${activityPub.host}/activitypub/@${name}`, + 'following': `${activityPub.endpoint}/activitypub/users/${name}/following`, + 'followers': `${activityPub.endpoint}/activitypub/users/${name}/followers`, + 'inbox': `${activityPub.endpoint}/activitypub/users/${name}/inbox`, + 'outbox': `${activityPub.endpoint}/activitypub/users/${name}/outbox`, + 'url': `${activityPub.endpoint}/activitypub/@${name}`, 'endpoints': { - 'sharedInbox': `https://${activityPub.host}/activitypub/inbox` + 'sharedInbox': `${activityPub.endpoint}/activitypub/inbox` }, 'publicKey': { - 'id': `https://${activityPub.host}/activitypub/users/${name}#main-key`, - 'owner': `https://${activityPub.host}/activitypub/users/${name}`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}#main-key`, + 'owner': `${activityPub.endpoint}/activitypub/users/${name}`, 'publicKeyPem': pubkey } } } export function createWebFinger (name) { + const { host } = new URL(activityPub.endpoint) return { - 'subject': `acct:${name}@${activityPub.host}`, + 'subject': `acct:${name}@${host}`, 'links': [ { 'rel': 'self', 'type': 'application/activity+json', - 'href': `https://${activityPub.host}/activitypub/users/${name}` + 'href': `${activityPub.endpoint}/activitypub/users/${name}` } ] } diff --git a/backend/src/activitypub/utils/collection.js b/backend/src/activitypub/utils/collection.js index 61670bb47..e3a63c74d 100644 --- a/backend/src/activitypub/utils/collection.js +++ b/backend/src/activitypub/utils/collection.js @@ -5,10 +5,10 @@ const debug = require('debug')('ea:utils:collections') export function createOrderedCollection (name, collectionName) { return { '@context': 'https://www.w3.org/ns/activitystreams', - 'id': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`, 'summary': `${name}s ${collectionName} collection`, 'type': 'OrderedCollection', - 'first': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}?page=true`, + 'first': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`, 'totalItems': 0 } } @@ -16,11 +16,11 @@ export function createOrderedCollection (name, collectionName) { export function createOrderedCollectionPage (name, collectionName) { return { '@context': 'https://www.w3.org/ns/activitystreams', - 'id': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}?page=true`, + 'id': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}?page=true`, 'summary': `${name}s ${collectionName} collection`, 'type': 'OrderedCollectionPage', 'totalItems': 0, - 'partOf': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}`, + 'partOf': `${activityPub.endpoint}/activitypub/users/${name}/${collectionName}`, 'orderedItems': [] } } diff --git a/backend/src/activitypub/utils/index.js b/backend/src/activitypub/utils/index.js index 4ae2b073f..e6853cecb 100644 --- a/backend/src/activitypub/utils/index.js +++ b/backend/src/activitypub/utils/index.js @@ -20,8 +20,8 @@ export function extractIdFromActivityId (uri) { return splitted[splitted.indexOf('status') + 1] } -export function constructIdFromName (name, fromDomain = activityPub.host) { - return `http://${fromDomain}/activitypub/users/${name}` +export function constructIdFromName (name, fromDomain = activityPub.endpoint) { + return `${fromDomain}/activitypub/users/${name}` } export function extractDomainFromUrl (url) { @@ -76,7 +76,7 @@ export function signAndSend (activity, fromName, targetDomain, url) { 'Host': targetDomain, 'Date': date, 'Signature': createSignature({ privateKey, - keyId: `http://${activityPub.host}/activitypub/users/${fromName}#main-key`, + keyId: `${activityPub.endpoint}/activitypub/users/${fromName}#main-key`, url, headers: { 'Host': targetDomain, diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js index 6ed0955e8..8380316ff 100644 --- a/backend/src/middleware/index.js +++ b/backend/src/middleware/index.js @@ -25,9 +25,11 @@ export default schema => { // add permisions middleware at the first position (unless we're seeding) // NOTE: DO NOT SET THE PERMISSION FLAT YOUR SELF - if (process.env.PERMISSIONS !== 'disabled' && process.env.NODE_ENV !== 'production') { - middleware.unshift(activityPubMiddleware) - middleware.unshift(permissionsMiddleware.generate(schema)) + if (process.env.NODE_ENV !== 'production') { + const DISABLED_MIDDLEWARES = process.env.DISABLED_MIDDLEWARES || '' + const disabled = DISABLED_MIDDLEWARES.split(',') + if (!disabled.includes('activityPub')) middleware.unshift(activityPubMiddleware) + if (!disabled.includes('permissions')) middleware.unshift(permissionsMiddleware.generate(schema)) } return middleware } diff --git a/backend/test/features/webfinger.feature b/backend/test/features/webfinger.feature index c9f9e587b..45f46c6a6 100644 --- a/backend/test/features/webfinger.feature +++ b/backend/test/features/webfinger.feature @@ -4,7 +4,7 @@ Feature: Webfinger discovery In order to follow the actor Background: - Given our own server runs at "http://localhost:4100" + Given our own server runs at "http://localhost:4123" And we have the following users in our database: | Slug | | peter-lustiger | diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 769b0fca0..6cac26ea1 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -133,7 +133,6 @@ module.exports = { '/activitypub': { // make this configurable (nuxt-dotenv) target: process.env.GRAPHQL_URI || 'http://localhost:4000', - pathRewrite: { '^/activitypub': '' }, toProxy: true, // cloudflare needs that headers: { Accept: 'application/json', From b78296a5468b45fa3b7d72f86b33a4bdfadbf81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 01:28:32 +0100 Subject: [PATCH 08/25] Fix cucumber features --- backend/test/features/activity-delete.feature | 2 +- backend/test/features/activity-follow.feature | 6 ++--- backend/test/features/activity-like.feature | 10 ++++---- backend/test/features/collection.feature | 24 +++++++++---------- backend/test/features/webfinger.feature | 20 ++++++++-------- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/backend/test/features/activity-delete.feature b/backend/test/features/activity-delete.feature index f5e269cce..76c734952 100644 --- a/backend/test/features/activity-delete.feature +++ b/backend/test/features/activity-delete.feature @@ -29,7 +29,7 @@ Feature: Delete an object """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/karl-heinz/status/a4DJ2afdg323v32641vna42lkj685kasd2", + "id": "http://localhost:4123/activitypub/users/karl-heinz/status/a4DJ2afdg323v32641vna42lkj685kasd2", "type": "Delete", "object": { "id": "https://aronda.org/activitypub/users/bernd-das-brot/status/kljsdfg9843jknsdf234", diff --git a/backend/test/features/activity-follow.feature b/backend/test/features/activity-follow.feature index 6634a342b..3cfe73340 100644 --- a/backend/test/features/activity-follow.feature +++ b/backend/test/features/activity-follow.feature @@ -15,7 +15,7 @@ Feature: Follow a user """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/stuart-little/status/83J23549sda1k72fsa4567na42312455kad83", + "id": "http://localhost:4123/activitypub/users/stuart-little/status/83J23549sda1k72fsa4567na42312455kad83", "type": "Follow", "actor": "http://localhost:4123/activitypub/users/stuart-little", "object": "http://localhost:4123/activitypub/users/tero-vota" @@ -32,11 +32,11 @@ Feature: Follow a user """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/tero-vota/status/a4DJ2afdg323v32641vna42lkj685kasd2", + "id": "http://localhost:4123/activitypub/users/tero-vota/status/a4DJ2afdg323v32641vna42lkj685kasd2", "type": "Undo", "actor": "http://localhost:4123/activitypub/users/tero-vota", "object": { - "id": "https://localhost:4123/activitypub/users/stuart-little/status/83J23549sda1k72fsa4567na42312455kad83", + "id": "http://localhost:4123/activitypub/users/stuart-little/status/83J23549sda1k72fsa4567na42312455kad83", "type": "Follow", "actor": "http://localhost:4123/activitypub/users/stuart-little", "object": "http://localhost:4123/activitypub/users/tero-vota" diff --git a/backend/test/features/activity-like.feature b/backend/test/features/activity-like.feature index 35d32c842..ec8c99110 100644 --- a/backend/test/features/activity-like.feature +++ b/backend/test/features/activity-like.feature @@ -13,14 +13,14 @@ Feature: Like an object like an article or note """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/karl-heinz/status/faslkasa7dasfzkjn2398hsfd", + "id": "http://localhost:4123/activitypub/users/karl-heinz/status/faslkasa7dasfzkjn2398hsfd", "type": "Create", - "actor": "https://localhost:4123/activitypub/users/karl-heinz", + "actor": "http://localhost:4123/activitypub/users/karl-heinz", "object": { - "id": "https://localhost:4123/activitypub/users/karl-heinz/status/dkasfljsdfaafg9843jknsdf", + "id": "http://localhost:4123/activitypub/users/karl-heinz/status/dkasfljsdfaafg9843jknsdf", "type": "Article", "published": "2019-02-07T19:37:55.002Z", - "attributedTo": "https://localhost:4123/activitypub/users/karl-heinz", + "attributedTo": "http://localhost:4123/activitypub/users/karl-heinz", "content": "Hi Max, how are you?", "to": "https://www.w3.org/ns/activitystreams#Public" } @@ -32,7 +32,7 @@ Feature: Like an object like an article or note """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/peter-lustiger/status/83J23549sda1k72fsa4567na42312455kad83", + "id": "http://localhost:4123/activitypub/users/peter-lustiger/status/83J23549sda1k72fsa4567na42312455kad83", "type": "Like", "actor": "http://localhost:4123/activitypub/users/peter-lustiger", "object": "http://localhost:4123/activitypub/users/karl-heinz/status/dkasfljsdfaafg9843jknsdf" diff --git a/backend/test/features/collection.feature b/backend/test/features/collection.feature index 536d3aa2d..1bb4737e0 100644 --- a/backend/test/features/collection.feature +++ b/backend/test/features/collection.feature @@ -14,10 +14,10 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/outbox", "summary": "renate-oberdorfers outbox collection", "type": "OrderedCollection", - "first": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true", + "first": "http://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true", "totalItems": 0 } """ @@ -29,10 +29,10 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/following", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/following", "summary": "renate-oberdorfers following collection", "type": "OrderedCollection", - "first": "https://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true", + "first": "http://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true", "totalItems": 0 } """ @@ -44,10 +44,10 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/followers", "summary": "renate-oberdorfers followers collection", "type": "OrderedCollection", - "first": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true", + "first": "http://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true", "totalItems": 0 } """ @@ -59,11 +59,11 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/outbox?page=true", "summary": "renate-oberdorfers outbox collection", "type": "OrderedCollectionPage", "totalItems": 0, - "partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/outbox", + "partOf": "http://localhost:4123/activitypub/users/renate-oberdorfer/outbox", "orderedItems": [] } """ @@ -75,11 +75,11 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/following?page=true", "summary": "renate-oberdorfers following collection", "type": "OrderedCollectionPage", "totalItems": 0, - "partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/following", + "partOf": "http://localhost:4123/activitypub/users/renate-oberdorfer/following", "orderedItems": [] } """ @@ -91,11 +91,11 @@ Feature: Receiving collections """ { "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true", + "id": "http://localhost:4123/activitypub/users/renate-oberdorfer/followers?page=true", "summary": "renate-oberdorfers followers collection", "type": "OrderedCollectionPage", "totalItems": 0, - "partOf": "https://localhost:4123/activitypub/users/renate-oberdorfer/followers", + "partOf": "http://localhost:4123/activitypub/users/renate-oberdorfer/followers", "orderedItems": [] } """ diff --git a/backend/test/features/webfinger.feature b/backend/test/features/webfinger.feature index 45f46c6a6..72062839a 100644 --- a/backend/test/features/webfinger.feature +++ b/backend/test/features/webfinger.feature @@ -19,7 +19,7 @@ Feature: Webfinger discovery { "rel": "self", "type": "application/activity+json", - "href": "https://localhost:4123/activitypub/users/peter-lustiger" + "href": "http://localhost:4123/activitypub/users/peter-lustiger" } ] } @@ -44,21 +44,21 @@ Feature: Webfinger discovery "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1" ], - "id": "https://localhost:4123/activitypub/users/peter-lustiger", + "id": "http://localhost:4123/activitypub/users/peter-lustiger", "type": "Person", "preferredUsername": "peter-lustiger", "name": "peter-lustiger", - "following": "https://localhost:4123/activitypub/users/peter-lustiger/following", - "followers": "https://localhost:4123/activitypub/users/peter-lustiger/followers", - "inbox": "https://localhost:4123/activitypub/users/peter-lustiger/inbox", - "outbox": "https://localhost:4123/activitypub/users/peter-lustiger/outbox", - "url": "https://localhost:4123/activitypub/@peter-lustiger", + "following": "http://localhost:4123/activitypub/users/peter-lustiger/following", + "followers": "http://localhost:4123/activitypub/users/peter-lustiger/followers", + "inbox": "http://localhost:4123/activitypub/users/peter-lustiger/inbox", + "outbox": "http://localhost:4123/activitypub/users/peter-lustiger/outbox", + "url": "http://localhost:4123/activitypub/@peter-lustiger", "endpoints": { - "sharedInbox": "https://localhost:4123/activitypub/inbox" + "sharedInbox": "http://localhost:4123/activitypub/inbox" }, "publicKey": { - "id": "https://localhost:4123/activitypub/users/peter-lustiger#main-key", - "owner": "https://localhost:4123/activitypub/users/peter-lustiger", + "id": "http://localhost:4123/activitypub/users/peter-lustiger#main-key", + "owner": "http://localhost:4123/activitypub/users/peter-lustiger", "publicKeyPem": "adglkjlk89235kjn8obn2384f89z5bv9..." } } From fca2fcf71303dce9fe25804124532eb2e7b5a8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 01:41:58 +0100 Subject: [PATCH 09/25] Fix lint --- backend/src/activitypub/ActivityPub.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/activitypub/ActivityPub.js b/backend/src/activitypub/ActivityPub.js index 9708b33c0..b89bc16da 100644 --- a/backend/src/activitypub/ActivityPub.js +++ b/backend/src/activitypub/ActivityPub.js @@ -22,9 +22,9 @@ let activityPub = null export { activityPub } export default class ActivityPub { - constructor (activity_pub_endpoint_uri, internal_graphql_uri) { - this.endpoint = activity_pub_endpoint_uri - this.dataSource = new NitroDataSource(internal_graphql_uri) + constructor (activityPubEndpointUri, internalGraphQlUri) { + this.endpoint = activityPubEndpointUri + this.dataSource = new NitroDataSource(internalGraphQlUri) this.collections = new Collections(this.dataSource) } From 51ca20335ac9d9680321ac6d1c38537585da6321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 00:09:35 +0100 Subject: [PATCH 10/25] Sketch :cucumber: for #240 --- cypress/integration/common/steps.js | 4 ++ .../identifier/PersistentLinks.feature | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 cypress/integration/identifier/PersistentLinks.feature diff --git a/cypress/integration/common/steps.js b/cypress/integration/common/steps.js index 1a9891a7a..d05f80f13 100644 --- a/cypress/integration/common/steps.js +++ b/cypress/integration/common/steps.js @@ -86,6 +86,10 @@ Given('my user account has the role {string}', role => { When('I log out', cy.logout) +When('I visit {string}', page => { + cy.openPage(page) +}) + When('I visit the {string} page', page => { cy.openPage(page) }) diff --git a/cypress/integration/identifier/PersistentLinks.feature b/cypress/integration/identifier/PersistentLinks.feature new file mode 100644 index 000000000..b4a55cee3 --- /dev/null +++ b/cypress/integration/identifier/PersistentLinks.feature @@ -0,0 +1,41 @@ +Feature: Persistent Links + As a user + I want to click on a link with an 'href' having an unmodifiable id of a user + In order to have persistent links even if the user changes a his/her slug + + | | Modifiable | Referenceable | Unique | Purpose | + | -- | -- | -- | -- | -- | + | ID | no | yes | yes | Identity, Traceability, Links | + | Slug | yes | yes | yes | @-Mentions, SEO-friendly URL | + | Name | yes | no | no | Search, self-description | + + + Background: + Given we have the following user accounts: + | id | name | slug | + | MHNqce98y1 | Stepen Hawking | thehawk | + And we have the following posts in our database: + | id | title | slug | + | bWBjpkTKZp | 101 Essays that will change the way you think | 101-essays | + And I have a user account + And I am logged in + + Scenario Outline: Link with slug only is valid and gets auto-completed + When I visit "" + Then I get redirected to "" + Examples: + | url | redirectUrl | + | /profile/thehawk | /profile/MHNqce98y1/thehawk | + | /post/101-essays | /post/bWBjpkTKZp/101-essays | + + Scenario: Link with id only will always point to the same user + When I visit "/profile/MHNqce98y1" + Then I get redirected to "/profile/MHNqce98y1/thehawk" + + Scenario Outline: ID takes precedence over slug + When I visit "" + Then I get redirected to "" + Examples: + | url | redirectUrl | + | /profile/MHNqce98y1/stephen-hawking | /profile/MHNqce98y1/thehawk | + | /post/bWBjpkTKZp/the-way-you-think | /post/bWBjpkTKZp/101-essays | From 327bd037a019a535703004f31283793d8e6cd9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 14:49:54 +0100 Subject: [PATCH 11/25] Implement redirect for user profile --- webapp/pages/profile/{ => _id}/_slug.vue | 0 webapp/pages/profile/_id/index.vue | 46 ++++++++++++++++++++++++ webapp/pages/profile/index.vue | 8 ----- 3 files changed, 46 insertions(+), 8 deletions(-) rename webapp/pages/profile/{ => _id}/_slug.vue (100%) create mode 100644 webapp/pages/profile/_id/index.vue delete mode 100644 webapp/pages/profile/index.vue diff --git a/webapp/pages/profile/_slug.vue b/webapp/pages/profile/_id/_slug.vue similarity index 100% rename from webapp/pages/profile/_slug.vue rename to webapp/pages/profile/_id/_slug.vue diff --git a/webapp/pages/profile/_id/index.vue b/webapp/pages/profile/_id/index.vue new file mode 100644 index 000000000..78e16f8fb --- /dev/null +++ b/webapp/pages/profile/_id/index.vue @@ -0,0 +1,46 @@ + diff --git a/webapp/pages/profile/index.vue b/webapp/pages/profile/index.vue deleted file mode 100644 index 7135a2c3d..000000000 --- a/webapp/pages/profile/index.vue +++ /dev/null @@ -1,8 +0,0 @@ - From 16e60e540f1bd511c89b7667e20800747516de68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 21:00:22 +0100 Subject: [PATCH 12/25] Fix typo Cypress tests currently fail because we cannot override `slug` in factories. --- cypress/integration/identifier/PersistentLinks.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/identifier/PersistentLinks.feature b/cypress/integration/identifier/PersistentLinks.feature index b4a55cee3..1095c6c72 100644 --- a/cypress/integration/identifier/PersistentLinks.feature +++ b/cypress/integration/identifier/PersistentLinks.feature @@ -12,8 +12,8 @@ Feature: Persistent Links Background: Given we have the following user accounts: - | id | name | slug | - | MHNqce98y1 | Stepen Hawking | thehawk | + | id | name | slug | + | MHNqce98y1 | Stephen Hawking | thehawk | And we have the following posts in our database: | id | title | slug | | bWBjpkTKZp | 101 Essays that will change the way you think | 101-essays | From 51cbc65f4b349ca7eef20cc31d2768749efd213b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 23:27:00 +0100 Subject: [PATCH 13/25] Rename `profile-id` to `profile-id-slug` route --- cypress/integration/common/report.js | 2 +- webapp/components/User.vue | 6 +++--- webapp/layouts/default.vue | 2 +- webapp/pages/moderation/index.vue | 10 +++++----- webapp/pages/profile/_id/_slug.vue | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cypress/integration/common/report.js b/cypress/integration/common/report.js index 12f1b326f..e3d3f3975 100644 --- a/cypress/integration/common/report.js +++ b/cypress/integration/common/report.js @@ -71,7 +71,7 @@ When('I click on the author', () => { }) When('I report the author', () => { - cy.get('.page-name-profile-slug').then(() => { + cy.get('.page-name-profile-id-slug').then(() => { invokeReportOnElement('.ds-card').then(() => { cy.get('button') .contains('Send') diff --git a/webapp/components/User.vue b/webapp/components/User.vue index 1c78b34cc..dd176a67d 100644 --- a/webapp/components/User.vue +++ b/webapp/components/User.vue @@ -153,9 +153,9 @@ export default { return count }, userLink() { - const { slug } = this.user - if (!slug) return '' - return { name: 'profile-slug', params: { slug } } + const { id, slug } = this.user + if (!(id && slug)) return '' + return { name: 'profile-id-slug', params: { slug, id } } } } } diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index bdb41f8b2..f749fda69 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -39,7 +39,7 @@ >
- + {{ scope.row.user.name | truncate(50) }}
@@ -69,7 +69,7 @@ slot="submitter" slot-scope="scope" > - + {{ scope.row.submitter.name }} @@ -79,19 +79,19 @@ > {{ scope.row.post.disabledBy.name | truncate(50) }} {{ scope.row.comment.disabledBy.name | truncate(50) }} {{ scope.row.user.disabledBy.name | truncate(50) }} diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue index 76011f456..a3b10529e 100644 --- a/webapp/pages/profile/_id/_slug.vue +++ b/webapp/pages/profile/_id/_slug.vue @@ -423,7 +423,7 @@ export default { border: #fff 5px solid; } -.page-name-profile-slug { +.page-name-profile-id-slug { .ds-flex-item:first-child .content-menu { position: absolute; top: $space-x-small; From b79ae683186125458e8904b5acfd8e6199c00d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Mon, 25 Mar 2019 23:31:47 +0100 Subject: [PATCH 14/25] User factory can override slug --- backend/src/seed/factories/users.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/seed/factories/users.js b/backend/src/seed/factories/users.js index 491b3f9e1..9fe957515 100644 --- a/backend/src/seed/factories/users.js +++ b/backend/src/seed/factories/users.js @@ -5,6 +5,7 @@ export default function create (params) { const { id = uuid(), name = faker.name.findName(), + slug = '', email = faker.internet.email(), password = '1234', role = 'user', @@ -19,6 +20,7 @@ export default function create (params) { CreateUser( id: "${id}", name: "${name}", + slug: "${slug}", password: "${password}", email: "${email}", avatar: "${avatar}", @@ -29,6 +31,7 @@ export default function create (params) { ) { id name + slug email avatar role From 203f2091de445d7f13136d158f617061bdc6f726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 14:28:55 +0100 Subject: [PATCH 15/25] Implement `id takes precedence over slug` --- .../pages/profile/{_id/index.vue => _id.vue} | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) rename webapp/pages/profile/{_id/index.vue => _id.vue} (51%) diff --git a/webapp/pages/profile/_id/index.vue b/webapp/pages/profile/_id.vue similarity index 51% rename from webapp/pages/profile/_id/index.vue rename to webapp/pages/profile/_id.vue index 78e16f8fb..eee8d610a 100644 --- a/webapp/pages/profile/_id/index.vue +++ b/webapp/pages/profile/_id.vue @@ -1,3 +1,7 @@ + + diff --git a/webapp/pages/post/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue similarity index 100% rename from webapp/pages/post/_slug/index.vue rename to webapp/pages/post/_id/_slug/index.vue diff --git a/webapp/pages/post/_slug/more-info.vue b/webapp/pages/post/_id/_slug/more-info.vue similarity index 100% rename from webapp/pages/post/_slug/more-info.vue rename to webapp/pages/post/_id/_slug/more-info.vue diff --git a/webapp/pages/post/_slug/take-action.vue b/webapp/pages/post/_id/_slug/take-action.vue similarity index 100% rename from webapp/pages/post/_slug/take-action.vue rename to webapp/pages/post/_id/_slug/take-action.vue diff --git a/webapp/pages/post/_slug.vue b/webapp/pages/post/_slug.vue deleted file mode 100644 index d4a233f0f..000000000 --- a/webapp/pages/post/_slug.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/webapp/pages/profile/_id.vue b/webapp/pages/profile/_id.vue index eee8d610a..ad9772358 100644 --- a/webapp/pages/profile/_id.vue +++ b/webapp/pages/profile/_id.vue @@ -23,7 +23,6 @@ const querySlug = gql` ` export default { - layout: 'blank', async asyncData(context) { const { params: { id, slug }, From 63d928e12e918a080d644b55ada3fc412b35ad1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Mar 2019 16:21:39 +0100 Subject: [PATCH 17/25] Change route `post-slug` to `post-id-slug` And query `slug` and `id` everywhere --- webapp/components/ContributionForm.vue | 4 ++-- webapp/components/PostCard.vue | 4 ++-- webapp/graphql/ModerationListQuery.js | 14 ++++++++++++-- webapp/graphql/UserProfileQuery.js | 6 ++++-- webapp/layouts/default.vue | 4 ++-- webapp/pages/index.vue | 4 ++-- webapp/pages/moderation/index.vue | 4 ++-- webapp/pages/post/_id/_slug/index.vue | 2 +- 8 files changed, 27 insertions(+), 15 deletions(-) diff --git a/webapp/components/ContributionForm.vue b/webapp/components/ContributionForm.vue index cf7c28ece..3ef041569 100644 --- a/webapp/components/ContributionForm.vue +++ b/webapp/components/ContributionForm.vue @@ -111,8 +111,8 @@ export default { const result = res.data[this.id ? 'UpdatePost' : 'CreatePost'] this.$router.push({ - name: 'post-slug', - params: { slug: result.slug } + name: 'post-id-slug', + params: { id: result.id, slug: result.slug } }) }) .catch(err => { diff --git a/webapp/components/PostCard.vue b/webapp/components/PostCard.vue index 8f534f6ff..767835f74 100644 --- a/webapp/components/PostCard.vue +++ b/webapp/components/PostCard.vue @@ -106,8 +106,8 @@ export default { methods: { href(post) { return this.$router.resolve({ - name: 'post-slug', - params: { slug: post.slug } + name: 'post-id-slug', + params: { id: post.id, slug: post.slug } }).href } } diff --git a/webapp/graphql/ModerationListQuery.js b/webapp/graphql/ModerationListQuery.js index d8105e388..4e3927bdd 100644 --- a/webapp/graphql/ModerationListQuery.js +++ b/webapp/graphql/ModerationListQuery.js @@ -9,43 +9,52 @@ export default app => { type createdAt submitter { + id disabled deleted name slug } user { + id name slug disabled deleted disabledBy { + id slug name + disabled + deleted } } comment { contentExcerpt author { + id name slug disabled deleted } post { + id disabled deleted title slug } disabledBy { + id + slug disabled deleted - slug name } } post { + id title slug disabled @@ -57,9 +66,10 @@ export default app => { slug } disabledBy { + id + slug disabled deleted - slug name } } diff --git a/webapp/graphql/UserProfileQuery.js b/webapp/graphql/UserProfileQuery.js index 683f0e3ac..f0d7720ae 100644 --- a/webapp/graphql/UserProfileQuery.js +++ b/webapp/graphql/UserProfileQuery.js @@ -6,6 +6,7 @@ export default app => { query User($slug: String!, $first: Int, $offset: Int) { User(slug: $slug) { id + slug name avatar about @@ -27,8 +28,8 @@ export default app => { followingCount following(first: 7) { id - name slug + name avatar disabled deleted @@ -49,10 +50,10 @@ export default app => { followedByCurrentUser followedBy(first: 7) { id + slug name disabled deleted - slug avatar followedByCount followedByCurrentUser @@ -87,6 +88,7 @@ export default app => { } author { id + slug avatar name disabled diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index f749fda69..991662350 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -182,8 +182,8 @@ export default { goToPost(item) { this.$nextTick(() => { this.$router.push({ - name: 'post-slug', - params: { slug: item.slug } + name: 'post-id-slug', + params: { id: item.id, slug: item.slug } }) }) }, diff --git a/webapp/pages/index.vue b/webapp/pages/index.vue index e28d6b2c3..ee824eb59 100644 --- a/webapp/pages/index.vue +++ b/webapp/pages/index.vue @@ -64,8 +64,8 @@ export default { }, href(post) { return this.$router.resolve({ - name: 'post-slug', - params: { slug: post.slug } + name: 'post-id-slug', + params: { id: post.id, slug: post.slug } }).href }, showMoreContributions() { diff --git a/webapp/pages/moderation/index.vue b/webapp/pages/moderation/index.vue index 5d9ab1eb0..fc5d1fbe6 100644 --- a/webapp/pages/moderation/index.vue +++ b/webapp/pages/moderation/index.vue @@ -14,7 +14,7 @@ slot-scope="scope" >
- + {{ scope.row.post.title | truncate(50) }}
- + {{ scope.row.comment.contentExcerpt | truncate(50) }}