Compare commits

..

No commits in common. "master" and "b2.3.0-286" have entirely different histories.

2748 changed files with 48187 additions and 226402 deletions

157
.codecov.yml Normal file
View File

@ -0,0 +1,157 @@
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

View File

@ -1,99 +0,0 @@
# CodeRabbit Configuration
# https://docs.coderabbit.ai/guides/configure-coderabbit
language: de
reviews:
# Automatisches Review für alle PRs
auto_review:
enabled: true
drafts: false
base_branches:
- master
- main
auto_approve:
enabled: true
# Review-Einstellungen
request_changes_workflow: true
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
changed_files_summary: true
# Pfad-Filter (Dateien die ignoriert werden)
path_filters:
- "!**/*.lock"
- "!**/package-lock.json"
- "!**/yarn.lock"
- "!**/*.snap"
- "!**/coverage/**"
- "!**/dist/**"
- "!**/node_modules/**"
- "!**/*.min.js"
- "!**/*.min.css"
- "!**/.git/**"
- "!**/storybook-static/**"
# Instruktionen für spezifische Pfade
path_instructions:
- path: "webapp/**/*.vue"
instructions: |
Prüfe Vue.js Best Practices:
- Composition API Verwendung
- Props Validierung
- Event-Handling
- Reaktivität
- path: "webapp/**/*.spec.js"
instructions: |
Prüfe Test-Qualität:
- Aussagekräftige Test-Namen
- Edge Cases abgedeckt
- Mocking korrekt verwendet
- path: "backend/**/*.js"
instructions: |
Prüfe Backend Best Practices:
- Error Handling
- Input Validierung
- SQL Injection Prevention
- Performance (N+1 Queries)
- path: "packages/ui/**/*.ts"
instructions: |
Prüfe UI Library Standards:
- TypeScript Typisierung
- Vue 2/3 Kompatibilität (vue-demi)
- Accessibility (WCAG 2.1)
- CVA Varianten-Pattern
- path: "packages/ui/**/*.vue"
instructions: |
Prüfe UI Komponenten:
- Render-Funktion Pattern für vue-demi
- Props mit korrekten Types
- Slots dokumentiert
- Keine Template-Syntax (nur h() für Vue 2/3 Kompatibilität)
# Chat-Befehle
chat:
auto_reply: true
# Ton und Stil
tone_instructions: |
Sei konstruktiv und freundlich.
Erkläre das "Warum" hinter Vorschlägen.
Priorisiere Sicherheit > Korrektheit > Performance > Lesbarkeit.
Schlage konkrete Code-Änderungen vor wenn möglich.
# Knowledge Base (Repository-spezifisches Wissen)
knowledge_base:
learnings:
scope: auto
issues:
scope: auto
pull_requests:
scope: auto

345
.github/dependabot.yml vendored
View File

@ -1,196 +1,179 @@
version: 2
updates:
- package-ecosystem: "github-actions"
open-pull-requests-limit: 99
- package-ecosystem: npm
directory: "/"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: cypress
versions:
- 6.3.0
- 6.4.0
- 6.5.0
- 6.6.0
- 6.7.1
- 6.8.0
- 7.0.0
- 7.0.1
- 7.1.0
- dependency-name: cypress-cucumber-preprocessor
versions:
- 4.0.0
- 4.0.1
- 4.0.3
- dependency-name: date-fns
versions:
- 2.16.1
- 2.17.0
- 2.18.0
- 2.19.0
- 2.20.0
- 2.20.1
- 2.20.2
- 2.20.3
- 2.21.0
- dependency-name: cypress-file-upload
versions:
- 5.0.2
- 5.0.3
- 5.0.4
- 5.0.5
- dependency-name: neo4j-driver
versions:
- 4.2.2
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
groups:
babel:
applies-to: version-updates
patterns:
- "@babel*"
cypress:
applies-to: version-updates
patterns:
- "*cypress*"
- "*cucumber*"
vuepress:
applies-to: version-updates
patterns:
- "*vuepress*"
# backend
- package-ecosystem: docker
open-pull-requests-limit: 99
directory: "/backend"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: y18n
versions:
- 4.0.1
- 4.0.2
- dependency-name: metascraper-publisher
versions:
- 5.16.16
- 5.18.1
- 5.18.12
- 5.18.2
- 5.18.4
- 5.18.5
- 5.18.6
- 5.18.9
- 5.20.0
- 5.21.0
- 5.21.2
- 5.21.3
- 5.21.4
- 5.21.5
- dependency-name: metascraper-author
versions:
- 5.16.16
- 5.18.1
- 5.18.12
- 5.18.2
- 5.18.4
- 5.18.5
- 5.18.6
- 5.18.9
- 5.20.0
- 5.21.0
- 5.21.2
- 5.21.3
- 5.21.4
- 5.21.5
- dependency-name: neo4j-driver
versions:
- 4.2.2
- dependency-name: neo4j-graphql-js
versions:
- 2.19.1
- dependency-name: mustache
versions:
- 4.1.0
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/webapp"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: nuxt
versions:
- 2.14.12
- 2.15.0
- 2.15.1
- 2.15.2
- 2.15.3
- dependency-name: v-tooltip
versions:
- 2.1.2
- dependency-name: "@vue/server-test-utils"
versions:
- 1.1.2
- 1.1.3
- dependency-name: node-notifier
versions:
- 8.0.1
- package-ecosystem: docker
directory: "/webapp"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: node
versions:
- ">= 15.5.a, < 15.6"
- dependency-name: node
versions:
- 15.10.0.pre.alpine3.10
- 15.11.0.pre.alpine3.10
- 15.12.0.pre.alpine3.10
- 15.13.0.pre.alpine3.10
- 15.7.0.pre.alpine3.10
- 15.8.0.pre.alpine3.10
- 15.9.0.pre.alpine3.10
- package-ecosystem: docker
directory: "/backend"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
groups:
apollo-server:
applies-to: version-updates
patterns:
- "*apollo-server*"
metascraper:
applies-to: version-updates
patterns:
- "metascraper*"
# webapp
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: node
versions:
- ">= 15.4.a, < 15.5"
- dependency-name: node
versions:
- ">= 15.5.a, < 15.6"
- dependency-name: node
versions:
- 15.10.0.pre.alpine3.10
- 15.11.0.pre.alpine3.10
- 15.12.0.pre.alpine3.10
- 15.13.0.pre.alpine3.10
- 15.7.0.pre.alpine3.10
- 15.8.0.pre.alpine3.10
- 15.9.0.pre.alpine3.10
- package-ecosystem: docker
open-pull-requests-limit: 99
directory: "/webapp"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/webapp"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
groups:
apollo:
applies-to: version-updates
patterns:
- "apollo*"
babel:
applies-to: version-updates
patterns:
- "@babel*"
jest:
applies-to: version-updates
patterns:
- "jest*"
mapbox:
applies-to: version-updates
patterns:
- "*mapbox*"
storybook:
applies-to: version-updates
patterns:
- "*storybook*"
vue:
applies-to: version-updates
patterns:
- "@vue*"
- package-ecosystem: docker
open-pull-requests-limit: 99
directory: "/neo4j"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
interval: daily
time: "04:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: neo4j
versions:
- 4.2.3
- 4.2.4
- package-ecosystem: docker
open-pull-requests-limit: 99
directory: "/deployment/src/docker"
rebase-strategy: "disabled"
directory: "/deployment/legacy-migration/maintenance-worker"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
# ui library
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/packages/ui"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
groups:
vue:
applies-to: version-updates
patterns:
- "vue*"
- "@vue*"
vite:
applies-to: version-updates
patterns:
- "vite*"
- "@vitejs*"
vitest:
applies-to: version-updates
patterns:
- "vitest*"
- "@vitest*"
# ui examples
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/packages/ui/examples/vue3-tailwind"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/packages/ui/examples/vue3-css"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/packages/ui/examples/vue2-tailwind"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
- package-ecosystem: npm
open-pull-requests-limit: 99
directory: "/packages/ui/examples/vue2-css"
rebase-strategy: "disabled"
schedule:
interval: weekly
day: "saturday"
timezone: "Europe/Berlin"
time: "03:00"
interval: daily
time: "04:00"
open-pull-requests-limit: 10

View File

@ -1,39 +0,0 @@
# These file filter patterns are used by the action https://github.com/dorny/paths-filter
ui: &ui
- '.github/workflows/ui-*.yml'
- 'packages/ui/**/*'
backend: &backend
- '.github/workflows/test-backend.yml'
- 'backend/**/*'
- 'neo4j/**/*'
- 'package.json'
docker: &docker
- 'docker-compose.*'
webapp: &webapp
- '.github/workflows/test-webapp.yml'
- 'webapp/**/*'
- 'styleguide/**/*'
- 'package.json'
- *ui
docs-check: &docs-check
- '.github/workflows/check-documentation.yml'
markdown: &markdown
- *docs-check
- '**/*.md'
- '.github/workflows/mlc_config.json'
vuepress: &vuepress
- *docs-check
- '.github/workflows/deploy-documentation.yml'
- '.vuepress/**/*'
- 'package.json'
documentation: &documentation
- *vuepress
- *markdown

View File

@ -1,65 +0,0 @@
name: ocelot.social check documentation
on: push
jobs:
files-changed:
name: Detect File Changes - Markdown files
runs-on: ubuntu-latest
outputs:
markdown: ${{ steps.changes.outputs.markdown }}
documentation: ${{ steps.changes.outputs.documentation }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for markdown file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
check-markdown-links:
name: Check Markdown links
needs: files-changed
runs-on: ubuntu-latest
if: needs.files-changed.outputs.markdown == 'true'
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Remove uncheckable documentation files
run: rm -rf ./CHANGELOG.md # workaround until https://github.com/gaurav-nelson/github-action-markdown-link-check/pull/183 has been done
- name: Check Markdown Links
uses: gaurav-nelson/github-action-markdown-link-check@3c3b66f1f7d0900e37b71eca45b63ea9eedfce31 # 1.0.15
with:
use-quiet-mode: 'yes'
use-verbose-mode: 'no'
# at any .md file change take the chance to check the links in all .md files
check-modified-files-only: 'no'
config-file: '.github/workflows/mlc_config.json'
base-branch: 'master'
folder-path: '.'
test-vuepress-build:
name: Test Vuepress build
needs: files-changed
runs-on: ubuntu-latest
if: needs.files-changed.outputs.documentation == 'true'
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node 20
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version: '20'
- name: npm-install
run: npm install
- name: npm-docs:build
run: npm run docs:build

View File

@ -1,48 +0,0 @@
name: ocelot.social deploy documentation
on:
push:
branches:
- master
jobs:
files-changed:
name: Detect File Changes - Markdown and Vuepress files
runs-on: ubuntu-latest
outputs:
documentation: ${{ steps.changes.outputs.documentation }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node 20
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version: 20
- name: Build Vuepress Pages
run: npm install && npm run docs:build
- name: Deploy Vuepress to Github Pages
uses: crazy-max/ghaction-github-pages@df5cc2bfa78282ded844b354faee141f06b41865 # v4.0.0
with:
target_branch: gh-pages
build_dir: .vuepress/dist
author: Wolfgang Huß <vorstand@busfaktor.org>
fqdn: docs.ocelot.social
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,91 +0,0 @@
name: docker-push
on: push
jobs:
build-and-push-images:
strategy:
matrix:
app:
- name: neo4j
context: neo4j
file: neo4j/Dockerfile
target: community
- name: backend-base
context: backend
file: backend/Dockerfile
target: base
- name: backend-build
context: backend
file: backend/Dockerfile
target: build
- name: backend
context: backend
file: backend/Dockerfile
target: production
- name: webapp-base
context: .
file: webapp/Dockerfile
target: base
- name: webapp-build
context: .
file: webapp/Dockerfile
target: build
- name: webapp
context: .
file: webapp/Dockerfile
target: production
- name: maintenance-base
context: .
file: webapp/Dockerfile.maintenance
target: base
- name: maintenance-build
context: .
file: webapp/Dockerfile.maintenance
target: build
- name: maintenance
context: .
file: webapp/Dockerfile.maintenance
target: production
runs-on: ubuntu-latest
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/${{ matrix.app.name }}
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Log in to the Container registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=schedule
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=ref,event=branch
type=ref,event=pr
type=sha
- name: Build and push Docker images
id: push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
with:
context: ${{ matrix.app.context }}
target: ${{ matrix.app.target }}
file: ${{ matrix.app.file }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@ -1,23 +1,19 @@
name: "test:lint pull request CI"
name: "ocelot.social lint pull request CI"
on:
pull_request:
pull_request_target:
types:
- opened
- edited
- synchronize
permissions:
pull-requests: write
statuses: write
jobs:
main:
name: Validate PR title
runs-on: ubuntu-latest
if: ${{ github.actor != 'dependabot[bot]' }}
steps:
- uses: amannn/action-semantic-pull-request@069817c298f23fab00a8f29a2e556a5eac0f6390 # v5.5.3
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@ -29,15 +25,9 @@ jobs:
# Configure which scopes are allowed (newline delimited).
scopes: |
backend
package/ui
webapp
maintenance
database
e2e
docu
docker
release
workflow
other
# Configure that a scope must always be provided.
requireScope: true

View File

@ -1,12 +0,0 @@
{
"timeout": "20s",
"retryOn429": true,
"retryCount": 2,
"fallbackRetryDelay": "30s",
"aliveStatusCodes": [200, 206, 303, 403],
"ignorePatterns": [
{
"pattern": "^(https:\/\/github.com\/Ocelot-Social-Community\/Ocelot-Social\/(pull|deployment\/src\/old)\/|http:\/\/localhost)"
}
]
}

View File

@ -1,48 +1,388 @@
name: publish
name: ocelot.social publish CI
on:
push:
branches:
- master
# - 5059-epic-groups # for testing while developing
# template branches in repo
# - template--separate-branch-auto-deployment--5059-epic-groups
jobs:
##############################################################################
# JOB: PREPARE ###############################################################
##############################################################################
prepare:
name: Prepare
runs-on: ubuntu-latest
# needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# TODO: DO STUFF ??? #####################################################
##########################################################################
- name: Check translation files
run: |
scripts/translations/sort.sh
scripts/translations/missing-keys.sh
##############################################################################
# JOB: DOCKER BUILD COMMUNITY NEO4J ##########################################
##############################################################################
build_production_neo4j:
name: Docker Build Production - Neo4J
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_DATE
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: ENV - BUILD_COMMIT
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
##########################################################################
# NEO4J ##################################################################
##########################################################################
- name: Neo4J | Build `community` image
run: docker build --target community -t "ocelotsocialnetwork/neo4j-community:latest" -t "ocelotsocialnetwork/neo4j-community:${VERSION}" -t "ocelotsocialnetwork/neo4j-community:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT neo4j/
- name: Neo4J | Save docker image
run: docker save "ocelotsocialnetwork/neo4j-community" > /tmp/neo4j.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-neo4j-community
path: /tmp/neo4j.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION BACKEND #######################################
##############################################################################
build_production_backend:
name: Docker Build Production - Backend
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_DATE
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: ENV - BUILD_COMMIT
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
##########################################################################
# BUILD BACKEND DOCKER IMAGE (production) ################################
##########################################################################
- name: Backend | Build `production` image
run: |
docker build --target base -t "ocelotsocialnetwork/backend:latest-base" -t "ocelotsocialnetwork/backend:${VERSION}-base" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
docker build --target code -t "ocelotsocialnetwork/backend:latest-code" -t "ocelotsocialnetwork/backend:${VERSION}-code" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
docker build --target production -t "ocelotsocialnetwork/backend:latest" -t "ocelotsocialnetwork/backend:${VERSION}" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- name: Backend | Save docker image
run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-backend-production
path: /tmp/backend.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION WEBAPP ########################################
##############################################################################
build_production_webapp:
name: Docker Build Production - WebApp
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_DATE
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: ENV - BUILD_COMMIT
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
##########################################################################
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
##########################################################################
- name: Webapp | Build `production` image
run: |
docker build --target base -t "ocelotsocialnetwork/webapp:latest-base" -t "ocelotsocialnetwork/webapp:${VERSION}-base" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
docker build --target code -t "ocelotsocialnetwork/webapp:latest-code" -t "ocelotsocialnetwork/webapp:${VERSION}-code" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
docker build --target production -t "ocelotsocialnetwork/webapp:latest" -t "ocelotsocialnetwork/webapp:${VERSION}" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- name: Webapp | Save docker image
run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-webapp-production
path: /tmp/webapp.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION MAINTENANCE ###################################
##############################################################################
build_production_maintenance:
name: Docker Build Production - Maintenance
runs-on: ubuntu-latest
needs: [prepare]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_DATE
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: ENV - BUILD_COMMIT
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
##########################################################################
# BUILD MAINTENANCE DOCKER IMAGE (build) #################################
##########################################################################
- name: Maintenance | Build `production` image
run: |
docker build --target base -t "ocelotsocialnetwork/maintenance:latest-base" -t "ocelotsocialnetwork/maintenance:${VERSION}-base" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
docker build --target code -t "ocelotsocialnetwork/maintenance:latest-code" -t "ocelotsocialnetwork/maintenance:${VERSION}-code" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
docker build --target production -t "ocelotsocialnetwork/maintenance:latest" -t "ocelotsocialnetwork/maintenance:${VERSION}" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- name: Maintenance | Save docker image
run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-maintenance-production
path: /tmp/maintenance.tar
##############################################################################
# JOB: UPLOAD TO DOCKERHUB ###################################################
##############################################################################
upload_to_dockerhub:
name: Upload to Dockerhub
runs-on: ubuntu-latest
needs: [build_production_neo4j,build_production_backend,build_production_webapp,build_production_maintenance]
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2
with:
name: docker-neo4j-community
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
with:
name: docker-backend-production
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
- name: Download Docker Image (WebApp)
uses: actions/download-artifact@v2
with:
name: docker-webapp-production
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
- name: Download Docker Image (Maintenance)
uses: actions/download-artifact@v2
with:
name: docker-maintenance-production
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/maintenance.tar
##########################################################################
# Upload #################################################################
##########################################################################
- name: login to dockerhub
run: echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
- name: Push neo4j
run: docker push --all-tags ocelotsocialnetwork/neo4j-community
- name: Push backend
run: docker push --all-tags ocelotsocialnetwork/backend
- name: Push webapp
run: docker push --all-tags ocelotsocialnetwork/webapp
- name: Push maintenance
run: docker push --all-tags ocelotsocialnetwork/maintenance
##############################################################################
# JOB: KUBERNETES DEPLOY ACTUAL/LATEST VERSION ######################################
##############################################################################
kubernetes_deploy:
# see example https://github.com/do-community/example-doctl-action
# see example https://github.com/do-community/example-doctl-action/blob/main/.github/workflows/workflow.yaml
name: Kubernetes deploy of latest version to stage.ocelot.social cluster at DigitalOcean
runs-on: ubuntu-latest
needs: [upload_to_dockerhub]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
##########################################################################
# Install DigitalOceans doctl and set kubeconfig #########################
##########################################################################
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Save DigitalOcean kubeconfig with short-lived credentials
run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 cluster-stage-ocelot-social
##########################################################################
# Deploy new Docker images to DigitalOcean Kubernetes cluster ############
##########################################################################
# - name: Deploy 'latest' to DigitalOcean Kubernetes
# run: |
# kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp:latest
# kubectl -n default rollout restart deployment/ocelot-webapp
# kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend:latest
# kubectl -n default rollout restart deployment/ocelot-backend
# kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance:latest
# kubectl -n default rollout restart deployment/ocelot-maintenance
# kubectl -n default set image deployment/ocelot-neo4j container-ocelot-neo4j=ocelotsocialnetwork/neo4j-community:latest
# kubectl -n default rollout restart deployment/ocelot-neo4j
- name: Deploy actual version '$BUILD_VERSION' to DigitalOcean Kubernetes
run: |
kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-webapp
kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-backend
kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-maintenance
kubectl -n default set image deployment/ocelot-neo4j container-ocelot-neo4j=ocelotsocialnetwork/neo4j-community:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-neo4j
# because this step 'kubectl -n default rollout status deployment/* --timeout=600s' does not work as expected
# and we need the pods to be up again for cleaning and seeding the Neo4j database and the backend.
# !!! this is not a perfect solution !!!
# deployments are regularly up again after 3 minutes and 10 seconds
- name: Sleep for 4 minutes, means 240 seconds
run: sleep 240s
shell: bash
- name: Verify deployment and wait for the pods of each deployment to get ready for cleaning and seeding of the database
run: |
kubectl -n default rollout status deployment/ocelot-backend --timeout=600s
kubectl -n default rollout status deployment/ocelot-neo4j --timeout=600s
kubectl -n default rollout status deployment/ocelot-maintenance --timeout=600s
kubectl -n default rollout status deployment/ocelot-webapp --timeout=600s
- name: Run migrations for Neo4j database via backend for staging
run: |
kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "yarn prod:migrate up"
- name: Reset and seed Neo4j database via backend for staging
# db cleaning and seeding is only possible in production if env 'PRODUCTION_DB_CLEAN_ALLOW=true' is set in deployment
run: |
kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "node --experimental-repl-await dist/db/clean.js && node --experimental-repl-await dist/db/seed.js"
##############################################################################
# JOB: GITHUB TAG LATEST VERSION #############################################
##############################################################################
github_tag:
name: Tag latest version on Github
runs-on: ubuntu-latest
needs: [upload_to_dockerhub]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
uses: actions/checkout@v2
with:
fetch-depth: 0 # Fetch full History for changelog
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: '.nvmrc'
- name: Setup env
run: |
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
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
##########################################################################
# SET ENVS ###############################################################
##########################################################################
- name: ENV - VERSION
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
- name: ENV - BUILD_DATE
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: ENV - BUILD_VERSION
run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
- name: ENV - BUILD_COMMIT
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
##########################################################################
# Push version tag to GitHub #############################################
##########################################################################
# TODO: this will error on duplicate
#- name: package-version-to-git-tag
# uses: pkgdeps/git-tag-action@v2
# with:
# github_token: ${{ secrets.GITHUB_TOKEN }}
# github_repo: ${{ github.repository }}
# version: ${{ env.VERSION }}
# git_commit_sha: ${{ github.sha }}
# git_tag_prefix: "v"
##########################################################################
# Push build tag to GitHub ###############################################
##########################################################################
- name: package-version-to-git-tag + build number
uses: pkgdeps/git-tag-action@v3.0.0 # v3.0.0
uses: pkgdeps/git-tag-action@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_repo: ${{ github.repository }}
version: ${{ env.BUILD_VERSION }}
git_commit_sha: ${{ github.sha }}
git_tag_prefix: "b"
- name: Generate changelog
run: |
yarn install
yarn auto-changelog --latest-version ${{ env.VERSION }} --unreleased-only
##########################################################################
# Push release tag to GitHub #############################################
##########################################################################
- name: yarn install
run: yarn install
- name: generate changelog
run: yarn auto-changelog --latest-version ${{ env.VERSION }} --unreleased-only
- name: package-version-to-git-release
continue-on-error: true # Will fail if tag exists
id: create_release
uses: actions/create-release@4c11c9fe1dcd9636620a16455165783b20fc7ea0 # v1.1.4
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
@ -51,46 +391,3 @@ jobs:
body_path: ./CHANGELOG.md
draft: false
prerelease: false
build_trigger:
name: Trigger successful build
runs-on: ubuntu-latest
needs: [github_tag]
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
with:
fetch-depth: 0 # Fetch full History for changelog
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: '.nvmrc'
- name: Setup env
run: |
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
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@f49a8ac5751834a0666df77deb0289abbe2b3a78 # v3.0.0
# 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@f49a8ac5751834a0666df77deb0289abbe2b3a78 # v3.0.0
with:
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
event-type: trigger-ocelot-build-success
repository: 'Ocelot-Social-Community/stage.ocelot.social'
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@f49a8ac5751834a0666df77deb0289abbe2b3a78 # v3.0.0
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}"}'

View File

@ -1,129 +0,0 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for backend file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Neo4J | Build 'community' image
run: |
docker compose -f docker-compose.yml -f docker-compose.test.yml build neo4j
docker save "ghcr.io/ocelot-social-community/ocelot-social/neo4j:community" > /tmp/neo4j.tar
- name: Cache docker images
id: cache-neo4j
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/neo4j.tar
key: ${{ github.run_id }}-backend-neo4j-cache
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: backend | Build 'test' image
run: |
docker compose -f docker-compose.yml -f docker-compose.test.yml build backend
docker save "ghcr.io/ocelot-social-community/ocelot-social/backend:test" > /tmp/backend.tar
- name: Cache docker images
id: cache-backend
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/backend.tar
key: ${{ github.run_id }}-backend-cache
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'backend/.nvmrc'
- 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Restore Neo4J cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/neo4j.tar
key: ${{ github.run_id }}-backend-neo4j-cache
fail-on-cache-miss: true
- name: Restore Backend cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/backend.tar
key: ${{ github.run_id }}-backend-cache
fail-on-cache-miss: true
- name: Load Docker Images
run: |
docker load < /tmp/neo4j.tar
docker load < /tmp/backend.tar
- name: backend | copy env files
run: |
cp webapp/.env.template webapp/.env
cp backend/.env.template backend/.env
- name: backend | docker compose
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach 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

View File

@ -1,215 +0,0 @@
name: ocelot.social end-to-end test CI
on: push
jobs:
prepare_backend_environment:
name: Fullstack | prepare backend environment
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Copy backend env file
run: |
cp backend/.env.test_e2e backend/.env
cp webapp/.env.template webapp/.env
- name: Build backend and dependencies
run: |
# Build and start all required images for backend
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach neo4j backend --build
# Save the build images
docker save "ghcr.io/ocelot-social-community/ocelot-social/backend:test" > /tmp/backend.tar
docker save "ghcr.io/ocelot-social-community/ocelot-social/neo4j:community" > /tmp/neo4j.tar
docker save "quay.io/minio/minio:latest" > /tmp/minio.tar
docker save "quay.io/minio/mc:latest" > /tmp/minio-mc.tar
docker save "maildev/maildev:latest" > /tmp/mailserver.tar
# Stop the containers
docker compose -f docker-compose.yml -f docker-compose.test.yml down
- name: Cache docker images
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/tmp/backend.tar
/tmp/neo4j.tar
/tmp/minio.tar
/tmp/minio-mc.tar
/tmp/mailserver.tar
key: ${{ github.run_id }}-e2e-backend-environment-cache
prepare_webapp_image:
name: Fullstack | prepare webapp image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Copy backend env file
run: |
cp backend/.env.test_e2e backend/.env
cp webapp/.env.template webapp/.env
- name: Build docker image
run: |
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach webapp --build --no-deps
docker save "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" > /tmp/webapp.tar
- name: Cache docker image
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-e2e-webapp-cache
prepare_cypress:
name: Fullstack | prepare cypress
runs-on: ubuntu-latest
steps:
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.4.0
with:
node-version-file: 'backend/.nvmrc'
cache: 'yarn'
- name: Copy env files
run: |
cp webapp/.env.template webapp/.env
cp backend/.env.test_e2e backend/.env
- name: Install cypress requirements
run: |
sudo wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
sudo chmod +x /opt/cucumber-json-formatter
cd backend
yarn install
yarn build
cd ..
yarn install
- name: Cache docker image
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/opt/cucumber-json-formatter
/home/runner/.cache/Cypress
/home/runner/work/Ocelot-Social/Ocelot-Social
key: ${{ github.run_id }}-e2e-cypress
list_features:
name: List Feature Files
runs-on: ubuntu-latest
outputs:
features: ${{ steps.list.outputs.features }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: List feature files
id: list
run: |
FEATURES=$(find cypress/e2e/ -maxdepth 1 -name "*.feature" -printf '%f\n' | sort | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "features=$FEATURES" >> $GITHUB_OUTPUT
fullstack_tests:
name: E2E | ${{ matrix.feature }}
if: success()
needs: [prepare_backend_environment, prepare_webapp_image, prepare_cypress, list_features]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
feature: ${{ fromJson(needs.list_features.outputs.features) }}
steps:
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.4.0
with:
node-version-file: 'backend/.nvmrc'
cache: 'yarn'
- name: Restore cypress cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/opt/cucumber-json-formatter
/home/runner/.cache/Cypress
/home/runner/work/Ocelot-Social/Ocelot-Social
key: ${{ github.run_id }}-e2e-cypress
restore-keys: ${{ github.run_id }}-e2e-cypress
- name: Restore backend environment cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/tmp/backend.tar
/tmp/neo4j.tar
/tmp/minio.tar
/tmp/minio-mc.tar
/tmp/mailserver.tar
key: ${{ github.run_id }}-e2e-backend-environment-cache
- name: Restore webapp cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-e2e-webapp-cache
- name: Boot up test system | docker compose
run: |
chmod +x /opt/cucumber-json-formatter
sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter
docker load < /tmp/neo4j.tar
docker load < /tmp/backend.tar
docker load < /tmp/minio.tar
docker load < /tmp/minio-mc.tar
docker load < /tmp/mailserver.tar
docker load < /tmp/webapp.tar
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach backend mailserver webapp
sleep 90s
- name: Full stack tests | run tests
id: e2e-tests
run: yarn run cypress:run --spec "cypress/e2e/${{ matrix.feature }}"
- name: Full stack tests | if tests failed, compile html report
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
run: |
cd cypress/
node create-cucumber-html-report.js
- name: Full stack tests | if tests failed, upload report
id: e2e-report
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: e2e-report-${{ matrix.feature }}
path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report
e2e_status:
name: E2E | Status
if: always()
needs: [fullstack_tests]
runs-on: ubuntu-latest
steps:
- name: Check E2E results
run: |
if [ "${{ needs.fullstack_tests.result }}" != "success" ]; then
echo "E2E tests failed or were cancelled (result: ${{ needs.fullstack_tests.result }})"
exit 1
fi

View File

@ -1,109 +0,0 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for frontend file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
prepare:
name: Prepare
if: needs.files-changed.outputs.webapp == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'webapp/.nvmrc'
- 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 == 'true'
needs: [files-changed, prepare]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Webapp | Build 'test' image
run: |
docker build --target test -f webapp/Dockerfile -t "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" .
docker save "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" > /tmp/webapp.tar
- name: Cache docker image
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-webapp-cache
lint_webapp:
name: Lint Webapp
if: needs.files-changed.outputs.webapp == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'webapp/.nvmrc'
- 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 == 'true'
needs: [files-changed, build_test_webapp]
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Restore webapp cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-webapp-cache
- name: Load Docker Image
run: docker load < /tmp/webapp.tar
- name: Copy env files
run: |
cp webapp/.env.template webapp/.env
cp backend/.env.template backend/.env
- name: Start webapp container
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

343
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,343 @@
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@v2
##########################################################################
# 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@v2
##########################################################################
# 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@v2
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@v2
##########################################################################
# 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@v2
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@v2
##########################################################################
# 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@v2
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@v2
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
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@v2
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
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]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2
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@v2
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]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
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: 63
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@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2
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@v2
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@v2
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
run: |
yarn install
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
##########################################################################
# UPLOAD SCREENSHOTS & VIDEO #############################################
##########################################################################
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: cypress-screenshots
path: cypress/screenshots/
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: cypress-videos
path: cypress/videos/

View File

@ -1,95 +0,0 @@
name: UI Build
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
build:
name: Build
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build library
run: npm run build
- name: Verify build output
run: |
echo "Checking build output..."
# Check that dist directory exists
if [ ! -d "dist" ]; then
echo "::error::dist directory not found"
exit 1
fi
# Check required files exist
FILES=(
"dist/index.mjs"
"dist/index.cjs"
"dist/index.d.ts"
"dist/index.d.cts"
"dist/tailwind.preset.mjs"
"dist/tailwind.preset.cjs"
"dist/tailwind.preset.d.ts"
"dist/tailwind.preset.d.cts"
"dist/style.css"
)
for file in "${FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "::error::Missing required file: $file"
exit 1
fi
echo "✓ $file"
done
echo ""
echo "All build outputs verified!"
- name: Validate package
run: npm run validate
- name: Upload build artifacts
uses: actions/upload-artifact@v6
with:
name: dist
path: packages/ui/dist/
retention-days: 7

View File

@ -1,121 +0,0 @@
name: UI Compatibility
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
build-library:
name: Build Library
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/ui
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build library
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v6
with:
name: ui-dist
path: packages/ui/dist/
retention-days: 1
test-compatibility:
name: Test Compatibility
if: needs.files-changed.outputs.ui == 'true'
needs: [files-changed, build-library]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- vue3-tailwind
- vue3-css
- vue2-tailwind
- vue2-css
defaults:
run:
working-directory: packages/ui/examples/${{ matrix.example }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: ui-dist
path: packages/ui/dist/
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/examples/${{ matrix.example }}/package-lock.json
- name: Install dependencies
run: npm install
- name: Lint
run: npm run lint
- name: Run tests
run: npm test
- name: Build example app
run: npm run build
compatibility-result:
name: Compatibility Result
if: always()
needs: [files-changed, test-compatibility]
runs-on: ubuntu-latest
steps:
- name: Skip if no UI changes
if: needs.files-changed.outputs.ui != 'true'
run: echo "No UI changes detected, skipping."
- name: Check matrix results
if: needs.files-changed.outputs.ui == 'true'
run: |
if [ "${{ needs.test-compatibility.result }}" != "success" ]; then
echo "Compatibility tests failed"
exit 1
fi

View File

@ -1,59 +0,0 @@
name: UI Docker
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
build:
name: Build Docker Image
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build development image
uses: docker/build-push-action@v6
with:
context: ./packages/ui
file: ./packages/ui/Dockerfile
target: development
push: false
tags: ocelot-social/ui:development
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build production image
uses: docker/build-push-action@v6
with:
context: ./packages/ui
file: ./packages/ui/Dockerfile
target: production
push: false
tags: ocelot-social/ui:latest
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -1,54 +0,0 @@
name: UI Lint
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
lint:
name: ESLint
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run TypeScript type check
run: npm run typecheck

View File

@ -1,65 +0,0 @@
name: UI Release
on:
push:
branches: [master]
paths:
- 'packages/ui/**'
- 'release-please-config.json'
- '.release-please-manifest.json'
permissions:
contents: write
pull-requests: write
jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs['packages/ui--release_created'] }}
tag_name: ${{ steps.release.outputs['packages/ui--tag_name'] }}
version: ${{ steps.release.outputs['packages/ui--version'] }}
steps:
- name: Release Please
id: release
uses: googleapis/release-please-action@v4
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
publish:
name: Publish to npm
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/ui
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Validate package
run: npm run validate
- name: Publish to npm
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -1,54 +0,0 @@
name: UI Size
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
size:
name: Bundle Size Check
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Check bundle size
run: npm run size

View File

@ -1,74 +0,0 @@
name: UI Storybook
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
build:
name: Build Storybook
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Build Storybook
run: npm run storybook:build
- name: Verify build output
run: |
echo "Checking Storybook build output..."
if [ ! -d "storybook-static" ]; then
echo "::error::storybook-static directory not found"
exit 1
fi
if [ ! -f "storybook-static/index.html" ]; then
echo "::error::index.html not found in storybook-static"
exit 1
fi
echo "✓ Storybook build verified!"
- name: Upload Storybook artifacts
uses: actions/upload-artifact@v6
with:
name: storybook-static
path: packages/ui/storybook-static/
retention-days: 7

View File

@ -1,59 +0,0 @@
name: UI Test
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
test:
name: Unit Tests
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@v6
if: always()
with:
name: coverage-report
path: packages/ui/coverage/
retention-days: 7

View File

@ -1,51 +0,0 @@
name: UI Verify
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
verify:
name: Completeness Check
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Check component completeness
run: npm run verify

View File

@ -1,64 +0,0 @@
name: UI Visual
on:
push:
branches: [master]
pull_request:
branches: [master]
defaults:
run:
working-directory: packages/ui
jobs:
files-changed:
name: Detect File Changes
runs-on: ubuntu-latest
outputs:
ui: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
visual:
name: Visual Regression
if: needs.files-changed.outputs.ui == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'packages/ui/.tool-versions'
cache: 'npm'
cache-dependency-path: packages/ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install chromium --with-deps
- name: Run visual tests
run: npm run test:visual
- name: Upload test results
uses: actions/upload-artifact@v6
if: failure()
with:
name: visual-test-results
path: |
packages/ui/test-results/
packages/ui/playwright-report/
retention-days: 7

11
.gitignore vendored
View File

@ -8,23 +8,14 @@ yarn-error.log*
.yarn-integrity
.eslintcache
kubeconfig.yaml
backup-cron-job.log
.vscode
.nuxt
node_modules/
cypress/videos
cypress/screenshots/
cypress.env.json
deployment/configurations/
.vuepress/.cache/
.vuepress/.temp/
.vuepress/dist/
!.gitkeep
**/coverage
release/
*~
*~

6
.gitmodules vendored
View File

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

2
.nvmrc
View File

@ -1 +1 @@
v25.3.0
v17.9.0

View File

@ -1,3 +0,0 @@
{
"packages/ui": "0.0.1"
}

84
.travis.yml Normal file
View File

@ -0,0 +1,84 @@
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

@ -1,12 +0,0 @@
import { defineUserConfig } from 'vuepress'
import { viteBundler } from '@vuepress/bundler-vite'
import meta from './config/meta'
import theme from './config/theme'
export default defineUserConfig({
pagePatterns: ['**/*.md', '!.vuepress', '!node_modules', '!backend/node_modules', '!webapp/node_modules', '!deployment/src/old'],
bundler: viteBundler(),
...meta,
theme,
})

View File

@ -1,8 +0,0 @@
export default {
base: '/',
title: 'Ocelot.Social Documentation',
description: 'Ocelot.Social Documentation',
head: [
['meta', {name: 'viewport', content: 'width=device-width,initial-scale=1'}],
],
}

View File

@ -1,114 +0,0 @@
import path from 'path'
import fs from 'fs'
import { hopeTheme } from 'vuepress-theme-hope'
export default hopeTheme({
favicon: 'favicon.ico',
logo: '/logo.svg',
docsRepo: 'https://github.com/Ocelot-Social-Community/Ocelot-Social',
docsBranch: 'master',
docsDir: '.',
editLink: true,
lastUpdated: false,
contributors: false,
print: false,
pure: true,
navbarLayout: {
start: ['Brand'],
center: ['Links'],
end: ['Repo', 'Outlook', 'Search'],
},
repo: 'Ocelot-Social-Community/Ocelot-Social',
repoLabel: 'GitHub',
repoDisplay: true,
displayFooter: true,
copyright: '&copy; by <a href="https://busfaktor.org" target="_blank">busFaktor() e.V.</a> & Authors | <a href="https://ocelot.social/en/impressum/">Imprint</a>',
footer: 'Released under the <a class="vt-link link link" href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener noreferrer"><!--[-->MIT License<!--]--><!----></a>.',
sidebar: generateSidebar('../../SUMMARY.md'),
navbar: [
{
text: 'Demo',
link: 'https://stage.ocelot.social/',
target: '_self',
},
],
plugins: {
mdEnhance: {
align: true,
demo: true,
imgLazyload: true,
imgMark: true,
imgSize: true,
sub: true,
sup: true,
tabs: true,
vPre: true,
},
searchPro: {
indexContent: true,
autoSuggestions: true,
customFields: [
{
getter: (page) => page.frontmatter.category,
formatter: "Category: $content",
},
{
getter: (page) => page.frontmatter.tag,
formatter: "Tag: $content",
},
],
}
}
})
function generateSidebar(summaryFileName) {
const summaryFile = path.resolve(__dirname, summaryFileName)
try {
return getSummaryData(summaryFile)
} catch (err) {
console.error(`Error generating sidebar from file ${summaryFileName}:`, err)
process.exit(1)
}
}
function getSummaryData(file) {
const lines = fs.readFileSync(file, 'utf8').split('\n')
const sidebarStructure = []
let currentParent = null
lines.forEach((line, i) => {
const level = line.search(/\S|$/) / 2
const match = line.match(/^\s*\*\s*\[([^\]]+)\]\(([^)]+)\)/)
if (match) {
const newEntry = { text: match[1], link: `/${match[2]}`, children: [] }
if (level === 0) {
sidebarStructure.push(newEntry)
currentParent = sidebarStructure[sidebarStructure.length - 1]
} else {
let parent = currentParent
for (let i = 1; i < level; i++) {
parent = parent.children[parent.children.length - 1]
}
parent.children.push(newEntry)
}
}
})
sidebarStructure.forEach(removeEmptyArrays)
return sidebarStructure
}
function removeEmptyArrays(item) {
if (item.children && item.children.length === 0) {
delete item.children;
} else if (item.children) {
item.children.forEach(removeEmptyArrays);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1 +0,0 @@
$theme-color: #17b53f;

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ Thank you so much for thinking of contributing to the [ocelot.social](https://oc
## Getting Set Up
Instructions for how to install all the necessary software and some code guidelines can be found in our main [Readme](./README.md) or in our [documentation](./SUMMARY.md).
Instructions for how to install all the necessary software and some code guidelines can be found in our main [Readme](/README.md) or in our [documentation](/SUMMARY.md).
To get you started we recommend that you join forces with a regular contributor. Please join [our Discord instance](https://discord.gg/AJSX9DCSUA) to chat with developers or just get in touch directly on an issue on either [Github](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues) or [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089):
@ -36,7 +36,7 @@ Every pull request needs to:
## Contribution Flow For Open Source Contributors
See [contributing in main README.md](./README.md#contributing)
See [contributing in main README.md](/README.md#contributing)
## The Team
@ -48,9 +48,6 @@ You can talk to our core team on [Discord](https://discord.gg/AJSX9DCSUA). And o
* Moriz (@Mogge)
* Wolle (@Tirokk)
* Alex (@ogerly)
* Hannes (@elweyn5803)
* Mathias (@mahula)
* Markus (@maeckes#1133)
<!-- * Robert (@roschaefer)
* Matt (@mattwr18)
@ -171,20 +168,40 @@ Please copy and paste the following quotes for the languages:
## Docker More Closely
### Apple Silicon Platform
### Apple M1 Platform
***ATTENTION:** For using Docker commands in Apple Silicon environments!*
***Attention:** For using Docker commands in Apple M1 environments!*
#### Environment Variable For Apple Silicon Platform (M1, M2 Chips)
#### Environment Variable For Apple M1 Platform
If you encounter trouble building the docker containers on an Apple M1 chip you can try to explicitly define the target platform docker builds and pulls images for:
To set the Docker platform environment variable in your terminal tab, run:
```bash
# set env variable for your shell
$ export DOCKER_DEFAULT_PLATFORM=linux/amd64
```
Or alternatively use a `YAML` docker compose overwrite file with `platform: linux/amd64`.
#### Docker Compose Override File For Apple M1 Platform
For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform:
```bash
# in main folder
# for development
$ docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend yarn prod:migrate init
# clean db
$ docker compose exec backend yarn db:reset
# seed db
$ docker compose exec backend yarn db:seed
# for production
$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend /bin/sh -c "yarn prod:migrate init"
```
### Analyzing Docker Builds

220
README.md
View File

@ -1,123 +1,49 @@
# Ocelot.Social
[![Backend Test CI](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-backend.yml/badge.svg?branch=master)](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-backend.yml)
[![Webapp Test CI](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-webapp.yml/badge.svg?branch=master)](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-webapp.yml)
[![E2E Test CI](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-e2e.yml/badge.svg?branch=master)](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test-e2e.yml)
[![Build Status Test](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/test.yml/badge.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions)
[![Build Status Publish](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions/workflows/publish.yml/badge.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social/actions)
[![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/LICENSE.md)
[![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/LICENSE.md)
[![Discord Channel](https://img.shields.io/discord/489522408076738561.svg)](https://discord.gg/AJSX9DCSUA)
[![Open Source Helpers](https://www.codetriage.com/ocelot-social-community/ocelot-social/badges/users.svg)](https://www.codetriage.com/ocelot-social-community/ocelot-social)
[Ocelot.social](https://ocelot.social) is free and open source software program code to run social networks. Its development is supported by a community of programmers and interested network operators.
[ocelot.social](https://ocelot.social) is free and open source software program code to run social networks. Its development is supported by a community of programmers and interested network operators.
<!-- markdownlint-disable MD033 -->
<!-- <p align="center"> -->
<p style="text-align: center;">
<a href="https://ocelot.social" target="_blank"><img src="https://raw.githubusercontent.com/Ocelot-Social-Community/Ocelot-Social/master/webapp/static/img/custom/logo-squared.svg" alt="ocelot.social" width="40%" height="40%"></a>
<p align="center">
<a href="https://ocelot.social" target="_blank"><img src="webapp/static/img/custom/logo-squared.svg" alt="ocelot.social" width="40%" height="40%"></a>
</p>
<!-- markdownlint-enable MD033 -->
Our goal is to enable people to participate fairly and equally in online social networks. The equality of opportunity applies both to the fundamental equality of all people and to the possibility of letting their diverse voices be heard.
We therefore consider it desirable that operators offer such networks so that people can choose where they want to be on the move.
Our vision for the future is that at some point it should be possible to link these networks together (ActivityPub, Fediverse) so that users can also connect with people from other networks - for example, by friending them or following posts or other contributions.
If you would like to help set up this capability with us, please contact us.
At the same time, it should be possible in the future to link these networks with each other (ActivityPub, Fediverse), so that users can also connect with people from other networks - for example by making friends or following posts or other contributions.
In other words, we are interested in a network of networks and in keeping the data as close as possible to the user and the operator they trusts.
## Screenshots
<!-- markdownlint-disable MD033 -->
<img src="https://user-images.githubusercontent.com/17728384/218597429-554e4082-3906-4721-8f68-0c13146fc218.png" alt="Post feed" title="Post feed" />
<!-- markdownlint-enable MD033 -->
Check out more screenshots [here](https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki/en:Screenshots).
## Features
Ocelot.social networks features:
- **news feed**
- **posts**
- as **articles** and **events**
- **comments**
- **filter**
- **search**
- **groups**
- **map**
- **user accounts**
- **user roles**
- make your own **branded network**
- and more …
Check out the [full feature list](https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki/en:FAQ#what-are-the-features).
## User Guide and Frequently Asked Questions
In the [wiki](https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki) you can find more information.
- [User Guide](https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki/en:User-Guide)
- [Frequently Asked Questions](https://github.com/Ocelot-Social-Community/Ocelot-Social/wiki/en:FAQ)
## Demo
Try out our live demo network, see [here](#live-demo-and-developer-logins).
## Help us
If you're wondering how you could help, there are plenty of ways, e.g.:
- Spread the good word about ocelot.social to make it more popular:
- Add the link [ocelot.social](https://ocelot.social) to your website.
- Give ocelot.social a Like at <https://alternativeto.net/software/ocelot-social/>.
- Star our project on GitHub at <https://github.com/Ocelot-Social-Community/Ocelot-Social/>.
- Promote it on your social networks.
- Tell your friends about it by word-of-mouth.
- Write a press article on ocelot.social or contact the editorial office of your local news page or radio station.
- Take a [good first issue](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) or issues that need help. Make sure to read [CONTRIBUTING.md](./CONTRIBUTING.md) first though.
- Testing and [reporting](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/new/choose) bugs.
- Translating: Please [contact us](#contact).
- Reviewing and testing pull requests.
## Donate
Your donation is very welcome and helps to enhance and improve the network. This software is mostly developed and maintained by the association [busFaktor() e.V.](https://www.busfaktor.org/en) . Please support us with a [donation](https://www.busfaktor.org/en/donations) to busFaktor() e.V. . Thanks a lot! ❤️
## Contact
Are you interested in operating your own ocelot.social network or becoming a user? Please contact us here:
- [hello@ocelot.social](mailto:hello@ocelot.social)
- our developer chat on [Discord](https://discord.gg/AJSX9DCSUA)
## For Developers and Contributors
### Introduction
## Introduction
Have a look into our short video:
[ocelot.social - GitHub - Developer Welcome - Tutorial (english)](https://www.youtube.com/watch?v=gZSL6KvBIiY&list=PLFMD5liPP01kbuReHxYXxv_1fI5rIgS1f&index=1)
### Directory Layout
## Directory Layout
There are three important directories:
- [Backend](./backend) runs on the server and is a middleware between database and frontend
- [Frontend](./webapp) is a server-side-rendered and client-side-rendered web frontend
- [Cypress](./cypress) contains end-to-end tests and executable feature specifications
* [Backend](./backend) runs on the server and is a middleware between database and frontend
* [Frontend](./webapp) is a server-side-rendered and client-side-rendered web frontend
* [Cypress](./cypress) contains end-to-end tests and executable feature specifications
In order to setup the application and start to develop features you have to
setup **webapp** and **backend**.
setup **frontend** and **backend**.
There are two approaches:
1. [Local](#local-installation) installation, which means you have to take care of dependencies yourself.
2. **Or** install everything through [Docker](#docker-installation) which takes care of dependencies for you.
2. **Or** Install everything through [Docker](#docker-installation) which takes care of dependencies for you.
### Installation
## Installation
#### Clone the Repository
### Clone the Repository
Clone the repository, this will create a new folder called `Ocelot-Social`:
@ -139,16 +65,16 @@ Change into the new folder.
$ cd Ocelot-Social
```
### Live Demo and Developer Logins
## Live Demo And Developer Logins
**Try out our deployed [development environment](https://stage.ocelot.social).**
Visit our staging networks:
- central staging network: [stage.ocelot.social](https://stage.ocelot.social)
* central staging network: [stage.ocelot.social](https://stage.ocelot.social)
<!-- - rebranded staging network: [rebrand.ocelot.social](https://stage.ocelot.social). -->
#### Login
### Login
Logins for the live demos and developers (local developers after the following installations) in the browser:
@ -158,25 +84,28 @@ Logins for the live demos and developers (local developers after the following i
| `moderator@example.org` | 1234 | moderator |
| `admin@example.org` | 1234 | admin |
#### Docker Installation
### Docker Installation
Docker is a software development container tool that combines software and its dependencies into one standardized unit that contains everything needed to run it. This helps us to avoid problems with dependencies and makes installation easier.
##### General Installation of Docker
#### General Installation of Docker
There are [several ways to install Docker](https://docs.docker.com/get-docker/) on your computer or server.
There are [several ways to install Docker CE](https://docs.docker.com/install/) on your computer or server.
* [install Docker Desktop on macOS](https://docs.docker.com/docker-for-mac/install/)
* [install Docker Desktop on Windows](https://docs.docker.com/docker-for-windows/install/)
* [install Docker CE on Linux](https://docs.docker.com/install/)
Check the correct Docker installation by checking the version before proceeding. E.g. we have the following versions:
```bash
# use Docker version 24.0.6 or newer
# includes Docker Compose
$ docker --version
Docker version 18.09.2
$ docker-compose --version
docker-compose version 1.23.2
```
##### Start Ocelot-Social via Docker Compose
***ATTENTION:** For using Docker commands in Apple Silicon environments see [here](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/CONTRIBUTING.md#apple-silicon-platform).*
#### Start Ocelot-Social via Docker-Compose
Prepare ENVs once beforehand:
@ -186,19 +115,20 @@ $ cp .env.template .env
# in folder backend/
$ cp .env.template .env
```
For Development:
```bash
# in main folder
$ docker compose up
$ docker-compose up
```
For Production:
```bash
# in main folder
$ docker compose -f docker-compose.yml up
$ docker-compose -f docker-compose.yml up
```
This will start all required Docker containers.
@ -207,36 +137,33 @@ Make sure your database is running on `http://localhost:7474/browser/`.
Prepare database once before you start by running the following command in a second terminal:
```bash
# in main folder while docker compose is up
$ docker compose exec backend yarn run db:migrate init
$ docker compose exec backend yarn run db:migrate up
# in main folder while docker-compose is up
$ docker-compose exec backend yarn run db:migrate init
```
Then clear and seed database by running the following command as well in the second terminal:
```bash
# in main folder while docker compose is up
$ docker compose exec backend yarn run db:reset
$ docker compose exec backend yarn run db:seed
# in main folder while docker-compose is up
$ docker-compose exec backend yarn run db:reset
$ docker-compose exec backend yarn run db:seed
```
For a closer description see [backend](./backend/README.md).
For a full documentation of the Docker installation see [summary](./SUMMARY.md).
For a closer description see [backend README.md](./backend/README.md).
For a full documentation see [SUMMARY](./SUMMARY.md).
#### Local Installation
### Local Installation
For a full documentation of the local installation see [summary](./SUMMARY.md).
For a full documentation see [SUMMARY](./SUMMARY.md).
### Contributing
## Contributing
Choose an issue (consider our label [good-first-issue](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)) and leave a comment there. We will then invite you to join our volunteers team.
To have the necessary permission to push directly to this repository, please accept our invitation to join our volunteers team, you will receive via the email, Github will send you, once invited. If we did not invite you yet, please request an invitation via Discord.
We are happy if you fork our repository, but we don't recommend it for development. You do not need a fork.
Clone this repository locally as [described above](#clone-the-repository), create your branch named `<issue-number>-<description>`, add your code and push your branch direct to this repository. Then create a PR by comparing it to our `master`.
***!!! Be aware:*** Please don't compare from a fork, because the tests are breaking caused by credential problems.
Clone this repository locally as [described above](#clone-the-repository), create your branch named `<issue-number>-<description>`, add your code and push your branch to this repository. Then create a PR by comparing it to our `master`.
Please run the following commands before you push:
@ -253,63 +180,46 @@ $ yarn locales --fix
$ yarn test
```
Check out our [contribution guideline](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/CONTRIBUTING.md), too!
Check out our [contribution guideline](./CONTRIBUTING.md), too!
#### Developer Chat
### Developer Chat
Join our friendly open-source community on [Discord](https://discord.gg/AJSX9DCSUA) 😻
Just introduce yourself at `#introduce-yourself` and mention a mentor or `@@Mentors` to get you onboard 🤓
Join our friendly open-source community on [Discord](https://discord.gg/AJSX9DCSUA) :heart_eyes_cat:
Just introduce yourself at `#introduce-yourself` and mention a mentor or `@@Mentors` to get you onboard :neckbeard:
We give write permissions to every developer who asks for it. Just text us on
[Discord](https://discord.gg/AJSX9DCSUA).
### Deployment
## Deployment
Deployment methods can be found in our `deployment` folder described in the [README](./deployment/README.md).
Our branding template is our [stage.ocelot.social](https://github.com/Ocelot-Social-Community/stage.ocelot.social) repository.
Place your branding repository inside `deployment/configurations`.
Deployment methods can be found in the [Ocelot-Social-Deploy-Rebranding](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding) repository.
The only deployment method in this repository for production purposes is [Kubernetes](https://kubernetes.io) for now.
But we just started to develop a deployment for [Docker Compose](https://docs.docker.com/compose/) as well.
The only deployment method in this repository for development purposes as described above is `docker-compose`.
The only deployment method with branding in this repository for development purposes as described above is [Docker Compose](https://docs.docker.com/compose/):
## Technology Stack
```bash
# in folder deployment/
# set your branding folder name in .env
# then run
$ docker compose up
```
* [VueJS](https://vuejs.org/)
* [NuxtJS](https://nuxtjs.org/)
* [GraphQL](https://graphql.org/)
* [NodeJS](https://nodejs.org/en/)
* [Neo4J](https://neo4j.com/)
The code is branded automatically.
To setup the Neo4j database see above.
### For Testing
### Technology Stack
* [Cypress](https://docs.cypress.io/)
* [Storybook](https://storybook.js.org/)
* [Jest](https://jestjs.io/)
* [Vue Test Utils](https://vue-test-utils.vuejs.org/)
* [ESLint](https://eslint.org/)
- [VueJS](https://vuejs.org/)
- [NuxtJS](https://nuxtjs.org/)
- [GraphQL](https://graphql.org/)
- [NodeJS](https://nodejs.org/en/)
- [Neo4J](https://neo4j.com/)
#### For Testing
- [Cypress](https://docs.cypress.io/)
- [Storybook](https://storybook.js.org/)
- [Jest](https://jestjs.io/)
- [Vue Test Utils](https://vue-test-utils.vuejs.org/)
- [ESLint](https://eslint.org/)
### Attributions
## Attributions
Locale Icons made by [Freepik](http://www.freepik.com/) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/).
Browser compatibility testing with [BrowserStack](https://www.browserstack.com/).
<!-- markdownlint-disable MD033 -->
<img alt="BrowserStack Logo" src="https://raw.githubusercontent.com/Ocelot-Social-Community/Ocelot-Social/master/.gitbook/assets/browserstack-logo.svg" width="256">
<!-- markdownlint-enable MD033 -->
<img alt="BrowserStack Logo" src=".gitbook/assets/browserstack-logo.svg" width="256">
### License
## License
See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT).

View File

@ -1,22 +1,24 @@
# Table of contents
* [Introduction](README.md)
* [Edit this Documentation](edit-this-documentation.md)
* [Neo4J](neo4j/README.md)
* [Backend](backend/README.md)
* [GraphQL](backend/graphql.md)
* [neo4j-graphql-js](backend/neo4j-graphql-js.md)
* [Webapp (Frontend)](webapp/README.md)
* [Webapp](webapp/README.md)
* [Components](webapp/components.md)
* [HTML](webapp/html.md)
* [SCSS](webapp/scss.md)
* [Vue](webapp/vue.md)
* [Testing Guide](testing.md)
* [End-to-end Tests](cypress/README.md)
* [Webapp (Frontend) Tests](webapp/testing.md)
* [Backend Tests](backend/testing.md)
* [Deployment](deployment/README.md)
* [End-to-end tests](cypress/README.md)
* [Frontend tests](webapp/testing.md)
* [Backend tests](backend/testing.md)
* [Docker More Closely](DOCKER_MORE_CLOSELY.md)
* [Deployment](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/blob/master/deployment/README.md)
* [Contributing](CONTRIBUTING.md)
* [Feature Specification](cypress/features.md)
* [Code of Conduct](CODE_OF_CONDUCT.md)
* [Documentation](documentation.md)
* [Code of conduct](CODE_OF_CONDUCT.md)
* [License](LICENSE.md)

2
backend/.codecov.yml Normal file
View File

@ -0,0 +1,2 @@
coverage:
range: "60...100"

View File

@ -12,9 +12,11 @@ docker-compose*.yml
./*.log
node_modules/
build/
scripts/
dist/
maintenance-worker/
neo4j/
public/uploads/*
!.gitkeep

View File

@ -1,51 +1,32 @@
DEBUG=neo4j-graphql-js
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=letmein
GRAPHQL_URI=http://localhost:4000
CLIENT_URI=http://localhost:3000
# E-Mail default settings
SUPPORT_EMAIL="devops@ocelot.social"
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
SMTP_HOST=
SMTP_PORT=
SMTP_IGNORE_TLS=true
SMTP_MAX_CONNECTIONS=5
SMTP_MAX_MESSAGES=Infinity
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_SECURE="false" # true for 465, false for other ports
SMTP_DKIM_DOMAINNAME=
SMTP_DKIM_KEYSELECTOR=
SMTP_DKIM_PRIVATEKEY=
# E-Mail settings for our 'docker compose up mailserver'
# SMTP_HOST=localhost
# SMTP_PORT=1025
# SMTP_IGNORE_TLS=true
# SMTP_USERNAME=
# SMTP_PASSWORD=
# SMTP_MAX_CONNECTIONS=1
# SMTP_MAX_MESSAGES= 10
JWT_SECRET="b/&&7b78BF&fv/Vd"
JWT_EXPIRES="2y"
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
SENTRY_DSN_BACKEND=
COMMIT=
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
AWS_ACCESS_KEY_ID=minio
AWS_SECRET_ACCESS_KEY=12341234
AWS_ENDPOINT=http://localhost:9000
AWS_REGION=local
AWS_BUCKET=ocelot
IMAGOR_PUBLIC_URL=http://localhost:8000
IMAGOR_SECRET=mysecret
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_ENDPOINT=
AWS_REGION=
AWS_BUCKET=
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
EMAIL_SUPPORT="devops@ocelot.social"
CATEGORIES_ACTIVE=false
MAX_PINNED_POSTS=1
MAX_GROUP_PINNED_POSTS=1

View File

@ -1,43 +0,0 @@
DEBUG=true
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=letmein
GRAPHQL_URI=http://localhost:4000
CLIENT_URI=http://localhost:3000
# E-Mail default settings
SUPPORT_EMAIL="devops@ocelot.social"
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
SMTP_HOST=mailserver
SMTP_PORT=1025
SMTP_IGNORE_TLS=true
SMTP_MAX_CONNECTIONS=5
SMTP_MAX_MESSAGES=Infinity
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_SECURE="false" # true for 465, false for other ports
SMTP_DKIM_DOMAINNAME=
SMTP_DKIM_KEYSELECTOR=
SMTP_DKIM_PRIVATEKEY=
JWT_SECRET="b/&&7b78BF&fv/Vd"
JWT_EXPIRES="2y"
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
SENTRY_DSN_BACKEND=
COMMIT=
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
AWS_ACCESS_KEY_ID=minio
AWS_SECRET_ACCESS_KEY=12341234
AWS_ENDPOINT=http://localhost:9000
AWS_REGION=local
AWS_BUCKET=ocelot
IMAGOR_PUBLIC_URL=http://localhost:8000
IMAGOR_SECRET=mysecret
CATEGORIES_ACTIVE=false
MAX_PINNED_POSTS=1
MAX_GROUP_PINNED_POSTS=1

View File

@ -1,3 +0,0 @@
node_modules/
build/
coverage/

View File

@ -1,263 +0,0 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
root: true,
env: {
node: true,
},
parser: '@typescript-eslint/parser',
plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise', 'security', 'no-catch-all'],
extends: [
'standard',
'eslint:recommended',
'plugin:n/recommended',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:promise/recommended',
'plugin:@eslint-community/eslint-comments/recommended',
'prettier',
],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
project: ['./tsconfig.json', './backend/tsconfig.json'],
},
node: true,
},
},
rules: {
'no-catch-all/no-catch-all': 'error',
'no-console': 'error',
camelcase: 'error',
'no-debugger': 'error',
'prettier/prettier': [
'error',
{
htmlWhitespaceSensitivity: 'ignore',
},
],
// import
'import/export': 'error',
// 'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'off', // not compatible with .eslintrc.cjs
'import/default': 'error',
'import/named': 'off', // has false positives
'import/namespace': 'error',
'import/no-absolute-path': 'error',
'import/no-cycle': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
'import/no-self-import': 'error',
'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': 'error',
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'off', // not compatible with neode
'import/no-default-export': 'off', // not compatible with neode
'import/no-duplicates': 'error',
'import/no-named-default': 'error',
'import/no-namespace': 'error',
'import/no-unassigned-import': 'error',
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'newlines-between': 'always',
pathGroups: [
{
pattern: '@?*/**',
group: 'external',
position: 'after',
},
{
pattern: '@/**',
group: 'external',
position: 'after',
},
],
alphabetize: {
order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
caseInsensitive: true /* ignore case. Options: [true, false] */,
},
distinctGroup: true,
},
],
'import/prefer-default-export': 'off',
// n
// 'n/callback-return': 'error',
'n/exports-style': 'error',
'n/file-extension-in-import': ['error', 'never'],
'n/global-require': 'error',
'n/handle-callback-err': 'error',
// 'n/hashbang': 'error', // part of n/recommended
'n/no-callback-literal': 'error',
// 'n/no-deprecated-api': 'error', // part of n/recommended
// 'n/no-exports-assign': 'error', // part of n/recommended
'n/no-extraneous-import': 'off', // duplicate of import/no-extraneous-dependencies // part of n/recommended
// 'n/no-extraneous-require': 'error', // part of n/recommended
'n/no-hide-core-modules': 'error',
'n/no-missing-import': 'off', // not compatible with typescript // part of n/recommended
// 'n/no-missing-require': 'error', // part of n/recommended
'n/no-mixed-requires': 'error',
'n/no-new-require': 'error',
'n/no-path-concat': 'error',
'n/no-process-env': 'error',
// 'n/no-process-exit': 'error', // part of n/recommended
'n/no-restricted-import': 'error',
'n/no-restricted-require': 'error',
'n/no-sync': 'error',
// 'n/no-unpublished-bin': 'error', // part of n/recommended
'n/no-unpublished-import': [
'error',
{ allowModules: ['apollo-server-testing', 'rosie', '@faker-js/faker', 'ts-jest'] },
], // part of n/recommended
'n/no-unpublished-require': ['error', { allowModules: ['ts-jest', 'require-json5'] }], // part of n/recommended
// 'n/no-unsupported-features/es-builtins': 'error', // part of n/recommended
// 'n/no-unsupported-features/es-syntax': 'error', // part of n/recommended
// 'n/no-unsupported-features/node-builtins': 'error', // part of n/recommended
'n/prefer-global/buffer': 'error',
'n/prefer-global/console': 'error',
'n/prefer-global/process': 'error',
'n/prefer-global/text-decoder': 'error',
'n/prefer-global/text-encoder': 'error',
'n/prefer-global/url': 'error',
'n/prefer-global/url-search-params': 'error',
'n/prefer-node-protocol': 'error',
'n/prefer-promises/dns': 'error',
'n/prefer-promises/fs': 'error',
// 'n/process-exit-as-throw': 'error', // part of n/recommended
'n/shebang': 'error',
// promise
// 'promise/always-return': 'error', // part of promise/recommended
'promise/avoid-new': 'error',
// 'promise/catch-or-return': 'error', // part of promise/recommended
// 'promise/no-callback-in-promise': 'warn', // part of promise/recommended
'promise/no-multiple-resolved': 'error',
'promise/no-native': 'off', // ES5 only
// 'promise/no-nesting': 'warn', // part of promise/recommended
// 'promise/no-new-statics': 'error', // part of promise/recommended
// 'promise/no-promise-in-callback': 'warn', // part of promise/recommended
// 'promise/no-return-in-finally': 'warn', // part of promise/recommended
// 'promise/no-return-wrap': 'error', // part of promise/recommended
// 'promise/param-names': 'error', // part of promise/recommended
'promise/prefer-await-to-callbacks': 'error',
'promise/prefer-catch': 'error',
'promise/spec-only': 'error',
// 'promise/valid-params': 'error', // part of promise/recommended
// eslint comments
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
'@eslint-community/eslint-comments/no-restricted-disable': 'error',
'@eslint-community/eslint-comments/no-use': 'off',
'@eslint-community/eslint-comments/require-description': 'off',
},
overrides: [
{
files: ['*.js', '*.cjs', '*.ts', '*.tsx'],
extends: ['plugin:security/recommended-legacy'],
},
// only for ts files
{
files: ['*.ts', '*.tsx'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier',
],
rules: {
// allow explicitly defined dangling promises
// '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
// ignore prefer-regexp-exec rule to allow string.match(regex)
'@typescript-eslint/prefer-regexp-exec': 'off',
// this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486
'import/unambiguous': 'off',
// this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable
'@typescript-eslint/no-unnecessary-condition': 'off',
// respect underscore as acceptable unused variable
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
},
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
// this is to properly reference the referenced project database without requirement of compiling it
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
},
},
{
files: ['*.spec.ts'],
plugins: ['jest'],
env: {
jest: true,
},
rules: {
'jest/no-disabled-tests': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'error',
'jest/valid-expect': 'error',
'@typescript-eslint/unbound-method': 'off',
'jest/unbound-method': 'error',
},
},
{
extends: ['plugin:jsonc/recommended-with-jsonc'],
files: ['*.json', '*.json5', '*.jsonc'],
parser: 'jsonc-eslint-parser',
},
{
files: ['*.graphql', '*.gql'],
parser: '@graphql-eslint/eslint-plugin',
plugins: ['@graphql-eslint'],
extends: ['plugin:@graphql-eslint/schema-recommended'],
rules: {
'@graphql-eslint/description-style': ['error', { style: 'inline' }],
'@graphql-eslint/require-description': 'off',
'@graphql-eslint/naming-convention': 'off',
'@graphql-eslint/strict-id-in-types': 'off',
'@graphql-eslint/no-typename-prefix': 'off',
// incompatible: `depends on a GraphQL validation rule "XXX" but it's not available in the "graphql" version you are using. Skipping…`
'@graphql-eslint/known-directives': 'off',
'@graphql-eslint/known-argument-names': 'off',
'@graphql-eslint/known-type-names': 'off',
'@graphql-eslint/lone-schema-definition': 'off',
'@graphql-eslint/provided-required-arguments': 'off',
'@graphql-eslint/unique-directive-names': 'off',
'@graphql-eslint/unique-directive-names-per-location': 'off',
'@graphql-eslint/unique-field-definition-names': 'off',
'@graphql-eslint/unique-operation-types': 'off',
'@graphql-eslint/unique-type-names': 'off',
},
parserOptions: {
schema: './src/graphql/types/**/*.gql',
assumeValid: true,
},
},
],
}

25
backend/.eslintrc.js Normal file
View File

@ -0,0 +1,25 @@
module.exports = {
env: {
es6: true,
node: true,
jest: true
},
parserOptions: {
parser: 'babel-eslint'
},
extends: [
'standard',
'plugin:prettier/recommended'
],
plugins: [
'jest'
],
rules: {
//'indent': [ 'error', 2 ],
//'quotes': [ "error", "single"],
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-console': ['error'],
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'prettier/prettier': ['error'],
},
};

5
backend/.gitignore vendored
View File

@ -3,10 +3,11 @@ node_modules/
.vscode
.idea
yarn-error.log
build/*
dist/*
coverage.lcov
.nyc_output/
public/uploads/*
!.gitkeep
# Apple macOS folder attribute file
.DS_Store
.DS_Store

3
backend/.graphqlconfig Normal file
View File

@ -0,0 +1,3 @@
{
"schemaPath": "./src/schema.graphql"
}

View File

@ -1 +1 @@
v25.3.0
v19.4.0

View File

@ -1,50 +1,103 @@
# syntax=docker/dockerfile:1
FROM node:25.6.0-alpine AS base
##################################################################################
# BASE (Is pushed to DockerHub for rebranding) ###################################
##################################################################################
FROM node:19.4.0-alpine3.17 as base
# ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
ENV DOCKER_WORKDIR="/app"
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
ARG BBUILD_DATE="1970-01-01T00:00:00.00Z"
ENV BUILD_DATE=$BBUILD_DATE
## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0
ARG BBUILD_VERSION="0.0.0-0"
ENV BUILD_VERSION=$BBUILD_VERSION
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
ARG BBUILD_COMMIT="0000000"
ENV BUILD_COMMIT=$BBUILD_COMMIT
## SET NODE_ENV
ENV NODE_ENV="production"
## App relevant Envs
ENV PORT="4000"
# Labels
LABEL org.label-schema.build-date="${BUILD_DATE}"
LABEL org.label-schema.name="ocelot.social:backend"
LABEL org.label-schema.description="Backend of the Social Network Software ocelot.social"
LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md"
LABEL org.label-schema.url="https://ocelot.social"
LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/backend"
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
LABEL org.label-schema.vendor="ocelot.social Community"
LABEL org.label-schema.version="${BUILD_VERSION}"
LABEL org.label-schema.schema-version="1.0"
LABEL maintainer="devops@ocelot.social"
ENV NODE_ENV="production"
ENV PORT="4000"
# Install Additional Software
## install: git
RUN apk --no-cache add git
# Settings
## Expose Container Port
EXPOSE ${PORT}
RUN apk --no-cache add git python3 make g++ bash linux-headers
RUN mkdir -p /app
WORKDIR /app
CMD ["/bin/bash", "-c", "yarn run start"]
FROM base AS development
CMD ["/bin/sh", "-c", "yarn install && yarn run dev"]
## Workdir
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
FROM base AS build
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
FROM base as development
# We don't need to copy or build anything since we gonna bind to the
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute yarn install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "yarn install && yarn run dev"
##################################################################################
# CODE (Does contain all code files and is pushed to DockerHub for rebranding) ###
##################################################################################
FROM base as code
# copy everything, but do not build.
COPY . .
ONBUILD COPY ./branding/constants/ src/config/tmp
# copy categories to brand them (use yarn prod:db:data:categories)
ONBUILD COPY ./branding/constants/ src/constants/
ONBUILD RUN tools/replace-constants.sh
ONBUILD COPY ./branding/email/ src/middleware/helpers/email/
ONBUILD COPY ./branding/middlewares/ src/middleware/branding/
ONBUILD COPY ./branding/data/ src/db/data
ONBUILD COPY ./branding/public/ public/
ONBUILD RUN --mount=type=cache,target=/yarn-cache,sharing=locked \
yarn install --production=false --frozen-lockfile --non-interactive --cache-folder /yarn-cache
ONBUILD RUN yarn run build
ONBUILD RUN mkdir /build
ONBUILD RUN cp -r ./build /build
ONBUILD RUN cp -r ./public /build
ONBUILD RUN cp -r ./package.json yarn.lock /build
ONBUILD RUN --mount=type=cache,target=/yarn-cache,sharing=locked \
cd /build && yarn install --production=true --frozen-lockfile --non-interactive --cache-folder /yarn-cache
FROM build AS test
# required for the migrations
# ONBUILD RUN cp -r ./src /src
CMD ["/bin/bash", "-c", "yarn run dev"]
##################################################################################
# BUILD (Does contain all files and the compilate and is therefore bloated) ######
##################################################################################
FROM code as build
FROM build AS production_build
# yarn install
RUN yarn install --production=false --frozen-lockfile --non-interactive
# yarn build
RUN yarn run build
FROM base AS production
COPY --from=production_build /build .
##################################################################################
# TEST ###########################################################################
##################################################################################
FROM build as test
# Run command
CMD /bin/sh -c "yarn run dev"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
##################################################################################
FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
# Copy static files
# TODO - externalize the uploads so we can copy the whole folder
COPY --from=build ${DOCKER_WORKDIR}/public/img/ ./public/img/
COPY --from=build ${DOCKER_WORKDIR}/public/providers.json ./public/providers.json
# Copy package.json for script definitions (lock file should not be needed)
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
# Run command
CMD /bin/sh -c "yarn run start"

View File

@ -6,12 +6,12 @@ Run the following command to install everything through docker.
The installation takes a bit longer on the first pass or on rebuild ...
```sh
```bash
# in main folder
$ docker compose up
$ docker-compose up
# or
# rebuild the containers for a cleanup
$ docker compose up --build
$ docker-compose up --build
```
Wait a little until your backend is up and running at [http://localhost:4000/](http://localhost:4000/).
@ -19,21 +19,23 @@ Wait a little until your backend is up and running at [http://localhost:4000/](h
## Installation without Docker
For the local installation you need a recent version of
[Node](https://nodejs.org/en/). We are using
`v25.3.0` and therefore we recommend to use the same version. You can use the
[Node](https://nodejs.org/en/) (&gt;= `v16.19.0`). We are using
`v19.4.0` and therefore we recommend to use the same version
([see](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4082)
some known problems with more recent node versions). You can use the
[node version manager](https://github.com/nvm-sh/nvm) `nvm` to switch
between different local Node versions:
```sh
# install Node using '.nvmrc' file
```bash
# install Node
$ cd backend
$ nvm install
$ nvm use
$ nvm install v19.4.0
$ nvm use v19.4.0
```
Install node dependencies with [yarn](https://yarnpkg.com/en/):
```sh
```bash
# in main folder
$ cd backend
$ yarn install
@ -45,7 +47,7 @@ $ nvm use && yarn
Copy Environment Variables:
```sh
```bash
# in backend/
$ cp .env.template .env
```
@ -55,14 +57,14 @@ a [local Neo4J](http://localhost:7474) instance is up and running.
Start the backend for development with:
```sh
```bash
# in backend/
$ yarn run dev
```
or start the backend in production environment with:
```sh
```bash
# in backend/
$ yarn run start
```
@ -77,141 +79,146 @@ More details about our GraphQL playground and how to use it with ocelot.social c
![GraphQL Playground](../.gitbook/assets/graphql-playground.png)
## Database
### Database Indices and Constraints
A fresh database needs to be initialized and migrated.
Database indices and constraints need to be created when the database and the
backend is running:
```sh
# in folder backend while database is running
yarn db:migrate init
# for docker environments:
docker exec ocelot-social-backend-1 yarn db:migrate init
# for docker production:
docker exec ocelot-social-backend-1 yarn prod:migrate init
{% tabs %}
{% tab title="Docker" %}
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate init
```
```sh
# in backend with database running (In docker or local)
yarn db:migrate up
{% endtab %}
{% tab title="Without Docker" %}
# for docker development:
docker exec ocelot-social-backend-1 yarn db:migrate up
# for docker production
docker exec ocelot-social-backend-1 yarn prod:migrate up
```bash
# in folder backend/ while database is running
# make sure your database is running on http://localhost:7474/browser/
yarn run db:migrate init
```
### Optional Data
{% endtab %}
{% endtabs %}
You can seed some optional data into the database.
#### Seed Database
To create the default admin <admin@example.org> with password `1234` use:
If you want your backend to return anything else than an empty response, you
need to seed your database:
```sh
# in backend with database running (In docker or local)
yarn db:data:admin
{% tabs %}
{% tab title="Docker" %}
In another terminal run:
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:seed
```
When using `CATEGORIES_ACTIVE=true` you also want to seed the categories with:
To reset the database run:
```sh
# in backend with database running (In docker or local)
yarn db:data:categories
```
### Branding Data
You might need to seed some branding specific data into the database.
To do so, run:
```sh
# in backend with database running (In docker or local)
yarn db:data:branding
# for docker
docker exec ocelot-social-backend-1 yarn db:data:branding
```
### Seed Data
For a predefined set of test data you can seed the database with:
```sh
# in backend with database running (In docker or local)
yarn db:seed
# for docker
docker exec ocelot-social-backend-1 yarn db:seed
```
### Reset Data
In order to reset the database you can run:
```sh
# in backend with database running (In docker or local)
yarn db:reset
# or deleting the migrations as well
yarn db:reset:withmigrations
# for docker
docker exec ocelot-social-backend-1 yarn db:reset
# or deleting the migrations as well
docker exec ocelot-social-backend-1 yarn db:reset:withmigrations
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:reset
# you could also wipe out your neo4j database and delete all volumes with:
docker compose down -v
$ docker-compose down -v
# if container is not running, run this command to set up your database indeces and contstraints
$ docker-compose exec backend yarn run db:migrate init
```
> Note: This just deletes the data and not the constraints, hence you do not need to rerun `yarn db:migrate init` or `yarn db:migrate up`.
{% endtab %}
{% tab title="Without Docker" %}
Run:
```bash
# in backend/ while database is running
$ yarn run db:seed
```
To reset the database run:
```bash
# in backend/ while database is running
$ yarn run db:reset
```
{% endtab %}
{% endtabs %}
### Data migrations
Although Neo4J is schema-less,you might find yourself in a situation in which
you have to migrate your data e.g. because your data modeling has changed.
{% tabs %}
{% tab title="Docker" %}
Generate a data migration file:
```sh
# in backend
$ yarn run db:migrate:create your_data_migration
# Edit the file in ./src/db/migrations/
# for docker
# in main folder while docker compose is running
$ docker compose exec ocelot-social-backend-1 yarn run db:migrate:create your_data_migration
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate:create your_data_migration
# Edit the file in ./src/db/migrations/
```
To run the migration:
```sh
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate up
```
{% endtab %}
{% tab title="Without Docker" %}
Generate a data migration file:
```bash
# in backend/
$ yarn run db:migrate:create your_data_migration
# Edit the file in ./src/db/migrations/
```
To run the migration:
```bash
# in backend/ while database is running
$ yarn run db:migrate up
# for docker
# in main folder while docker compose is running
$ docker exec backend yarn run db:migrate up
```
{% endtab %}
{% endtabs %}
## Testing
**Beware**: We have no multiple database setup at the moment. We clean the
database after each test, running the tests will wipe out all your data!
{% tabs %}
{% tab title="Docker" %}
Run the unit tests:
```sh
```bash
# in main folder while docker-compose is running
$ docker-compose exec backend yarn run test
```
{% endtab %}
{% tab title="Without Docker" %}
Run the unit tests:
```bash
# in backend/ while database is running
$ yarn run test
# for docker
# in main folder while docker compose is running
$ docker exec ocelot-social-backend-1 yarn run test
```
If the snapshots of the emails must be updated, you have to run the tests in docker! Otherwise the CI will fail.
```sh
# in main folder while docker compose is running
$ docker exec ocelot-social-backend-1 yarn run test -u src/emails/
```
{% endtab %}
{% endtabs %}

15
backend/babel.config.json Normal file
View File

@ -0,0 +1,15 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "10"
}
}
]
],
"plugins": [
"@babel/plugin-proposal-throw-expressions"
]
}

View File

@ -2,13 +2,12 @@
GraphQL is a data query language which provides an alternative to REST and ad-hoc web service architectures. It allows clients to define the structure of the data required, and exactly the same structure of the data is returned from the server.
We have a closer description for the [GraphQL Playground](./src/graphql/GraphQL-Playground.md).
![GraphQL Playground](../.gitbook/assets/graphql-playground%20%281%29.png)
![GraphQL Playground](../../../.gitbook/assets/graphql-playground%20%281%29.png)
## Middleware keeps resolvers clean
![Middleware schema](../.gitbook/assets/grafik-4.png)
![](../.gitbook/assets/grafik-4.png)
A well-organized codebase is key for the ability to maintain and easily introduce changes into an app. Figuring out the right structure for your code remains a continuous challenge - especially as an application grows and more developers are joining a project.

View File

@ -1,27 +0,0 @@
/* eslint-disable import/no-commonjs */
const requireJSON5 = require('require-json5')
const { pathsToModuleNameMapper } = require('ts-jest')
const { compilerOptions } = requireJSON5('./tsconfig.json')
module.exports = {
verbose: true,
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: [
'**/*.ts',
'!**/node_modules/**',
'!**/test/**',
'!**/build/**',
'!**/src/**/?(*.)+(spec|test).ts?(x)',
'!**/src/db/**',
],
coverageThreshold: {
global: {
lines: 93,
},
},
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
}

14
backend/jest.config.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
verbose: true,
collectCoverage: true,
collectCoverageFrom: [
'**/*.js',
'!**/node_modules/**',
'!**/test/**',
'!**/dist/**',
'!**/src/**/?(*.)+(spec|test).js?(x)'
],
coverageReporters: ['lcov', 'text'],
testMatch: ['**/src/**/?(*.)+(spec|test).js?(x)'],
setupFilesAfterEnv: ['<rootDir>/test/setup.js']
}

View File

@ -1,147 +1,130 @@
{
"name": "ocelot-social-backend",
"version": "3.14.1",
"version": "2.3.0",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
"license": "MIT",
"private": false,
"main": "src/index.ts",
"main": "src/index.js",
"scripts": {
"start": "node build/src/",
"build": "tsc && tsc-alias && ./scripts/build.copy.files.sh",
"dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql",
"dev:debug": "nodemon --exec node --inspect=0.0.0.0:9229 build/src/index.js -e js,ts,gql",
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc,.graphql,.gql .",
"__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",
"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",
"db:reset": "ts-node --require tsconfig-paths/register src/db/reset.ts",
"db:reset:withmigrations": "ts-node --require tsconfig-paths/register src/db/reset-with-migrations.ts",
"db:seed": "ts-node --require tsconfig-paths/register src/db/seed.ts",
"db:data:admin": "ts-node --require tsconfig-paths/register src/db/admin.ts",
"db:data:badges": "ts-node --require tsconfig-paths/register src/db/badges.ts",
"db:data:branding": "ts-node --require tsconfig-paths/register src/db/data-branding.ts",
"db:data:categories": "ts-node --require tsconfig-paths/register src/db/categories.ts",
"db:migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --store ./src/db/migrate/store.ts",
"db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create",
"db:func:disable:notifications": "ts-node --require tsconfig-paths/register src/db/disable-notifications.ts",
"prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js",
"prod:db:data:branding": "node build/src/db/data-branding.js",
"prod:db:data:categories": "node build/src/db/categories.js",
"prod:db:data:admin": "node build/src/db/admin.js",
"prod:db:func:disable:notifications": "node build/src/db/disable-notifications.js"
"db:clean": "babel-node src/db/clean.js",
"db:reset": "yarn run db:clean",
"db:seed": "babel-node src/db/seed.js",
"db:migrate": "yarn run __migrate --store ./src/db/migrate/store.js",
"db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.js --date-format 'yyyymmddHHmmss' create"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.990.0",
"@aws-sdk/lib-storage": "^3.985.0",
"@sentry/node": "^5.30.0",
"@types/mime-types": "^3.0.1",
"@babel/cli": "~7.8.4",
"@babel/core": "~7.9.0",
"@babel/node": "~7.8.7",
"@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",
"apollo-link-context": "~1.0.20",
"apollo-link-http": "~1.5.17",
"apollo-server": "~2.14.2",
"apollo-server-express": "^2.14.2",
"bcryptjs": "~3.0.3",
"body-parser": "^1.20.3",
"cheerio": "~1.2.0",
"cross-env": "~10.1.0",
"dotenv": "~17.0.1",
"email-templates": "^13.0.1",
"express": "^4.22.1",
"aws-sdk": "^2.652.0",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.1.0",
"babel-jest": "~25.2.6",
"babel-plugin-transform-runtime": "^6.23.0",
"bcryptjs": "~2.4.3",
"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-middleware": "~6.1.35",
"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.7.0",
"graphql-redis-subscriptions": "^2.2.1",
"graphql-shield": "~7.2.2",
"graphql-subscriptions": "^1.1.0",
"graphql-tag": "~2.10.3",
"graphql-upload": "^13.0.0",
"helmet": "~8.1.0",
"ioredis": "^5.9.2",
"helmet": "~3.22.0",
"ioredis": "^4.16.1",
"jsonwebtoken": "~8.5.1",
"languagedetect": "^2.0.0",
"linkify-html": "^4.3.2",
"linkifyjs": "^4.3.2",
"lodash": "~4.17.23",
"linkifyjs": "~2.1.8",
"lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.8",
"metascraper": "^5.49.19",
"metascraper-author": "^5.49.19",
"metascraper-date": "^5.49.19",
"metascraper-description": "^5.49.19",
"metascraper-image": "^5.49.19",
"metascraper-lang": "^5.49.19",
"metascraper": "^5.33.5",
"metascraper-audio": "^5.33.5",
"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",
"metascraper-lang": "^5.33.5",
"metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.49.19",
"metascraper-publisher": "^5.49.19",
"metascraper-soundcloud": "^5.34.4",
"metascraper-title": "^5.49.19",
"metascraper-url": "^5.49.19",
"metascraper-video": "^5.49.19",
"metascraper-youtube": "^5.49.20",
"migrate": "^2.1.0",
"mime-types": "^3.0.2",
"minimatch": "^10.1.2",
"metascraper-logo": "^5.33.5",
"metascraper-publisher": "^5.33.5",
"metascraper-soundcloud": "^5.33.5",
"metascraper-title": "^5.33.5",
"metascraper-url": "^5.33.5",
"metascraper-video": "^5.33.5",
"metascraper-youtube": "^5.33.5",
"migrate": "^1.7.0",
"mime-types": "^2.1.26",
"minimatch": "^3.0.4",
"mustache": "^4.2.0",
"neo4j-driver": "^4.4.11",
"neo4j-graphql-js": "2.11.5",
"neode": "^0.4.9",
"node-fetch": "^2.7.0",
"nodemailer": "^8.0.1",
"neo4j-driver": "^4.0.2",
"neo4j-graphql-js": "^2.11.5",
"neode": "^0.4.8",
"node-fetch": "~2.6.1",
"nodemailer": "^6.4.4",
"nodemailer-html-to-text": "^3.2.0",
"preview-email": "^3.1.1",
"pug": "^3.0.3",
"sanitize-html": "~2.17.0",
"slugify": "^1.6.6",
"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",
"tslog": "^4.10.2",
"uuid": "~9.0.1",
"validator": "^13.15.26",
"xregexp": "^5.1.2"
"uuid": "~8.3.2",
"validator": "^13.0.0",
"wait-on": "~4.0.1",
"xregexp": "^4.3.0"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
"@faker-js/faker": "9.9.0",
"@graphql-eslint/eslint-plugin": "^3.20.1",
"@types/email-templates": "^10.0.4",
"@types/jest": "^30.0.0",
"@types/jsonwebtoken": "~8.5.1",
"@types/lodash": "^4.17.23",
"@types/node": "^25.2.3",
"@types/request": "^2.48.13",
"@types/slug": "^5.0.9",
"@types/uuid": "~9.0.1",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@faker-js/faker": "5.1.0",
"apollo-server-testing": "~2.11.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.13.0",
"eslint-plugin-jsonc": "^2.21.1",
"eslint-plugin-n": "^17.23.2",
"eslint-plugin-no-catch-all": "^1.1.0",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-security": "^3.0.1",
"jest": "^30.2.0",
"nodemon": "~3.1.11",
"prettier": "^3.8.1",
"require-json5": "^1.3.0",
"rosie": "^2.1.1",
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.16",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.8.3"
"chai": "~4.2.0",
"cucumber": "~6.0.5",
"eslint": "~6.8.0",
"eslint-config-prettier": "~6.15.0",
"eslint-config-standard": "~14.1.1",
"eslint-plugin-import": "~2.20.2",
"eslint-plugin-jest": "~23.8.2",
"eslint-plugin-node": "~11.1.0",
"eslint-plugin-prettier": "~3.4.1",
"eslint-plugin-promise": "~4.3.1",
"eslint-plugin-standard": "~4.0.1",
"jest": "~25.3.0",
"nodemon": "~2.0.2",
"prettier": "~2.3.2",
"rosie": "^2.0.1",
"supertest": "~4.0.2"
},
"resolutions": {
"**/**/fs-capacitor": "^6.2.0",
"**/**/fs-capacitor":"^6.2.0",
"**/graphql-upload": "^11.0.0",
"**/strip-ansi": "6.0.1",
"**/string-width": "4.2.0",
"**/wrap-ansi": "7.0.0",
"**/jwa": "^2.0.1",
"**/@types/express": "4.17.25"
},
"engines": {
"node": ">=20.12.1"
"nan": "2.17.0"
}
}

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="400" height="346.67" version="1.1" viewBox="0 0 400 346.67" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="linearGradient4" x1="708.76" x2="493.17" y1="280.91" y2="65.326" gradientTransform="translate(-404.06 .215)" gradientUnits="userSpaceOnUse">
<stop stop-color="#c1c1c1" offset="0"/>
<stop stop-color="#fcfcfc" offset="1"/>
</linearGradient>
</defs>
<path d="m-0.21505 173.98 100.65-173.76h198.71l101.08 173.76-99.785 172.04-201.29 0.43011z" fill="#bebebe"/>
<path d="m22.482 173.91 89.236-154.07h176.18l89.617 154.07-88.473 152.54-178.47 0.38135z" fill="url(#linearGradient4)"/>
</svg>

Before

Width:  |  Height:  |  Size: 773 B

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="513"
height="444"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<g
fill="none"
fill-rule="evenodd"
id="g3">
<path
fill="#333"
d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"
id="path1"
style="fill:#868383;fill-opacity:1" />
<g
fill="#ffffff"
id="g2"
transform="translate(92)">
<path
d="m 35.01,367.726 c -0.08,-21.169 -0.205,-53.162 21.257,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.367,0.994 L 35.024,371 Z"
id="path2" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 637 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1 @@
<svg width="513" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"/><path d="M35.01 367.726c-.08-21.169-.205-53.162 21.257-71.332 3.817-3.253 9.93-7.497 17.321-9.224 2.575-.523 4.956-.756 7.262-.979 4.438-.431 8.27-.804 12.054-2.9l4.954-2.846c9.87-5.655 19.194-10.996 28.226-17.377 5.085-3.632 6.726-15.73 6.095-25.428-.214-2.792-1.893-5.7-3.67-8.777-1.097-1.901-2.232-3.867-3.065-5.916l-.073-.199a56.976 56.976 0 0 1-.422-1.443c-1.195-4.205-1.933-6.378-2.386-7.476-7.029-.944-11.8-8.647-12.888-21.006l-.031-.557c-.645-12.785.808-16.13 2.316-17.716.24-.254.505-.475.783-.666-1.754-16.051 3.115-32.521 13.358-44.704 9.314-11.079 21.955-17.18 35.592-17.18 3.73 0 7.55.458 11.355 1.362 25.63 6.228 41.679 30.27 40.062 59.227.53.251 1.018.61 1.44 1.066 2.752 2.964 2.47 10.97 2.22 14.276l-.024.41c-.335 5.236-.684 10.65-3.052 15.73-1.739 3.918-4.405 6.242-6.76 8.29-2.396 2.089-4.288 3.735-5.294 6.885-.7 2.416-1.645 4.866-2.559 7.235-1.752 4.538-3.407 8.827-3.54 13.244-.427 10.222 1.17 18.391 4.172 21.359 5.097 5.163 13.003 9.391 19.978 13.121 1.6.855 3.166 1.692 4.654 2.517 9.28 5.052 16.07 7.915 25.309 8.557 9.118.849 18.056 5.193 24.754 11.97.736.641 1.82 1.744 3.694 3.648 4.416 4.492 4.416 4.492 4.426 5.852l.007.758c10.783 17.702 11.14 40.656 11.415 58.169l.05 3.28-3.278.028c-42.05.363-84.058.677-126.058.993-42.12.314-84.232.632-126.367.994l-3.273.029-.014-3.274zM329.011 135.763a5.232 5.232 0 0 0-5.223 5.23 5.232 5.232 0 0 0 5.223 5.23 5.236 5.236 0 0 0 5.231-5.23 5.236 5.236 0 0 0-5.23-5.23m0 40.237C309.705 176 294 160.297 294 140.993 294 121.698 309.706 106 329.011 106 348.303 106 364 121.698 364 140.993 364 160.297 348.303 176 329.011 176" fill="#FFF"/><path d="M330.511 101C308.173 101 290 119.164 290 141.492 290 163.828 308.173 182 330.511 182 352.836 182 371 163.828 371 141.492 371 119.164 352.836 101 330.511 101m0 51.022c5.823 0 10.531-4.716 10.531-10.53a10.517 10.517 0 0 0-10.53-10.529 10.51 10.51 0 0 0-10.523 10.529c0 5.814 4.7 10.53 10.522 10.53m0-40.496c16.563 0 29.963 13.406 29.963 29.966 0 16.555-13.4 29.982-29.963 29.982-16.555 0-29.985-13.427-29.985-29.982 0-16.56 13.43-29.966 29.985-29.966" fill="#AD245D"/><path d="M331 106.209c-20.305 0-36.825 16.06-36.825 35.799 0 19.747 16.52 35.813 36.825 35.813 20.306 0 36.827-16.066 36.827-35.813 0-19.74-16.521-35.8-36.827-35.8zM314.287 215l-4.11-21.345c-.324-.129-.648-.265-.972-.404l-18.012 12.169-23.607-23.609 12.186-18.009a63.31 63.31 0 0 1-.403-.968L258 158.712v-33.383l21.361-4.13c.131-.327.267-.652.407-.979l-12.18-18.025 23.608-23.612 18.015 12.198c.322-.137.643-.27.964-.4L314.287 69h33.416l4.13 21.387c.319.13.638.26.956.396l18.024-12.2 23.608 23.612-12.186 18.031c.139.324.273.648.402.971L404 125.33v33.381l-21.37 4.124c-.13.32-.262.64-.398.96l12.19 18.017-23.606 23.609-18.021-12.171c-.32.137-.642.27-.964.402L347.701 215h-33.414z" fill="#FFF"/><path d="M330 171.448c-17.342 0-31.45-13.656-31.45-30.44 0-16.778 14.108-30.427 31.45-30.427 17.341 0 31.449 13.649 31.449 30.426 0 16.785-14.108 30.441-31.45 30.441zM350.979 63h-41.97l-1.64 8.517-1.953 10.156-8.55-5.788-7.18-4.862-6.132 6.132-17.399 17.4-6.126 6.126 4.85 7.178 5.8 8.583-10.173 1.966-8.506 1.646v41.932l8.51 1.643 10.16 1.962-5.787 8.553-4.858 7.179 6.13 6.13 17.399 17.399 6.126 6.126 7.177-4.85 8.56-5.782 1.954 10.141 1.64 8.513H350.975l1.645-8.507 1.964-10.15 8.566 5.787 7.178 4.846 6.124-6.124 17.4-17.399 6.13-6.13-4.858-7.18-5.788-8.554 10.153-1.96 8.51-1.643v-41.932l-8.507-1.646-10.165-1.965 5.8-8.584 4.85-7.178-6.125-6.126-17.4-17.4-6.13-6.13-7.18 4.858-8.558 5.792-1.964-10.166L350.98 63zm-20.98 118.948c23.176 0 41.95-18.318 41.95-40.94 0-22.607-18.774-40.927-41.95-40.927-23.174 0-41.948 18.32-41.948 40.926 0 22.623 18.774 40.941 41.949 40.941zM342.313 73.5l3.855 19.963a47.184 47.184 0 0 1 6.037 2.502l16.824-11.386 17.4 17.4-11.362 16.818a53.171 53.171 0 0 1 2.502 6.066l19.932 3.855v24.601l-19.932 3.848a56.644 56.644 0 0 1-2.502 6.066l11.362 16.795-17.4 17.4-16.824-11.364a44.931 44.931 0 0 1-6.037 2.504l-3.855 19.932H317.68l-3.84-19.932a43.821 43.821 0 0 1-6.043-2.504l-16.818 11.364-17.4-17.4 11.364-16.795a53.759 53.759 0 0 1-2.51-6.066l-19.933-3.848v-24.601l19.933-3.855a50.617 50.617 0 0 1 2.51-6.066l-11.364-16.818 17.4-17.4 16.818 11.386a45.957 45.957 0 0 1 6.043-2.502l3.84-19.963h24.632z" fill="#AD245D"/></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1 @@
<svg width="513" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"/><g fill="#FFF"><path d="M35.01 367.726c-.08-21.169-.205-53.162 21.257-71.332 3.817-3.253 9.93-7.497 17.321-9.224 2.575-.523 4.956-.756 7.262-.979 4.438-.431 8.27-.804 12.054-2.9l4.954-2.846c9.87-5.655 19.194-10.996 28.226-17.377 5.085-3.632 6.726-15.73 6.095-25.428-.214-2.792-1.893-5.7-3.67-8.777-1.097-1.901-2.232-3.867-3.065-5.916l-.073-.199a56.976 56.976 0 0 1-.422-1.443c-1.195-4.205-1.933-6.378-2.386-7.476-7.029-.944-11.8-8.647-12.888-21.006l-.031-.557c-.645-12.785.808-16.13 2.316-17.716.24-.254.505-.475.783-.666-1.754-16.051 3.115-32.521 13.358-44.704 9.314-11.079 21.955-17.18 35.592-17.18 3.73 0 7.55.458 11.355 1.362 25.63 6.228 41.679 30.27 40.062 59.227.53.251 1.018.61 1.44 1.066 2.752 2.964 2.47 10.97 2.22 14.276l-.024.41c-.335 5.236-.684 10.65-3.052 15.73-1.739 3.918-4.405 6.242-6.76 8.29-2.396 2.089-4.288 3.735-5.294 6.885-.7 2.416-1.645 4.866-2.559 7.235-1.752 4.538-3.407 8.827-3.54 13.244-.427 10.222 1.17 18.391 4.172 21.359 5.097 5.163 13.003 9.391 19.978 13.121 1.6.855 3.166 1.692 4.654 2.517 9.28 5.052 16.07 7.915 25.309 8.557 9.118.849 18.056 5.193 24.754 11.97.736.641 1.82 1.744 3.694 3.648 4.416 4.492 4.416 4.492 4.426 5.852l.007.758c10.783 17.702 11.14 40.656 11.415 58.169l.05 3.28-3.278.028c-42.05.363-84.058.677-126.058.993-42.12.314-84.232.632-126.367.994l-3.273.029-.014-3.274z"/><text font-family="Impact" font-size="118" font-style="condensed" font-weight="700" transform="translate(1 -1)"><tspan x="256" y="208">&lt;/&gt;</tspan></text></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg width="512" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384 .297L511.392 221.65l-128 221.702-255.392.352L.608 222.35 128.608.65z"/><g fill="#FFF"><path d="M34.944 367.726c-.081-21.169-.205-53.162 21.215-71.332 3.81-3.253 9.91-7.497 17.288-9.224 2.57-.523 4.946-.756 7.247-.979 4.43-.431 8.254-.804 12.03-2.9l4.945-2.846c9.851-5.655 19.157-10.996 28.171-17.377 5.075-3.632 6.713-15.73 6.082-25.428-.213-2.792-1.888-5.7-3.662-8.777-1.095-1.901-2.228-3.867-3.059-5.916l-.073-.199a57.061 57.061 0 0 1-.42-1.443c-1.194-4.205-1.93-6.378-2.382-7.476-7.015-.944-11.778-8.647-12.864-21.006l-.03-.557c-.644-12.785.806-16.13 2.31-17.716.241-.254.505-.475.783-.666-1.75-16.051 3.11-32.521 13.331-44.704 9.296-11.079 21.912-17.18 35.524-17.18 3.722 0 7.535.458 11.332 1.362 25.58 6.228 41.597 30.27 39.983 59.227.53.251 1.016.61 1.439 1.066 2.745 2.964 2.464 10.97 2.215 14.276l-.024.41c-.335 5.236-.683 10.65-3.046 15.73-1.736 3.918-4.397 6.242-6.747 8.29-2.391 2.089-4.28 3.735-5.284 6.885-.698 2.416-1.642 4.866-2.554 7.235-1.749 4.538-3.4 8.827-3.534 13.244-.425 10.222 1.17 18.391 4.165 21.359 5.087 5.163 12.977 9.391 19.939 13.121 1.597.855 3.16 1.692 4.645 2.517 9.262 5.052 16.038 7.915 25.259 8.557 9.1.849 18.02 5.193 24.706 11.97.735.641 1.817 1.744 3.687 3.648 4.408 4.492 4.408 4.492 4.417 5.852l.007.758c10.762 17.702 11.12 40.656 11.392 58.169l.05 3.28-3.271.028c-41.968.363-83.894.677-125.812.993-42.038.314-84.067.632-126.12.994l-3.267.029-.013-3.274zM332.387 115.763h15.318v86.734h-15.318v.513l-22.127-17.64v.12h-10.21V211h-22.128v-25.51h-20.424v-52.722h52.762v-.508l22.127-17.065v.568zm34.313 72.093l-7.988-4.591c15.803-27.453 1.717-46.642 1.106-47.443l7.304-5.607c.774.993 18.547 24.675-.422 57.641zm27.361 21.216l-13.866-7.975c27.437-47.66 2.98-80.973 1.918-82.36L394.795 109c1.343 1.723 32.2 42.839-.734 100.072z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="513"
height="444"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs5" />
<g
fill="none"
fill-rule="evenodd"
id="g5">
<path
fill="#ad245d"
d="M 384.5,0.297 512.325,221.9 384.325,443.602 128.5,443.704 0.675,222.1 128.675,0.4 Z"
id="path1"
style="display:inline;fill:#e67919;fill-opacity:1" />
<path
d="m 83.051688,348.226 c -0.08,-21.169 -0.205,-53.162 21.257002,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.367002,0.994 l -3.273,0.029 z"
id="path2-5"
style="display:inline;fill:#ffffff" />
<path
d="m 351.97006,182.43811 c -2.88526,0.005 -5.2219,2.34474 -5.223,5.23 0.001,2.88526 2.33774,5.22504 5.223,5.23 2.88747,-0.003 5.22769,-2.34253 5.231,-5.23 -0.003,-2.88708 -2.34292,-5.22669 -5.23,-5.23 m 0,40.237 c -19.307,0 -35.012,-15.703 -35.012,-35.007 0,-19.295 15.706,-34.993 35.011,-34.993 19.292,0 34.989,15.698 34.989,34.993 0,19.304 -15.697,35.007 -34.989,35.007"
fill="#ffffff"
id="path2"
style="display:inline" />
<path
d="m 351.97006,147.67511 c -22.338,0 -40.511,18.164 -40.511,40.492 0,22.336 18.173,40.508 40.511,40.508 22.325,0 40.489,-18.172 40.489,-40.508 0,-22.328 -18.164,-40.492 -40.489,-40.492 m 0,51.022 c 5.823,0 10.531,-4.716 10.531,-10.53 a 10.517,10.517 0 0 0 -10.53,-10.529 10.51,10.51 0 0 0 -10.523,10.529 c 0,5.814 4.7,10.53 10.522,10.53 m 0,-40.496 c 16.563,0 29.963,13.406 29.963,29.966 0,16.555 -13.4,29.982 -29.963,29.982 -16.555,0 -29.985,-13.427 -29.985,-29.982 0,-16.56 13.43,-29.966 29.985,-29.966"
fill="#ad245d"
id="path3"
style="display:inline;fill:#e67919;fill-opacity:1" />
<path
d="m 351.95906,152.88411 c -20.305,0 -36.825,16.06 -36.825,35.799 0,19.747 16.52,35.813 36.825,35.813 20.306,0 36.827,-16.066 36.827,-35.813 0,-19.74 -16.521,-35.8 -36.827,-35.8 z m -16.713,108.791 -4.11,-21.345 c -0.324,-0.129 -0.648,-0.265 -0.972,-0.404 l -18.012,12.169 -23.607,-23.609 12.186,-18.009 a 63.31,63.31 0 0 1 -0.403,-0.968 l -21.369,-4.122 v -33.383 l 21.361,-4.13 c 0.131,-0.327 0.267,-0.652 0.407,-0.979 l -12.18,-18.025 23.608,-23.612 18.015,12.198 c 0.322,-0.137 0.643,-0.27 0.964,-0.4 l 4.112,-21.381 h 33.416 l 4.13,21.387 c 0.319,0.13 0.638,0.26 0.956,0.396 l 18.024,-12.2 23.608,23.612 -12.186,18.031 c 0.139,0.324 0.273,0.648 0.402,0.971 l 21.363,4.133 v 33.381 l -21.37,4.124 c -0.13,0.32 -0.262,0.64 -0.398,0.96 l 12.19,18.017 -23.606,23.609 -18.021,-12.171 c -0.32,0.137 -0.642,0.27 -0.964,0.402 l -4.13,21.348 z"
fill="#ffffff"
id="path4"
style="display:inline" />
<path
d="m 351.95956,218.12311 c -17.342,0 -31.45,-13.656 -31.45,-30.44 0,-16.778 14.108,-30.427 31.45,-30.427 17.341,0 31.449,13.649 31.449,30.426 0,16.785 -14.108,30.441 -31.45,30.441 z m 20.979,-108.448 h -41.97 l -1.64,8.517 -1.953,10.156 -8.55,-5.788 -7.18,-4.862 -6.132,6.132 -17.399,17.4 -6.126,6.126 4.85,7.178 5.8,8.583 -10.173,1.966 -8.506,1.646 v 41.932 l 8.51,1.643 10.16,1.962 -5.787,8.553 -4.858,7.179 6.13,6.13 17.399,17.399 6.126,6.126 7.177,-4.85 8.56,-5.782 1.954,10.141 1.64,8.513 h 41.964 l 1.645,-8.507 1.964,-10.15 8.566,5.787 7.178,4.846 6.124,-6.124 17.4,-17.399 6.13,-6.13 -4.858,-7.18 -5.788,-8.554 10.153,-1.96 8.51,-1.643 v -41.932 l -8.507,-1.646 -10.165,-1.965 5.8,-8.584 4.85,-7.178 -6.125,-6.126 -17.4,-17.4 -6.13,-6.13 -7.18,4.858 -8.558,5.792 -1.964,-10.166 -1.64,-8.509 z m -20.98,118.948 c 23.176,0 41.95,-18.318 41.95,-40.94 0,-22.607 -18.774,-40.927 -41.95,-40.927 -23.174,0 -41.948,18.32 -41.948,40.926 0,22.623 18.774,40.941 41.949,40.941 z m 12.314,-108.448 3.855,19.963 a 47.184,47.184 0 0 1 6.037,2.502 l 16.824,-11.386 17.4,17.4 -11.362,16.818 a 53.171,53.171 0 0 1 2.502,6.066 l 19.932,3.855 v 24.601 l -19.932,3.848 a 56.644,56.644 0 0 1 -2.502,6.066 l 11.362,16.795 -17.4,17.4 -16.824,-11.364 a 44.931,44.931 0 0 1 -6.037,2.504 l -3.855,19.932 h -24.633 l -3.84,-19.932 a 43.821,43.821 0 0 1 -6.043,-2.504 l -16.818,11.364 -17.4,-17.4 11.364,-16.795 a 53.759,53.759 0 0 1 -2.51,-6.066 l -19.933,-3.848 v -24.601 l 19.933,-3.855 a 50.617,50.617 0 0 1 2.51,-6.066 l -11.364,-16.818 17.4,-17.4 16.818,11.386 a 45.957,45.957 0 0 1 6.043,-2.502 l 3.84,-19.963 h 24.632 z"
fill="#ad245d"
id="path5"
style="display:inline;fill:#e67919;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="513"
height="444"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<g
fill="none"
fill-rule="evenodd"
id="g3">
<path
fill="#AD245D"
d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"
id="path1"
style="fill:#e67919;fill-opacity:1" />
<path
d="m 80.877371,348.226 c -0.08,-21.169 -0.205,-53.162 21.256999,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.366999,0.994 l -3.273,0.029 z"
id="path2-5"
style="display:inline;fill:#ffffff" />
<text
font-family="Impact"
font-size="118px"
font-style="condensed"
font-weight="700"
id="text2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;font-family:Monospace;-inkscape-font-specification:Monospace;display:inline;fill:#ffffff"
x="6.8672509"
y="17"><tspan
x="262.86725"
y="225"
id="tspan2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;font-family:Monospace;-inkscape-font-specification:Monospace;fill:#ffffff">&lt;/&gt;</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="444"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<g
fill="none"
fill-rule="evenodd"
id="g3">
<path
fill="#AD245D"
d="M384 .297L511.392 221.65l-128 221.702-255.392.352L.608 222.35 128.608.65z"
id="path1"
style="fill:#e67919;fill-opacity:1" />
<path
d="m 85.310542,348.226 c -0.08,-21.169 -0.205,-53.162 21.256998,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.366998,0.994 l -3.273,0.029 z"
id="path2-5"
style="display:inline;fill:#ffffff" />
<path
d="m 349.88573,158.65792 h 15.318 v 86.734 h -15.318 v 0.513 l -22.127,-17.64 v 0.12 h -10.21 v 25.51 h -22.128 v -25.51 h -20.424 v -52.722 h 52.762 v -0.508 l 22.127,-17.065 z m 34.313,72.093 -7.988,-4.591 c 15.803,-27.453 1.717,-46.642 1.106,-47.443 l 7.304,-5.607 c 0.774,0.993 18.547,24.675 -0.422,57.641 z m 27.361,21.216 -13.866,-7.975 c 27.437,-47.66 2.98,-80.973 1.918,-82.36 l 12.682,-9.737 c 1.343,1.723 32.2,42.839 -0.734,100.072 z"
id="path2-1"
style="display:inline;fill:#ffffff" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,234 +1,257 @@
[
{
"provider_name": "Codepen",
"provider_url": "https://codepen.io",
"endpoints": [
{
"schemes": ["http://codepen.io/*", "https://codepen.io/*"],
"url": "http://codepen.io/api/oembed"
}
]
},
{
"provider_name": "DTube",
"provider_url": "https://d.tube/",
"endpoints": [
{
"schemes": ["https://d.tube/v/*"],
"url": "https://api.d.tube/oembed",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Post)",
"provider_url": "https://www.facebook.com/",
"endpoints": [
{
"schemes": [
"https://www.facebook.com/*/posts/*",
"https://www.facebook.com/photos/*",
"https://www.facebook.com/*/photos/*",
"https://www.facebook.com/photo.php*",
"https://www.facebook.com/photo.php",
"https://www.facebook.com/*/activity/*",
"https://www.facebook.com/permalink.php",
"https://www.facebook.com/media/set?set=*",
"https://www.facebook.com/questions/*",
"https://www.facebook.com/notes/*/*/*"
],
"url": "https://www.facebook.com/plugins/post/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Video)",
"provider_url": "https://www.facebook.com/",
"endpoints": [
{
"schemes": ["https://www.facebook.com/*/videos/*", "https://www.facebook.com/video.php"],
"url": "https://www.facebook.com/plugins/video/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Flickr",
"provider_url": "https://www.flickr.com/",
"endpoints": [
{
"schemes": [
"http://*.flickr.com/photos/*",
"http://flic.kr/p/*",
"https://*.flickr.com/photos/*",
"https://flic.kr/p/*"
],
"url": "https://www.flickr.com/services/oembed/",
"discovery": true
}
]
},
{
"provider_name": "GIPHY",
"provider_url": "https://giphy.com",
"endpoints": [
{
"schemes": [
"https://giphy.com/gifs/*",
"http://gph.is/*",
"https://media.giphy.com/media/*/giphy.gif"
],
"url": "https://giphy.com/services/oembed",
"discovery": true
}
]
},
{
"provider_name": "Instagram",
"provider_url": "https://instagram.com",
"endpoints": [
{
"schemes": [
"http://instagram.com/p/*",
"http://instagr.am/p/*",
"http://www.instagram.com/p/*",
"http://www.instagr.am/p/*",
"https://instagram.com/p/*",
"https://instagr.am/p/*",
"https://www.instagram.com/p/*",
"https://www.instagr.am/p/*"
],
"url": "https://api.instagram.com/oembed",
"formats": ["json"]
}
]
},
{
"provider_name": "Meetup",
"provider_url": "http://www.meetup.com",
"endpoints": [
{
"schemes": [
"http://meetup.com/*",
"https://www.meetup.com/*",
"https://meetup.com/*",
"http://meetu.ps/*"
],
"url": "https://api.meetup.com/oembed",
"formats": ["json"]
}
]
},
{
"provider_name": "MixCloud",
"provider_url": "https://mixcloud.com/",
"endpoints": [
{
"schemes": ["http://www.mixcloud.com/*/*/", "https://www.mixcloud.com/*/*/"],
"url": "https://www.mixcloud.com/oembed/"
}
]
},
{
"provider_name": "Reddit",
"provider_url": "https://reddit.com/",
"endpoints": [
{
"schemes": [
"https://reddit.com/r/*/comments/*/*",
"https://www.reddit.com/r/*/comments/*/*"
],
"url": "https://www.reddit.com/oembed"
}
]
},
{
"provider_name": "SlideShare",
"provider_url": "http://www.slideshare.net/",
"endpoints": [
{
"schemes": [
"http://www.slideshare.net/*/*",
"http://fr.slideshare.net/*/*",
"http://de.slideshare.net/*/*",
"http://es.slideshare.net/*/*",
"http://pt.slideshare.net/*/*"
],
"url": "http://www.slideshare.net/api/oembed/2",
"discovery": true
}
]
},
{
"provider_name": "SoundCloud",
"provider_url": "http://soundcloud.com/",
"endpoints": [
{
"schemes": ["http://soundcloud.com/*", "https://soundcloud.com/*"],
"url": "https://soundcloud.com/oembed"
}
]
},
{
"provider_name": "Twitch",
"provider_url": "https://www.twitch.tv",
"endpoints": [
{
"schemes": [
"http://clips.twitch.tv/*",
"https://clips.twitch.tv/*",
"http://www.twitch.tv/*",
"https://www.twitch.tv/*",
"http://twitch.tv/*",
"https://twitch.tv/*"
],
"url": "https://api.twitch.tv/v4/oembed",
"formats": ["json"]
}
]
},
{
"provider_name": "Twitter",
"provider_url": "http://www.twitter.com/",
"endpoints": [
{
"schemes": ["https://twitter.com/*/status/*", "https://*.twitter.com/*/status/*"],
"url": "https://publish.twitter.com/oembed"
}
]
},
{
"provider_name": "Vimeo",
"provider_url": "https://vimeo.com/",
"endpoints": [
{
"schemes": [
"https://vimeo.com/*",
"https://vimeo.com/album/*/video/*",
"https://vimeo.com/channels/*/*",
"https://vimeo.com/groups/*/videos/*",
"https://vimeo.com/ondemand/*/*",
"https://player.vimeo.com/video/*"
],
"url": "https://vimeo.com/api/oembed.{format}",
"discovery": true
}
]
},
{
"provider_name": "YouTube",
"provider_url": "https://www.youtube.com/",
"endpoints": [
{
"schemes": [
"https://*.youtube.com/watch*",
"https://*.youtube.com/v/*",
"https://youtu.be/*"
],
"url": "https://www.youtube.com/oembed",
"discovery": true
}
]
}
]
{
"provider_name": "Codepen",
"provider_url": "https:\/\/codepen.io",
"endpoints": [
{
"schemes": [
"http:\/\/codepen.io\/*",
"https:\/\/codepen.io\/*"
],
"url": "http:\/\/codepen.io\/api\/oembed"
}
]
},
{
"provider_name": "DTube",
"provider_url": "https:\/\/d.tube\/",
"endpoints": [
{
"schemes": [
"https:\/\/d.tube\/v\/*"
],
"url": "https:\/\/api.d.tube\/oembed",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Post)",
"provider_url": "https:\/\/www.facebook.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/www.facebook.com\/*\/posts\/*",
"https:\/\/www.facebook.com\/photos\/*",
"https:\/\/www.facebook.com\/*\/photos\/*",
"https:\/\/www.facebook.com\/photo.php*",
"https:\/\/www.facebook.com\/photo.php",
"https:\/\/www.facebook.com\/*\/activity\/*",
"https:\/\/www.facebook.com\/permalink.php",
"https:\/\/www.facebook.com\/media\/set?set=*",
"https:\/\/www.facebook.com\/questions\/*",
"https:\/\/www.facebook.com\/notes\/*\/*\/*"
],
"url": "https:\/\/www.facebook.com\/plugins\/post\/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Video)",
"provider_url": "https:\/\/www.facebook.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/www.facebook.com\/*\/videos\/*",
"https:\/\/www.facebook.com\/video.php"
],
"url": "https:\/\/www.facebook.com\/plugins\/video\/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Flickr",
"provider_url": "https:\/\/www.flickr.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/*.flickr.com\/photos\/*",
"http:\/\/flic.kr\/p\/*",
"https:\/\/*.flickr.com\/photos\/*",
"https:\/\/flic.kr\/p\/*"
],
"url": "https:\/\/www.flickr.com\/services\/oembed\/",
"discovery": true
}
]
},
{
"provider_name": "GIPHY",
"provider_url": "https:\/\/giphy.com",
"endpoints": [
{
"schemes": [
"https:\/\/giphy.com\/gifs\/*",
"http:\/\/gph.is\/*",
"https:\/\/media.giphy.com\/media\/*\/giphy.gif"
],
"url": "https:\/\/giphy.com\/services\/oembed",
"discovery": true
}
]
},
{
"provider_name": "Instagram",
"provider_url": "https:\/\/instagram.com",
"endpoints": [
{
"schemes": [
"http:\/\/instagram.com\/p\/*",
"http:\/\/instagr.am\/p\/*",
"http:\/\/www.instagram.com\/p\/*",
"http:\/\/www.instagr.am\/p\/*",
"https:\/\/instagram.com\/p\/*",
"https:\/\/instagr.am\/p\/*",
"https:\/\/www.instagram.com\/p\/*",
"https:\/\/www.instagr.am\/p\/*"
],
"url": "https:\/\/api.instagram.com\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "Meetup",
"provider_url": "http:\/\/www.meetup.com",
"endpoints": [
{
"schemes": [
"http:\/\/meetup.com\/*",
"https:\/\/www.meetup.com\/*",
"https:\/\/meetup.com\/*",
"http:\/\/meetu.ps\/*"
],
"url": "https:\/\/api.meetup.com\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "MixCloud",
"provider_url": "https:\/\/mixcloud.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/www.mixcloud.com\/*\/*\/",
"https:\/\/www.mixcloud.com\/*\/*\/"
],
"url": "https:\/\/www.mixcloud.com\/oembed\/"
}
]
},
{
"provider_name": "Reddit",
"provider_url": "https:\/\/reddit.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/reddit.com\/r\/*\/comments\/*\/*",
"https:\/\/www.reddit.com\/r\/*\/comments\/*\/*"
],
"url": "https:\/\/www.reddit.com\/oembed"
}
]
},
{
"provider_name": "SlideShare",
"provider_url": "http:\/\/www.slideshare.net\/",
"endpoints": [
{
"schemes": [
"http:\/\/www.slideshare.net\/*\/*",
"http:\/\/fr.slideshare.net\/*\/*",
"http:\/\/de.slideshare.net\/*\/*",
"http:\/\/es.slideshare.net\/*\/*",
"http:\/\/pt.slideshare.net\/*\/*"
],
"url": "http:\/\/www.slideshare.net\/api\/oembed\/2",
"discovery": true
}
]
},
{
"provider_name": "SoundCloud",
"provider_url": "http:\/\/soundcloud.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/soundcloud.com\/*",
"https:\/\/soundcloud.com\/*"
],
"url": "https:\/\/soundcloud.com\/oembed"
}
]
},
{
"provider_name": "Twitch",
"provider_url": "https:\/\/www.twitch.tv",
"endpoints": [
{
"schemes": [
"http:\/\/clips.twitch.tv\/*",
"https:\/\/clips.twitch.tv\/*",
"http:\/\/www.twitch.tv\/*",
"https:\/\/www.twitch.tv\/*",
"http:\/\/twitch.tv\/*",
"https:\/\/twitch.tv\/*"
],
"url": "https:\/\/api.twitch.tv\/v4\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "Twitter",
"provider_url": "http:\/\/www.twitter.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/twitter.com\/*\/status\/*",
"https:\/\/*.twitter.com\/*\/status\/*"
],
"url": "https:\/\/publish.twitter.com\/oembed"
}
]
},
{
"provider_name": "Vimeo",
"provider_url": "https:\/\/vimeo.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/vimeo.com\/*",
"https:\/\/vimeo.com\/album\/*\/video\/*",
"https:\/\/vimeo.com\/channels\/*\/*",
"https:\/\/vimeo.com\/groups\/*\/videos\/*",
"https:\/\/vimeo.com\/ondemand\/*\/*",
"https:\/\/player.vimeo.com\/video\/*"
],
"url": "https:\/\/vimeo.com\/api\/oembed.{format}",
"discovery": true
}
]
},
{
"provider_name": "YouTube",
"provider_url": "https:\/\/www.youtube.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/*.youtube.com\/watch*",
"https:\/\/*.youtube.com\/v\/*",
"https:\/\/youtu.be\/*"
],
"url": "https:\/\/www.youtube.com\/oembed",
"discovery": true
}
]
}
]

View File

@ -1,24 +0,0 @@
#!/bin/sh
# public
cp -r public/ build/public/
# email files
mkdir -p build/src/emails/templates/
cp -r src/emails/templates/ build/src/emails/
mkdir -p build/src/emails/locales/
cp -r src/emails/locales/ build/src/emails/
# gql files
mkdir -p build/src/graphql/types/
cp -r src/graphql/types/*.gql build/src/graphql/types/
mkdir -p build/src/graphql/types/enum/
cp -r src/graphql/types/enum/*.gql build/src/graphql/types/enum/
mkdir -p build/src/graphql/types/scalar/
cp -r src/graphql/types/scalar/*.gql build/src/graphql/types/scalar/
mkdir -p build/src/graphql/types/type/
cp -r src/graphql/types/type/*.gql build/src/graphql/types/type/

View File

@ -0,0 +1,240 @@
// import { extractDomainFromUrl, signAndSend } from './utils'
import { extractNameFromId, signAndSend } from './utils'
import { isPublicAddressed } from './utils/activity'
// import { isPublicAddressed, sendAcceptActivity, sendRejectActivity } from './utils/activity'
import request from 'request'
// import as from 'activitystrea.ms'
import NitroDataSource from './NitroDataSource'
import router from './routes'
import Collections from './Collections'
import { v4 as uuid } from 'uuid'
import CONFIG from '../config'
const debug = require('debug')('ea')
let activityPub = null
export { activityPub }
export default class ActivityPub {
constructor(activityPubEndpointUri, internalGraphQlUri) {
this.endpoint = activityPubEndpointUri
this.dataSource = new NitroDataSource(internalGraphQlUri)
this.collections = new Collections(this.dataSource)
}
static init(server) {
if (!activityPub) {
activityPub = new ActivityPub(CONFIG.CLIENT_URI, CONFIG.GRAPHQL_URI)
// integrate into running graphql express server
server.express.set('ap', activityPub)
server.express.use(router)
console.log('-> ActivityPub middleware added to the graphql express server') // eslint-disable-line no-console
} else {
console.log('-> ActivityPub middleware already added to the graphql express server') // eslint-disable-line no-console
}
}
// handleFollowActivity(activity) {
// debug(`inside FOLLOW ${activity.actor}`)
// const toActorName = extractNameFromId(activity.object)
// const fromDomain = extractDomainFromUrl(activity.actor)
// const dataSource = this.dataSource
// return new Promise((resolve, reject) => {
// request(
// {
// url: activity.actor,
// headers: {
// Accept: 'application/activity+json',
// },
// },
// async (err, response, toActorObject) => {
// if (err) return reject(err)
// // save shared inbox
// toActorObject = JSON.parse(toActorObject)
// await this.dataSource.addSharedInboxEndpoint(toActorObject.endpoints.sharedInbox)
// const followersCollectionPage = await this.dataSource.getFollowersCollectionPage(
// activity.object,
// )
// const followActivity = as
// .follow()
// .id(activity.id)
// .actor(activity.actor)
// .object(activity.object)
// // add follower if not already in collection
// if (followersCollectionPage.orderedItems.includes(activity.actor)) {
// debug('follower already in collection!')
// debug(`inbox = ${toActorObject.inbox}`)
// resolve(
// sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
// )
// } else {
// followersCollectionPage.orderedItems.push(activity.actor)
// }
// debug(`toActorObject = ${toActorObject}`)
// toActorObject =
// typeof toActorObject !== 'object' ? JSON.parse(toActorObject) : toActorObject
// debug(`followers = ${JSON.stringify(followersCollectionPage.orderedItems, null, 2)}`)
// debug(`inbox = ${toActorObject.inbox}`)
// debug(`outbox = ${toActorObject.outbox}`)
// debug(`followers = ${toActorObject.followers}`)
// debug(`following = ${toActorObject.following}`)
// try {
// await dataSource.saveFollowersCollectionPage(followersCollectionPage)
// debug('follow activity saved')
// resolve(
// sendAcceptActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
// )
// } catch (e) {
// debug('followers update error!', e)
// resolve(
// sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
// )
// }
// },
// )
// })
// }
handleUndoActivity(activity) {
debug('inside UNDO')
switch (activity.object.type) {
case 'Follow': {
const followActivity = activity.object
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
}
case 'Like': {
return this.dataSource.deleteShouted(activity)
}
}
}
handleCreateActivity(activity) {
debug('inside create')
switch (activity.object.type) {
case 'Note': {
const articleObject = activity.object
if (articleObject.inReplyTo) {
return this.dataSource.createComment(activity)
} else {
return this.dataSource.createPost(activity)
}
}
}
}
handleDeleteActivity(activity) {
debug('inside delete')
switch (activity.object.type) {
case 'Article':
case 'Note':
return this.dataSource.deletePost(activity)
default:
}
}
handleUpdateActivity(activity) {
debug('inside update')
switch (activity.object.type) {
case 'Note':
case 'Article':
return this.dataSource.updatePost(activity)
default:
}
}
handleLikeActivity(activity) {
// TODO differ if activity is an Article/Note/etc.
return this.dataSource.createShouted(activity)
}
handleDislikeActivity(activity) {
// TODO differ if activity is an Article/Note/etc.
return this.dataSource.deleteShouted(activity)
}
async handleAcceptActivity(activity) {
debug('inside accept')
switch (activity.object.type) {
case 'Follow': {
const followObject = activity.object
const followingCollectionPage = await this.collections.getFollowingCollectionPage(
followObject.actor,
)
followingCollectionPage.orderedItems.push(followObject.object)
await this.dataSource.saveFollowingCollectionPage(followingCollectionPage)
}
}
}
getActorObject(url) {
return new Promise((resolve, reject) => {
request(
{
url: url,
headers: {
Accept: 'application/json',
},
},
(err, response, body) => {
if (err) {
reject(err)
}
resolve(JSON.parse(body))
},
)
})
}
generateStatusId(slug) {
return `https://${this.host}/activitypub/users/${slug}/status/${uuid()}`
}
async sendActivity(activity) {
delete activity.send
const fromName = extractNameFromId(activity.actor)
if (Array.isArray(activity.to) && isPublicAddressed(activity)) {
debug('is public addressed')
const sharedInboxEndpoints = await this.dataSource.getSharedInboxEndpoints()
// serve shared inbox endpoints
sharedInboxEndpoints.map((sharedInbox) => {
return this.trySend(activity, fromName, new URL(sharedInbox).host, sharedInbox)
})
activity.to = activity.to.filter((recipient) => {
return !isPublicAddressed({ to: recipient })
})
// serve the rest
activity.to.map(async (recipient) => {
debug('serve rest')
const actorObject = await this.getActorObject(recipient)
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
})
} else if (typeof activity.to === 'string') {
debug('is string')
const actorObject = await this.getActorObject(activity.to)
return this.trySend(activity, fromName, new URL(activity.to).host, actorObject.inbox)
} else if (Array.isArray(activity.to)) {
activity.to.map(async (recipient) => {
const actorObject = await this.getActorObject(recipient)
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
})
}
}
async trySend(activity, fromName, host, url, tries = 5) {
try {
return await signAndSend(activity, fromName, host, url)
} catch (e) {
if (tries > 0) {
setTimeout(function () {
return this.trySend(activity, fromName, host, url, --tries)
}, 20000)
}
}
}
}

View File

@ -0,0 +1,29 @@
export default class Collections {
constructor(dataSource) {
this.dataSource = dataSource
}
getFollowersCollection(actorId) {
return this.dataSource.getFollowersCollection(actorId)
}
getFollowersCollectionPage(actorId) {
return this.dataSource.getFollowersCollectionPage(actorId)
}
getFollowingCollection(actorId) {
return this.dataSource.getFollowingCollection(actorId)
}
getFollowingCollectionPage(actorId) {
return this.dataSource.getFollowingCollectionPage(actorId)
}
getOutboxCollection(actorId) {
return this.dataSource.getOutboxCollection(actorId)
}
getOutboxCollectionPage(actorId) {
return this.dataSource.getOutboxCollectionPage(actorId)
}
}

View File

@ -0,0 +1,575 @@
import {
throwErrorIfApolloErrorOccurred,
extractIdFromActivityId,
extractNameFromId,
constructIdFromName,
} from './utils'
import { createOrderedCollection, createOrderedCollectionPage } from './utils/collection'
import { createArticleObject, isPublicAddressed } from './utils/activity'
import crypto from 'crypto'
import gql from 'graphql-tag'
import { createHttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { InMemoryCache } from 'apollo-cache-inmemory'
import fetch from 'node-fetch'
import { ApolloClient } from 'apollo-client'
import trunc from 'trunc-html'
const debug = require('debug')('ea:datasource')
export default class NitroDataSource {
constructor(uri) {
this.uri = uri
const defaultOptions = {
query: {
fetchPolicy: 'network-only',
errorPolicy: 'all',
},
}
const link = createHttpLink({ uri: this.uri, fetch: fetch }) // eslint-disable-line
const cache = new InMemoryCache()
const authLink = setContext((_, { headers }) => {
// generate the authentication token (maybe from env? Which user?)
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiUGV0ZXIgTHVzdGlnIiwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9qb2huY2FmYXp6YS8xMjguanBnIiwiaWQiOiJ1MSIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5vcmciLCJzbHVnIjoicGV0ZXItbHVzdGlnIiwiaWF0IjoxNTUyNDIwMTExLCJleHAiOjE2Mzg4MjAxMTEsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsInN1YiI6InUxIn0.G7An1yeQUViJs-0Qj-Tc-zm0WrLCMB3M02pfPnm6xzw'
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
Authorization: token ? `Bearer ${token}` : '',
},
}
})
this.client = new ApolloClient({
link: authLink.concat(link),
cache: cache,
defaultOptions,
})
}
async getFollowersCollection(actorId) {
const slug = extractNameFromId(actorId)
debug(`slug= ${slug}`)
const result = await this.client.query({
query: gql`
query {
User(slug: "${slug}") {
followedByCount
}
}
`,
})
debug('successfully fetched followers')
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const followersCount = actor.followedByCount
const followersCollection = createOrderedCollection(slug, 'followers')
followersCollection.totalItems = followersCount
return followersCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async getFollowersCollectionPage(actorId) {
const slug = extractNameFromId(actorId)
debug(`getFollowersPage slug = ${slug}`)
const result = await this.client.query({
query: gql`
query {
User(slug:"${slug}") {
followedBy {
slug
}
followedByCount
}
}
`,
})
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const followers = actor.followedBy
const followersCount = actor.followedByCount
const followersCollection = createOrderedCollectionPage(slug, 'followers')
followersCollection.totalItems = followersCount
debug(`followers = ${JSON.stringify(followers, null, 2)}`)
await Promise.all(
followers.map(async (follower) => {
followersCollection.orderedItems.push(constructIdFromName(follower.slug))
}),
)
return followersCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async getFollowingCollection(actorId) {
const slug = extractNameFromId(actorId)
const result = await this.client.query({
query: gql`
query {
User(slug:"${slug}") {
followingCount
}
}
`,
})
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const followingCount = actor.followingCount
const followingCollection = createOrderedCollection(slug, 'following')
followingCollection.totalItems = followingCount
return followingCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async getFollowingCollectionPage(actorId) {
const slug = extractNameFromId(actorId)
const result = await this.client.query({
query: gql`
query {
User(slug:"${slug}") {
following {
slug
}
followingCount
}
}
`,
})
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const following = actor.following
const followingCount = actor.followingCount
const followingCollection = createOrderedCollectionPage(slug, 'following')
followingCollection.totalItems = followingCount
await Promise.all(
following.map(async (user) => {
followingCollection.orderedItems.push(await constructIdFromName(user.slug))
}),
)
return followingCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async getOutboxCollection(actorId) {
const slug = extractNameFromId(actorId)
const result = await this.client.query({
query: gql`
query {
User(slug:"${slug}") {
contributions {
title
slug
content
contentExcerpt
createdAt
}
}
}
`,
})
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const posts = actor.contributions
const outboxCollection = createOrderedCollection(slug, 'outbox')
outboxCollection.totalItems = posts.length
return outboxCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async getOutboxCollectionPage(actorId) {
const slug = extractNameFromId(actorId)
debug(`inside getting outbox collection page => ${slug}`)
const result = await this.client.query({
query: gql`
query {
User(slug:"${slug}") {
actorId
contributions {
id
activityId
objectId
title
slug
content
contentExcerpt
createdAt
author {
slug
}
}
}
}
`,
})
debug(result.data)
if (result.data) {
const actor = result.data.User[0]
const posts = actor.contributions
const outboxCollection = createOrderedCollectionPage(slug, 'outbox')
outboxCollection.totalItems = posts.length
await Promise.all(
posts.map(async (post) => {
outboxCollection.orderedItems.push(
await createArticleObject(
post.activityId,
post.objectId,
post.content,
post.author.slug,
post.id,
post.createdAt,
),
)
}),
)
debug('after createNote')
return outboxCollection
} else {
throwErrorIfApolloErrorOccurred(result)
}
}
async undoFollowActivity(fromActorId, toActorId) {
const fromUserId = await this.ensureUser(fromActorId)
const toUserId = await this.ensureUser(toActorId)
const result = await this.client.mutate({
mutation: gql`
mutation {
RemoveUserFollowedBy(from: {id: "${fromUserId}"}, to: {id: "${toUserId}"}) {
from { name }
}
}
`,
})
debug(`undoFollowActivity result = ${JSON.stringify(result, null, 2)}`)
throwErrorIfApolloErrorOccurred(result)
}
async saveFollowersCollectionPage(followersCollection, onlyNewestItem = true) {
debug('inside saveFollowers')
let orderedItems = followersCollection.orderedItems
const toUserName = extractNameFromId(followersCollection.id)
const toUserId = await this.ensureUser(constructIdFromName(toUserName))
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
return Promise.all(
orderedItems.map(async (follower) => {
debug(`follower = ${follower}`)
const fromUserId = await this.ensureUser(follower)
debug(`fromUserId = ${fromUserId}`)
debug(`toUserId = ${toUserId}`)
const result = await this.client.mutate({
mutation: gql`
mutation {
AddUserFollowedBy(from: {id: "${fromUserId}"}, to: {id: "${toUserId}"}) {
from { name }
}
}
`,
})
debug(`addUserFollowedBy edge = ${JSON.stringify(result, null, 2)}`)
throwErrorIfApolloErrorOccurred(result)
debug('saveFollowers: added follow edge successfully')
}),
)
}
async saveFollowingCollectionPage(followingCollection, onlyNewestItem = true) {
debug('inside saveFollowers')
let orderedItems = followingCollection.orderedItems
const fromUserName = extractNameFromId(followingCollection.id)
const fromUserId = await this.ensureUser(constructIdFromName(fromUserName))
orderedItems = onlyNewestItem ? [orderedItems.pop()] : orderedItems
return Promise.all(
orderedItems.map(async (following) => {
debug(`follower = ${following}`)
const toUserId = await this.ensureUser(following)
debug(`fromUserId = ${fromUserId}`)
debug(`toUserId = ${toUserId}`)
const result = await this.client.mutate({
mutation: gql`
mutation {
AddUserFollowing(from: {id: "${fromUserId}"}, to: {id: "${toUserId}"}) {
from { name }
}
}
`,
})
debug(`addUserFollowing edge = ${JSON.stringify(result, null, 2)}`)
throwErrorIfApolloErrorOccurred(result)
debug('saveFollowing: added follow edge successfully')
}),
)
}
async createPost(activity) {
// TODO how to handle the to field? Now the post is just created, doesn't matter who is the recipient
// createPost
const postObject = activity.object
if (!isPublicAddressed(postObject)) {
return debug(
'createPost: not send to public (sending to specific persons is not implemented yet)',
)
}
const title = postObject.summary
? postObject.summary
: postObject.content.split(' ').slice(0, 5).join(' ')
const postId = extractIdFromActivityId(postObject.id)
debug('inside create post')
let result = await this.client.mutate({
mutation: gql`
mutation {
CreatePost(content: "${postObject.content}", contentExcerpt: "${trunc(
postObject.content,
120,
)}", title: "${title}", id: "${postId}", objectId: "${postObject.id}", activityId: "${
activity.id
}") {
id
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
// ensure user and add author to post
const userId = await this.ensureUser(postObject.attributedTo)
debug(`userId = ${userId}`)
debug(`postId = ${postId}`)
result = await this.client.mutate({
mutation: gql`
mutation {
AddPostAuthor(from: {id: "${userId}"}, to: {id: "${postId}"}) {
from {
name
}
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
}
async deletePost(activity) {
const result = await this.client.mutate({
mutation: gql`
mutation {
DeletePost(id: "${extractIdFromActivityId(activity.object.id)}") {
title
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
}
async updatePost(activity) {
const postObject = activity.object
const postId = extractIdFromActivityId(postObject.id)
const date = postObject.updated ? postObject.updated : new Date().toISOString()
const result = await this.client.mutate({
mutation: gql`
mutation {
UpdatePost(content: "${postObject.content}", contentExcerpt: "${
trunc(postObject.content, 120).html
}", id: "${postId}", updatedAt: "${date}") {
title
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
}
async createShouted(activity) {
const userId = await this.ensureUser(activity.actor)
const postId = extractIdFromActivityId(activity.object)
const result = await this.client.mutate({
mutation: gql`
mutation {
AddUserShouted(from: {id: "${userId}"}, to: {id: "${postId}"}) {
from {
name
}
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
if (!result.data.AddUserShouted) {
debug('something went wrong shouting post')
throw Error('User or Post not exists')
}
}
async deleteShouted(activity) {
const userId = await this.ensureUser(activity.actor)
const postId = extractIdFromActivityId(activity.object)
const result = await this.client.mutate({
mutation: gql`
mutation {
RemoveUserShouted(from: {id: "${userId}"}, to: {id: "${postId}"}) {
from {
name
}
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
if (!result.data.AddUserShouted) {
debug('something went wrong disliking a post')
throw Error('User or Post not exists')
}
}
async getSharedInboxEndpoints() {
const result = await this.client.query({
query: gql`
query {
SharedInboxEndpoint {
uri
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
return result.data.SharedInboxEnpoint
}
async addSharedInboxEndpoint(uri) {
try {
const result = await this.client.mutate({
mutation: gql`
mutation {
CreateSharedInboxEndpoint(uri: "${uri}")
}
`,
})
throwErrorIfApolloErrorOccurred(result)
return true
} catch (e) {
return false
}
}
async createComment(activity) {
const postObject = activity.object
let result = await this.client.mutate({
mutation: gql`
mutation {
CreateComment(content: "${
postObject.content
}", activityId: "${extractIdFromActivityId(activity.id)}") {
id
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
const toUserId = await this.ensureUser(activity.actor)
const result2 = await this.client.mutate({
mutation: gql`
mutation {
AddCommentAuthor(from: {id: "${result.data.CreateComment.id}"}, to: {id: "${toUserId}"}) {
id
}
}
`,
})
throwErrorIfApolloErrorOccurred(result2)
const postId = extractIdFromActivityId(postObject.inReplyTo)
result = await this.client.mutate({
mutation: gql`
mutation {
AddCommentPost(from: { id: "${result.data.CreateComment.id}", to: { id: "${postId}" }}) {
id
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
}
/**
* This function will search for user existence and will create a disabled user with a random 16 bytes password when no user is found.
*
* @param actorId
* @returns {Promise<*>}
*/
async ensureUser(actorId) {
debug(`inside ensureUser = ${actorId}`)
const name = extractNameFromId(actorId)
const queryResult = await this.client.query({
query: gql`
query {
User(slug: "${name}") {
id
}
}
`,
})
if (
queryResult.data &&
Array.isArray(queryResult.data.User) &&
queryResult.data.User.length > 0
) {
debug('ensureUser: user exists.. return id')
// user already exists.. return the id
return queryResult.data.User[0].id
} else {
debug('ensureUser: user not exists.. createUser')
// user does not exist.. create it
const pw = crypto.randomBytes(16).toString('hex')
const slug = name.toLowerCase().split(' ').join('-')
const result = await this.client.mutate({
mutation: gql`
mutation {
CreateUser(password: "${pw}", slug:"${slug}", actorId: "${actorId}", name: "${name}", email: "${slug}@test.org") {
id
}
}
`,
})
throwErrorIfApolloErrorOccurred(result)
return result.data.CreateUser.id
}
}
}

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