Merge branch 'master' into cypress-migrate-to-v10

This commit is contained in:
Ulf Gebhardt 2023-06-02 12:49:49 +02:00
commit 287827b3db
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
167 changed files with 5417 additions and 6774 deletions

View File

@ -1,157 +0,0 @@
codecov:
#token: uuid # Your private repository token
#url: "http" # for Codecov Enterprise customers
#slug: "owner/repo" # for Codecov Enterprise customers
#branch: master # override the default branch
#bot: username # set user whom will be the consumer of oauth requests
#ci: # Custom CI domains if Codecov does not identify them automatically
# - ci.domain.com
# - !provider # ignore these providers when checking if CI passed
# # ex. You may test on Travis, Circle, and AppVeyor, but only need
# # to check if Travis passes. Therefore add: !circle and !appveyor
notify:
#after_n_builds: null # number of expected builds to recieve before sending notifications
# # after: check ci status unless disabled via require_ci_to_pass
require_ci_to_pass: yes # yes: will delay sending notifications until all ci is finished
# no: will send notifications without checking ci status and wait till "after_n_builds" are uploaded
#countdown: null # number of seconds to wait before first ci build check
#delay: null # number of seconds to wait between ci build checks
coverage:
precision: 2 # 2 = xx.xx%, 0 = xx%
round: nearest # down|up|nearest - default down
# range: 50...60 # default 70...90. red...green
#notify:
# irc:
# default:
# server: "chat.freenode.net"|encrypted
# branches: null # all branches by default
# threshold: 1%
# message: "Coverage {{changed}} for {{owner}}/{{repo}}" # customize the message
# flags: null
# paths: null
#
# slack:
# default:
# url: "http"|encrypted
# threshold: 1%
# branches: null # all branches by default
# message: "Coverage {{changed}} for {{owner}}/{{repo}}" # customize the message
# attachments: "sunburst, diff"
# only_pulls: false
# flags: null
# paths: null
#
# email:
# default:
# to:
# - example@domain.com
# - &author
# threshold: 1%
# only_pulls: false
# layout: header, diff, trends
# flags: null
# paths: null
#
# hipchat:
# default:
# url: "http"|encrypted
# room: name|id
# threshold: 1%
# token: encrypted
# branches: null # all branches by default
# notify: false # if the hipchat message is silent or loud (default false)
# message: "Coverage {{changed}} for {{owner}}/{{repo}}" # customize the message
# flags: null
# paths: null
#
# gitter:
# url: "http"|encrypted
# threshold: 1%
# branches: null # all branches by default
# message: "Coverage {{changed}} for {{owner}}/{{repo}}" # customize the message
#
# webhooks:
# _name_:
# url: "http"|encrypted
# threshold: 1%
# branches: null # all branches by default
status:
project:
default: false # disable the default status that measures entire project
backend: # declare a new status context "backend"
against: parent
target: auto
threshold: null
#threshold: 1%
base: auto
if_no_uploads: error
if_not_found: success
if_ci_failed: error
only_pulls: false
#branches:
# - master
#flags:
# - integration
paths:
- backend/ # only include coverage in "backend/" folder
webapp: # declare a new status context "frontend"
against: parent
target: auto
threshold: null
#threshold: 1%
base: auto
if_no_uploads: error
if_not_found: success
if_ci_failed: error
only_pulls: false
#branches:
# - master
#flags:
# - integration
paths:
- webapp/ # only include coverage in "webapp/" folder
patch:
default: false
# against: parent
# target: 80%
# branches: null
# if_no_uploads: success
# if_not_found: success
# if_ci_failed: error
# only_pulls: false
# flags:
# - integration
# paths:
# - folder
#changes:
# default:
# against: parent
# branches: null
# if_no_uploads: error
# if_not_found: success
# if_ci_failed: error
# only_pulls: false
# flags:
# - integration
# paths:
# - folder
#flags:
# integration:
# branches:
# - master
# ignore:
# - app/ui
#ignore: # files and folders for processing
# - tests/*
#fixes:
# - "old_path::new_path"
comment: off

9
.github/file-filters.yml vendored Normal file
View File

@ -0,0 +1,9 @@
backend: &backend
- 'backend/**/*'
- 'neo4j/**/*'
docker: &docker
- 'docker-compose.*'
webapp: &webapp
- 'webapp/**/*'

View File

@ -1,79 +0,0 @@
name: publish-branded
on:
repository_dispatch:
types: [trigger-build-success]
jobs:
build_branded:
name: Docker Build Branded
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
ref: ${{ github.event.client_payload.ref }}
- name: Setup env
run: echo "OCELOT_VERSION=${{ github.event.client_payload.BUILD_VERSION }}" >> $GITHUB_ENV
- name: Build branded images
run: |
deployment/scripts/branded-images.build.sh
docker save "ocelotsocialnetwork/backend-branded" > /tmp/backend-branded.tar
docker save "ocelotsocialnetwork/webapp-branded" > /tmp/webapp-branded.tar
docker save "ocelotsocialnetwork/maintenance-branded" > /tmp/maintenance-branded.tar
- name: Upload Artifact (Backend)
uses: actions/upload-artifact@v2
with:
name: docker-backend-branded
path: /tmp/backend-branded.tar
- name: Upload Artifact (Webapp)
uses: actions/upload-artifact@v2
with:
name: docker-webapp-branded
path: /tmp/webapp-branded.tar
- name: Upload Artifact (Maintenance)
uses: actions/upload-artifact@v2
with:
name: docker-maintenance-branded
path: /tmp/maintenance-branded.tar
upload_to_dockerhub:
name: Upload to Dockerhub
runs-on: ubuntu-latest
needs: [build_branded]
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
ref: ${{ github.event.client_payload.ref }}
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
with:
name: docker-backend-branded
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend-branded.tar
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
with:
name: docker-webapp-branded
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp-branded.tar
- name: Download Docker Image (Maintenance)
uses: actions/download-artifact@v2
with:
name: docker-maintenance-branded
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/maintenance-branded.tar
- name: Upload to dockerhub
run: deployment/scripts/branded-images.upload.sh

View File

@ -292,18 +292,26 @@ jobs:
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
- run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ github.token }}
event-type: trigger-build-success
repository: ${{ github.repository }}
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
#- name: Repository Dispatch
# uses: peter-evans/repository-dispatch@v2
# with:
# token: ${{ github.token }}
# event-type: trigger-ocelot-build-success
# repository: ${{ github.repository }}
# client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
- name: Repository Dispatch stage.ocelot.social
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
event-type: trigger-build-success
event-type: trigger-ocelot-build-success
repository: 'Ocelot-Social-Community/stage.ocelot.social'
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
- name: Repository Dispatch stage.yunite.me
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
event-type: trigger-ocelot-build-success
repository: 'Yunite-Net/stage.yunite.me'
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'

120
.github/workflows/test-backend.yml vendored Normal file
View File

@ -0,0 +1,120 @@
name: ocelot.social backend test CI
on: [push]
jobs:
files-changed:
name: Detect File Changes - Backend
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.changes.outputs.backend }}
docker: ${{ steps.changes.outputs.docker }}
steps:
- uses: actions/checkout@v3.3.0
- name: Check for frontend file changes
uses: dorny/paths-filter@v2.11.1
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
build_test_neo4j:
name: Docker Build Test - Neo4J
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Neo4J | Build 'community' image
run: |
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-neo4j-image
path: /tmp/neo4j.tar
build_test_backend:
name: Docker Build Test - Backend
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: backend | Build 'test' image
run: |
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-backend-test
path: /tmp/backend.tar
lint_backend:
name: Lint Backend
if: needs.files-changed.outputs.backend == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: backend | Lint
run: cd backend && yarn && yarn run lint
unit_test_backend:
name: Unit tests - Backend
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true'
needs: [files-changed, build_test_neo4j, build_test_backend]
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v3
with:
name: docker-neo4j-image
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
- name: backend | copy env files webapp
run: cp webapp/.env.template webapp/.env
- name: backend | copy env files backend
run: cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
- name: backend | Initialize Database
run: docker-compose exec -T backend yarn db:migrate init
- name: backend | Migrate Database Up
run: docker-compose exec -T backend yarn db:migrate up
- name: backend | Unit test incl. coverage check
run: docker-compose exec -T backend yarn test

41
.github/workflows/test-e2e.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: ocelot.social end-to-end test CI
on: push
jobs:
fullstack_tests:
name: Fullstack tests
runs-on: ubuntu-latest
env:
jobs: 8
strategy:
matrix:
# run copies of the current job in parallel
job: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: webapp | copy env file
run: cp webapp/.env.template webapp/.env
- name: backend | copy env file
run: cp backend/.env.template backend/.env
- name: boot up test system | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
- name: cypress | Fullstack tests
id: e2e-tests
run: |
yarn install
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
##########################################################################
# UPLOAD SCREENSHOTS - IF TESTS FAIL #####################################
##########################################################################
- name: Full stack tests | if any test failed, upload screenshots
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: cypress-screenshots
path: cypress/screenshots/

101
.github/workflows/test-webapp.yml vendored Normal file
View File

@ -0,0 +1,101 @@
name: ocelot.social webapp test CI
on: [push]
jobs:
files-changed:
name: Detect File Changes - Webapp
runs-on: ubuntu-latest
outputs:
docker: ${{ steps.changes.outputs.docker }}
webapp: ${{ steps.changes.outputs.webapp }}
steps:
- uses: actions/checkout@v3.3.0
- name: Check for frontend file changes
uses: dorny/paths-filter@v2.11.1
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
prepare:
name: Prepare
if: needs.files-changed.outputs.webapp
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Check translation files
run: |
scripts/translations/sort.sh
scripts/translations/missing-keys.sh
build_test_webapp:
name: Docker Build Test - Webapp
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp
needs: [files-changed, prepare]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: webapp | Build 'test' image
run: |
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-webapp-test
path: /tmp/webapp.tar
lint_webapp:
name: Lint Webapp
if: needs.files-changed.outputs.webapp
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: webapp | Lint
run: cd webapp && yarn && yarn run lint
unit_test_webapp:
name: Unit Tests - Webapp
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp
needs: [files-changed, build_test_webapp]
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
- name: backend | copy env files webapp
run: cp webapp/.env.template webapp/.env
- name: backend | copy env files backend
run: cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
- name: webapp | Unit tests incl. coverage check
run: docker-compose exec -T webapp yarn test

View File

@ -1,344 +0,0 @@
name: ocelot.social test CI
on: [push]
jobs:
##############################################################################
# JOB: PREPARE #####################################################
##############################################################################
prepare:
name: Prepare
runs-on: ubuntu-latest
# needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# TODO: DO STUFF ??? #####################################################
##########################################################################
- name: Check translation files
run: |
scripts/translations/sort.sh
scripts/translations/missing-keys.sh
##############################################################################
# JOB: DOCKER BUILD TEST NEO4J ###############################################
##############################################################################
build_test_neo4j:
name: Docker Build Test - Neo4J
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# NEO4J ##################################################################
##########################################################################
- name: Neo4J | Build `community` image
run: |
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-neo4j-image
path: /tmp/neo4j.tar
##############################################################################
# JOB: DOCKER BUILD TEST BACKEND #############################################
##############################################################################
build_test_backend:
name: Docker Build Test - Backend
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# BUILD BACKEND DOCKER IMAGE (build) #####################################
##########################################################################
- name: backend | Build `test` image
run: |
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-backend-test
path: /tmp/backend.tar
##############################################################################
# JOB: DOCKER BUILD TEST WEBAPP ##############################################
##############################################################################
build_test_webapp:
name: Docker Build Test - WebApp
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
##########################################################################
- name: webapp | Build `test` image
run: |
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: docker-webapp-test
path: /tmp/webapp.tar
##############################################################################
# JOB: LINT BACKEND ##########################################################
##############################################################################
lint_backend:
name: Lint backend
runs-on: ubuntu-latest
needs: [build_test_backend]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
##########################################################################
# LINT BACKEND ###########################################################
##########################################################################
- name: backend | Lint
run: docker run --rm ocelotsocialnetwork/backend:test yarn run lint
##############################################################################
# JOB: LINT WEBAPP ###########################################################
##############################################################################
lint_webapp:
name: Lint webapp
runs-on: ubuntu-latest
needs: [build_test_webapp]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
##########################################################################
# LINT WEBAPP ############################################################
##########################################################################
- name: webapp | Lint
run: docker run --rm ocelotsocialnetwork/webapp:test yarn run lint
##############################################################################
# JOB: UNIT TEST BACKEND #####################################################
##############################################################################
unit_test_backend:
name: Unit tests - backend
runs-on: ubuntu-latest
needs: [build_test_neo4j,build_test_backend]
permissions:
checks: write
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v3
with:
name: docker-neo4j-image
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
##########################################################################
# UNIT TESTS BACKEND #####################################################
##########################################################################
- name: backend | copy env files webapp
run: cp webapp/.env.template webapp/.env
- name: backend | copy env files backend
run: cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
- name: backend | Initialize Database
run: docker-compose exec -T backend yarn db:migrate init
- name: backend | Migrate Database Up
run: docker-compose exec -T backend yarn db:migrate up
- name: backend | Unit test
run: docker-compose exec -T backend yarn test
##########################################################################
# COVERAGE CHECK BACKEND #################################################
##########################################################################
- name: backend | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage Backend
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 57
token: ${{ github.token }}
##############################################################################
# JOB: UNIT TEST WEBAPP ######################################################
##############################################################################
unit_test_webapp:
name: Unit tests - webapp
runs-on: ubuntu-latest
needs: [build_test_webapp]
permissions:
checks: write
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
##########################################################################
# UNIT TESTS WEBAPP ######################################################
##########################################################################
- name: backend | copy env files webapp
run: cp webapp/.env.template webapp/.env
- name: backend | copy env files backend
run: cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
- name: webapp | Unit tests
run: docker-compose exec -T webapp yarn test
##########################################################################
# COVERAGE REPORT FRONTEND ################################################
##########################################################################
#- name: frontend | Coverage report
# uses: romeovs/lcov-reporter-action@v0.2.21
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# lcov-file: ./coverage/lcov.info
##########################################################################
# COVERAGE CHECK WEBAPP ##################################################
##########################################################################
- name: webapp | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage Webapp
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 83
token: ${{ github.token }}
##############################################################################
# JOB: FULLSTACK TESTS #######################################################
##############################################################################
fullstack_tests:
name: Fullstack tests
runs-on: ubuntu-latest
needs: [build_test_webapp, build_test_backend, build_test_neo4j]
env:
jobs: 8
strategy:
matrix:
# run copies of the current job in parallel
job: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v3
with:
name: docker-neo4j-image
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
##########################################################################
# FULLSTACK TESTS CYPRESS ################################################
##########################################################################
- name: webapp | copy env files webapp
run: cp webapp/.env.template webapp/.env
- name: backend | copy env files backend
run: cp backend/.env.template backend/.env
- name: backend | docker-compose
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
- name: cypress | Fullstack tests
id: e2e-tests
run: |
yarn install
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
##########################################################################
# UPLOAD SCREENSHOTS - IF TESTS FAIL #####################################
##########################################################################
- name: Full stack tests | if any test failed, upload screenshots
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@v3
with:
name: cypress-screenshots
path: cypress/screenshots/

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "styleguide"]
path = styleguide
url = https://github.com/Human-Connection/Nitro-Styleguide.git
[submodule "deployment/configurations/stage.ocelot.social"]
path = deployment/configurations/stage.ocelot.social
url = git@github.com:Ocelot-Social-Community/stage.ocelot.social.git

View File

@ -1,84 +0,0 @@
dist: xenial
language: node_js
node_js: lts/*
cache:
yarn: false
npm: false
addons:
apt:
packages:
- libgconf-2-4
snaps:
- docker
firefox: "latest-esr"
install:
- yarn global add wait-on
# Install Codecov
- yarn install --frozen-lockfile
- cp backend/.env.template backend/.env
before_script:
- docker-compose -f docker-compose.yml build --parallel
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml build # just tagging, just be quite fast
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml up -d
- wait-on http://localhost:7474
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml exec backend yarn run db:migrate init
script:
- export CYPRESS_RETRIES=1
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
- echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH"
# Miscellaneous
- ./scripts/translations/sort.sh
- ./scripts/translations/missing-keys.sh
# Backend
- docker-compose exec backend yarn run lint
- docker-compose exec backend yarn run test --ci --verbose=false --coverage
- docker-compose exec backend yarn run db:seed
- docker-compose exec backend yarn run db:reset
# Frontend
- docker-compose exec webapp yarn run lint
- docker-compose exec webapp yarn run test --ci --verbose=false --coverage
# Fullstack
- docker-compose down
- docker-compose -f docker-compose.yml up -d
- wait-on http://localhost:7474
# disable for last deploy, because of flakiness!
# - yarn run cypress:run --record
# - yarn run cucumber
# Coverage
# disable this uneffective thing for last deploy, because of easyness!
# - yarn run codecov
after_success:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- ./send.sh success $WEBHOOK_URL
- if [ $TRAVIS_BRANCH == "master" ] && [ $TRAVIS_EVENT_TYPE == "push" ]; then
wget https://raw.githubusercontent.com/Human-Connection/Discord-Bot/develop/tester.sh &&
chmod +x tester.sh &&
./tester.sh staging $WEBHOOK_URL;
fi
after_failure:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- ./send.sh failure $WEBHOOK_URL
before_deploy:
- go get -u github.com/tcnksm/ghr
# stop deployment to kubernetes until we have set it up
# - ./scripts/setup_kubernetes.sh
deploy:
- provider: script
script: bash scripts/docker_push.sh
on:
branch: master
# stop deployment to kubernetes until we have set it up
# - provider: script
# script: bash scripts/deploy.sh
# on:
# branch: master

View File

@ -4,8 +4,177 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [2.6.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.5.1...2.6.0)
- fix(other): docker-compose for rebranding deployment [`#6265`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6265)
- feat(webapp): default categories of group for posts in group [`#6259`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6259)
- refactor(webapp): make action radius select in group form a reusable component [`#6244`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6244)
- fix(webapp): show avatar for group members [`#6258`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6258)
- fix(webapp): fix search for 3 chars [`#6256`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6256)
- fix(backend): group posts cannot be pinned [`#6242`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6242)
- Bump metascraper-soundcloud from 5.33.5 to 5.34.2 in /backend [`#6213`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6213)
- refactor(other): refactor test workflows [`#6151`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6151)
- fix(other): deployment fix typo, update stage.ocelot.social reference [`#6230`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6230)
- fix(other): bump metascraper-url from 5.33.5 to 5.34.2 in /backend [`#6217`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6217)
- fix(other): workflow typo [`#6235`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6235)
- feat(other): publish stage.yunite.me hook [`#6234`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6234)
- fix(other): reduce kubernetes memory limits [`#6229`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6229)
- fix(other): deployment for branded image with custom names [`#6228`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6228)
- Bump node from 19.8.1-alpine3.17 to 19.9.0-alpine3.17 in /backend [`#6219`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6219)
- Bump node from 19.8.1-alpine3.17 to 19.9.0-alpine3.17 in /webapp [`#6220`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6220)
- feat(other): deployment pod resources [`#6132`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6132)
- Bump @babel/core from 7.12.17 to 7.21.4 in /webapp [`#6215`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6215)
- Bump jsonwebtoken from 8.5.1 to 9.0.0 in /webapp [`#6079`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6079)
- Bump jest from 29.4.2 to 29.5.0 in /webapp [`#6094`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6094)
- Bump babel-jest from 29.4.2 to 29.5.0 in /webapp [`#6095`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6095)
- Bump neode from 0.4.8 to 0.4.9 in /backend [`#6075`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6075)
- Bump node from 19.4.0-alpine3.17 to 19.8.1-alpine3.17 in /webapp [`#6155`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6155)
- Bump node from 19.4.0-alpine3.17 to 19.8.1-alpine3.17 in /backend [`#6156`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6156)
- Bump @babel/core from 7.9.0 to 7.21.4 [`#6200`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6200)
- Bump @babel/preset-env from 7.12.7 to 7.21.4 [`#6204`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6204)
- Bump expect from 25.3.0 to 29.5.0 [`#6098`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6098)
- separate test workflows [`3533a36`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3533a36cdc811c0e1dae218fbc2184f7c4bc3951)
- get it working [`8df7d5d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8df7d5d265b0c5ba16f167a213631d765d2f985e)
- feat(webapp): group categories on posts [`3244f3f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3244f3f86d1e8c09e0fd49f43c49f0a3aa8b85ab)
#### [2.5.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.5.0...2.5.1)
> 23 March 2023
- chore(other): release v2.5.1 fix filter menu width [`#6180`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6180)
- feat(webapp): add tooltips to all menu icons [`#6185`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6185)
- fix(webapp): popup filter max-width [`#6177`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6177)
- Add tooltip to header notifications menu [`28505a5`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/28505a5b181008ebcde6fa58b7a4a8459a492018)
- Add tooltip to header avatar menu [`4c0469f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/4c0469f61a3c2fae23e50c6a5a2a91b63fac149a)
- Release v2.5.1 - fix filter menu width [`08def14`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/08def14cafef7816d8e43f1896430400bda9635d)
#### [2.5.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.4.0...2.5.0)
> 21 March 2023
- chore(other): release v2.5.0 [`#6172`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6172)
- fix(other): publish transmit github run number properly [`#6171`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6171)
- feat(other): deployment include GitHub run number on branded images [`#6170`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6170)
- fix(backend): new migration to create search indexes [`#6167`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6167)
- fix(other): prevent exposing secrets in the github actions by not setting debug mode on cluster deploy [`#6168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6168)
- feat(other): echo current configuration when running scripts [`#6169`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6169)
- refactor(other): build & deployment of branded images on stage.ocelot.social [`#6163`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6163)
- fix(webapp): close popover notification menu [`#6166`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6166)
- fix(webapp): small buttons in notifications menu [`#6162`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6162)
- fix(webapp): revert pr `on newsfeed add bigger y-gap between posts #6121` [`#6164`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6164)
- fix(webapp): join leave button for pending members [`#6152`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6152)
- feat(backend): notifications for groups [`#6150`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6150)
- feat(webapp): add hint text to create and edit a post of a group aside of the save and abort buttons [`#6114`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6114)
- fix(other): use access token to trigger build on external repo [`#6145`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6145)
- feat(webapp): set different icon and text on join leave button, when member is pending [`#6124`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6124)
- refactor(other): deployment upgrade to specific version, trigger build on stage [`#6143`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6143)
- feat(webapp): show 2 GroupItem per row in GroupList [`#6135`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6135)
- fix(backend): do not delete migrations on db reset [`#6136`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6136)
- feat(other): deployment reseed [`#6129`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6129)
- feat(other): deployment secrets [`#6120`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6120)
- feat(webapp): refactor notification page [`#6116`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6116)
- fix(webapp): fix ribbons position [`#6130`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6130)
- fix(webapp): fix donation bar blink [`#6107`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6107)
- feat(webapp): change order of groups to new first [`#6122`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6122)
- fix(webapp): error in user profil on the 'show x more' button [`#6093`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6093)
- feat(webapp): filter button style change [`#6109`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6109)
- refactor(other): deactivate video recording & only upload screenshots, if tests fail for e2e tests [`#6125`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6125)
- fix(webapp): change icon color of active topics (categories) of post teaser [`#6112`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6112)
- docs(other): correct **/README.md [`#6059`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6059)
- fix(webapp): fix `viewport``width=device-width` by removing it [`#6110`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6110)
- feat(webapp): on newsfeed add bigger y-gap between posts [`#6121`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6121)
- Bump peter-evans/repository-dispatch from 1 to 2 [`#6117`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6117)
- fix(webapp): fix gap news feed [`#6065`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6065)
- feat(webapp): change shout icon `bullhorn` to `heart-o` [`#6115`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6115)
- refactor(webapp): change invite code button and menu to copy invite link [`#6108`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6108)
- feat(webapp): change group filter button text [`#6105`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6105)
- feat(webapp): 🍰 allows mark all notifications as read [`#3922`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/3922)
- fix(other): deploy branded - upload to dockerhub [`#6099`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6099)
- feat(webapp): remove user from group [`#6072`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6072)
- chore(other): remove apple m1 docker overrides [`#6056`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6056)
- refactor(other): ocelot publish workflows [`#6083`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6083)
- fix(other): lint pr only on pullrequest [`#6088`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6088)
- fix(other): dependabot do not rebase [`#6096`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6096)
- feat(backend): filter posts in my groups [`#6091`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6091)
- refactor(backend): withdraw mock of gql and replace with graphql-tag function [`#6022`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6022)
- docs(other): update readme.md [`#6090`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6090)
- feat(webapp): adjust and create branding possibilities [`#6063`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6063)
- fix(other): exclude dependabot from pr title linting [`#6085`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6085)
- feat(webapp): change ``Benutzer`` to ``Nutzer`` [`#6027`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6027)
- docs(webapp): change the used nvm version [`#6023`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6023)
- feat(other): backup script [`#6051`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6051)
- fix(backend): user or group has null as description [`#5996`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5996)
- feat(other): script to set a cluster in maintenance mode [`#6042`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6042)
- feat(other): fix dependabot [`#6035`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6035)
- fix(other): fix maintenance page & maintenance build [`#6032`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6032)
- refactor(webapp): upgrade jest and vue test util [`#5968`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5968)
- refactor(other): deployment [`#5975`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5975)
- fix(webapp): remove additional markers from map [`#6021`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6021)
- feat(webapp): add align center to the user profile slug [`#6012`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6012)
- chore(other): update github actions for node 16 [`#5971`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5971)
- refactor(backend): update jest [`#5964`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5964)
- Revert "V2.3.0" [`#95`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/95)
- V2.3.0 [`#94`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/94)
- v2.3.0-281 [`#93`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/93)
- rebranding for yunite [`#26`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/26)
- feat(webapp): copy fonts from assets [`#25`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/25)
- feat(webapp): copy fonts from assets [`#92`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/92)
- chore: 🍰 Release v2.2.0-267 Fixes And Groups Page Etc. [`#19`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/19)
- fix: 🍰 Change Hasttag Menu Search Color To `#17b53f` [`#21`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/21)
- chore: 🍰 Release v2.2.0-267 [`#90`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/90)
- Docu Updates [`#86`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/86)
- chore: 🍰 Release v2.1.0-253 Merge Groups Into master Epic Groups [`#12`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/12)
- chore: 🍰 Release v2.1.0-253 [`#87`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/87)
- chore: [WIP] 🍰 Release v2.0.0-250 Groups Configurable Header Menu Is Translatable Etc. [`#16`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/16)
- chore: 🍰 Release v2.0.0 Groups Etc. Extend Rebranding And Deployment [`#83`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/83)
- fix: 🍰 Fix Implementation Of Overwriting Locales [`#80`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/80)
- feat: 🍰 Merge Locales [`#77`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/77)
- chore: 🍰 Groups Release v1.1.1-231 - Show New Features [`#14`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/14)
- chore: 🍰 Release v1.1.1 First Design Rebranding [`#10`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/10)
- chore: [WIP] 🍰 Refine Rebanding And Deployment [`#8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8)
- chore: 🍰 Fix Folder Name From `branding/assets/styles/import` To `branding/assets/styles/imports` [`#71`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/71)
- chore: 🍰 Release v1.1.1-228 Refactor Rebranding [`#68`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/68)
- chore: 🍰 Release v1.1.0-205 [`#6`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6)
- chore: 🍰 Release v1.1.0-225 [`#66`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/66)
- chore: [WIP] 🍰 Refine Deployment Setup And Documentation [`#46`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/46)
- chore: [WIP] 🍰 Release v1.1.0 - Implement Categories Again [`#63`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/63)
- Update issue templates [`#3`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/3)
- chore: 🍰 Release v1.0.9-199 [`#59`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/59)
- chore: 🍰 Implement `PRODUCTION_DB_CLEAN_ALLOW` for Staging Production Environments [`#56`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/56)
- chore: 🍰 Add Docker Compose Override For Apple M1 And Docu [`#57`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/57)
- Chore: 🍰 Fix `apiVersion: cert-manager.io/v1alpha2` To `*/v1` [`#53`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/53)
- chore: 🍰 Adjust `maintenance` Container Name In Deployment With Helm [`#50`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/50)
- chore: 🍰 Release New Build Version `v1.0.8-184` [`#51`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/51)
- chore: 🍰 Rename Neo4j Docker Image In General To `neo4j-community:*` [`#49`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/49)
- feat: 🍰 Release v1.0.8 Configure Cookie Expire Time second run [`#44`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/44)
- feat: 🍰 Configure Cookie Expire Time [`#43`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/43)
- docs: 🍰 Add Docs For Backups, Rollbacks, And Release v1.0.7-171 [`#40`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/40)
- chore: 🍰 Realease v1.0.6-170 [`#37`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/37)
- chore: 🍰 Refactor FAQ Link To Use Internal Page Which Was Implemented Before [`#36`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/36)
- chore: 🍰 Add Branding Templates For Terms And Conditions Etc. [`#32`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/32)
- feat: 🍰 Add Rebranded E-Mails For Sending Notifications [`#29`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/29)
- bug: 🍰 Fix Minor Errors And Clarify Docs [`#28`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/28)
- chore: 🍰 Implement New Internal Pages Main Release v1.0.5 [`#25`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/25)
- feat: [WIP] 🍰 Add Landing Page Etc. [`#22`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/22)
- docs: 🍰 [WIP] Add Configure And Rebranding Readme.md [`#20`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/20)
- chore: [WIP] 🍰 New Deployment With 'base' And 'code' Docker Images [`#15`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/15)
- feat: [WIP] 🍰 Flexible Footer Links [`#17`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/17)
- 💥 Release v1.0.3 [`#12`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/12)
- 🛠 [Refactor] Add README And LICENSE [`#11`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/11)
- chore: 🍰 Implement Use Of package.json 'Version' Etc. For Build And Refine Helm Chart [`#8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8)
- chore: 🍰 Replace Ocelot Logos 619x593 With 600x570 [`#6`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6)
- brand_as_default [`#4`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4)
- limit_replicasets [`#3`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/3)
- Deployment [`#2`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/2)
- moved example into stage.ocelot.social [`61b5112`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/61b5112b8b547a7491d644c7c4dbfead39b61d79)
- feat(backand): upgrade jest to 29.4 [`4390d72`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/4390d72477fb941f69bc9bdc24ac7713ef06e827)
- pages tests nearly working [`4850e45`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/4850e456fe5b7c158f23acc7f153576472604300)
#### [2.4.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.3.0...2.4.0)
> 10 February 2023
- chore(release): v2.4.0 [`#5961`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5961)
- fix(webapp): post teaser width in mobile view [`#5958`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5958)
- fix(webapp): navbar disappears when scrolling to top [`#5957`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5957)
- fix(webapp): close mobile menu on all links [`#5956`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5956)

View File

@ -13,7 +13,7 @@ docker-compose*.yml
node_modules/
scripts/
dist/
build/
maintenance-worker/
neo4j/

View File

@ -1,3 +1,5 @@
DEBUG=true
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=letmein

2
backend/.gitignore vendored
View File

@ -3,7 +3,7 @@ node_modules/
.vscode
.idea
yarn-error.log
dist/*
build/*
coverage.lcov
.nyc_output/
public/uploads/*

View File

@ -1 +1 @@
v19.4.0
v20.2.0

View File

@ -1,7 +1,7 @@
##################################################################################
# BASE (Is pushed to DockerHub for rebranding) ###################################
##################################################################################
FROM node:19.4.0-alpine3.17 as base
FROM node:20.2.0-alpine3.17 as base
# ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
@ -90,7 +90,7 @@ CMD /bin/sh -c "yarn run dev"
FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
COPY --from=build ${DOCKER_WORKDIR}/build ./build
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
# Copy static files
# TODO - externalize the uploads so we can copy the whole folder

View File

@ -5,10 +5,14 @@ module.exports = {
'**/*.js',
'!**/node_modules/**',
'!**/test/**',
'!**/dist/**',
'!**/build/**',
'!**/src/**/?(*.)+(spec|test).js?(x)'
],
coverageReporters: ['lcov', 'text'],
coverageThreshold: {
global: {
lines: 57,
},
},
testMatch: ['**/src/**/?(*.)+(spec|test).js?(x)'],
setupFilesAfterEnv: ['<rootDir>/test/setup.js']
}

View File

@ -1,6 +1,6 @@
{
"name": "ocelot-social-backend",
"version": "2.4.0",
"version": "2.6.0",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
@ -9,10 +9,10 @@
"main": "src/index.js",
"scripts": {
"__migrate": "migrate --compiler 'js:@babel/register' --migrations-dir ./src/db/migrations",
"prod:migrate": "migrate --migrations-dir ./dist/db/migrations --store ./dist/db/migrate/store.js",
"start": "node dist/",
"build": "babel src/ -d dist/ --copy-files",
"dev": "nodemon --exec babel-node src/ -e js,gql",
"prod:migrate": "migrate --migrations-dir ./build/db/migrations --store ./build/db/migrate/store.js",
"start": "node build/",
"build": "tsc && mkdir -p build/middleware/helpers/email/templates/ && cp -r src/middleware/helpers/email/templates/*.html build/middleware/helpers/email/templates/ && mkdir -p build/middleware/helpers/email/templates/en/ && cp -r src/middleware/helpers/email/templates/en/*.html build/middleware/helpers/email/templates/en/ && mkdir -p build/middleware/helpers/email/templates/de/ && cp -r src/middleware/helpers/email/templates/de/*.html build/middleware/helpers/email/templates/de/ && mkdir -p build/schema/types/ && cp -r src/schema/types/*.gql build/schema/types/ && mkdir -p build/schema/types/enum/ && cp -r src/schema/types/enum/*.gql build/schema/types/enum/ && mkdir -p build/schema/types/scalar/ && cp -r src/schema/types/scalar/*.gql build/schema/types/scalar/ && mkdir -p build/schema/types/type/ && cp -r src/schema/types/type/*.gql build/schema/types/type/",
"dev": "nodemon --exec ts-node src/ -e js,ts,gql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql",
"lint": "eslint src --config .eslintrc.js",
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles",
@ -29,7 +29,6 @@
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
"@babel/preset-env": "~7.9.5",
"@babel/register": "^7.9.0",
"@hapi/joi": "^17.1.1",
"@sentry/node": "^5.15.4",
"apollo-cache-inmemory": "~1.6.5",
"apollo-client": "~2.6.8",
@ -46,19 +45,16 @@
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~7.0.3",
"date-fns": "2.22.1",
"debug": "~4.1.1",
"dotenv": "~8.2.0",
"express": "^4.17.1",
"graphql": "^14.6.0",
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~4.0.2",
"graphql-middleware-sentry": "^3.2.1",
"graphql-redis-subscriptions": "^2.2.1",
"graphql-shield": "~7.2.2",
"graphql-tag": "~2.10.3",
"helmet": "~3.22.0",
"helmet": "~7.0.0",
"ioredis": "^4.16.1",
"jsonwebtoken": "~8.5.1",
"languagedetect": "^2.0.0",
@ -66,9 +62,8 @@
"lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.8",
"metascraper": "^5.33.5",
"metascraper-audio": "^5.33.5",
"metascraper-audio": "^5.34.4",
"metascraper-author": "^5.33.5",
"metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.33.5",
"metascraper-description": "^5.33.5",
"metascraper-image": "^5.33.5",
@ -76,9 +71,9 @@
"metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.33.5",
"metascraper-publisher": "^5.33.5",
"metascraper-soundcloud": "^5.33.5",
"metascraper-soundcloud": "^5.34.4",
"metascraper-title": "^5.33.5",
"metascraper-url": "^5.33.5",
"metascraper-url": "^5.34.2",
"metascraper-video": "^5.33.5",
"metascraper-youtube": "^5.33.5",
"migrate": "^1.7.0",
@ -87,23 +82,21 @@
"mustache": "^4.2.0",
"neo4j-driver": "^4.0.2",
"neo4j-graphql-js": "^2.11.5",
"neode": "^0.4.8",
"neode": "^0.4.9",
"node-fetch": "~2.6.1",
"nodemailer": "^6.4.4",
"nodemailer-html-to-text": "^3.2.0",
"npm-run-all": "~4.1.5",
"request": "~2.88.2",
"sanitize-html": "~1.22.0",
"slug": "~6.0.0",
"subscriptions-transport-ws": "^0.9.19",
"trunc-html": "~1.1.2",
"uuid": "~8.3.2",
"validator": "^13.0.0",
"wait-on": "~4.0.1",
"validator": "^13.9.0",
"xregexp": "^4.3.0"
},
"devDependencies": {
"@faker-js/faker": "5.1.0",
"@faker-js/faker": "7.6.0",
"apollo-server-testing": "~2.11.0",
"chai": "~4.2.0",
"cucumber": "~6.0.5",
@ -120,7 +113,8 @@
"nodemon": "~2.0.2",
"prettier": "~2.3.2",
"rosie": "^2.0.1",
"supertest": "~4.0.2"
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"resolutions": {
"**/**/fs-capacitor": "^6.2.0",

View File

@ -2,7 +2,7 @@ import { v4 as uuid } from 'uuid'
import slugify from 'slug'
import { hashSync } from 'bcryptjs'
import { Factory } from 'rosie'
import faker from '@faker-js/faker'
import { faker } from '@faker-js/faker'
import { getDriver, getNeode } from './neo4j'
import CONFIG from '../config/index.js'
import generateInviteCode from '../schema/resolvers/helpers/generateInviteCode.js'
@ -63,7 +63,7 @@ Factory.define('basicUser')
.option('password', '1234')
.attrs({
id: uuid,
name: faker.name.findName,
name: faker.name.fullName,
password: '1234',
role: 'user',
termsAndConditionsAgreedVersion: '0.0.1',

View File

@ -86,13 +86,6 @@ class Store {
if (CONFIG.CATEGORIES_ACTIVE) await createCategories(session)
const writeTxResultPromise = session.writeTransaction(async (txc) => {
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices and constraints
return Promise.all(
[
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])',
].map((statement) => txc.run(statement)),
)
})
try {
await writeTxResultPromise

View File

@ -0,0 +1,68 @@
import { getDriver } from '../../db/neo4j'
export const description = ''
export async function up(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
// Drop indexes if they exist because due to legacy code they might be set already
const indexesResponse = await transaction.run(`CALL db.indexes()`)
const indexes = indexesResponse.records.map((record) => record.get('indexName'))
if (indexes.indexOf('user_fulltext_search') > -1) {
await transaction.run(`CALL db.index.fulltext.drop("user_fulltext_search")`)
}
if (indexes.indexOf('post_fulltext_search') > -1) {
await transaction.run(`CALL db.index.fulltext.drop("post_fulltext_search")`)
}
if (indexes.indexOf('tag_fulltext_search') > -1) {
await transaction.run(`CALL db.index.fulltext.drop("tag_fulltext_search")`)
}
// Create indexes
await transaction.run(
`CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])`,
)
await transaction.run(
`CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])`,
)
await transaction.run(
`CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])`,
)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}
export async function down(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
await transaction.run(`CALL db.index.fulltext.drop("user_fulltext_search")`)
await transaction.run(`CALL db.index.fulltext.drop("post_fulltext_search")`)
await transaction.run(`CALL db.index.fulltext.drop("tag_fulltext_search")`)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}

View File

@ -0,0 +1,53 @@
import { getDriver } from '../../db/neo4j'
export const description = 'Add to all existing posts the Article label'
export async function up(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
await transaction.run(`
MATCH (post:Post)
SET post:Article
RETURN post
`)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}
export async function down(next) {
const driver = getDriver()
const session = driver.session()
const transaction = session.beginTransaction()
try {
await transaction.run(`
MATCH (post:Post)
REMOVE post:Article
RETURN post
`)
await transaction.commit()
next()
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
await transaction.rollback()
// eslint-disable-next-line no-console
console.log('rolled back')
throw new Error(error)
} finally {
session.close()
}
}

View File

@ -2,7 +2,7 @@ import sample from 'lodash/sample'
import { createTestClient } from 'apollo-server-testing'
import CONFIG from '../config'
import createServer from '../server'
import faker from '@faker-js/faker'
import { faker } from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
import {
@ -173,6 +173,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
{
email: 'moderator@example.org',
avatar: null,
},
),
Factory.build(
@ -209,6 +210,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
{
email: 'dewey@example.org',
avatar: null,
},
),
Factory.build(

View File

@ -1,5 +1,7 @@
# GraphQL Playground
***Attention:** For using the GraphQL Playground set `DEBUG=true` in your backend `.env`, see `.env.template`!*
To use GraphQL Playground, we need to know some basics:
## How To Login?

View File

@ -11,6 +11,8 @@ export const createPostMutation = () => {
$content: String!
$categoryIds: [ID]
$groupId: ID
$postType: PostType
$eventInput: _EventInput
) {
CreatePost(
id: $id
@ -19,11 +21,31 @@ export const createPostMutation = () => {
content: $content
categoryIds: $categoryIds
groupId: $groupId
postType: $postType
eventInput: $eventInput
) {
id
slug
title
content
disabled
deleted
postType
author {
name
}
categories {
id
}
eventStart
eventEnd
eventLocationName
eventVenue
eventIsOnline
eventLocation {
lng
lat
}
}
}
`
@ -50,6 +72,7 @@ export const filterPosts = () => {
id
title
content
eventStart
}
}
`

View File

@ -51,6 +51,50 @@ const publishNotifications = async (context, promises) => {
})
}
const handleJoinGroup = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(context, [
notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context),
])
}
return user
}
const handleLeaveGroup = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(context, [
notifyOwnersOfGroup(groupId, userId, 'user_left_group', context),
])
}
return user
}
const handleChangeGroupMemberRole = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(context, [
notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context),
])
}
return user
}
const handleRemoveUserFromGroup = async (resolve, root, args, context, resolveInfo) => {
const { groupId, userId } = args
const user = await resolve(root, args, context, resolveInfo)
if (user) {
await publishNotifications(context, [
notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context),
])
}
return user
}
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
const idsOfUsers = extractMentionedUsers(args.content)
const post = await resolve(root, args, context, resolveInfo)
@ -94,6 +138,72 @@ const postAuthorOfComment = async (commentId, { context }) => {
}
}
const notifyOwnersOfGroup = async (groupId, userId, reason, context) => {
const cypher = `
MATCH (group:Group { id: $groupId })<-[membership:MEMBER_OF]-(owner:User)
WHERE membership.role = 'owner'
WITH owner, group
MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(owner)
WITH group, owner, notification
SET notification.read = FALSE
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
SET notification.updatedAt = toString(datetime())
SET notification.relatedUserId = $userId
RETURN notification {.*, from: group, to: properties(owner)}
`
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const notificationTransactionResponse = await transaction.run(cypher, {
groupId,
reason,
userId,
})
return notificationTransactionResponse.records.map((record) => record.get('notification'))
})
try {
const notifications = await writeTxResultPromise
return notifications
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
}
const notifyMemberOfGroup = async (groupId, userId, reason, context) => {
const { user: owner } = context
const cypher = `
MATCH (user:User { id: $userId })
MATCH (group:Group { id: $groupId })
WITH user, group
MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(user)
WITH group, user, notification
SET notification.read = FALSE
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
SET notification.updatedAt = toString(datetime())
SET notification.relatedUserId = $ownerId
RETURN notification {.*, from: group, to: properties(user)}
`
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const notificationTransactionResponse = await transaction.run(cypher, {
groupId,
reason,
userId,
ownerId: owner.id,
})
return notificationTransactionResponse.records.map((record) => record.get('notification'))
})
try {
const notifications = await writeTxResultPromise
return notifications
} catch (error) {
throw new Error(error)
} finally {
session.close()
}
}
const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
if (!(idsOfUsers && idsOfUsers.length)) return []
await validateNotifyUsers(label, reason)
@ -188,5 +298,9 @@ export default {
UpdatePost: handleContentDataOfPost,
CreateComment: handleContentDataOfComment,
UpdateComment: handleContentDataOfComment,
JoinGroup: handleJoinGroup,
LeaveGroup: handleLeaveGroup,
ChangeGroupMemberRole: handleChangeGroupMemberRole,
RemoveUserFromGroup: handleRemoveUserFromGroup,
},
}

View File

@ -3,6 +3,13 @@ import { cleanDatabase } from '../../db/factories'
import { createTestClient } from 'apollo-server-testing'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer, { pubsub } from '../../server'
import {
createGroupMutation,
joinGroupMutation,
leaveGroupMutation,
changeGroupMemberRoleMutation,
removeUserFromGroupMutation,
} from '../../graphql/groups'
let server, query, mutate, notifiedUser, authenticatedUser
let publishSpy
@ -92,6 +99,9 @@ describe('notifications', () => {
read
reason
createdAt
relatedUser {
id
}
from {
__typename
... on Post {
@ -102,6 +112,9 @@ describe('notifications', () => {
id
content
}
... on Group {
id
}
}
}
}
@ -185,6 +198,7 @@ describe('notifications', () => {
id: 'c47',
content: commentContent,
},
relatedUser: null,
},
],
},
@ -357,6 +371,7 @@ describe('notifications', () => {
id: 'p47',
content: expectedUpdatedContent,
},
relatedUser: null,
},
],
},
@ -513,6 +528,7 @@ describe('notifications', () => {
id: 'c47',
content: commentContent,
},
relatedUser: null,
},
],
},
@ -547,6 +563,7 @@ describe('notifications', () => {
id: 'c47',
content: commentContent,
},
relatedUser: null,
},
],
},
@ -616,4 +633,232 @@ describe('notifications', () => {
})
})
})
describe('group notifications', () => {
let groupOwner
beforeEach(async () => {
groupOwner = await neode.create(
'User',
{
id: 'group-owner',
name: 'Group Owner',
slug: 'group-owner',
},
{
email: 'owner@example.org',
password: '1234',
},
)
authenticatedUser = await groupOwner.toJson()
await mutate({
mutation: createGroupMutation(),
variables: {
id: 'closed-group',
name: 'The Closed Group',
about: 'Will test the closed group!',
description: 'Some description' + Array(50).join('_'),
groupType: 'public',
actionRadius: 'regional',
categoryIds,
},
})
})
describe('user joins group', () => {
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
await mutate({
mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: authenticatedUser.id,
},
})
authenticatedUser = await groupOwner.toJson()
})
it('has the notification in database', async () => {
await expect(
query({
query: notificationQuery,
}),
).resolves.toMatchObject({
data: {
notifications: [
{
read: false,
reason: 'user_joined_group',
createdAt: expect.any(String),
from: {
__typename: 'Group',
id: 'closed-group',
},
relatedUser: {
id: 'you',
},
},
],
},
errors: undefined,
})
})
})
describe('user leaves group', () => {
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
await mutate({
mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: authenticatedUser.id,
},
})
await mutate({
mutation: leaveGroupMutation(),
variables: {
groupId: 'closed-group',
userId: authenticatedUser.id,
},
})
authenticatedUser = await groupOwner.toJson()
})
it('has two the notification in database', async () => {
await expect(
query({
query: notificationQuery,
}),
).resolves.toMatchObject({
data: {
notifications: [
{
read: false,
reason: 'user_left_group',
createdAt: expect.any(String),
from: {
__typename: 'Group',
id: 'closed-group',
},
relatedUser: {
id: 'you',
},
},
{
read: false,
reason: 'user_joined_group',
createdAt: expect.any(String),
from: {
__typename: 'Group',
id: 'closed-group',
},
relatedUser: {
id: 'you',
},
},
],
},
errors: undefined,
})
})
})
describe('user role in group changes', () => {
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
await mutate({
mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: authenticatedUser.id,
},
})
authenticatedUser = await groupOwner.toJson()
await mutate({
mutation: changeGroupMemberRoleMutation(),
variables: {
groupId: 'closed-group',
userId: 'you',
roleInGroup: 'admin',
},
})
authenticatedUser = await notifiedUser.toJson()
})
it('has notification in database', async () => {
await expect(
query({
query: notificationQuery,
}),
).resolves.toMatchObject({
data: {
notifications: [
{
read: false,
reason: 'changed_group_member_role',
createdAt: expect.any(String),
from: {
__typename: 'Group',
id: 'closed-group',
},
relatedUser: {
id: 'group-owner',
},
},
],
},
errors: undefined,
})
})
})
describe('user is removed from group', () => {
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
await mutate({
mutation: joinGroupMutation(),
variables: {
groupId: 'closed-group',
userId: authenticatedUser.id,
},
})
authenticatedUser = await groupOwner.toJson()
await mutate({
mutation: removeUserFromGroupMutation(),
variables: {
groupId: 'closed-group',
userId: 'you',
},
})
authenticatedUser = await notifiedUser.toJson()
})
it('has notification in database', async () => {
await expect(
query({
query: notificationQuery,
}),
).resolves.toMatchObject({
data: {
notifications: [
{
read: false,
reason: 'removed_user_from_group',
createdAt: expect.any(String),
from: {
__typename: 'Group',
id: 'closed-group',
},
relatedUser: {
id: 'group-owner',
},
},
],
},
errors: undefined,
})
})
})
})
})

View File

@ -126,7 +126,7 @@ describe('Query', () => {
author: null,
date: expect.any(String),
description:
'Human Connection, Weilheim an der Teck. Gefällt 24.407 Mal. An upcoming non-profit social network focused on local and global positive change. Twitter accounts : @hc_world (EN), @hc_deutschland (GE),...',
'Human Connection, Weilheim an der Teck. Gefällt 24.407 Mal. An upcoming non-profit social network focused on local and global positive change. Twitter accounts : @hc_world (EN), @hc_deutschland (GE),',
html: null,
image:
'https://scontent.ftxl3-1.fna.fbcdn.net/v/t1.0-1/c5.0.200.200a/p200x200/12108307_997373093648222_70057205881020137_n.jpg?_nc_cat=110&_nc_oc=AQnPPYQlR0dU556gOfl4xkXr7IPZdRIAUfQeXl3fpUv4DAsFN8T4PfgOjPwuq85GPKGZ5S5E5mWQ8IVV1UiRBAIZ&_nc_ht=scontent.ftxl3-1.fna&oh=90309adddaab38839782f16e7d4b7bcf&oe=5DEEDFE5',
@ -196,7 +196,7 @@ Have all the information for the brand in separate config files. Set these defau
publisher: 'YouTube',
date: expect.any(String),
description:
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al...',
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al',
url: 'https://www.youtube.com/watch?v=qkdXAtO40Fo',
image: 'https://i.ytimg.com/vi/qkdXAtO40Fo/maxresdefault.jpg',
audio: null,

View File

@ -17,7 +17,6 @@ const metascraper = Metascraper([
require('metascraper-lang')(),
require('metascraper-lang-detector')(),
require('metascraper-logo')(),
// require('metascraper-clearbit-logo')(),
require('metascraper-publisher')(),
require('metascraper-title')(),
require('metascraper-url')(),

View File

@ -0,0 +1,230 @@
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
import CONFIG from '../../config'
import { filterPosts, createPostMutation } from '../../graphql/posts'
CONFIG.CATEGORIES_ACTIVE = false
const driver = getDriver()
const neode = getNeode()
let query
let mutate
let authenticatedUser
let user
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
})
afterAll(async () => {
await cleanDatabase()
driver.close()
})
describe('Filter Posts', () => {
const now = new Date()
beforeAll(async () => {
user = await Factory.build('user', {
id: 'user',
name: 'User',
about: 'I am a user.',
})
authenticatedUser = await user.toJson()
await mutate({
mutation: createPostMutation(),
variables: {
id: 'a1',
title: 'I am an article',
content: 'I am an article written by user.',
},
})
await mutate({
mutation: createPostMutation(),
variables: {
id: 'a2',
title: 'I am anonther article',
content: 'I am another article written by user.',
},
})
await mutate({
mutation: createPostMutation(),
variables: {
id: 'e1',
title: 'Illegaler Kindergeburtstag',
content: 'Elli wird fünf. Wir feiern ihren Geburtstag.',
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventVenue: 'Garten der Familie Maier',
},
},
})
await mutate({
mutation: createPostMutation(),
variables: {
id: 'e2',
title: 'Räuber-Treffen',
content: 'Planung der nächsten Räuberereien',
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
eventVenue: 'Wirtshaus im Spessart',
},
},
})
})
describe('no filters set', () => {
it('finds all posts', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts() })
expect(result).toHaveLength(4)
expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: 'a1' }),
expect.objectContaining({ id: 'a2' }),
expect.objectContaining({ id: 'e1' }),
expect.objectContaining({ id: 'e2' }),
]),
)
})
})
describe('post type filter set to ["Article"]', () => {
it('finds the articles', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Article'] } } })
expect(result).toHaveLength(2)
expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: 'a1' }),
expect.objectContaining({ id: 'a2' }),
]),
)
})
})
describe('post type filter set to ["Event"]', () => {
it('finds the articles', async () => {
const {
data: { Post: result },
} = await query({ query: filterPosts(), variables: { filter: { postType_in: ['Event'] } } })
expect(result).toHaveLength(2)
expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: 'e1' }),
expect.objectContaining({ id: 'e2' }),
]),
)
})
})
describe('post type filter set to ["Article", "Event"]', () => {
it('finds all posts', async () => {
const {
data: { Post: result },
} = await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Article', 'Event'] } },
})
expect(result).toHaveLength(4)
expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: 'a1' }),
expect.objectContaining({ id: 'a2' }),
expect.objectContaining({ id: 'e1' }),
expect.objectContaining({ id: 'e2' }),
]),
)
})
})
describe('order events by event start descending', () => {
it('finds the events orderd accordingly', async () => {
const {
data: { Post: result },
} = await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_desc'] },
})
expect(result).toHaveLength(2)
expect(result).toEqual([
expect.objectContaining({
id: 'e1',
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
}),
expect.objectContaining({
id: 'e2',
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
}),
])
})
})
describe('order events by event start ascending', () => {
it('finds the events orderd accordingly', async () => {
const {
data: { Post: result },
} = await query({
query: filterPosts(),
variables: { filter: { postType_in: ['Event'] }, orderBy: ['eventStart_asc'] },
})
expect(result).toHaveLength(2)
expect(result).toEqual([
expect.objectContaining({
id: 'e2',
eventStart: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString(),
}),
expect.objectContaining({
id: 'e1',
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
}),
])
})
})
describe('filter events by event start date', () => {
it('finds only events after given date', async () => {
const {
data: { Post: result },
} = await query({
query: filterPosts(),
variables: {
filter: {
postType_in: ['Event'],
eventStart_gte: new Date(
now.getFullYear(),
now.getMonth(),
now.getDate() + 2,
).toISOString(),
},
},
})
expect(result).toHaveLength(1)
expect(result).toEqual([
expect.objectContaining({
id: 'e1',
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
}),
])
})
})
})

View File

@ -0,0 +1,47 @@
import { UserInputError } from 'apollo-server'
export const validateEventParams = (params) => {
if (params.postType && params.postType === 'Event') {
const { eventInput } = params
validateEventDate(eventInput.eventStart)
params.eventStart = eventInput.eventStart
if (eventInput.eventEnd) {
validateEventEnd(eventInput.eventStart, eventInput.eventEnd)
params.eventEnd = eventInput.eventEnd
}
if (eventInput.eventLocationName && !eventInput.eventVenue) {
throw new UserInputError('Event venue must be present if event location is given!')
}
params.eventVenue = eventInput.eventVenue
params.eventLocationName = eventInput.eventLocationName
params.eventIsOnline = !!eventInput.eventIsOnline
}
delete params.eventInput
let locationName
if (params.eventLocationName) {
locationName = params.eventLocationName
} else {
params.eventLocationName = null
locationName = null
}
return locationName
}
const validateEventDate = (dateString) => {
const date = new Date(dateString)
if (date.toString() === 'Invalid Date')
throw new UserInputError('Event start date must be a valid date!')
const now = new Date()
if (date.getTime() < now.getTime()) {
throw new UserInputError('Event start date must be in the future!')
}
}
const validateEventEnd = (start, end) => {
const endDate = new Date(end)
if (endDate.toString() === 'Invalid Date')
throw new UserInputError('Event end date must be a valid date!')
const startDate = new Date(start)
if (endDate < startDate)
throw new UserInputError('Event end date must be a after event start date!')
}

View File

@ -47,12 +47,22 @@ export default {
`
MATCH (resource {deleted: false, disabled: false})-[notification:NOTIFIED]->(user:User {id:$id})
${whereClause}
WITH user, notification, resource,
OPTIONAL MATCH (relatedUser:User { id: notification.relatedUserId })
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(relatedUser)
WITH user, notification, resource, membership, relatedUser,
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
RETURN notification {.*, from: finalResource, to: properties(user)}
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
WITH resource, user, notification, authors, posts, relatedUser, membership,
resource {.*,
__typename: labels(resource)[0],
author: authors[0],
post: posts[0],
myRole: membership.role } AS finalResource
RETURN notification {.*,
from: finalResource,
to: properties(user),
relatedUser: properties(relatedUser)
}
${orderByClause}
${offset} ${limit}
`,
@ -80,9 +90,10 @@ export default {
SET notification.read = TRUE
WITH user, notification, resource,
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
WITH resource, user, notification, authors, posts, membership,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role } AS finalResource
RETURN notification {.*, from: finalResource, to: properties(user)}
`,
{ resourceId: args.id, id: currentUser.id },
@ -109,9 +120,10 @@ export default {
SET notification.read = TRUE
WITH user, notification, resource,
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author), postType: filter(l IN labels(post) WHERE NOT l = "Post")} ] AS posts
OPTIONAL MATCH (resource)<-[membership:MEMBER_OF]-(user)
WITH resource, user, notification, authors, posts, membership,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], myRole: membership.role} AS finalResource
RETURN notification {.*, from: finalResource, to: properties(user)}
`,
{ id: currentUser.id },

View File

@ -397,18 +397,20 @@ describe('given some notifications', () => {
it('returns all as read', async () => {
const response = await mutate({ mutation: markAllAsReadMutation(), variables })
expect(response.data.markAllAsRead).toEqual([
{
createdAt: '2019-08-30T19:33:48.651Z',
from: { __typename: 'Comment', content: 'You have been mentioned in a comment' },
read: true,
},
{
createdAt: '2019-08-31T17:33:48.651Z',
from: { __typename: 'Post', content: 'You have been mentioned in a post' },
read: true,
},
])
expect(response.data.markAllAsRead).toEqual(
expect.arrayContaining([
{
createdAt: '2019-08-30T19:33:48.651Z',
from: { __typename: 'Comment', content: 'You have been mentioned in a comment' },
read: true,
},
{
createdAt: '2019-08-31T17:33:48.651Z',
from: { __typename: 'Post', content: 'You have been mentioned in a post' },
read: true,
},
]),
)
expect(response.errors).toBeUndefined()
})
})

View File

@ -7,6 +7,8 @@ import Resolver from './helpers/Resolver'
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
import { filterInvisiblePosts } from './helpers/filterInvisiblePosts'
import { filterPostsOfMyGroups } from './helpers/filterPostsOfMyGroups'
import { validateEventParams } from './helpers/events'
import { createOrUpdateLocations } from './users/location'
import CONFIG from '../../config'
const maintainPinnedPosts = (params) => {
@ -81,6 +83,9 @@ export default {
CreatePost: async (_parent, params, context, _resolveInfo) => {
const { categoryIds, groupId } = params
const { image: imageInput } = params
const locationName = validateEventParams(params)
delete params.categoryIds
delete params.image
delete params.groupId
@ -125,12 +130,13 @@ export default {
SET post.updatedAt = toString(datetime())
SET post.clickedCount = 0
SET post.viewedTeaserCount = 0
SET post:${params.postType}
WITH post
MATCH (author:User {id: $userId})
MERGE (post)<-[:WROTE]-(author)
${categoriesCypher}
${groupCypher}
RETURN post {.*}
RETURN post {.*, postType: filter(l IN labels(post) WHERE NOT l = "Post") }
`,
{ userId: context.user.id, categoryIds, groupId, params },
)
@ -142,6 +148,9 @@ export default {
})
try {
const post = await writeTxResultPromise
if (locationName) {
await createOrUpdateLocations('Post', post.id, locationName, session)
}
return post
} catch (e) {
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
@ -154,6 +163,9 @@ export default {
UpdatePost: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
const { image: imageInput } = params
const locationName = validateEventParams(params)
delete params.categoryIds
delete params.image
const session = context.driver.session()
@ -183,7 +195,16 @@ export default {
`
}
updatePostCypher += `RETURN post {.*}`
if (params.postType) {
updatePostCypher += `
REMOVE post:Article
REMOVE post:Event
SET post:${params.postType}
WITH post
`
}
updatePostCypher += `RETURN post {.*, postType: filter(l IN labels(post) WHERE NOT l = "Post")}`
const updatePostVariables = { categoryIds, params }
try {
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
@ -196,6 +217,9 @@ export default {
return post
})
const post = await writeTxResultPromise
if (locationName) {
await createOrUpdateLocations('Post', post.id, locationName, session)
}
return post
} finally {
session.close()
@ -310,6 +334,7 @@ export default {
`
MATCH (user:User {id: $userId}) WHERE user.role = 'admin'
MATCH (post:Post {id: $params.id})
WHERE NOT((post)-[:IN]->(:Group))
MERGE (user)-[pinned:PINNED {createdAt: toString(datetime())}]->(post)
SET post.pinned = true
RETURN post, pinned.createdAt as pinnedAt
@ -322,10 +347,12 @@ export default {
}))
})
const [transactionResult] = await writeTxResultPromise
const { pinnedPost, pinnedAt } = transactionResult
pinnedPostWithNestedAttributes = {
...pinnedPost,
pinnedAt,
if (transactionResult) {
const { pinnedPost, pinnedAt } = transactionResult
pinnedPostWithNestedAttributes = {
...pinnedPost,
pinnedAt,
}
}
} finally {
session.close()
@ -382,7 +409,19 @@ export default {
},
Post: {
...Resolver('Post', {
undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'],
undefinedToNull: [
'activityId',
'objectId',
'language',
'pinnedAt',
'pinned',
'eventVenue',
'eventLocation',
'eventLocationName',
'eventStart',
'eventEnd',
'eventIsOnline',
],
hasMany: {
tags: '-[:TAGGED]->(related:Tag)',
categories: '-[:CATEGORIZED]->(related:Category)',
@ -395,6 +434,7 @@ export default {
pinnedBy: '<-[:PINNED]-(related:User)',
image: '-[:HERO_IMAGE]->(related:Image)',
group: '-[:IN]->(related:Group)',
eventLocation: '-[:IS_IN]->(related:Location)',
},
count: {
commentsCount:

View File

@ -3,6 +3,10 @@ import Factory, { cleanDatabase } from '../../db/factories'
import gql from 'graphql-tag'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
import { createPostMutation } from '../../graphql/posts'
import CONFIG from '../../config'
CONFIG.CATEGORIES_ACTIVE = true
const driver = getDriver()
const neode = getNeode()
@ -15,29 +19,6 @@ let user
const categoryIds = ['cat9', 'cat4', 'cat15']
let variables
const createPostMutation = gql`
mutation ($id: ID, $title: String!, $content: String!, $language: String, $categoryIds: [ID]) {
CreatePost(
id: $id
title: $title
content: $content
language: $language
categoryIds: $categoryIds
) {
id
title
content
slug
disabled
deleted
language
author {
name
}
}
}
`
beforeAll(async () => {
await cleanDatabase()
@ -281,7 +262,7 @@ describe('CreatePost', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: createPostMutation, variables })
const { errors } = await mutate({ mutation: createPostMutation(), variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -296,7 +277,7 @@ describe('CreatePost', () => {
data: { CreatePost: { title: 'I am a title', content: 'Some content' } },
errors: undefined,
}
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject(
expected,
)
})
@ -313,25 +294,327 @@ describe('CreatePost', () => {
},
errors: undefined,
}
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject(
expected,
)
})
it('`disabled` and `deleted` default to `false`', async () => {
const expected = { data: { CreatePost: { disabled: false, deleted: false } } }
await expect(mutate({ mutation: createPostMutation, variables })).resolves.toMatchObject(
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject(
expected,
)
})
it('has label "Article" as default', async () => {
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject({
data: { CreatePost: { postType: ['Article'] } },
})
})
describe('with invalid post type', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: createPostMutation(),
variables: { ...variables, postType: 'not-valid' },
}),
).resolves.toMatchObject({
errors: [
{
message:
'Variable "$postType" got invalid value "not-valid"; Expected type PostType.',
},
],
})
})
})
describe('with post type "Event"', () => {
describe('without event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
},
}),
).resolves.toMatchObject({
errors: [
{
message: "Cannot read properties of undefined (reading 'eventStart')",
},
],
})
})
})
describe('with invalid event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: 'no date',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be a valid date!',
},
],
})
})
})
describe('with event start date in the past', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() - 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be in the future!',
},
],
})
})
})
describe('with valid start date and invalid end date', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: 'not-valid',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event end date must be a valid date!',
},
],
})
})
})
describe('with valid start date and end date before start date', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event end date must be a after event start date!',
},
],
})
})
})
describe('with valid start date and valid end date', () => {
it('creates the event', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
eventIsOnline: false,
},
},
errors: undefined,
})
})
})
describe('with valid start date and event is online', () => {
it('creates the event', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: true,
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: true,
},
},
errors: undefined,
})
})
})
describe('event location name is given but event venue is missing', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Berlin',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event venue must be present if event location is given!',
},
],
})
})
})
describe('valid event input without location', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: false,
},
},
errors: undefined,
})
})
})
describe('valid event input with location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: createPostMutation(),
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
eventLocation: {
lng: 12.374733,
lat: 51.340632,
},
},
},
errors: undefined,
})
})
})
})
})
})
describe('UpdatePost', () => {
let author, newlyCreatedPost
const updatePostMutation = gql`
mutation ($id: ID!, $title: String!, $content: String!, $image: ImageInput) {
UpdatePost(id: $id, title: $title, content: $content, image: $image) {
mutation (
$id: ID!
$title: String!
$content: String!
$image: ImageInput
$categoryIds: [ID]
$postType: PostType
$eventInput: _EventInput
) {
UpdatePost(
id: $id
title: $title
content: $content
image: $image
categoryIds: $categoryIds
postType: $postType
eventInput: $eventInput
) {
id
title
content
@ -341,26 +624,34 @@ describe('UpdatePost', () => {
}
createdAt
updatedAt
categories {
id
}
postType
eventStart
eventLocationName
eventVenue
eventLocation {
lng
lat
}
}
}
`
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
newlyCreatedPost = await Factory.build(
'post',
{
id: 'p9876',
authenticatedUser = await author.toJson()
const { data } = await mutate({
mutation: createPostMutation(),
variables: {
title: 'Old title',
content: 'Old content',
},
{
author,
categoryIds,
},
)
})
newlyCreatedPost = data.CreatePost
variables = {
id: 'p9876',
id: newlyCreatedPost.id,
title: 'New title',
content: 'New content',
}
@ -394,7 +685,7 @@ describe('UpdatePost', () => {
it('updates a post', async () => {
const expected = {
data: { UpdatePost: { id: 'p9876', content: 'New content' } },
data: { UpdatePost: { id: newlyCreatedPost.id, content: 'New content' } },
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
@ -405,7 +696,11 @@ describe('UpdatePost', () => {
it('updates a post, but maintains non-updated attributes', async () => {
const expected = {
data: {
UpdatePost: { id: 'p9876', content: 'New content', createdAt: expect.any(String) },
UpdatePost: {
id: newlyCreatedPost.id,
content: 'New content',
createdAt: expect.any(String),
},
},
errors: undefined,
}
@ -415,23 +710,20 @@ describe('UpdatePost', () => {
})
it('updates the updatedAt attribute', async () => {
newlyCreatedPost = await newlyCreatedPost.toJson()
const {
data: { UpdatePost },
} = await mutate({ mutation: updatePostMutation, variables })
expect(newlyCreatedPost.updatedAt).toBeTruthy()
expect(Date.parse(newlyCreatedPost.updatedAt)).toEqual(expect.any(Number))
expect(UpdatePost.updatedAt).toBeTruthy()
expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
})
/* describe('no new category ids provided for update', () => {
describe('no new category ids provided for update', () => {
it('resolves and keeps current categories', async () => {
const expected = {
data: {
UpdatePost: {
id: 'p9876',
id: newlyCreatedPost.id,
categories: expect.arrayContaining([{ id: 'cat9' }, { id: 'cat4' }, { id: 'cat15' }]),
},
},
@ -441,9 +733,9 @@ describe('UpdatePost', () => {
expected,
)
})
}) */
})
/* describe('given category ids', () => {
describe('given category ids', () => {
beforeEach(() => {
variables = { ...variables, categoryIds: ['cat27'] }
})
@ -452,7 +744,7 @@ describe('UpdatePost', () => {
const expected = {
data: {
UpdatePost: {
id: 'p9876',
id: newlyCreatedPost.id,
categories: expect.arrayContaining([{ id: 'cat27' }]),
},
},
@ -462,9 +754,160 @@ describe('UpdatePost', () => {
expected,
)
})
}) */
})
describe('params.image', () => {
describe('change post type to event', () => {
describe('with missing event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: updatePostMutation,
variables: { ...variables, postType: 'Event' },
}),
).resolves.toMatchObject({
errors: [
{
message: "Cannot read properties of undefined (reading 'eventStart')",
},
],
})
})
})
describe('with invalid event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: updatePostMutation,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: 'no-date',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be a valid date!',
},
],
})
})
})
describe('with event start date in the past', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() - 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be in the future!',
},
],
})
})
})
describe('event location name is given but event venue is missing', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Berlin',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event venue must be present if event location is given!',
},
],
})
})
})
describe('valid event input without location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
UpdatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
errors: undefined,
})
})
})
describe('valid event input with location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
},
},
}),
).resolves.toMatchObject({
data: {
UpdatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
eventLocation: {
lng: 12.374733,
lat: 51.340632,
},
},
},
errors: undefined,
})
})
})
})
describe.skip('params.image', () => {
describe('is object', () => {
beforeEach(() => {
variables = { ...variables, image: { sensitive: true } }

View File

@ -818,11 +818,13 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
]),
},
@ -846,11 +848,13 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
]),
},
@ -874,11 +878,13 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
]),
},
@ -902,11 +908,13 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
]),
},
@ -930,21 +938,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1319,16 +1331,19 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
]),
},
@ -1361,21 +1376,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1410,16 +1429,19 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1452,11 +1474,13 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
]),
},
@ -1489,21 +1513,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1534,21 +1562,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1579,21 +1611,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1628,21 +1664,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1675,21 +1715,25 @@ describe('Posts in Groups', () => {
id: 'post-to-public-group',
title: 'A post to a public group',
content: 'I am posting into a public group as a member of the group',
eventStart: null,
},
{
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
eventStart: null,
},
{
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},
@ -1739,11 +1783,13 @@ describe('Posts in Groups', () => {
id: 'post-to-closed-group',
title: 'A post to a closed group',
content: 'I am posting into a closed group as a member of the group',
eventStart: null,
},
{
id: 'post-to-hidden-group',
title: 'A post to a hidden group',
content: 'I am posting into a hidden group as a member of the group',
eventStart: null,
},
]),
},

View File

@ -223,8 +223,7 @@ export default {
},
searchResults: async (_parent, args, context, _resolveInfo) => {
const { query, limit } = args
let userId = null
if (context.user) userId = context.user.id
const userId = context.user?.id || null
const searchType = query.replace(/^([!@#&]?).*$/, '$1')
const searchString = query.replace(/^([!@#&])/, '')

View File

@ -33,7 +33,7 @@ const matchSomeWordsExactly = (str, boost = 2) => {
const matchBeginningOfWords = (str) => {
return str
.split(' ')
.filter((s) => s.length > 3)
.filter((s) => s.length >= 2)
.map((s) => s + '*')
.join(' ')
}

View File

@ -37,7 +37,7 @@ describe('queryString', () => {
describe('globbing for longer words', () => {
it('globs words with more than three characters', () => {
expect(queryString('a couple of words')).toContain('couple* words*')
expect(queryString('a couple of words')).toContain('couple* of* words*')
})
})
})

View File

@ -22,7 +22,7 @@ const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru']
const createLocation = async (session, mapboxData) => {
const data = {
id: mapboxData.id,
id: mapboxData.id + (mapboxData.address ? `-${mapboxData.address}` : ''),
nameEN: mapboxData.text_en,
nameDE: mapboxData.text_de,
nameFR: mapboxData.text_fr,
@ -33,6 +33,7 @@ const createLocation = async (session, mapboxData) => {
namePL: mapboxData.text_pl,
nameRU: mapboxData.text_ru,
type: mapboxData.id.split('.')[0].toLowerCase(),
address: mapboxData.address,
lng: mapboxData.center && mapboxData.center.length ? mapboxData.center[0] : null,
lat: mapboxData.center && mapboxData.center.length ? mapboxData.center[1] : null,
}
@ -54,6 +55,10 @@ const createLocation = async (session, mapboxData) => {
if (data.lat && data.lng) {
mutation += ', l.lat = $lat, l.lng = $lng'
}
if (data.address) {
mutation += ', l.address = $address'
}
mutation += ' RETURN l.id'
await session.writeTransaction((transaction) => {
@ -72,7 +77,7 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
locationName,
)}.json?access_token=${
CONFIG.MAPBOX_TOKEN
}&types=region,place,country&language=${locales.join(',')}`,
}&types=region,place,country,address&language=${locales.join(',')}`,
)
debug(res)
@ -103,6 +108,10 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s
let parent = data
if (parent.address) {
parent.id += `-${parent.address}`
}
if (data.context) {
await asyncForEach(data.context, async (ctx) => {
await createLocation(session, ctx)

View File

@ -0,0 +1,4 @@
enum PostType {
Article
Event
}

View File

@ -1,5 +0,0 @@
enum ReasonNotification {
mentioned_in_post
mentioned_in_comment
commented_on_post
}

View File

@ -1,17 +1,3 @@
type Mutation {
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!
changePassword(oldPassword: String!, newPassword: String!): String!
requestPasswordReset(email: String!): Boolean!
resetPassword(email: String!, nonce: String!, newPassword: String!): Boolean!
# Shout the given Type and ID
shout(id: ID!, type: ShoutTypeEnum): Boolean!
# Unshout the given Type and ID
unshout(id: ID!, type: ShoutTypeEnum): Boolean!
followUser(id: ID!): User
unfollowUser(id: ID!): User
}
enum ShoutTypeEnum {
Post
}
@ -27,4 +13,4 @@ type Reward {
type SharedInboxEndpoint {
id: ID!
uri: String
}
}

View File

@ -6,9 +6,10 @@ type NOTIFIED {
updatedAt: String!
read: Boolean
reason: NotificationReason
relatedUser: User
}
union NotificationSource = Post | Comment
union NotificationSource = Post | Comment | Group
enum NotificationOrdering {
createdAt_asc
@ -21,6 +22,10 @@ enum NotificationReason {
mentioned_in_post
mentioned_in_comment
commented_on_post
user_joined_group
user_left_group
changed_group_member_role
removed_user_from_group
}
type Query {

View File

@ -83,6 +83,8 @@ input _PostFilter {
emotions_every: _PostEMOTEDFilter
group: _GroupFilter
postsInMyGroups: Boolean
postType_in: [PostType]
eventStart_gte: String
}
enum _PostOrdering {
@ -104,6 +106,8 @@ enum _PostOrdering {
language_desc
pinned_asc
pinned_desc
eventStart_asc
eventStart_desc
}
@ -171,12 +175,30 @@ type Post {
@cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)")
group: Group @relation(name: "IN", direction: "OUT")
postType: [PostType]
@cypher(statement: "RETURN filter(l IN labels(this) WHERE NOT l = 'Post')")
eventLocationName: String
eventLocation: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
eventVenue: String
eventStart: String
eventEnd: String
eventIsOnline: Boolean
}
input _PostInput {
id: ID!
}
input _EventInput {
eventStart: String!
eventEnd: String
eventVenue: String
eventLocationName: String
eventIsOnline: Boolean
}
type Mutation {
CreatePost(
id: ID
@ -189,6 +211,8 @@ type Mutation {
categoryIds: [ID]
contentExcerpt: String
groupId: ID
postType: PostType = Article
eventInput: _EventInput
): Post
UpdatePost(
id: ID!
@ -200,6 +224,8 @@ type Mutation {
visibility: Visibility
language: String
categoryIds: [ID]
postType: PostType
eventInput: _EventInput
): Post
DeletePost(id: ID!): Post
AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
@ -207,6 +233,11 @@ type Mutation {
pinPost(id: ID!): Post
unpinPost(id: ID!): Post
markTeaserAsViewed(id: ID!): Post
# Shout the given Type and ID
shout(id: ID!, type: ShoutTypeEnum): Boolean!
# Unshout the given Type and ID
unshout(id: ID!, type: ShoutTypeEnum): Boolean!
}
type Query {

View File

@ -214,10 +214,21 @@ type Mutation {
muteUser(id: ID!): User
unmuteUser(id: ID!): User
blockUser(id: ID!): User
unblockUser(id: ID!): User
followUser(id: ID!): User
unfollowUser(id: ID!): User
switchUserRole(role: UserRole!, id: ID!): User
saveCategorySettings(activeCategories: [String]): Boolean
requestPasswordReset(email: String!): Boolean!
resetPassword(email: String!, nonce: String!, newPassword: String!): Boolean!
changePassword(oldPassword: String!, newPassword: String!): String!
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!
}

View File

@ -82,7 +82,13 @@ const createServer = (options) => {
const app = express()
app.set('driver', driver)
app.use(helmet())
// TODO: this exception is required for the graphql playground, since the playground loads external resources
// See: https://github.com/graphql/graphql-playground/issues/1283
app.use(
helmet(
(CONFIG.DEBUG && { contentSecurityPolicy: false, crossOriginEmbedderPolicy: false }) || {},
),
)
app.use('/.well-known/', webfinger())
app.use(express.static('public'))
app.use(bodyParser.json({ limit: '10mb' }))

111
backend/tsconfig.json Normal file
View File

@ -0,0 +1,111 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./build", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["./src/**/*"],
"exclude": ["./src/**/*.spec.ts", "./src/**/*.spec.js"]
}

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,62 @@ You have the following options for a deployment:
After the first deployment of the new network on your server, the database is initialized with the default administrator:
- E-mail: admin@example.org
- Password: 1234
- E-mail: `admin@example.org`
- Password: `1234`
***ATTENTION:*** When you are logged in for the first time, please change your (the admin's) e-mail to an existing one and change your password to a secure one !!!
## Use the Scripts
To use all the scripts you have to set the variable `CONFIGURATION` in your terminal by entering:
```bash
# in deployment folder
# set configuration name to folder name in 'configurations' folder (network name)
$ export CONFIGURATION=`<your-configuration-name>`
# to check this
$ echo $CONFIGURATION
```
### Secrets Encrypt/Decrypt
To encrypt and decrypt the secrets of your network in your terminal set a correct password in a (new) file `configurations/<your-configuration-name>/SECRET`.
If done please enter:
```bash
# in deployment folder
# encrypt secrets
$ scripts/secrets.encrypt.sh
# decrypt secrets
$ scripts/secrets.decrypt.sh
```
### Maintenance Mode On/Off
Activate or deactivate maintenance mode in your terminal:
```bash
# in deployment folder
# activate maintenance mode
$ scripts/cluster.maintenance.sh on
# deactivate maintenance mode
$ scripts/cluster.maintenance.sh off
```
### Backup Script
To save a locale backup of the database and uploaded images:
```bash
# in deployment folder
# save backup
$ scripts/cluster.backup.sh
```
The backup will be saved into your network folders `backup` folder in a new folder with the date and time.

View File

@ -2,7 +2,9 @@
When you overtake this deploy and rebrand repo to your network you have to recognize the following changes and doings:
## Version >= 2.4.0 with 'ocelotDockerVersionTag' 2.4.0-XXX
## Version >= 2.4.0 with 'ocelotDockerVersionTag' 2.4.0-298
- You have to set `SHOW_CONTENT_FILTER_HEADER_MENU` and `SHOW_CONTENT_FILTER_MASONRY_GRID` in `branding/constants/filter.js` originally in main code file `webapp/constants/filter.js` to your preferred value.
### Main Code PR feat(webapp): map #5843

View File

@ -1,5 +0,0 @@
# Configure And Branding
In this folder you will find all configuration files and logo images to customise the configuration and branding of the [ocelot.social](https://github.com/Ocelot-Social-Community/Ocelot-Social) network code to your own needs.
Please change these and they will be used automatically as part of the [deployment](/deployment/README.md) process.

View File

@ -1,5 +0,0 @@
/*
*
* Here, all SCSS variables and classes can be adapted to your custom design.
*
*/

View File

@ -1 +0,0 @@
export const PROGRESS_BAR_COLOR_TYPE = 'gradient' // 'uni' is the other option

View File

@ -1,8 +0,0 @@
// this file is duplicated in `backend/src/config/` and `webapp/constants/` and replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/constants/
export default {
SUPPORT_EMAIL: 'hello@ocelot.social',
MODERATION_EMAIL: 'hello@ocelot.social',
// ATTENTION: the following links have to be defined even for internal pages with full URLs as example like 'https://staging.ocelot.social/support', because they are used in e-mails!
ORGANIZATION_LINK: 'https://ocelot.social',
SUPPORT_LINK: 'https://ocelot.social',
}

View File

@ -1,5 +0,0 @@
// this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js`
export const NAME_LENGTH_MIN = 3
export const NAME_LENGTH_MAX = 50
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags
export const SHOW_GROUP_BUTTON_IN_HEADER = true

View File

@ -1,13 +0,0 @@
export default {
MENU: [
// {
// nameIdent: 'nameIdent',
// path: '/',
// },
// {
// nameIdent: 'nameIdent',
// url: 'https://ocelot.social',
// target: '_blank',
// },
],
}

View File

@ -1,152 +0,0 @@
// this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/constants/
import { defaultPageParamsPages } from '~/components/utils/InternalPages.js'
const ORGANIZATION = defaultPageParamsPages.ORGANIZATION.overwrite({
// if defined it's dominating
externalLink: {
url: 'https://ocelot.social',
target: '_blank',
},
internalPage: {
// footerIdent: 'site.made', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.made', // localized string identifier, if undefined default is used
// headlineIdent: 'site.made', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const DONATE = defaultPageParamsPages.DONATE.overwrite({
// if defined it's dominating
externalLink: {
// we use 'ocelot-social.herokuapp.com' at the moment, because redirections of 'ocelot.social' subpages are not working correctly
url: 'https://ocelot-social.herokuapp.com/donations',
target: '_blank',
},
internalPage: {
// footerIdent: 'site.donate', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.donate', // localized string identifier, if undefined default is used
// headlineIdent: 'site.donate', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const IMPRINT = defaultPageParamsPages.IMPRINT.overwrite({
// if defined it's dominating
externalLink: {
// we use 'ocelot-social.herokuapp.com' at the moment, because redirections of 'ocelot.social' subpages are not working correctly
url: 'https://ocelot-social.herokuapp.com/imprint',
target: '_blank',
},
internalPage: {
// footerIdent: 'site.imprint', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.imprint', // localized string identifier, if undefined default is used
// headlineIdent: 'site.imprint', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const TERMS_AND_CONDITIONS = defaultPageParamsPages.TERMS_AND_CONDITIONS.overwrite({
// externalLink: null, // if defined it's dominating
internalPage: {
// footerIdent: 'site.termsAndConditions', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.termsAndConditions', // localized string identifier, if undefined default is used
// headlineIdent: 'site.termsAndConditions', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const CODE_OF_CONDUCT = defaultPageParamsPages.CODE_OF_CONDUCT.overwrite({
// externalLink: null, // if defined it's dominating
internalPage: {
// footerIdent: 'site.code-of-conduct', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.code-of-conduct', // localized string identifier, if undefined default is used
// headlineIdent: 'site.code-of-conduct', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const DATA_PRIVACY = defaultPageParamsPages.DATA_PRIVACY.overwrite({
// externalLink: null, // if defined it's dominating
internalPage: {
// footerIdent: 'site.data-privacy', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.data-privacy', // localized string identifier, if undefined default is used
// headlineIdent: 'site.data-privacy', // localized string identifier, on null it's hidden, if undefined default is used
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const FAQ = defaultPageParamsPages.FAQ.overwrite({
// externalLink: null, // if defined it's dominating
internalPage: {
// footerIdent: 'site.faq', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.faq', // localized string identifier, if undefined default is used
// headlineIdent: 'site.faq', // on null default is used, on empty string it's hidden
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
const SUPPORT = defaultPageParamsPages.SUPPORT.overwrite({
// if defined it's dominating
externalLink: {
url: 'https://ocelot.social',
target: '_blank',
},
internalPage: {
// footerIdent: 'site.support', // localized string identifier, if undefined default is used
// headTitleIdent: 'site.support', // localized string identifier, if undefined default is used
// headlineIdent: 'site.support', // on null default is used, on empty string it's hidden
hasContainer: true,
hasBaseCard: true,
hasLoginInHeader: true,
// in case internal page content is here 'branding/locales/html/'
},
})
export default {
LANDING_PAGE: '/login', // examples: '/login', '/registration', '/organization', or external 'https://ocelot.social'
// you can find and store templates for 👇🏼 at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/
ORGANIZATION,
DONATE,
IMPRINT,
TERMS_AND_CONDITIONS,
CODE_OF_CONDUCT,
DATA_PRIVACY,
FAQ,
SUPPORT,
FOOTER_LINK_LIST: [
ORGANIZATION,
TERMS_AND_CONDITIONS,
CODE_OF_CONDUCT,
DATA_PRIVACY,
FAQ,
DONATE,
IMPRINT,
SUPPORT,
],
}

View File

@ -1,24 +0,0 @@
// this file is duplicated in `backend/src/config/logos.js` and `webapp/constants/logos.js` and replaced on rebranding
// this are the paths in the webapp
export default {
LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg',
LOGO_HEADER_WIDTH: '130px',
LOGO_HEADER_CLICK: {
// externalLink: {
// url: 'https://ocelot.social',
// target: '_blank',
// },
externalLink: null,
internalPath: {
to: {
name: 'index',
},
scrollTo: '.main-navigation',
},
},
LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg',
LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg',
LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg',
LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg',
LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg',
}

View File

@ -1,9 +0,0 @@
// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js` and replaced on rebranding
export default {
APPLICATION_NAME: 'ocelot.social',
APPLICATION_SHORT_NAME: 'ocelot.social',
APPLICATION_DESCRIPTION: 'Ocelot Social Community',
COOKIE_NAME: 'ocelot-social-token',
ORGANIZATION_NAME: 'busFaktor e.V.',
ORGANIZATION_JURISDICTION: 'Deutschland',
}

View File

@ -1 +0,0 @@
we can put multilanguage e-mails and a layout.html in here

View File

@ -1 +0,0 @@
we can put translated e-mails in here

View File

@ -1 +0,0 @@
we can put translated e-mails in here

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Präambel
</h3>
<p>
Ich bin der Inhalt vom Verhaltenskodex.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Information über die Erhebung personenbezogener Daten
</h3>
<p>
Das hier wäre der Inhalt der Datenschutzbestimmungen.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Wohin kann ich spenden?
</h3>
<p>
Hier steht was zu den Spenden.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,67 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Wie bediene ich dieses Netzwerk?
</h3>
<p>
Hier findest Du die
<a href="https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki" target="_blank" >Bedienungsanleitung</a>.<br>
</p>
<h3>
Betreiberspezifische FAQs
</h3>
<p>
Hier steht was zu den betreiberspezifischen FAQs.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Betreiber
</h3>
<p>
Ich bin das Impressum.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Das Entwicklernetzwerk
</h3>
<p>
Hier wird das Netzwerk beschrieben.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Ansprechpartner
</h3>
<p>
Ich bin der Inhalt vom Support.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,61 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
Für das soziale Netzwerk Ocelot.Social Staging
</h2>
<h3>
Nutzung und Lizenz
</h3>
<p>
Ich bin der Inhalt der Seite "Nutzungsbedingungen".
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Präambel
</h3>
<p>
I am the content of the code of conduct.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Information about the collection of personal data
</h3>
<p>
This would be our data privacy section.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Where can I donate?
</h3>
<p>
Here's what it says about donations.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,67 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
How do I operate this network?
</h3>
<p>
Here you can find the
<a href="https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki" target="_blank" >user manual</a>.<br>
</p>
<h3>
Operator-Specific FAQs
</h3>
<p>
Here are the operator-specific FAQs.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Operator
</h3>
<p>
I am the imprint.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
The Developers Network
</h3>
<p>
Here the network is described.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Contact
</h3>
<p>
I am the content of the support.
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,60 +0,0 @@
<!-- this file is replaced on rebranding by https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/locales/html/ -->
<!-- you can find and store templates at https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/tree/master/branding/templates/ -->
<div class="info-page">
<h2>
For the social network Ocelot.Social Staging
</h2>
<h3>
Use and License
</h3>
<p>
I am the content of the page "Terms And Conditions".
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,62 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/de/faq.html' -->
<!-- template for the 'FAQ' in general -->
<div class="info-page">
<h2>
Für das soziale Netzwerk {{ organization }}
</h2>
<h3>
Wie bediene ich dieses Netzwerk?
</h3>
<p>
Hier findest Du die
<a href="https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki" target="_blank" >Bedienungsanleitung</a>.<br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,62 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/de/faq.html' -->
<!-- template for the 'FAQ' in general -->
<div class="info-page">
<h2>
For the social network of {{ organization }}
</h2>
<h3>
How do I operate this network?
</h3>
<p>
Here you can find the
<a href="https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki" target="_blank" >user manual</a>.<br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,163 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/en/code-of-conduct.html' -->
<!-- template for the 'Code Of Conduct' from the old HC network. in the old repo you will find other languages as well -->
<div class="info-page">
<h2>
Für das soziale Netzwerk {{ organization }}
</h2>
<h3>
Präambel
</h3>
<p>
{{ networkName}} ist ein gemeinnütziges soziales Wissens- und Aktionsnetzwerk der nächsten Generation.
Von Menschen für Menschen.
Free-Software, Open-Source, fair und transparent.
Für positiven lokalen und globalen Wandel in allen Lebensbereichen.
Wir gestalten den öffentlichen Austausch von Wissen, Ideen und Projekten völlig neu.
Die Funktionen von {{ networkName }} bringen die Menschen zusammen offline und online so dass wir die Welt zu einem besseren Ort machen können.<br>
</p>
<h3>
Zweck
</h3>
<p>
Mit diesen Verhaltensregeln regeln wir die wesentlichen Grundsätze für das Verhalten in unserem Sozialen Netzwerk.
Dabei ist die Menschenrechtscharta der Vereinten Nationen unsere Orientierung und bildet das Herz unseres Werteverständnisses.
Die Verhaltensregeln dienen als Leitsätze für den persönlichen Auftritt und den Umgang untereinander.
Wer als Nutzer im {{ networkName }} Netzwerk aktiv ist, Beiträge verfasst, kommentiert oder mit anderen Nutzern, auch außerhalb des Netzwerkes, Kontakt aufnimmt, erkennt diese Verhaltensregeln als verbindlich an.<br>
</p>
<h3>
Erwartetes Verhalten
</h3>
<p>
Die folgenden Verhaltensweisen werden von allen Community-Mitgliedern erwartet und gefordert:<br>
</p>
<ul>
<li>
Sei rücksichtsvoll und respektvoll, bei dem, was Du schreibst und tust.
</li>
<li>
Versuche auf andere zuzugehen, bevor ein Konflikt entsteht.
</li>
<li>
Vermeide erniedrigende, diskriminierende oder belästigende Verhaltensweisen und Ausdrücke.
</li>
<li>
Achte Dein Umfeld und Deine Mitmenschen. Warne die Verantwortlichen der Community, falls Du eine gefährliche Situation, jemanden in Not oder Verstöße gegen diesen Verhaltenskodex bemerkst, auch wenn diese unbedeutend erscheinen.
</li>
</ul>
<h3>
Nichtakzeptables Verhalten
</h3>
<p>
Die folgenden Verhaltensweisen sind in unserer Community inakzeptabel:<br>
</p>
<ul>
<li>
Diskriminierende Beiträge, Kommentare, Äußerungen oder Beleidigungen, insbesondere solche, die sich auf Geschlecht, sexuelle Orientierung, Rasse, Religion, politische oder weltanschauliche Ausrichtung oder Behinderung beziehen
</li>
<li>
Das Senden oder Verlinken eindeutig pornografischen Materials
</li>
<li>
Verherrlichung oder Verharmlosung grausamer oder unmenschlicher Gewalttätigkeiten
</li>
<li>
Das Veröffentlichen von personenbezogenen Daten anderer ohne deren Einverständnis oder das Androhen dessen („Doxing“)
</li>
<li>
Absichtliche Einschüchterung, Stalking oder Verfolgung
</li>
<li>
Bewerben von Produkten und Dienstleistungen mit kommerzieller Absicht
</li>
<li>
Strafbares Verhalten bzw. Verstoß gegen deutsches Recht
</li>
<li>
Befürworten oder Ermutigen zu diesen Verhaltensweisen
</li>
</ul>
<h3>
Konsequenzen inakzeptablen Verhaltens
</h3>
<p>
Wenn ein Gemeinschaftsmitglied inakzeptables Verhalten an den Tag legt, können die verantwortlichen Betreiber, Moderatoren und Administratoren des Netzwerks angemessene Maßnahmen ergreifen, u.a.:<br>
</p>
<ul>
<li>
Auffordern zum sofortigen Abstellen des inakzeptablen Verhaltens
</li>
<li>
Sperren oder Löschen von Kommentaren
</li>
<li>
Vorübergehender Ausschluss aus dem jeweiligen Beitrag
</li>
<li>
Sperren bzw. Löschen von Inhalten
</li>
<li>
Vorübergehender Entzug von Schreibrechten
</li>
<li>
Vorübergehender Ausschluss aus dem Netzwerk
</li>
<li>
Endgültiger Ausschluss aus dem Netzwerk
</li>
<li>
Verstöße gegen deutsches Recht können zur Anzeige gebracht werden.
</li>
</ul>
<p>
Wenn Du einem inakzeptablen Verhalten ausgesetzt bist, es miterlebst oder andere Bedenken hast, melde bitte so schnell wie möglich den oder die entsprechenden Inhalte an die Moderatoren.
Bitte klicke beim Beitrag, Kommentar oder Benutzer auf die drei Punkte und melde ihn über das aufgeklappte Menü.<br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,136 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/de/terms-and-conditions.html' -->
<!-- template for the 'Terms And Conditions' from the old HC network. in the old repo you will find other languages as well -->
<div class="info-page">
<h2>
Für das soziale Netzwerk {{ organization }}
</h2>
<h3>
Nutzungsbedingungen
</h3>
<p>
Die folgenden Nutzungsbedingungen sind Basis für die Nutzung unseres {{ networkName }} Netzwerkes.
Beim Registrieren musst Du diese anerkennen und wir werden Dich auch später über ggf. stattfindende Änderungen informieren.
Unser Netzwerk wird in der {{ organizationLocation }} betrieben und unterliegt daher {{ organizationLocation2 }} Recht.
Gerichtsstand ist {{ legacyLocation }}.
Zu Details schau in unser Impressum: <a href="https://{{ networkURL }}/imprint/" target="_blank" >https://{{ networkURL }}/imprint/</a>.<br>
</p>
<h3>
Nutzung und Lizenz
</h3>
<p>
Sind Inhalte, die Du bei uns einstellst, durch Rechte am geistigen Eigentum geschützt, erteilst Du uns eine nicht-exklusive, übertragbare, unterlizenzierbare und weltweite Lizenz für die Nutzung dieser Inhalte für die Bereitstellung in unserem Netzwerk.
Diese Lizenz endet, sobald Du Deine Inhalte oder Deinen ganzen Account löscht.
Bedenke, dass andere Deine Inhalte weiter teilen können und wir diese nicht löschen können.<br>
</p>
<h3>
Datenschutz
</h3>
<p>
Unser Netzwerk ist ein soziales Wissens- und Aktionsnetzwerk.
Daher ist es uns besonders wichtig, dass möglichst viele Inhalte öffentlich zugänglich sind.
Im Laufe der Entwicklung unseres Netzwerkes wird es mehr und mehr die Möglichkeit geben, über die Sichtbarkeit der selbst angegebenen bzw. persönlichen Daten zu entscheiden.
Über diese neuen Funktionen werden wir Euch informieren.
Ansonsten gilt, dass Du immer darüber nachdenken solltest, welche persönlichen Daten Du über Dich (oder andere) preisgibst.
Dies gilt insbesondere für Inhalte von Beiträgen und Kommentaren, da diese einen weitgehend öffentlichen Charakter haben.
Später wird es Möglichkeiten geben, die Sichtbarkeit Deines Profils einzuschränken.
Teil der Nutzungsbedingungen ist unsere Datenschutzerklärung, die Dich über die einzelnen Datenverarbeitungen in unserem Netzwerk informiert: <a href="https://{{ networkURL }}/data-privacy" target="_blank">https://{{ networkURL }}/data-privacy</a>.
Unsere Datenschutzerklärung ist an die Gesetzeslage und die Charakteristika unseres Netzwerks angepasst und gilt immer in der aktuellsten Version.<br>
</p>
<h3>
Verhaltenscodex
</h3>
<p>
Unser Verhaltenskodex dient als Leitfaden für das persönliche Auftreten und den Umgang miteinander.
Wer als Nutzer im {{ networkName }} Netzwerk aktiv ist, Beiträge verfasst, kommentiert oder mit anderen Nutzern, auch außerhalb des Netzwerkes, Kontakt aufnimmt, erkennt diese Verhaltensregeln als verbindlich an. <a href="https://{{ networkURL }}/code-of-conduct" target="_blank">https://{{ networkURL }}/code-of-conduct</a><br>
</p>
<h3>
Moderation
</h3>
<p>
Bis unsere finanziellen Möglichkeiten uns erlauben, das Community-Moderationssystem zu implementieren, moderieren wir mit einem vereinfachten System und eigenen bzw. ggf. ehrenamtlichen Mitarbeitern.
Wir schulen diese Moderatoren und aus diesem Grund treffen auch nur diese entsprechende Entscheidungen.
Diese Moderatoren führen Ihre Tätigkeit anonym aus.
Du kannst uns Beiträge, Kommentare und auch Nutzer melden (wenn diese zum Beispiel in ihrem Profil Angaben machen oder Bilder haben, die diese Nutzungsbedingungen verletzen).
Wenn Du uns etwas meldest, kannst Du einen Meldegrund angeben und noch eine kurze Erläuterung mitgeben.
Wir schauen uns dann das Gemeldete an und sanktionieren ggf., z.B. indem wir Beiträge, Kommentare oder Nutzer sperren.
Du und auch der Betroffene erhalten derzeitig von uns leider noch keine Rückmeldung, das ist aber in Planung.
Unabhängig davon behalten wir uns prinzipiell Sanktionen vor aus Gründen, die unter Umständen nicht oder noch nicht in unserem Verhaltenscodex oder diesen Nutzungsbedingungen aufgeführt sind.<br>
</p>
<h3>
Fehler und Rückmeldungen
</h3>
<p>
Wir sind sehr bemüht, unser Netzwerk und unsere Daten sicher und abrufbar zu erhalten.
Jede neue Version der Software durchläuft sowohl automatisierte als auch manuelle Tests.
Es können jedoch unvorhergesehene Fehler auftreten.
Deshalb sind wir dankbar für jeden gemeldeten Fehler.
Du kannst gerne jeden von Dir entdeckten Fehler dem Support/der Hilfe-Assistenz mitteilen: <a href="https://{{ networkURL }}/support" target="_blank">https://{{ networkURL }}/support</a>.<br>
</p>
<h3>
Keine kommerzielle Nutzung
</h3>
<p>
Die Nutzung des {{ networkName }} Netzwerkes ist nicht für kommerzielle Zwecke gestattet.
Darunter fällt unter anderem das Bewerben von Produkten mit kommerzieller Absicht, das Einstellen von Affiliate-Links (Geschäftspartner-Links), direkter Aufruf zu Spenden oder finanzieller Unterstützung für Zwecke, die steuerlich nicht als gemeinnützig anerkannt sind.<br>
</p>
<h3>
Keine politische Nutzung
</h3>
<p>
Nutzerkonten von politischen Parteien oder offizielle Nutzerkonten eines politischen Vertreters sind unzulässig.<br>
</p>
<h3>
Hilfe und Fragen
</h3>
<p>
Für Hilfe und Fragen haben wir Dir eine umfassende Sammlung an häufig gestellten Fragen und Antworten (FAQ) zusammengestellt; Du findest diese auf <a href="https://{{ networkURL }}/faq" target="_blank">https://{{ networkURL }}/faq</a>.<br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,163 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/en/code-of-conduct.html' -->
<!-- template for the 'Code Of Conduct' from the old HC network. in the old repo you will find other languages as well -->
<div class="info-page">
<h2>
For the social network of {{ organization }}
</h2>
<h3>
Präambel
</h3>
<p>
{{ networkName }} is a non-profit social knowledge and action network of the next generation.
By people - for people. Free software, open source, fair and transparent.
For positive local and global change in all areas of life.
We completely redesign the public exchange of knowledge, ideas and projects.
The functions of {{ networkName }} bring people together - offline and online - so that we can make the world a better place.<br>
</p>
<h3>
Purpose
</h3>
<p>
With these code of conduct we regulate the essential principles for behavior in our social network.
The United Nations Charter of Human Rights is our orientation and forms the heart of our understanding of values.
The code of conduct serves as guiding principles for our personal appearance and interaction with one another.
Anyone who is active as a user in the {{ networkName }} Network, writes articles, comments or contacts other users, including those outside the network,acknowledges these rules of conduct as binding.<br>
</p>
<h3>
Expected Behaviour
</h3>
<p>
The following behaviors are expected and requested of all community members:<br>
</p>
<ul>
<li>
Exercise consideration and respect in your speech and actions.
</li>
<li>
Attempt collaboration before conflict.
</li>
<li>
Refrain from demeaning, discriminatory, or harassing behavior and speech.
</li>
<li>
Be mindful of your surroundings and of your fellow participants.
Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
</li>
</ul>
<h3>
Unacceptable Behavior
</h3>
<p>
The following behaviors are unacceptable within our community:<br>
</p>
<ul>
<li>
Discriminatory posts, comments, utterances or insults, particularly those relating to gender, sexual orientation, race, religion, political or philosophical orientation or disability.
</li>
<li>
Posting or linking of clearly pornographic material.
</li>
<li>
Glorification or trivialization of cruel or inhuman acts of violence.
</li>
<li>
The disclosure of others' personal information without their consent or threat there of ("doxing").
</li>
<li>
Intentional intimidation, stalking or persecution.
</li>
<li>
Advertising products and services with commercial intent.
</li>
<li>
Criminal behavior or violation of German law.
</li>
<li>
Endorse or encourage such conduct.
</li>
</ul>
<h3>
Consequences of Unacceptable Behavior
</h3>
<p>
If a community member exhibits unacceptable behaviour, the responsible operators, moderators and administrators of the network may take appropriate measures, including but not limited to:<br>
</p>
<ul>
<li>
Request for immediate cessation of unacceptable conduct
</li>
<li>
Locking or deleting comments
</li>
<li>
Temporary exclusion from the respective post or contribution
</li>
<li>
Blocking or deleting of content
</li>
<li>
Temporary withdrawal of write permissions
</li>
<li>
Temporary exclusion from the network
</li>
<li>
Final exclusion from the network
</li>
<li>
Violations of German law can be reported.
</li>
</ul>
<p>
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible and link or refer to the corresponding content.
Please click on the three dots on the post, comment or user and report it using the drop-down menu.<br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,135 +0,0 @@
<!-- this file is a template for rebranding of 'branding/locales/html/en/terms-and-conditions.html' -->
<!-- template for the 'Terms And Conditions' from the old HC network. in the old repo you will find other languages as well -->
<div class="info-page">
<h2>
For the social network of {{ organization }}
</h2>
<h3>
Terms of Service
</h3>
<p>
The following terms of use form the basis for the use of our network.
When you register, you must accept them and we will inform you later about any changes that may take place.
The {{ networkName }} Network is operated in Germany and is therefore subject to German law.
Place of jurisdiction is {{ organizationLocation }}.
For details see our imprint: <a href="https://{{ networkURL }}/imprint/" target="_blank" >https://{{ networkURL }}/imprint/</a><br>
</p>
<h3>
Use and License
</h3>
<p>
If any content you post to us is protected by intellectual property rights, you grant us a non-exclusive, transferable, sublicensable, worldwide license to use such content for posting to our network.
This license expires when you delete your content or your entire account.
Remember that others may share your content and we cannot delete it.<br>
</p>
<h3>
Privacy Statement
</h3>
<p>
Our network is a social knowledge and action network.
It is therefore particularly important to us that as much content as possible is publicly accessible.
In the course of the development of our network there will be more and more the possibility to decide about the visibility of the personal data.
We will inform you about these new features.
Otherwise, you should always think about which personal data you disclose about yourself (or others).
This applies in particular to the content of posts and comments, as these have a largely public character.
Later there will be possibilities to limit the visibility of your profile.
Part of the terms of service is our privacy statement, which informs you about the individual data processing operations in our network: <a href="https://{{ networkURL }}/data-privacy" target="_blank">https://{{ networkURL }}/data-privacy</a>.
Our privacy statement is adapted to the legal situation and characteristics of our network and is always valid in the most current version.<br>
</p>
<h3>
Code of Conduct
</h3>
<p>
Our code of conduct serves as a handbook for personal appearance and interaction with each other.
Whoever is active as a user in the {{ networkName }} network, writes articles, comments or makes contact with other users, even outside the network, acknowledges these rules of conduct as binding. <a href="https://{{ networkURL }}/code-of-conduct" target="_blank">https://{{ networkURL }}/code-of-conduct</a><br>
</p>
<h3>
Moderation
</h3>
<p>
Until our financial possibilities allow us to implement the community moderation system, we moderate with a simplified system and with our own or possibly volunteer staff.
We train these moderators and for this reason only they make the appropriate decisions.
These moderators carry out their work anonymously.
You can report posts, comments and users to us (for example, if they provide information in their profile or have images that violate these Terms of Use).
If you report something to us, you can give us a reason and a short explanation.
We will then take a look at what you have reported and sanction you if necessary, e.g. by blocking contributions, comments or users.
Unfortunately, you and the person concerned will not receive any feedback from us at this time, but this is in the planning stage.
Irrespective of this, we reserve the right to impose sanctions in principle for reasons that may not or not yet be listed in our Code of Conduct or these terms of service.<br>
</p>
<h3>
Errors and Feedback
</h3>
<p>
We make every effort to keep our network and data secure and available.
Each new release of the software goes through both automated and manual testing.
However, unforeseen errors may occur. Therefore, we are grateful for any reported bugs.
You are welcome to report any bugs you discover by emailing Support at <a href="https://{{ networkURL }}/support" target="_blank">https://{{ networkURL }}/support</a><br>
</p>
<h3>
No Commercial Use
</h3>
<p>
The use of the {{ networkName }} Network is not permitted for commercial purposes.
This includes, but is not limited to, advertising products with commercial intent, posting affiliate links, directly soliciting donations, or providing financial support for purposes that are not recognized as charitable for tax purposes.<br>
</p>
<h3>
No Political Use
</h3>
<p>
User accounts of political parties or official user accounts of a political representative are not permitted.<br>
</p>
<h3>
Help and Questions
</h3>
<p>
For help and questions we have compiled a comprehensive collection of frequently asked questions and answers (FAQ) for you. You can find them here: <a href="https://{{ networkURL }}/faq" target="_blank">https://{{ networkURL }}/faq</a><br>
</p>
</div>
<style type="text/css">
.info-page {
margin-bottom: 48px;
}
.info-page h2 {
margin: 24px 0;
}
.info-page h3 {
margin: 24px 0 16px 0;
}
.info-page h4 {
margin: 16px 0 8px 0;
}
.info-page p {
margin: 8px 0;
}
.info-page ul {
list-style-type: disc;
margin: 16px 0 16px 14px;
}
.info-page table {
background-color: #fff;
border: 1px solid #e0dede;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,.08),inset 0 0 0 1px rgba(255,255,255,.5);
margin: 16px 0;
max-width: 100%;
}
.info-page table thead {
background-color: #f0f0f0;
}
.info-page table td,
.info-page table th {
border: 1px solid #e0dede;
padding: 10px;
}
</style>

View File

@ -1,12 +0,0 @@
# please duplicate template file and rename to "dns.values.yaml" and fill in your value
provider: digitalocean
digitalocean:
# create the API token at https://cloud.digitalocean.com/account/api/tokens
# needs read + write
apiToken: "TODO"
domainFilters:
# domains you want external-dns to be able to edit
- TODO.TODO
rbac:
create: true

View File

@ -1,120 +0,0 @@
# please duplicate template file and rename to "values.yaml" and fill in your value
# change all the below if needed
MAPBOX_TOKEN: "pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
PRODUCTION_DB_CLEAN_ALLOW: false # only true for production environments on staging servers
PUBLIC_REGISTRATION: false
INVITE_REGISTRATION: false
COOKIE_EXPIRE_TIME: 730 # days (730 days, two years is the default in main code)
CATEGORIES_ACTIVE: false
BACKEND:
# change all the below if needed
# DOCKER_IMAGE_REPO - change that to your branded docker image
# label is appended based on .Chart.appVersion
DOCKER_IMAGE_REPO: "ocelotsocialnetwork/backend-branded"
CLIENT_URI: "https://staging.ocelot.social"
# create a new one for your network
JWT_SECRET: "b/&&7b78BF&fv/Vd"
PRIVATE_KEY_PASSPHRASE: "a7dsf78sadg87ad87sfagsadg78"
# ocelot.social mail dummy
EMAIL_DEFAULT_SENDER: "devops@ocelot.social"
SMTP_HOST: "mail.ocelot.social"
SMTP_USERNAME: "devops@ocelot.social"
SMTP_PASSWORD: "devops@ocelot.social"
SMTP_PORT: "587"
SMTP_IGNORE_TLS: 'false'
SMTP_SECURE: 'false' # true for 465, false for other ports
# or
# SMTP_PORT: "465"
# SMTP_IGNORE_TLS: 'true'
# SMTP_SECURE: 'true' # true for 465, false for other ports
# most likely you don't need to change this
MIN_READY_SECONDS: "15"
PROGRESS_DEADLINE_SECONDS: "60"
REVISIONS_HISTORY_LIMIT: "25"
CONTAINER_RESTART_POLICY: "Always"
CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS: "30"
DOCKER_IMAGE_PULL_POLICY: "Always"
STORAGE_UPLOADS: "25Gi"
WEBAPP:
# change all the below if needed
# DOCKER_IMAGE_REPO - change that to your branded docker image
# label is appended based on .Chart.appVersion
DOCKER_IMAGE_REPO: "ocelotsocialnetwork/webapp-branded"
WEBSOCKETS_URI: "wss://staging.ocelot.social/api/graphql"
# Most likely you don't need to change this
REPLICAS: "2"
MIN_READY_SECONDS: "15"
PROGRESS_DEADLINE_SECONDS: "60"
REVISIONS_HISTORY_LIMIT: "25"
CONTAINER_RESTART_POLICY: "Always"
CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS: "30"
DOCKER_IMAGE_PULL_POLICY: "Always"
NEO4J:
# most likely you don't need to change this
REVISIONS_HISTORY_LIMIT: "25"
DOCKER_IMAGE_REPO: "ocelotsocialnetwork/neo4j-community-branded"
DOCKER_IMAGE_PULL_POLICY: "Always"
CONTAINER_RESTART_POLICY: "Always"
CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS: "30"
STORAGE: "5Gi"
# RESOURCE_REQUESTS_MEMORY configures the memory available for requests.
RESOURCE_REQUESTS_MEMORY: "2G"
# RESOURCE_LIMITS_MEMORY configures the memory limits available.
RESOURCE_LIMITS_MEMORY: "4G"
# required for Neo4j Enterprice version
#ACCEPT_LICENSE_AGREEMENT: "yes"
ACCEPT_LICENSE_AGREEMENT: "no"
AUTH: "none"
#DBMS_CONNECTOR_BOLT_THREAD_POOL_MAX_SIZE: "10000" # hc value
DBMS_CONNECTOR_BOLT_THREAD_POOL_MAX_SIZE: "400" # default value
#DBMS_MEMORY_HEAP_INITIAL_SIZE: "500MB" # HC value
DBMS_MEMORY_HEAP_INITIAL_SIZE: "" # default
#DBMS_MEMORY_HEAP_MAX_SIZE: "500MB" # HC value
DBMS_MEMORY_HEAP_MAX_SIZE: "" # default
#DBMS_MEMORY_PAGECACHE_SIZE: "490M" # HC value
DBMS_MEMORY_PAGECACHE_SIZE: "" # default
#APOC_IMPORT_FILE_ENABLED: "true" # HC value
APOC_IMPORT_FILE_ENABLED: "false" # default
DBMS_SECURITY_PROCEDURES_UNRESTRICTED: "algo.*,apoc.*"
MAINTENANCE:
# change all the below if needed
# DOCKER_IMAGE_REPO - change that to your branded docker image
# label is appended based on .Chart.appVersion
DOCKER_IMAGE_REPO: "ocelotsocialnetwork/maintenance-branded"
# Most likely you don't need to change this
REVISIONS_HISTORY_LIMIT: "25"
CONTAINER_RESTART_POLICY: "Always"
CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS: "30"
DOCKER_IMAGE_PULL_POLICY: "Always"
LETSENCRYPT:
# change all the below if needed
# ISSUER is used by cert-manager to set up certificates with the given provider.
# change it to "letsencrypt-production" once you are ready to have valid cetrificates.
# Be aware that the is an issuing limit with letsencrypt, so a dry run with staging might be wise
ISSUER: "letsencrypt-staging"
EMAIL: "devops@ocelot.social"
DOMAINS:
- "staging.ocelot.social"
- "www.staging.ocelot.social"
NGINX:
# most likely you don't need to change this
PROXY_BODY_SIZE: "10m"
STORAGE:
# change all the below if needed
PROVISIONER: "dobs.csi.digitalocean.com"
# most likely you don't need to change this
RECLAIM_POLICY: "Retain"
VOLUME_BINDING_MODE: "Immediate"
ALLOW_VOLUME_EXPANSION: true

@ -0,0 +1 @@
Subproject commit be3ac7ad29f37d6a00fb3203db302cd91cebb9fa

View File

@ -65,10 +65,10 @@ services:
backend:
image: ocelotsocialnetwork/backend-branded:local-${CONFIGURATION}
container_name: backend
container_name: backend-branded
build:
dockerfile: src/docker/backend.Dockerfile
target: branded-branded
target: branded
context: .
args:
- CONFIGURATION=$CONFIGURATION
@ -143,7 +143,7 @@ services:
neo4j:
image: ocelotsocialnetwork/neo4j-community:latest
container_name: neo4j
container_name: neo4j-branded
networks:
- test-network
volumes:

View File

@ -9,20 +9,38 @@
SCRIPT_PATH=$(realpath $0)
SCRIPT_DIR=$(dirname $SCRIPT_PATH)
# check CONFIGURATION
if [ -z ${CONFIGURATION} ]; then
echo "You must provide a `CONFIGURATION` via environment variable"
exit 1
fi
echo "Using CONFIGURATION=${CONFIGURATION}"
# check DOCKERHUB_BRAND_VARRIANT
if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then
echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable"
exit 1
fi
echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}"
# configuration
CONFIGURATION=${CONFIGURATION:-"example"}
DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"}
OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")}
OCELOT_GITHUB_RUN_NUMBER=${OCELOT_GITHUB_RUN_NUMBER:-master}
OCELOT_VERSION_BUILD=${OCELOT_VERSION_BUILD:-${OCELOT_VERSION}-${OCELOT_GITHUB_RUN_NUMBER}}
BRANDED_VERSION=${BRANDED_VERSION:-${GITHUB_RUN_NUMBER:-"local"}}
BUILD_DATE=${BUILD_DATE:-$(date -u +'%Y-%m-%dT%H:%M:%SZ')}
BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION}
BUILD_VERSION_BASE=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION}
BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION_BUILD}
BUILD_COMMIT=${GITHUB_SHA:-"0000000"}
# backend
docker build --target branded \
-t "${DOCKERHUB_ORGANISATION}/backend-branded:latest" \
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest" \
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
-t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
-f "${SCRIPT_DIR}/../src/docker/backend.Dockerfile" \
--build-arg "CONFIGURATION=${CONFIGURATION}" \
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \
@ -31,9 +49,11 @@ docker build --target branded \
# webapp
docker build --target branded \
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:latest" \
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest" \
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
-t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
-f "${SCRIPT_DIR}/../src/docker/webapp.Dockerfile" \
--build-arg "CONFIGURATION=${CONFIGURATION}" \
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \
@ -42,9 +62,11 @@ docker build --target branded \
# mainteance
docker build --target branded \
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:latest" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \
-t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \
-f "${SCRIPT_DIR}/../src/docker/maintenance.Dockerfile" \
--build-arg "CONFIGURATION=${CONFIGURATION}" \
--build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \

View File

@ -10,26 +10,42 @@
SCRIPT_PATH=$(realpath $0)
SCRIPT_DIR=$(dirname $SCRIPT_PATH)
# check DOCKERHUB_BRAND_VARRIANT
if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then
echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable"
exit 1
fi
echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}"
# configuration
DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"}
OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")}
OCELOT_GITHUB_RUN_NUMBER=${OCELOT_GITHUB_RUN_NUMBER:-master}
OCELOT_VERSION_BUILD=${OCELOT_VERSION_BUILD:-${OCELOT_VERSION}-${OCELOT_GITHUB_RUN_NUMBER}}
BRANDED_VERSION=${BRANDED_VERSION:-${GITHUB_RUN_NUMBER:-"local"}}
BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION}
BUILD_VERSION_BASE=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION}
BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION_BUILD}
# login to dockerhub
echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
# push backend images
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:latest
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/backend-branded:${BUILD_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}
# push webapp images
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:latest
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/webapp-branded:${BUILD_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}
# push maintenance images
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:latest
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/maintenance-branded:${BUILD_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}
docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}

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