Merge branch 'master' of github.com:Human-Connection/Human-Connection into 691_handle-large-file-sizes-bug

This commit is contained in:
Matt Rider 2019-06-01 16:05:43 -03:00
commit e09ef35c4a
133 changed files with 2200 additions and 1365 deletions

View File

@ -1,4 +1,4 @@
FROM node:10-alpine as base
FROM node:12.3.1-alpine as base
LABEL Description="Backend of the Social Network Human-Connection.org" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)"
EXPOSE 4000

View File

@ -6,8 +6,8 @@
"scripts": {
"build": "babel src/ -d dist/ --copy-files",
"start": "node dist/",
"dev": "nodemon --exec babel-node src/ -e js,graphql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,graphql",
"dev": "nodemon --exec babel-node src/ -e js,gql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,gql",
"lint": "eslint src --config .eslintrc.js",
"test": "run-s test:jest test:cucumber",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null",
@ -47,7 +47,7 @@
"apollo-client": "~2.5.1",
"apollo-link-context": "~1.0.14",
"apollo-link-http": "~1.5.14",
"apollo-server": "~2.5.0",
"apollo-server": "~2.5.1",
"bcryptjs": "~2.4.3",
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
@ -68,6 +68,7 @@
"jsonwebtoken": "~8.5.1",
"linkifyjs": "~2.1.8",
"lodash": "~4.17.11",
"merge-graphql-schemas": "^1.5.8",
"ms": "~2.1.1",
"neo4j-driver": "~1.7.4",
"neo4j-graphql-js": "git+https://github.com/Human-Connection/neo4j-graphql-js.git#temporary_fixes",
@ -87,15 +88,15 @@
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.4.5",
"@babel/register": "~7.4.4",
"apollo-server-testing": "~2.5.0",
"apollo-server-testing": "~2.5.1",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.1",
"babel-jest": "~24.8.0",
"chai": "~4.2.0",
"cucumber": "~5.1.0",
"eslint": "~5.16.0",
"eslint-config-standard": "~12.0.0",
"eslint-config-prettier": "~4.3.0",
"eslint-config-standard": "~12.0.0",
"eslint-plugin-import": "~2.17.3",
"eslint-plugin-jest": "~22.6.4",
"eslint-plugin-node": "~9.1.0",

View File

@ -1,41 +1,2 @@
import fs from 'fs'
import path from 'path'
import userManagement from './resolvers/user_management.js'
import statistics from './resolvers/statistics.js'
import reports from './resolvers/reports.js'
import posts from './resolvers/posts.js'
import moderation from './resolvers/moderation.js'
import follow from './resolvers/follow.js'
import shout from './resolvers/shout.js'
import rewards from './resolvers/rewards.js'
import socialMedia from './resolvers/socialMedia.js'
import notifications from './resolvers/notifications'
import comments from './resolvers/comments'
import users from './resolvers/users'
export const typeDefs = fs
.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql'))
.toString('utf-8')
export const resolvers = {
Query: {
...statistics.Query,
...userManagement.Query,
...notifications.Query,
...comments.Query,
},
Mutation: {
...userManagement.Mutation,
...reports.Mutation,
...posts.Mutation,
...moderation.Mutation,
...follow.Mutation,
...shout.Mutation,
...rewards.Mutation,
...socialMedia.Mutation,
...notifications.Mutation,
...comments.Mutation,
...users.Mutation,
},
}
export { default as typeDefs } from './types'
export { default as resolvers } from './resolvers'

View File

@ -41,8 +41,8 @@ describe('badges', () => {
mutation(
$id: ID
$key: String!
$type: BadgeTypeEnum!
$status: BadgeStatusEnum!
$type: BadgeType!
$status: BadgeStatus!
$icon: String!
) {
CreateBadge(id: $id, key: $key, type: $type, status: $status, icon: $icon) {

View File

@ -0,0 +1,5 @@
import path from 'path'
import { fileLoader, mergeResolvers } from 'merge-graphql-schemas'
const resolversArray = fileLoader(path.join(__dirname, './!(*.spec).js'))
export default mergeResolvers(resolversArray)

View File

@ -14,8 +14,8 @@ export default function(params) {
mutation(
$id: ID
$key: String!
$type: BadgeTypeEnum!
$status: BadgeStatusEnum!
$type: BadgeType!
$status: BadgeStatus!
$icon: String!
) {
CreateBadge(id: $id, key: $key, type: $type, status: $status, icon: $icon) {

View File

@ -26,7 +26,7 @@ export default function(params) {
$title: String!
$content: String!
$image: String
$visibility: VisibilityEnum
$visibility: Visibility
$deleted: Boolean
) {
CreatePost(

View File

@ -23,7 +23,7 @@ export default function create(params) {
$email: String!
$avatar: String
$about: String
$role: UserGroupEnum
$role: UserGroup
) {
CreateUser(
id: $id

View File

@ -0,0 +1,4 @@
enum BadgeStatus {
permanent
temporary
}

View File

@ -0,0 +1,4 @@
enum BadgeType {
role
crowdfunding
}

View File

@ -0,0 +1,5 @@
enum UserGroup {
admin
moderator
user
}

View File

@ -0,0 +1,5 @@
enum Visibility {
public
friends
private
}

View File

@ -0,0 +1,30 @@
import fs from 'fs'
import path from 'path'
import { mergeTypes } from 'merge-graphql-schemas'
const findGqlFiles = dir => {
var results = []
var list = fs.readdirSync(dir)
list.forEach(file => {
file = path.join(dir, file).toString('utf-8')
var stat = fs.statSync(file)
if (stat && stat.isDirectory()) {
// Recurse into a subdirectory
results = results.concat(findGqlFiles(file))
} else {
if (path.extname(file) === '.gql') {
// Is a gql file
results.push(file)
}
}
})
return results
}
let typeDefs = []
findGqlFiles(__dirname).forEach(file => {
typeDefs.push(fs.readFileSync(file).toString('utf-8'))
})
export default mergeTypes(typeDefs, { all: true })

View File

@ -0,0 +1 @@
scalar Date

View File

@ -0,0 +1 @@
scalar DateTime

View File

@ -0,0 +1 @@
scalar Time

View File

@ -0,0 +1 @@
scalar Upload

View File

@ -0,0 +1,134 @@
type Query {
isLoggedIn: Boolean!
# Get the currently logged in User based on the given JWT Token
currentUser: User
# Get the latest Network Statistics
statistics: Statistics!
findPosts(filter: String!, limit: Int = 10): [Post]! @cypher(
statement: """
CALL db.index.fulltext.queryNodes('full_text_search', $filter)
YIELD node as post, score
MATCH (post)<-[:WROTE]-(user:User)
WHERE score >= 0.2
AND NOT user.deleted = true AND NOT user.disabled = true
AND NOT post.deleted = true AND NOT post.disabled = true
RETURN post
LIMIT $limit
"""
)
CommentByPost(postId: ID!): [Comment]!
}
type Mutation {
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!
signup(email: String!, password: String!): Boolean!
changePassword(oldPassword:String!, newPassword: String!): String!
report(id: ID!, description: String): Report
disable(id: ID!): ID
enable(id: ID!): ID
reward(fromBadgeId: ID!, toUserId: ID!): ID
unreward(fromBadgeId: ID!, toUserId: ID!): ID
# Shout the given Type and ID
shout(id: ID!, type: ShoutTypeEnum): Boolean!
# Unshout the given Type and ID
unshout(id: ID!, type: ShoutTypeEnum): Boolean!
# Follow the given Type and ID
follow(id: ID!, type: FollowTypeEnum): Boolean!
# Unfollow the given Type and ID
unfollow(id: ID!, type: FollowTypeEnum): Boolean!
}
type Statistics {
countUsers: Int!
countPosts: Int!
countComments: Int!
countNotifications: Int!
countOrganizations: Int!
countProjects: Int!
countInvites: Int!
countFollows: Int!
countShouts: Int!
}
type Notification {
id: ID!
read: Boolean,
user: User @relation(name: "NOTIFIED", direction: "OUT")
post: Post @relation(name: "NOTIFIED", direction: "IN")
createdAt: String
}
type Location {
id: ID!
name: String!
nameEN: String
nameDE: String
nameFR: String
nameNL: String
nameIT: String
nameES: String
namePT: String
namePL: String
type: String!
lat: Float
lng: Float
parent: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
}
type Report {
id: ID!
submitter: User @relation(name: "REPORTED", direction: "IN")
description: String
type: String! @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
createdAt: String
comment: Comment @relation(name: "REPORTED", direction: "OUT")
post: Post @relation(name: "REPORTED", direction: "OUT")
user: User @relation(name: "REPORTED", direction: "OUT")
}
enum ShoutTypeEnum {
Post
Organization
Project
}
enum FollowTypeEnum {
User
Organization
Project
}
type Reward {
id: ID!
user: User @relation(name: "REWARDED", direction: "IN")
rewarderId: ID
createdAt: String
badge: Badge @relation(name: "REWARDED", direction: "OUT")
}
type Organization {
id: ID!
createdBy: User @relation(name: "CREATED_ORGA", direction: "IN")
ownedBy: [User] @relation(name: "OWNING_ORGA", direction: "IN")
name: String!
slug: String
description: String!
descriptionExcerpt: String
deleted: Boolean
disabled: Boolean
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
}
type SharedInboxEndpoint {
id: ID!
uri: String
}
type SocialMedia {
id: ID!
url: String
ownedBy: [User]! @relation(name: "OWNED", direction: "IN")
}

View File

@ -0,0 +1,13 @@
type Badge {
id: ID!
key: String!
type: BadgeType!
status: BadgeStatus!
icon: String!
#createdAt: DateTime
#updatedAt: DateTime
createdAt: String
updatedAt: String
rewarded: [User]! @relation(name: "REWARDED", direction: "OUT")
}

View File

@ -0,0 +1,13 @@
type Category {
id: ID!
name: String!
slug: String
icon: String!
#createdAt: DateTime
#updatedAt: DateTime
createdAt: String
updatedAt: String
posts: [Post]! @relation(name: "CATEGORIZED", direction: "IN")
postCount: Int! @cypher(statement: "MATCH (this)<-[:CATEGORIZED]-(r:Post) RETURN COUNT(r)")
}

View File

@ -0,0 +1,14 @@
type Comment {
id: ID!
activityId: String
postId: ID
author: User @relation(name: "WROTE", direction: "IN")
content: String!
contentExcerpt: String
post: Post @relation(name: "COMMENTS", direction: "OUT")
createdAt: String
updatedAt: String
deleted: Boolean
disabled: Boolean
disabledBy: User @relation(name: "DISABLED", direction: "IN")
}

View File

@ -0,0 +1,43 @@
type Post {
id: ID!
activityId: String
objectId: String
author: User @relation(name: "WROTE", direction: "IN")
title: String!
slug: String
content: String!
contentExcerpt: String
image: String
imageUpload: Upload
visibility: Visibility
deleted: Boolean
disabled: Boolean
disabledBy: User @relation(name: "DISABLED", direction: "IN")
createdAt: String
updatedAt: String
relatedContributions: [Post]! @cypher(
statement: """
MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post)
RETURN DISTINCT post
LIMIT 10
"""
)
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
comments: [Comment]! @relation(name: "COMMENTS", direction: "IN")
commentsCount: Int! @cypher(statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN")
shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
# Has the currently logged in user shouted that post?
shoutedByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
}

View File

@ -0,0 +1,10 @@
type Tag {
id: ID!
name: String!
taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN")
taggedOrganizations: [Organization]! @relation(name: "TAGGED", direction: "IN")
taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)")
taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
deleted: Boolean
disabled: Boolean
}

View File

@ -0,0 +1,80 @@
type User {
id: ID!
actorId: String
name: String
email: String!
slug: String
password: String!
avatar: String
coverImg: String
avatarUpload: Upload
deleted: Boolean
disabled: Boolean
disabledBy: User @relation(name: "DISABLED", direction: "IN")
role: UserGroup
publicKey: String
privateKey: String
wasInvited: Boolean
wasSeeded: Boolean
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
locationName: String
about: String
socialMedia: [SocialMedia]! @relation(name: "OWNED", direction: "OUT")
#createdAt: DateTime
#updatedAt: DateTime
createdAt: String
updatedAt: String
notifications(read: Boolean): [Notification]! @relation(name: "NOTIFIED", direction: "IN")
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)")
following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)")
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
# Is the currently logged in user following that user?
followedByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
#contributions: [WrittenPost]!
#contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
# @cypher(
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
# )
contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
WHERE (NOT exists(r.deleted) OR r.deleted = false)
AND (NOT exists(r.disabled) OR r.disabled = false)
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
organizationsCreated: [Organization] @relation(name: "CREATED_ORGA", direction: "OUT")
organizationsOwned: [Organization] @relation(name: "OWNING_ORGA", direction: "OUT")
blacklisted: [User]! @relation(name: "BLACKLISTED", direction: "OUT")
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
}

View File

@ -2,6 +2,13 @@
# yarn lockfile v1
"@apollographql/apollo-tools@^0.3.6":
version "0.3.7"
resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.7.tgz#3bc9c35b9fff65febd4ddc0c1fc04677693a3d40"
integrity sha512-+ertvzAwzkYmuUtT8zH3Zi6jPdyxZwOgnYaZHY7iLnMVJDhQKWlkyjLMF8wyzlPiEdDImVUMm5lOIBZo7LkGlg==
dependencies:
apollo-env "0.5.1"
"@apollographql/apollo-tools@^0.3.6-alpha.1":
version "0.3.6-alpha.1"
resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.6-alpha.1.tgz#5199b36c65c2fddc4f8bc8bb97642f74e9fb00c5"
@ -9,10 +16,10 @@
dependencies:
apollo-env "0.4.1-alpha.1"
"@apollographql/graphql-playground-html@^1.6.6":
version "1.6.6"
resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.6.tgz#022209e28a2b547dcde15b219f0c50f47aa5beb3"
integrity sha512-lqK94b+caNtmKFs5oUVXlSpN3sm5IXZ+KfhMxOtr0LR2SqErzkoJilitjDvJ1WbjHlxLI7WtCjRmOLdOGJqtMQ==
"@apollographql/graphql-playground-html@1.6.20":
version "1.6.20"
resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz#bf9f2acdf319c0959fad8ec1239741dd2ead4e8d"
integrity sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==
"@babel/cli@~7.4.4":
version "7.4.4"
@ -1281,13 +1288,13 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
apollo-cache-control@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.6.0.tgz#df22db28f850ea90a5722f5e92654d30c96e7f91"
integrity sha512-66aCF6MHe0/FdD3knphwTv6CCIdb1ZxrMsiRpxP474qqyYVe2jAwBu6aJBn4emffZHZ7i6gp9dY6cPHThjnbKA==
apollo-cache-control@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.6.1.tgz#c73ff521fe606faf18edcbd3463c421a966f3e5d"
integrity sha512-M3cDeQDXtRxYPQ/sL4pu3IVE5Q/9jpBlENB2IjwxTDir+WFZbJV1CAnvVwyJdL1DvS6ESR35DFOurJH4Ws/OPA==
dependencies:
apollo-server-env "2.3.0"
graphql-extensions "0.6.0"
graphql-extensions "0.6.1"
apollo-cache-control@^0.1.0:
version "0.1.1"
@ -1353,17 +1360,17 @@ apollo-engine-reporting-protobuf@0.3.0:
dependencies:
protobufjs "^6.8.6"
apollo-engine-reporting@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.1.0.tgz#10def3d3bf3f11ddb24765c19d9c81e30cb9d55c"
integrity sha512-Dj0BwgcluHL0QVUaquhAoYoLX9Z4DRP/n2REcIwO8d2iy52r+1wN5QqZLx97dEFh7CjhNjTWeysJzc8XMWKa1Q==
apollo-engine-reporting@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.1.1.tgz#f5a3240bc5c5afb210ff8c45d72995de7b0d2a13"
integrity sha512-K7BDsj99jr8ftd9NIuHL4oF/S7CBFcgMGjL0ChhfxpkgUv80FPxJ+9Fs+9ZkKIVylV3PCi2WnihpDeEO10eZAw==
dependencies:
apollo-engine-reporting-protobuf "0.3.0"
apollo-graphql "^0.2.1-alpha.1"
apollo-server-core "2.5.0"
apollo-server-core "2.5.1"
apollo-server-env "2.3.0"
async-retry "^1.2.1"
graphql-extensions "0.6.0"
graphql-extensions "0.6.1"
apollo-env@0.4.1-alpha.1:
version "0.4.1-alpha.1"
@ -1374,6 +1381,15 @@ apollo-env@0.4.1-alpha.1:
node-fetch "^2.2.0"
sha.js "^2.4.11"
apollo-env@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.5.1.tgz#b9b0195c16feadf0fe9fd5563edb0b9b7d9e97d3"
integrity sha512-fndST2xojgSdH02k5hxk1cbqA9Ti8RX4YzzBoAB4oIe1Puhq7+YlhXGXfXB5Y4XN0al8dLg+5nAkyjNAR2qZTw==
dependencies:
core-js "^3.0.1"
node-fetch "^2.2.0"
sha.js "^2.4.11"
apollo-errors@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/apollo-errors/-/apollo-errors-1.9.0.tgz#f1ed0ca0a6be5cd2f24e2eaa7b0860a10146ff51"
@ -1440,24 +1456,24 @@ apollo-server-caching@0.4.0:
dependencies:
lru-cache "^5.0.0"
apollo-server-core@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.5.0.tgz#89fc28ba1018ebf9240bc3cc0c103fe705309023"
integrity sha512-7hyQ/Rt0hC38bUfxMQmLNHDBIGEBykFWo9EO0W+3o/cno/SqBKd1KKichrABVv+v+SCvZAUutX6gYS5l3G+ULQ==
apollo-server-core@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.5.1.tgz#0fdb6cfca56a0f5b5b3aecffb48db17b3c8e1d71"
integrity sha512-4QNrW1AUM3M/p0+hbBX/MsjSjZTy+2rt7JpiKKkG9RmeEIzd/VG7hwwwloAZSLjYx3twz0+BnASJ9y+rGEPC8A==
dependencies:
"@apollographql/apollo-tools" "^0.3.6-alpha.1"
"@apollographql/graphql-playground-html" "^1.6.6"
"@apollographql/apollo-tools" "^0.3.6"
"@apollographql/graphql-playground-html" "1.6.20"
"@types/ws" "^6.0.0"
apollo-cache-control "0.6.0"
apollo-cache-control "0.6.1"
apollo-datasource "0.4.0"
apollo-engine-reporting "1.1.0"
apollo-engine-reporting "1.1.1"
apollo-server-caching "0.4.0"
apollo-server-env "2.3.0"
apollo-server-errors "2.3.0"
apollo-server-plugin-base "0.4.0"
apollo-tracing "0.6.0"
apollo-server-plugin-base "0.4.1"
apollo-tracing "0.6.1"
fast-json-stable-stringify "^2.0.0"
graphql-extensions "0.6.0"
graphql-extensions "0.6.1"
graphql-subscriptions "^1.0.0"
graphql-tag "^2.9.2"
graphql-tools "^4.0.0"
@ -1488,18 +1504,18 @@ apollo-server-errors@2.3.0:
resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061"
integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw==
apollo-server-express@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.5.0.tgz#ff6cbd3fcb8933f6316c5a5edd4db12d9a56fa65"
integrity sha512-2gd3VWIqji2jyDYMTTqKzVU4/znjEjugtLUmPgVl5SoBvJSMTsO7VgJv+roBubZGDK8jXXUEXr2a33RtIeHe4g==
apollo-server-express@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.5.1.tgz#b112d9795f2fb39076d9cbc109f5eeb7835bed6b"
integrity sha512-528wDQnOMIenDaICkYPFWQykdXQZwpygxd+Ar0PmZiaST042NSVExV4iRWI09p1THqfsuyHygqpkK+K94bUtBA==
dependencies:
"@apollographql/graphql-playground-html" "^1.6.6"
"@apollographql/graphql-playground-html" "1.6.20"
"@types/accepts" "^1.3.5"
"@types/body-parser" "1.17.0"
"@types/cors" "^2.8.4"
"@types/express" "4.16.1"
accepts "^1.3.5"
apollo-server-core "2.5.0"
apollo-server-core "2.5.1"
body-parser "^1.18.3"
cors "^2.8.4"
graphql-subscriptions "^1.0.0"
@ -1527,36 +1543,36 @@ apollo-server-module-graphiql@^1.3.4, apollo-server-module-graphiql@^1.4.0:
resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec"
integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA==
apollo-server-plugin-base@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.4.0.tgz#38a3c37767043873dd1b07143d4e70eecbb09562"
integrity sha512-iD7ARNtwnvHGd1EMPK0CuodM8d8hgDvFwTfIDzJY04QIQ6/KrBFaWhnCXJsy+HMb47GovwBbq67IK6eb2WJgBg==
apollo-server-plugin-base@0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.4.1.tgz#be380b28d71ad3b6b146d0d6a8f7ebf5675b07ff"
integrity sha512-D2G6Ca/KBdQgEbmSfYqZqYbdVJnk/rrSv7Vj2NntwjfL7WJf0TjufxYJlrTH5jF6xCbsszDNGqfmt2Nm8x/o4g==
apollo-server-testing@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.5.0.tgz#6c4c386ddbcc5e555a02afc2c625955150827969"
integrity sha512-mjUjcdsm6np7dnx5Dy7v0k0cwNHIdTHuTZUUgLuYUPtJUS+QOmOQ4yNpglPnHwY8TXx/asFnKGKvrO5mUrUedA==
apollo-server-testing@~2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.5.1.tgz#9f210caa738a275a30269b6d8a3b29bce01b69a1"
integrity sha512-npaj92Z33sNt4beBuoLJqlicqtawqI/41CA9/IrcswI9WwlWIxNKhfpcMYo9MpQWRT8aCIGOOAkBdG2jZlhH4Q==
dependencies:
apollo-server-core "2.5.0"
apollo-server-core "2.5.1"
apollo-server@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.5.0.tgz#a88a550dbc5ff0c6713142d1cab3b61b4a36e483"
integrity sha512-85A3iAnXVP5QiXc0xvAJRyGsoxov06+8AzttKqehR4Q50UC1Is62xY5WZk58oW7fm+awpqh+sXB2F2E6tObSmg==
apollo-server@~2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.5.1.tgz#bfcfbebc123f692c0e6d85b0c56739646bd1bb7e"
integrity sha512-eH3ubq300xhpFAxek28kb+5WZINXpWcwzyNqBQDbuasTlW8qSsqY7xrV6IIz6WUYKdX+ET0mx+Ta1DdaYQPrqw==
dependencies:
apollo-server-core "2.5.0"
apollo-server-express "2.5.0"
apollo-server-core "2.5.1"
apollo-server-express "2.5.1"
express "^4.0.0"
graphql-subscriptions "^1.0.0"
graphql-tools "^4.0.0"
apollo-tracing@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.6.0.tgz#afc2b9cbea173dc4c315a5d98053797469518083"
integrity sha512-OpYPHVBgcQ/HT2WLXJQWwhilzR1rrl01tZeMU2N7yinsp/oyKngF5aUSMtuvX1k/T3abilQo+w10oAQlBCGdPA==
apollo-tracing@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.6.1.tgz#48a6d6040f9b2f2b4365a890c2e97cb763eb2392"
integrity sha512-rrDBgTHa9GDA3wY8O7rDsFwC6ePIVzRGxpUsThgmLvIVkkCr0KS4wJJ4C01c+v4xsOXNuQwx0IyYhxZt4twwcA==
dependencies:
apollo-server-env "2.3.0"
graphql-extensions "0.6.0"
graphql-extensions "0.6.1"
apollo-tracing@^0.1.0:
version "0.1.4"
@ -2440,6 +2456,11 @@ core-js@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0.tgz#a8dbfa978d29bfc263bfb66c556d0ca924c28957"
integrity sha512-WBmxlgH2122EzEJ6GH8o9L/FeoUKxxxZ6q6VUxoTlsE4EvbTWKJb447eyVxTEuq0LpXjlq/kCB2qgBvsYRkLvQ==
core-js@^3.0.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.3.tgz#95700bca5f248f5f78c0ec63e784eca663ec4138"
integrity sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -2656,6 +2677,11 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepmerge@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
define-properties@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@ -3723,10 +3749,10 @@ graphql-deduplicator@^2.0.1:
resolved "https://registry.yarnpkg.com/graphql-deduplicator/-/graphql-deduplicator-2.0.2.tgz#d8608161cf6be97725e178df0c41f6a1f9f778f3"
integrity sha512-0CGmTmQh4UvJfsaTPppJAcHwHln8Ayat7yXXxdnuWT+Mb1dBzkbErabCWzjXyKh/RefqlGTTA7EQOZHofMaKJA==
graphql-extensions@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.6.0.tgz#3ee3aa57fe213f90aec5cd31275f6d04767c6a23"
integrity sha512-SshzmbD68fHXRv2q3St29olMOxHDLQ5e9TOh+Tz2BYxinrfhjFaPNcEefiK/vF295wW827Y58bdO11Xmhf8J+Q==
graphql-extensions@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.6.1.tgz#e61c4cb901e336dc5993a61093a8678a021dda59"
integrity sha512-vB2WNQJn99pncHfvxgcdyVoazmG3cD8XzkgcaDrHTvV+xJGJEBP6056EWi0mNt1d6ukYyRS2zexdekmMCjcq0w==
dependencies:
"@apollographql/apollo-tools" "^0.3.6-alpha.1"
@ -5388,6 +5414,15 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-graphql-schemas@^1.5.8:
version "1.5.8"
resolved "https://registry.yarnpkg.com/merge-graphql-schemas/-/merge-graphql-schemas-1.5.8.tgz#89457b60312aabead44d5b2b7625643f8ab9e369"
integrity sha512-0TGOKebltvmWR9h9dPYS2vAqMPThXwJ6gVz7O5MtpBp2sunAg/M25iMSNI7YhU6PDJVtGtldTfqV9a+55YhB+A==
dependencies:
deepmerge "^2.2.1"
glob "^7.1.3"
is-glob "^4.0.0"
merge-stream@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"

View File

@ -38,7 +38,7 @@ your deployed kubernetes pods.
## Create a namespace
```bash
# in folder deployment/human-connection/
# in folder deployment/
$ kubectl apply -f namespace.yaml
```

View File

@ -56,7 +56,7 @@ Deploy one-time maintenance-worker pod:
```bash
# in deployment/legacy-migration/
$ kubectl apply -f db-migration-worker.yaml
$ kubectl apply -f maintenance-worker.yaml
pod/nitro-maintenance-worker created
```
@ -65,7 +65,7 @@ Import legacy database and uploads:
```bash
$ kubectl --namespace=human-connection exec -it nitro-maintenance-worker bash
$ import_legacy_db
$ import_uploads
$ import_legacy_uploads
$ exit
```

View File

@ -8,6 +8,9 @@
containers:
- name: nitro-maintenance-worker
image: humanconnection/maintenance-worker:latest
env:
- name: NEO4J_apoc_import_file_enabled
value: "true"
envFrom:
- configMapRef:
name: maintenance-worker
@ -18,7 +21,7 @@
readOnly: false
mountPath: /root/.ssh
- name: uploads
mountPath: /nitro-backend/public/uploads
mountPath: /uploads
- name: neo4j-data
mountPath: /data/
volumes:

View File

@ -3,9 +3,19 @@ FROM humanconnection/neo4j:latest
ENV NODE_ENV=maintenance
EXPOSE 7687 7474
ENV BUILD_DEPS="gettext" \
RUNTIME_DEPS="libintl"
RUN set -x && \
apk add --update $RUNTIME_DEPS && \
apk add --virtual build_deps $BUILD_DEPS && \
cp /usr/bin/envsubst /usr/local/bin/envsubst && \
apk del build_deps
RUN apk upgrade --update
RUN apk add --no-cache mongodb-tools openssh nodejs yarn rsync
COPY known_hosts /root/.ssh/known_hosts
COPY migration ./migration
COPY migration /migration
COPY ./binaries/* /usr/local/bin/

View File

@ -5,11 +5,11 @@
# Import Settings
# On Windows this resolves to C:\Users\dornhoeschen\AppData\Local\Temp\mongo-export (MinGW)
IMPORT_PATH='/tmp/mongo-export/'
IMPORT_CHUNK_PATH='/tmp/mongo-export/splits/current-chunk.json'
IMPORT_CHUNK_PATH='/tmp/mongo-export/splits/'
IMPORT_CHUNK_PATH_CQL='/tmp/mongo-export/splits/current-chunk.json'
IMPORT_CHUNK_PATH_CQL='/tmp/mongo-export/splits/'
# On Windows this path needs to be windows style since the cypher-shell runs native - note the forward slash
# IMPORT_CHUNK_PATH_CQL='C:/Users/dornhoeschen/AppData/Local/Temp/mongo-export/splits/current-chunk.json'
# IMPORT_CHUNK_PATH_CQL='C:/Users/dornhoeschen/AppData/Local/Temp/mongo-export/splits/'
IMPORT_CYPHERSHELL_BIN='cypher-shell'
# On Windows use something like this

View File

@ -0,0 +1 @@
MATCH (n) DETACH DELETE n;

View File

@ -1,4 +1,46 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as badge
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[?] image: {
[?] path: { // Path is incorrect in Nitro - is icon the correct name for this field?
[X] type: String,
[X] required: true
},
[ ] alt: { // If we use an image - should we not have an alt?
[ ] type: String,
[ ] required: true
}
},
[?] status: {
[X] type: String,
[X] enum: ['permanent', 'temporary'],
[ ] default: 'permanent', // Default value is missing in Nitro
[X] required: true
},
[?] type: {
[?] type: String, // in nitro this is a defined enum - seems good for now
[X] required: true
},
[X] key: {
[X] type: String,
[X] required: true
},
[?] createdAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
},
[?] updatedAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
}
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as badge
MERGE(b:Badge {id: badge._id["$oid"]})
ON CREATE SET
b.key = badge.key,

View File

@ -0,0 +1 @@
MATCH (n:Badge) DETACH DELETE n;

View File

@ -1,4 +1,35 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as category
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[X] title: {
[X] type: String,
[X] required: true
},
[?] slug: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[ ] unique: true // Unique value is not enforced in Nitro?
},
[?] icon: { // Nitro adds required: true
[X] type: String,
[ ] unique: true // Unique value is not enforced in Nitro?
},
[?] createdAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
},
[?] updatedAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
}
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as category
MERGE(c:Category {id: category._id["$oid"]})
ON CREATE SET
c.name = category.title,
@ -8,6 +39,7 @@ c.createdAt = category.createdAt.`$date`,
c.updatedAt = category.updatedAt.`$date`
;
// Transform icon names
MATCH (c:Category)
WHERE (c.icon = "categories-justforfun")
SET c.icon = 'smile'

View File

@ -0,0 +1 @@
MATCH (n:Category) DETACH DELETE n;

View File

@ -1,15 +1,65 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as json
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[?] userId: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[?] contributionId: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[X] content: {
[X] type: String,
[X] required: true
},
[?] contentExcerpt: { // Generated from content
[X] type: String,
[ ] required: true // Not required in Nitro
},
[ ] hasMore: { type: Boolean },
[ ] upvotes: {
[ ] type: Array,
[ ] default: []
},
[ ] upvoteCount: {
[ ] type: Number,
[ ] default: 0
},
[?] deleted: {
[X] type: Boolean,
[ ] default: false, // Default value is missing in Nitro
[-] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
MERGE (comment:Comment {id: json._id["$oid"]})
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as comment
MERGE (c:Comment {id: comment._id["$oid"]})
ON CREATE SET
comment.content = json.content,
comment.contentExcerpt = json.contentExcerpt,
comment.deleted = json.deleted,
comment.disabled = false
WITH comment, json, json.contributionId as postId
c.content = comment.content,
c.contentExcerpt = comment.contentExcerpt,
c.deleted = comment.deleted,
c.disabled = false
WITH c, comment, comment.contributionId as postId
MATCH (post:Post {id: postId})
WITH comment, post, json.userId as userId
WITH c, post, comment.userId as userId
MATCH (author:User {id: userId})
MERGE (comment)-[:COMMENTS]->(post)
MERGE (author)-[:WROTE]->(comment)
MERGE (c)-[:COMMENTS]->(post)
MERGE (author)-[:WROTE]->(c)
;

View File

@ -0,0 +1 @@
MATCH (n:Comment) DETACH DELETE n;

View File

@ -1,4 +1,132 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as post
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
[?] { //Modeled incorrect as Post
[?] userId: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[ ] organizationId: {
[ ] type: String,
[-] index: true
},
[X] categoryIds: {
[X] type: Array,
[-] index: true
},
[X] title: {
[X] type: String,
[X] required: true
},
[?] slug: { // Generated from title
[X] type: String,
[ ] required: true, // Not required in Nitro
[?] unique: true, // Unique value is not enforced in Nitro?
[-] index: true
},
[ ] type: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] cando: {
[ ] difficulty: {
[ ] type: String,
[ ] enum: ['easy', 'medium', 'hard']
},
[ ] reasonTitle: { type: String },
[ ] reason: { type: String }
},
[X] content: {
[X] type: String,
[X] required: true
},
[?] contentExcerpt: { // Generated from content
[X] type: String,
[?] required: true // Not required in Nitro
},
[ ] hasMore: { type: Boolean },
[?] teaserImg: { type: String }, // Path is incorrect in Nitro
[ ] language: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] shoutCount: {
[ ] type: Number,
[ ] default: 0,
[-] index: true
},
[ ] meta: {
[ ] hasVideo: {
[ ] type: Boolean,
[ ] default: false
},
[ ] embedds: {
[ ] type: Object,
[ ] default: {}
}
},
[?] visibility: {
[X] type: String,
[X] enum: ['public', 'friends', 'private'],
[ ] default: 'public', // Default value is missing in Nitro
[-] index: true
},
[?] isEnabled: {
[X] type: Boolean,
[ ] default: true, // Default value is missing in Nitro
[-] index: true
},
[?] tags: { type: Array }, // ensure this is working properly
[ ] emotions: {
[ ] type: Object,
[-] index: true,
[ ] default: {
[ ] angry: {
[ ] count: 0,
[ ] percent: 0
[ ] },
[ ] cry: {
[ ] count: 0,
[ ] percent: 0
[ ] },
[ ] surprised: {
[ ] count: 0,
[ ] percent: 0
},
[ ] happy: {
[ ] count: 0,
[ ] percent: 0
},
[ ] funny: {
[ ] count: 0,
[ ] percent: 0
}
}
},
[?] deleted: {
[X] type: Boolean,
[ ] default: false, // Default value is missing in Nitro
[-] index: true
},
[?] createdAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
},
[?] updatedAt: {
[?] type: Date, // Type is modeled as string in Nitro which is incorrect
[ ] default: Date.now // Default value is missing in Nitro
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as post
MERGE (p:Post {id: post._id["$oid"]})
ON CREATE SET
p.title = post.title,

View File

@ -0,0 +1,2 @@
MATCH (n:Post) DETACH DELETE n;
MATCH (n:Tag) DETACH DELETE n;

View File

@ -0,0 +1,35 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] userId: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] contributionId: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] rated: {
[ ] type: String,
[ ] required: true,
[ ] enum: ['funny', 'happy', 'surprised', 'cry', 'angry']
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as emotion;

View File

@ -1,4 +1,36 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as follow
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[?] userId: {
[-] type: String,
[ ] required: true,
[-] index: true
},
[?] foreignId: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[?] foreignService: { // db.getCollection('follows').distinct('foreignService') returns 'organizations' and 'users'
[ ] type: String,
[ ] required: true,
[ ] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
index:
[?] { userId: 1, foreignId: 1, foreignService: 1 },{ unique: true } // is the unique constrain modeled?
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as follow
MATCH (u1:User {id: follow.userId}), (u2:User {id: follow.foreignId})
MERGE (u1)-[:FOLLOWS]->(u2)
;

View File

@ -0,0 +1 @@
// this is just a relation between users(?) - no need to delete

View File

@ -6,14 +6,44 @@ set -o allexport
source $(dirname "$0")/.env
set +o allexport
# Delete collection function defintion
function delete_collection () {
# Delete from Database
echo "Delete $1"
"${IMPORT_CYPHERSHELL_BIN}" < $(dirname "$0")/$1_delete.cql > /dev/null
# Delete index file
rm -f "${IMPORT_PATH}splits/$1.index"
}
# Import collection function defintion
function import_collection () {
# index file of those chunks we have already imported
INDEX_FILE="${IMPORT_PATH}splits/$1.index"
# load index file
if [ -f "$INDEX_FILE" ]; then
readarray -t IMPORT_INDEX <$INDEX_FILE
else
declare -a IMPORT_INDEX
fi
# for each chunk import data
for chunk in ${IMPORT_PATH}splits/$1/*
do
mv $chunk ${IMPORT_CHUNK_PATH}
NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL}' < $(dirname "$0")/$1.cql)"
echo "Import ${chunk}"
echo "${NEO4J_COMMAND}" | "${IMPORT_CYPHERSHELL_BIN}" -u ${NEO4J_USERNAME} -p ${NEO4J_PASSWORD}
CHUNK_FILE_NAME=$(basename "${chunk}")
# does the index not contain the chunk file name?
if [[ ! " ${IMPORT_INDEX[@]} " =~ " ${CHUNK_FILE_NAME} " ]]; then
# calculate the path of the chunk
export IMPORT_CHUNK_PATH_CQL_FILE="${IMPORT_CHUNK_PATH_CQL}$1/${CHUNK_FILE_NAME}"
# load the neo4j command and replace file variable with actual path
NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL_FILE}' < $(dirname "$0")/$1.cql)"
# run the import of the chunk
echo "Import $1 ${CHUNK_FILE_NAME} (${chunk})"
echo "${NEO4J_COMMAND}" | "${IMPORT_CYPHERSHELL_BIN}" > /dev/null
# add file to array and file
IMPORT_INDEX+=("${CHUNK_FILE_NAME}")
echo "${CHUNK_FILE_NAME}" >> ${INDEX_FILE}
else
echo "Skipping $1 ${CHUNK_FILE_NAME} (${chunk})"
fi
done
}
@ -22,7 +52,26 @@ SECONDS=0
# Delete all Neo4J Database content
echo "Deleting Database Contents"
echo "MATCH (n) DETACH DELETE n;" | "${IMPORT_CYPHERSHELL_BIN}" -u ${NEO4J_USERNAME} -p ${NEO4J_PASSWORD}
delete_collection "badges"
delete_collection "categories"
delete_collection "users"
delete_collection "follows"
delete_collection "contributions"
delete_collection "shouts"
delete_collection "comments"
#delete_collection "emotions"
#delete_collection "invites"
#delete_collection "notifications"
#delete_collection "organizations"
#delete_collection "pages"
#delete_collection "projects"
#delete_collection "settings"
#delete_collection "status"
#delete_collection "systemnotifications"
#delete_collection "userscandos"
#delete_collection "usersettings"
echo "DONE"
# Import Data
echo "Start Importing Data"
@ -33,16 +82,19 @@ import_collection "follows"
import_collection "contributions"
import_collection "shouts"
import_collection "comments"
#import_collection "emotions"
#import_collection "invites"
#import_collection "notifications"
#import_collection "organizations"
#import_collection "pages"
#import_collection "projects"
#import_collection "settings"
#import_collection "status"
#import_collection "systemnotifications"
#import_collection "userscandos"
#import_collection "usersettings"
import_collection "emotions"
import_collection "invites"
import_collection "notifications"
import_collection "organizations"
import_collection "pages"
import_collection "projects"
import_collection "settings"
import_collection "status"
import_collection "systemnotifications"
import_collection "userscandos"
import_collection "usersettings"
echo "DONE"
echo "Time elapsed: $SECONDS seconds"

View File

@ -0,0 +1,39 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] email: {
[ ] type: String,
[ ] required: true,
[-] index: true,
[ ] unique: true
},
[ ] code: {
[ ] type: String,
[-] index: true,
[ ] required: true
},
[ ] role: {
[ ] type: String,
[ ] enum: ['admin', 'moderator', 'manager', 'editor', 'user'],
[ ] default: 'user'
},
[ ] invitedByUserId: { type: String },
[ ] language: { type: String },
[ ] badgeIds: [],
[ ] wasUsed: {
[ ] type: Boolean,
[-] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as invite;

View File

@ -0,0 +1,48 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] userId: { // User this notification is sent to
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] type: {
[ ] type: String,
[ ] required: true,
[ ] enum: ['comment','comment-mention','contribution-mention','following-contribution']
},
[ ] relatedUserId: {
[ ] type: String,
[-] index: true
},
[ ] relatedContributionId: {
[ ] type: String,
[-] index: true
},
[ ] relatedOrganizationId: {
[ ] type: String,
[-] index: true
},
[ ] relatedCommentId: {type: String },
[ ] unseen: {
[ ] type: Boolean,
[ ] default: true,
[-] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as notification;

View File

@ -0,0 +1,137 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] name: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] slug: {
[ ] type: String,
[ ] required: true,
[ ] unique: true,
[-] index: true
},
[ ] followersCounts: {
[ ] users: {
[ ] type: Number,
[ ] default: 0
},
[ ] organizations: {
[ ] type: Number,
[ ] default: 0
},
[ ] projects: {
[ ] type: Number,
[ ] default: 0
}
},
[ ] followingCounts: {
[ ] users: {
[ ] type: Number,
[ ] default: 0
},
[ ] organizations: {
[ ] type: Number,
[ ] default: 0
},
[ ] projects: {
[ ] type: Number,
[ ] default: 0
}
},
[ ] categoryIds: {
[ ] type: Array,
[ ] required: true,
[-] index: true
},
[ ] logo: { type: String },
[ ] coverImg: { type: String },
[ ] userId: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] description: {
[ ] type: String,
[ ] required: true
},
[ ] descriptionExcerpt: { type: String }, // will be generated automatically
[ ] publicEmail: { type: String },
[ ] url: { type: String },
[ ] type: {
[ ] type: String,
[-] index: true,
[ ] enum: ['ngo', 'npo', 'goodpurpose', 'ev', 'eva']
},
[ ] language: {
[ ] type: String,
[ ] required: true,
[ ] default: 'de',
[-] index: true
},
[ ] addresses: {
[ ] type: [{
[ ] street: {
[ ] type: String,
[ ] required: true
},
[ ] zipCode: {
[ ] type: String,
[ ] required: true
},
[ ] city: {
[ ] type: String,
[ ] required: true
},
[ ] country: {
[ ] type: String,
[ ] required: true
},
[ ] lat: {
[ ] type: Number,
[ ] required: true
},
[ ] lng: {
[ ] type: Number,
[ ] required: true
}
}],
[ ] default: []
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] isEnabled: {
[ ] type: Boolean,
[ ] default: false,
[-] index: true
},
[ ] reviewedBy: {
[ ] type: String,
[ ] default: null,
[-] index: true
},
[ ] tags: {
[ ] type: Array,
[-] index: true
},
[ ] deleted: {
[ ] type: Boolean,
[ ] default: false,
[-] index: true
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as organisation;

View File

@ -0,0 +1,55 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] title: {
[ ] type: String,
[ ] required: true
},
[ ] slug: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] type: {
[ ] type: String,
[ ] required: true,
[ ] default: 'page'
},
[ ] key: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] content: {
[ ] type: String,
[ ] required: true
},
[ ] language: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] active: {
[ ] type: Boolean,
[ ] default: true,
[-] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
index:
[ ] { slug: 1, language: 1 },{ unique: true }
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as page;

View File

@ -0,0 +1,44 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] name: {
[ ] type: String,
[ ] required: true
},
[ ] slug: { type: String },
[ ] followerIds: [],
[ ] categoryIds: { type: Array },
[ ] logo: { type: String },
[ ] userId: {
[ ] type: String,
[ ] required: true
},
[ ] description: {
[ ] type: String,
[ ] required: true
},
[ ] content: {
[ ] type: String,
[ ] required: true
},
[ ] addresses: {
[ ] type: Array,
[ ] default: []
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as project;

View File

@ -0,0 +1,36 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] key: {
[ ] type: String,
[ ] default: 'system',
[-] index: true,
[ ] unique: true
},
[ ] invites: {
[ ] userCanInvite: {
[ ] type: Boolean,
[ ] required: true,
[ ] default: false
},
[ ] maxInvitesByUser: {
[ ] type: Number,
[ ] required: true,
[ ] default: 1
},
[ ] onlyUserWithBadgesCanInvite: {
[ ] type: Array,
[ ] default: []
}
},
[ ] maintenance: false
}, {
[ ] timestamps: true
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as setting;

View File

@ -1,4 +1,36 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as shout
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[?] userId: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[?] foreignId: {
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[?] foreignService: { // db.getCollection('shots').distinct('foreignService') returns 'contributions'
[X] type: String,
[ ] required: true, // Not required in Nitro
[-] index: true
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
index:
[?] { userId: 1, foreignId: 1 },{ unique: true } // is the unique constrain modeled?
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as shout
MATCH (u:User {id: shout.userId}), (p:Post {id: shout.foreignId})
MERGE (u)-[:SHOUTED]->(p)
;

View File

@ -0,0 +1 @@
// this is just a relation between users and contributions - no need to delete

View File

@ -0,0 +1,19 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] maintenance: {
[ ] type: Boolean,
[ ] default: false
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
}
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as status;

View File

@ -0,0 +1,61 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] type: {
[ ] type: String,
[ ] default: 'info',
[ ] required: true,
[-] index: true
},
[ ] title: {
[ ] type: String,
[ ] required: true
},
[ ] content: {
[ ] type: String,
[ ] required: true
},
[ ] slot: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] language: {
[ ] type: String,
[ ] required: true,
[-] index: true
},
[ ] permanent: {
[ ] type: Boolean,
[ ] default: false
},
[ ] requireConfirmation: {
[ ] type: Boolean,
[ ] default: false
},
[ ] active: {
[ ] type: Boolean,
[ ] default: true,
[-] index: true
},
[ ] totalCount: {
[ ] type: Number,
[ ] default: 0
},
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as systemnotification;

View File

@ -1,4 +1,101 @@
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL}") YIELD value as user
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[?] email: {
[X] type: String,
[-] index: true,
[X] required: true,
[?] unique: true //unique constrain missing in Nitro
},
[?] password: { // Not required in Alpha -> verify if always present
[X] type: String
},
[X] name: { type: String },
[X] slug: {
[X] type: String,
[-] index: true
},
[ ] gender: { type: String },
[ ] followersCounts: {
[ ] users: {
[ ] type: Number,
[ ] default: 0
},
[ ] organizations: {
[ ] type: Number,
[ ] default: 0
},
[ ] projects: {
[ ] type: Number,
[ ] default: 0
}
},
[ ] followingCounts: {
[ ] users: {
[ ] type: Number,
[ ] default: 0
},
[ ] organizations: {
[ ] type: Number,
[ ] default: 0
},
[ ] projects: {
[ ] type: Number,
[ ] default: 0
}
},
[ ] timezone: { type: String },
[?] avatar: { type: String }, // Path is incorrect in Nitro
[?] coverImg: { type: String }, // Path is incorrect in Nitro, was not modeled in latest Nitro - do we want this?
[ ] doiToken: { type: String },
[ ] confirmedAt: { type: Date },
[?] badgeIds: [], // Verify this is working properly
[?] deletedAt: { type: Date }, // The Date of deletion is not saved in Nitro
[?] createdAt: {
[?] type: Date, // Modeled as String in Nitro
[ ] default: Date.now // Default value is missing in Nitro
},
[?] updatedAt: {
[?] type: Date, // Modeled as String in Nitro
[ ] default: Date.now // Default value is missing in Nitro
},
[ ] lastActiveAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] isVerified: { type: Boolean },
[?] role: {
[X] type: String,
[-] index: true,
[?] enum: ['admin', 'moderator', 'manager', 'editor', 'user'], // missing roles manager & editor in Nitro
[ ] default: 'user' // Default value is missing in Nitro
},
[ ] verifyToken: { type: String },
[ ] verifyShortToken: { type: String },
[ ] verifyExpires: { type: Date },
[ ] verifyChanges: { type: Object },
[ ] resetToken: { type: String },
[ ] resetShortToken: { type: String },
[ ] resetExpires: { type: Date },
[X] wasSeeded: { type: Boolean },
[X] wasInvited: { type: Boolean },
[ ] language: {
[ ] type: String,
[ ] default: 'en'
},
[ ] termsAndConditionsAccepted: { type: Date }, // we display the terms and conditions on registration
[ ] systemNotificationsSeen: {
[ ] type: Array,
[ ] default: []
}
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as user
MERGE(u:User {id: user._id["$oid"]})
ON CREATE SET
u.name = user.name,
@ -8,6 +105,7 @@ u.password = user.password,
u.avatar = user.avatar,
u.coverImg = user.coverImg,
u.wasInvited = user.wasInvited,
u.wasSeeded = user.wasSeeded,
u.role = toLower(user.role),
u.createdAt = user.createdAt.`$date`,
u.updatedAt = user.updatedAt.`$date`,

View File

@ -0,0 +1 @@
MATCH (n:User) DETACH DELETE n;

View File

@ -0,0 +1,35 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] userId: {
[ ] type: String,
[ ] required: true
},
[ ] contributionId: {
[ ] type: String,
[ ] required: true
},
[ ] done: {
[ ] type: Boolean,
[ ] default: false
},
[ ] doneAt: { type: Date },
[ ] createdAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
},
[ ] wasSeeded: { type: Boolean }
}
index:
[ ] { userId: 1, contributionId: 1 },{ unique: true }
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as usercando;

View File

@ -0,0 +1,43 @@
/*
// Alpha Model
// [ ] Not modeled in Nitro
// [X] Modeled in Nitro
// [-] Omitted in Nitro
// [?] Unclear / has work to be done for Nitro
{
[ ] userId: {
[ ] type: String,
[ ] required: true,
[ ] unique: true
},
[ ] blacklist: {
[ ] type: Array,
[ ] default: []
},
[ ] uiLanguage: {
[ ] type: String,
[ ] required: true
},
[ ] contentLanguages: {
[ ] type: Array,
[ ] default: []
},
[ ] filter: {
[ ] categoryIds: {
[ ] type: Array,
[ ] index: true
},
[ ] emotions: {
[ ] type: Array,
[ ] index: true
}
},
[ ] hideUsersWithoutTermsOfUseSigniture: {type: Boolean},
[ ] updatedAt: {
[ ] type: Date,
[ ] default: Date.now
}
}
*/
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as usersetting;

View File

@ -9,4 +9,4 @@
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storage: 25Gi

View File

@ -10,7 +10,7 @@ module.exports = {
},
extends: [
'standard',
'plugin:vue/recommended',
'plugin:vue/essential',
'plugin:prettier/recommended'
],
// required to lint *.vue files
@ -25,7 +25,9 @@ module.exports = {
'no-console': ['error'],
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/component-name-in-template-casing': ['error', 'kebab-case'],
'prettier/prettier': ['error'],
'prettier/prettier': ['error', {
htmlWhitespaceSensitivity: 'ignore'
}],
// 'newline-per-chained-call': [2]
}
}

View File

@ -1,4 +1,4 @@
FROM node:10-alpine as base
FROM node:12.3.1-alpine as base
LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)"
EXPOSE 3000
@ -18,7 +18,7 @@ COPY . .
FROM base as build-and-test
RUN cp .env.template .env
RUN yarn install --production=false --frozen-lockfile --non-interactive
RUN yarn install --ignore-engines --production=false --frozen-lockfile --non-interactive
RUN yarn run build
FROM base as production

View File

@ -1,10 +1,5 @@
<template>
<ds-avatar
:image="avatarUrl"
:name="userName"
class="avatar"
:size="size"
/>
<ds-avatar :image="avatarUrl" :name="userName" class="avatar" :size="size" />
</template>
<script>

View File

@ -1,20 +1,7 @@
<template>
<div
:class="[
(badges.length === 2) && 'hc-badges-dual'
]"
class="hc-badges"
>
<div
v-for="badge in badges"
:key="badge.key"
class="hc-badge-container"
>
<hc-image
:title="badge.key"
:image-props="{ src: badge.icon}"
class="hc-badge"
/>
<div :class="[badges.length === 2 && 'hc-badges-dual']" class="hc-badges">
<div v-for="badge in badges" :key="badge.key" class="hc-badge-container">
<hc-image :title="badge.key" :image-props="{ src: badge.icon }" class="hc-badge" />
</div>
</div>
</template>

View File

@ -1,9 +1,6 @@
<template>
<ds-tag>
<ds-icon
size="large"
:name="icon"
/>
<ds-icon size="large" :name="icon" />
{{ name }}
</ds-tag>
</template>

View File

@ -1,17 +1,11 @@
<template>
<div v-if="(comment.deleted || comment.disabled) && !isModerator">
<ds-text
style="padding-left: 40px; font-weight: bold;"
color="soft"
>
<ds-text style="padding-left: 40px; font-weight: bold;" color="soft">
<ds-icon name="ban" />
{{ this.$t('comment.content.unavailable-placeholder') }}
</ds-text>
</div>
<div
v-else
:class="{'comment': true, 'disabled-content': (comment.deleted || comment.disabled)}"
>
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
<ds-space margin-bottom="x-small">
<hc-user :user="author" />
</ds-space>
@ -27,10 +21,7 @@
<!-- eslint-disable vue/no-v-html -->
<!-- TODO: replace editor content with tiptap render view -->
<ds-space margin-bottom="small" />
<div
style="padding-left: 40px;"
v-html="comment.contentExcerpt"
/>
<div style="padding-left: 40px;" v-html="comment.contentExcerpt" />
<!-- eslint-enable vue/no-v-html -->
</div>
</template>

View File

@ -1,32 +1,13 @@
<template>
<dropdown
class="content-menu"
:placement="placement"
offset="5"
>
<template
slot="default"
slot-scope="{toggleMenu}"
>
<slot
name="button"
:toggleMenu="toggleMenu"
>
<ds-button
class="content-menu-trigger"
size="small"
ghost
@click.prevent="toggleMenu"
>
<dropdown class="content-menu" :placement="placement" offset="5">
<template slot="default" slot-scope="{ toggleMenu }">
<slot name="button" :toggleMenu="toggleMenu">
<ds-button class="content-menu-trigger" size="small" ghost @click.prevent="toggleMenu">
<ds-icon name="ellipsis-v" />
</ds-button>
</slot>
</template>
<div
slot="popover"
slot-scope="{toggleMenu}"
class="content-menu-popover"
>
<div slot="popover" slot-scope="{ toggleMenu }" class="content-menu-popover">
<ds-menu :routes="routes">
<ds-menu-item
slot="menuitem"

View File

@ -1,35 +1,13 @@
<template>
<ds-form
ref="contributionForm"
v-model="form"
:schema="formSchema"
@submit="submit"
>
<ds-form ref="contributionForm" v-model="form" :schema="formSchema" @submit="submit">
<template slot-scope="{ errors }">
<ds-card>
<ds-input
model="title"
class="post-title"
placeholder="Title"
name="title"
autofocus
/>
<ds-input model="title" class="post-title" placeholder="Title" name="title" autofocus />
<no-ssr>
<hc-editor
:users="users"
:value="form.content"
@input="updateEditorContent"
/>
<hc-editor :users="users" :value="form.content" @input="updateEditorContent" />
</no-ssr>
<div
slot="footer"
style="text-align: right"
>
<ds-button
:disabled="loading || disabled"
ghost
@click.prevent="$router.back()"
>
<div slot="footer" style="text-align: right">
<ds-button :disabled="loading || disabled" ghost @click.prevent="$router.back()">
{{ $t('actions.cancel') }}
</ds-button>
<ds-button

View File

@ -1,9 +1,6 @@
<template>
<span>
<no-ssr
placeholder="0"
tag="span"
>
<no-ssr placeholder="0" tag="span">
<count-to
:start-val="lastEndVal || startVal"
:end-val="endVal"

View File

@ -7,17 +7,8 @@
trigger="manual"
:offset="offset"
>
<slot
:toggleMenu="toggleMenu"
:openMenu="openMenu"
:closeMenu="closeMenu"
:isOpen="isOpen"
/>
<div
slot="popover"
@mouseover="popoverMouseEnter"
@mouseleave="popoveMouseLeave"
>
<slot :toggleMenu="toggleMenu" :openMenu="openMenu" :closeMenu="closeMenu" :isOpen="isOpen" />
<div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoveMouseLeave">
<slot
name="popover"
:toggleMenu="toggleMenu"

View File

@ -1,10 +1,6 @@
<template>
<div class="editor">
<div
v-show="showSuggestions"
ref="suggestions"
class="suggestion-list"
>
<div v-show="showSuggestions" ref="suggestions" class="suggestion-list">
<template v-if="hasResults">
<div
v-for="(user, index) in filteredUsers"
@ -16,10 +12,7 @@
@{{ user.slug }}
</div>
</template>
<div
v-else
class="suggestion-list__item is-empty"
>
<div v-else class="suggestion-list__item is-empty">
No users found
</div>
</div>
@ -154,10 +147,7 @@
</ds-button>
</div>
</editor-floating-menu>
<editor-content
ref="editor"
:editor="editor"
/>
<editor-content ref="editor" :editor="editor" />
</div>
</template>

View File

@ -1,9 +1,5 @@
<template>
<ds-space
class="hc-empty"
centered
:margin="margin"
>
<ds-space class="hc-empty" centered :margin="margin">
<ds-text>
<img
:src="iconPath"
@ -11,12 +7,9 @@
class="hc-empty-icon"
style="margin-bottom: 5px"
alt="Empty"
><br>
<ds-text
v-show="message"
class="hc-empty-message"
color="softer"
>
/>
<br />
<ds-text v-show="message" class="hc-empty-message" color="softer">
{{ message }}
</ds-text>
</ds-text>

View File

@ -1,8 +1,5 @@
<template>
<img
v-bind="imageProps"
:src="imageSrc"
>
<img v-bind="imageProps" :src="imageSrc" />
</template>
<script>

View File

@ -1,14 +1,6 @@
<template>
<ds-space
margin-top="large"
style="text-align: center"
>
<ds-button
:loading="loading"
icon="arrow-down"
ghost
@click="$emit('click')"
>
<ds-space margin-top="large" style="text-align: center">
<ds-button :loading="loading" icon="arrow-down" ghost @click="$emit('click')">
{{ $t('actions.loadMore') }}
</ds-button>
</ds-space>

View File

@ -1,30 +1,19 @@
<template>
<dropdown
ref="menu"
:placement="placement"
:offset="offset"
>
<dropdown ref="menu" :placement="placement" :offset="offset">
<a
slot="default"
slot-scope="{toggleMenu}"
slot-scope="{ toggleMenu }"
class="locale-menu"
href="#"
@click.prevent="toggleMenu()"
>
<ds-icon
style="margin-right: 2px;"
name="globe"
/>
<ds-icon style="margin-right: 2px;" name="globe" />
{{ current.code.toUpperCase() }}
<ds-icon
style="margin-left: 2px"
size="xx-small"
name="angle-down"
/>
<ds-icon style="margin-left: 2px" size="xx-small" name="angle-down" />
</a>
<ds-menu
slot="popover"
slot-scope="{toggleMenu}"
slot-scope="{ toggleMenu }"
class="locale-menu-popover"
:matcher="matcher"
:routes="routes"

View File

@ -1,15 +1,7 @@
<template>
<ds-modal
:title="title"
:is-open="isOpen"
@cancel="cancel"
>
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
<transition name="ds-transition-fade">
<ds-flex
v-if="success"
class="hc-modal-success"
centered
>
<ds-flex v-if="success" class="hc-modal-success" centered>
<sweetalert-icon icon="success" />
</ds-flex>
</transition>
@ -17,24 +9,12 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="message" />
<template
slot="footer"
>
<ds-button
class="cancel"
icon="close"
@click="cancel"
>
<template slot="footer">
<ds-button class="cancel" icon="close" @click="cancel">
{{ $t('post.delete.cancel') }}
</ds-button>
<ds-button
danger
class="confirm"
icon="trash"
:loading="loading"
@click="confirm"
>
<ds-button danger class="confirm" icon="trash" :loading="loading" @click="confirm">
{{ $t('post.delete.submit') }}
</ds-button>
</template>

View File

@ -1,26 +1,14 @@
<template>
<ds-modal
:title="title"
:is-open="isOpen"
@cancel="cancel"
>
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="message" />
<template slot="footer">
<ds-button
class="cancel"
@click="cancel"
>
<ds-button class="cancel" @click="cancel">
{{ $t('disable.cancel') }}
</ds-button>
<ds-button
danger
class="confirm"
icon="exclamation-circle"
@click="confirm"
>
<ds-button danger class="confirm" icon="exclamation-circle" @click="confirm">
{{ $t('disable.submit') }}
</ds-button>
</template>

View File

@ -1,15 +1,7 @@
<template>
<ds-modal
:title="title"
:is-open="isOpen"
@cancel="cancel"
>
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
<transition name="ds-transition-fade">
<ds-flex
v-if="success"
class="hc-modal-success"
centered
>
<ds-flex v-if="success" class="hc-modal-success" centered>
<sweetalert-icon icon="success" />
</ds-flex>
</transition>
@ -17,14 +9,8 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<p v-html="message" />
<template
slot="footer"
>
<ds-button
class="cancel"
icon="close"
@click="cancel"
>
<template slot="footer">
<ds-button class="cancel" icon="close" @click="cancel">
{{ $t('report.cancel') }}
</ds-button>

View File

@ -27,11 +27,7 @@
/>
<password-strength :password="formData.newPassword" />
<ds-space margin-top="base">
<ds-button
:loading="loading"
:disabled="disabled"
primary
>
<ds-button :loading="loading" :disabled="disabled" primary>
{{ $t('settings.security.change-password.button') }}
</ds-button>
</ds-space>

View File

@ -1,16 +1,10 @@
<template>
<div class="field">
<div class="password-strength-meter">
<div
class="password-strength-meter-inner"
:class="'strength-' + strength"
/>
<div class="password-strength-meter-inner" :class="'strength-' + strength" />
</div>
<p class="help">
<span
v-if="pass"
:class="{ insecure: !isSecure }"
>
<span v-if="pass" :class="{ insecure: !isSecure }">
{{ $t('settings.security.change-password.passwordSecurity') }}:
<strong>{{ $t(`settings.security.change-password.passwordStrength${strength}`) }}</strong>
</span>

View File

@ -1,8 +1,5 @@
<template>
<ds-card
:image="post.image"
:class="{'post-card': true, 'disabled-content': post.disabled}"
>
<ds-card :image="post.image" :class="{ 'post-card': true, 'disabled-content': post.disabled }">
<!-- Post Link Target -->
<nuxt-link
class="post-link"
@ -14,30 +11,20 @@
<!-- Username, Image & Date of Post -->
<div>
<no-ssr>
<hc-user
:user="post.author"
:trunc="35"
:date-time="post.createdAt"
/>
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
</no-ssr>
<hc-ribbon :text="$t('post.name')" />
</div>
<ds-space margin-bottom="small" />
<!-- Post Title -->
<ds-heading
tag="h3"
no-margin
>
<ds-heading tag="h3" no-margin>
{{ post.title }}
</ds-heading>
<ds-space margin-bottom="small" />
<!-- Post Content Excerpt -->
<!-- eslint-disable vue/no-v-html -->
<!-- TODO: replace editor content with tiptap render view -->
<div
class="hc-editor-content"
v-html="excerpt"
/>
<div class="hc-editor-content" v-html="excerpt" />
<!-- eslint-enable vue/no-v-html -->
<!-- Footer o the Post -->
<template slot="footer">
@ -46,29 +33,25 @@
<hc-category
v-for="category in post.categories"
:key="category.id"
v-tooltip="{content: category.name, placement: 'bottom-start', delay: { show: 500 }}"
v-tooltip="{ content: category.name, placement: 'bottom-start', delay: { show: 500 } }"
:icon="category.icon"
/>
</div>
<no-ssr>
<div style="display: inline-block; float: right">
<!-- Shouts Count -->
<span :style="{ opacity: post.shoutedCount ? 1 : .5 }">
<span :style="{ opacity: post.shoutedCount ? 1 : 0.5 }">
<ds-icon name="bullhorn" />
<small>{{ post.shoutedCount }}</small>
</span>
&nbsp;
<!-- Comments Count -->
<span :style="{ opacity: post.commentsCount ? 1 : .5 }">
<span :style="{ opacity: post.commentsCount ? 1 : 0.5 }">
<ds-icon name="comments" />
<small>{{ post.commentsCount }}</small>
</span>
<!-- Menu -->
<content-menu
resource-type="contribution"
:resource="post"
:is-owner="isAuthor"
/>
<content-menu resource-type="contribution" :resource="post" :is-owner="isAuthor" />
</div>
</no-ssr>
</template>

View File

@ -5,16 +5,12 @@
role="search"
:class="{
'is-active': isActive,
'is-open': isOpen
'is-open': isOpen,
}"
>
<div class="field">
<div class="control">
<a
v-if="isActive"
class="search-clear-btn"
@click="clear"
>
<a v-if="isActive" class="search-clear-btn" @click="clear">
&nbsp;
</a>
<ds-select
@ -42,42 +38,31 @@
@input.native="handleInput"
@click.capture.native="isOpen = true"
>
<template
slot="option"
slot-scope="{option}"
>
<template slot="option" slot-scope="{ option }">
<ds-flex>
<ds-flex-item class="search-option-label">
<ds-text>
{{ option.label | truncate(70) }}
</ds-text>
</ds-flex-item>
<ds-flex-item
class="search-option-meta"
width="280px"
>
<ds-flex-item class="search-option-meta" width="280px">
<ds-flex>
<ds-flex-item>
<ds-text
size="small"
color="softer"
class="search-meta"
>
<ds-text size="small" color="softer" class="search-meta">
<span style="text-align: right;">
<b>{{ option.commentsCount }}</b> <ds-icon name="comments" />
<b>{{ option.commentsCount }}</b>
<ds-icon name="comments" />
</span>
<span style="width: 36px; display: inline-block; text-align: right;">
<b>{{ option.shoutedCount }}</b> <ds-icon name="bullhorn" />
<b>{{ option.shoutedCount }}</b>
<ds-icon name="bullhorn" />
</span>
</ds-text>
</ds-flex-item>
<ds-flex-item>
<ds-text
size="small"
color="softer"
align="right"
>
{{ option.author.name | truncate(32) }} - {{ option.createdAt | dateTime('dd.MM.yyyy') }}
<ds-text size="small" color="softer" align="right">
{{ option.author.name | truncate(32) }} -
{{ option.createdAt | dateTime('dd.MM.yyyy') }}
</ds-text>
</ds-flex-item>
</ds-flex>

View File

@ -1,8 +1,5 @@
<template>
<ds-space
margin="large"
style="text-align: center"
>
<ds-space margin="large" style="text-align: center">
<ds-button
:loading="loading"
:disabled="disabled"
@ -13,16 +10,9 @@
@click="toggle"
/>
<ds-space margin-bottom="xx-small" />
<ds-text
color="soft"
class="shout-button-text"
>
<ds-heading
style="display: inline"
tag="h3"
>
{{ shoutedCount }}x
</ds-heading> {{ $t('shoutButton.shouted') }}
<ds-text color="soft" class="shout-button-text">
<ds-heading style="display: inline" tag="h3">{{ shoutedCount }}x</ds-heading>
{{ $t('shoutButton.shouted') }}
</ds-text>
</ds-space>
</template>

View File

@ -36,7 +36,9 @@ export default {
},
computed: {
backgroundImage() {
const { avatar } = this.user || {}
const avatar =
this.user.avatar ||
'https://human-connection.org/wp-content/uploads/2019/03/human-connection-logo.svg'
const userAvatar = avatar.startsWith('/') ? avatar.replace('/', '/api/') : avatar
return {
backgroundImage: `url(${userAvatar})`,

View File

@ -6,53 +6,24 @@
<hc-avatar />
</div>
<div style="display: inline-block; height: 100%; vertical-align: middle;">
<b
class="username"
style="vertical-align: middle;"
>{{ $t('profile.userAnonym') }}</b>
<b class="username" style="vertical-align: middle;">{{ $t('profile.userAnonym') }}</b>
</div>
</div>
<dropdown
v-else
:class="{'disabled-content': user.disabled}"
placement="top-start"
offset="0"
>
<template
slot="default"
slot-scope="{openMenu, closeMenu, isOpen}"
>
<nuxt-link
:to="userLink"
:class="['user', isOpen && 'active']"
>
<div
@mouseover="openMenu(true)"
@mouseleave="closeMenu(true)"
>
<dropdown v-else :class="{ 'disabled-content': user.disabled }" placement="top-start" offset="0">
<template slot="default" slot-scope="{ openMenu, closeMenu, isOpen }">
<nuxt-link :to="userLink" :class="['user', isOpen && 'active']">
<div @mouseover="openMenu(true)" @mouseleave="closeMenu(true)">
<div
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
>
<hc-avatar
:user="user"
/>
<hc-avatar :user="user" />
</div>
<div style="display: inline-block; height: 100%; vertical-align: middle;">
<b
class="username"
style="vertical-align: middle;"
>{{ userName | truncate(18) }}</b>
<b class="username" style="vertical-align: middle;">{{ userName | truncate(18) }}</b>
</div>
<!-- Time -->
<div
v-if="dateTime"
style="display: inline;"
>
<ds-text
align="left"
size="small"
color="soft"
>
<div v-if="dateTime" style="display: inline;">
<ds-text align="left" size="small" color="soft">
<ds-icon name="clock" />
<no-ssr>
<hc-relative-date-time :date-time="dateTime" />
@ -64,10 +35,7 @@
</template>
<template slot="popover">
<div style="min-width: 250px">
<hc-badges
v-if="user.badges && user.badges.length"
:badges="user.badges"
/>
<hc-badges v-if="user.badges && user.badges.length" :badges="user.badges" />
<ds-text
v-if="user.location"
align="center"
@ -82,11 +50,7 @@
<ds-flex style="margin-top: -10px">
<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">
<ds-number
:count="fanCount"
:label="$t('profile.followers')"
size="x-large"
/>
<ds-number :count="fanCount" :label="$t('profile.followers')" size="x-large" />
</ds-space>
</ds-flex-item>
<ds-flex-item class="ds-tab-nav-item ds-tab-nav-item-active">
@ -106,20 +70,16 @@
</ds-space>
</ds-flex-item>
</ds-flex>
<ds-flex
v-if="!itsMe"
gutter="x-small"
style="margin-bottom: 0;"
>
<ds-flex-item :width="{base: 3}">
<ds-flex v-if="!itsMe" gutter="x-small" style="margin-bottom: 0;">
<ds-flex-item :width="{ base: 3 }">
<hc-follow-button
:follow-id="user.id"
:is-followed="user.followedByCurrentUser"
@optimistic="follow => user.followedByCurrentUser = follow"
@update="follow => user.followedByCurrentUser = follow"
@optimistic="follow => (user.followedByCurrentUser = follow)"
@update="follow => (user.followedByCurrentUser = follow)"
/>
</ds-flex-item>
<ds-flex-item :width="{base: 1}">
<ds-flex-item :width="{ base: 1 }">
<ds-button fullwidth>
<ds-icon name="user-times" />
</ds-button>

View File

@ -1,36 +1,18 @@
<template>
<ds-form
v-model="form"
@submit="handleSubmit"
>
<ds-form v-model="form" @submit="handleSubmit">
<template slot-scope="{ errors }">
<ds-card>
<hc-editor
ref="editor"
:users="users"
:value="form.content"
@input="updateEditorContent"
/>
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
<ds-space />
<ds-flex :gutter="{ base: 'small', md: 'small', sm: 'x-large', xs: 'x-large' }">
<ds-flex-item :width="{ base: '0%', md: '50%', sm: '0%', xs: '0%' }" />
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '30%', xs: '30%' }">
<ds-button
:disabled="disabled"
ghost
class="cancelBtn"
@click.prevent="clear"
>
<ds-button :disabled="disabled" ghost class="cancelBtn" @click.prevent="clear">
{{ $t('actions.cancel') }}
</ds-button>
</ds-flex-item>
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '40%', xs: '40%' }">
<ds-button
type="submit"
:loading="loading"
:disabled="disabled || errors"
primary
>
<ds-button type="submit" :loading="loading" :disabled="disabled || errors" primary>
{{ $t('post.comment.submit') }}
</ds-button>
</ds-flex-item>

View File

@ -9,26 +9,17 @@
color="primary"
size="small"
round
>{{ comments.length }}</ds-tag>&nbsp; Comments
>
{{ comments.length }}
</ds-tag>
&nbsp; Comments
</span>
</h3>
<ds-space margin-bottom="large" />
<div
v-if="comments && comments.length"
id="comments"
class="comments"
>
<comment
v-for="comment in comments"
:key="comment.id"
:comment="comment"
/>
<div v-if="comments && comments.length" id="comments" class="comments">
<comment v-for="comment in comments" :key="comment.id" :comment="comment" />
</div>
<hc-empty
v-else
name="empty"
icon="messages"
/>
<hc-empty v-else name="empty" icon="messages" />
</div>
</template>
<script>

View File

@ -1,18 +1,11 @@
<template>
<ds-space
:class="{'notification': true, 'read': notification.read}"
margin-bottom="x-small"
>
<ds-space :class="{ notification: true, read: notification.read }" margin-bottom="x-small">
<no-ssr>
<ds-space margin-bottom="x-small">
<hc-user
:user="post.author"
:date-time="post.createdAt"
:trunc="35"
/>
<hc-user :user="post.author" :date-time="post.createdAt" :trunc="35" />
</ds-space>
<ds-text color="soft">
{{ $t("notifications.menu.mentioned") }}
{{ $t('notifications.menu.mentioned') }}
</ds-text>
</no-ssr>
<ds-space margin-bottom="x-small" />
@ -22,16 +15,11 @@
@click.native="$emit('read')"
>
<ds-space margin-bottom="x-small">
<ds-card
:header="post.title"
:image="post.image"
hover
space="x-small"
>
<ds-card :header="post.title" :image="post.image" hover space="x-small">
<ds-space margin-bottom="x-small" />
<!-- eslint-disable vue/no-v-html -->
<div v-html="excerpt" />
<!-- eslint-enable vue/no-v-html -->
<!-- eslint-enable vue/no-v-html -->
</ds-card>
</ds-space>
</nuxt-link>

View File

@ -1,36 +1,16 @@
<template>
<ds-button
v-if="totalNotifications <= 0"
class="notifications-menu"
disabled
icon="bell"
>
<ds-button v-if="totalNotifications <= 0" class="notifications-menu" disabled icon="bell">
{{ totalNotifications }}
</ds-button>
<dropdown
v-else
class="notifications-menu"
>
<template
slot="default"
slot-scope="{toggleMenu}"
>
<ds-button
primary
icon="bell"
@click.prevent="toggleMenu"
>
<dropdown v-else class="notifications-menu">
<template slot="default" slot-scope="{ toggleMenu }">
<ds-button primary icon="bell" @click.prevent="toggleMenu">
{{ totalNotifications }}
</ds-button>
</template>
<template
slot="popover"
>
<template slot="popover">
<div class="notifications-menu-popover">
<notification-list
:notifications="notifications"
@markAsRead="markAsRead"
/>
<notification-list :notifications="notifications" @markAsRead="markAsRead" />
</div>
</template>
</dropdown>

Some files were not shown because too many files have changed in this diff Show More