mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-04-04 00:25:47 +00:00
Compare commits
No commits in common. "master" and "b3.11.0-2" have entirely different histories.
104
.coderabbit.yaml
104
.coderabbit.yaml
@ -1,104 +0,0 @@
|
||||
# CodeRabbit Configuration
|
||||
# https://docs.coderabbit.ai/guides/configure-coderabbit
|
||||
|
||||
language: de
|
||||
|
||||
reviews:
|
||||
pre_merge_checks:
|
||||
docstrings:
|
||||
mode: "off"
|
||||
|
||||
# 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"
|
||||
- "!**/*.png"
|
||||
- "!**/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
|
||||
180
.github/dependabot.yml
vendored
180
.github/dependabot.yml
vendored
@ -127,107 +127,79 @@ updates:
|
||||
timezone: "Europe/Berlin"
|
||||
time: "03:00"
|
||||
|
||||
# maintenance
|
||||
- package-ecosystem: docker
|
||||
open-pull-requests-limit: 99
|
||||
directory: "/maintenance"
|
||||
rebase-strategy: "disabled"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "saturday"
|
||||
timezone: "Europe/Berlin"
|
||||
time: "03:00"
|
||||
- package-ecosystem: npm
|
||||
open-pull-requests-limit: 99
|
||||
directory: "/maintenance"
|
||||
rebase-strategy: "disabled"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "saturday"
|
||||
timezone: "Europe/Berlin"
|
||||
time: "03:00"
|
||||
groups:
|
||||
nuxt:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "nuxt*"
|
||||
- "@nuxt*"
|
||||
- "@nuxtjs*"
|
||||
vitest:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "vitest*"
|
||||
- "@vitest*"
|
||||
tailwind:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "tailwindcss*"
|
||||
- "@tailwindcss*"
|
||||
|
||||
# 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"
|
||||
# frontend
|
||||
# - package-ecosystem: npm
|
||||
# open-pull-requests-limit: 99
|
||||
# directory: "/frontend"
|
||||
# rebase-strategy: "disabled"
|
||||
# schedule:
|
||||
# interval: weekly
|
||||
# day: "saturday"
|
||||
# timezone: "Europe/Berlin"
|
||||
# time: "03:00"
|
||||
# groups:
|
||||
# eslint:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "eslint*"
|
||||
# - "@eslint*"
|
||||
# pinia:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "pinia*"
|
||||
# react:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "react*"
|
||||
# remark:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "remark*"
|
||||
# storybook:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "storybook"
|
||||
# - "@storybook*"
|
||||
# stylelint:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "stylelint*"
|
||||
# typescript:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "ts*"
|
||||
# - "@types*"
|
||||
# - "typescript"
|
||||
# vite:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "vite"
|
||||
# - "vite-plugin*"
|
||||
# - "@vitejs/plugin-vue"
|
||||
# vitest:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "vitest"
|
||||
# - "@vitest*"
|
||||
# vue:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "*vue?(/)*"
|
||||
# exclude-patterns:
|
||||
# - "vuetify"
|
||||
# - "*vuepress*"
|
||||
# - "vue-tsc"
|
||||
# vuepress:
|
||||
# applies-to: version-updates
|
||||
# patterns:
|
||||
# - "vuepress"
|
||||
# - "@vuepress*"
|
||||
# - package-ecosystem: docker
|
||||
# open-pull-requests-limit: 99
|
||||
# directory: "/frontend"
|
||||
# rebase-strategy: "disabled"
|
||||
# schedule:
|
||||
# interval: weekly
|
||||
# day: "saturday"
|
||||
# timezone: "Europe/Berlin"
|
||||
# time: "03:00"
|
||||
|
||||
33
.github/file-filters.yml
vendored
33
.github/file-filters.yml
vendored
@ -1,14 +1,5 @@
|
||||
# These file filter patterns are used by the action https://github.com/dorny/paths-filter
|
||||
|
||||
ui: &ui
|
||||
- '.github/workflows/ui-*.yml'
|
||||
- 'packages/ui/**/*'
|
||||
|
||||
maintenance: &maintenance
|
||||
- '.github/workflows/maintenance-*.yml'
|
||||
- 'maintenance/**/*'
|
||||
- *ui
|
||||
|
||||
backend: &backend
|
||||
- '.github/workflows/test-backend.yml'
|
||||
- 'backend/**/*'
|
||||
@ -21,9 +12,7 @@ docker: &docker
|
||||
webapp: &webapp
|
||||
- '.github/workflows/test-webapp.yml'
|
||||
- 'webapp/**/*'
|
||||
- 'styleguide/**/*'
|
||||
- 'package.json'
|
||||
- *ui
|
||||
|
||||
docs-check: &docs-check
|
||||
- '.github/workflows/check-documentation.yml'
|
||||
@ -41,4 +30,24 @@ vuepress: &vuepress
|
||||
|
||||
documentation: &documentation
|
||||
- *vuepress
|
||||
- *markdown
|
||||
- *markdown
|
||||
|
||||
# frontend
|
||||
frontend-test-lint-code: &frontend-test-lint-code
|
||||
- 'frontend/**/*'
|
||||
|
||||
frontend-test-unit-code: &frontend-test-unit-code
|
||||
- 'frontend/**/*'
|
||||
|
||||
frontend-test-build-code: &frontend-test-build-code
|
||||
- 'frontend/**/*'
|
||||
|
||||
frontend-test-build-docker: &frontend-test-build-docker
|
||||
- 'frontend/**/*'
|
||||
|
||||
frontend-test-build-docs: &frontend-test-build-docs
|
||||
- 'frontend/**/*.md'
|
||||
- 'frontend/.vuepress/*'
|
||||
|
||||
frontend-test-build-storybook: &frontend-test-build-storybook
|
||||
- 'frontend/**/*'
|
||||
12
.github/workflows/check-documentation.yml
vendored
12
.github/workflows/check-documentation.yml
vendored
@ -11,10 +11,10 @@ jobs:
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Check for markdown file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
@ -28,13 +28,13 @@ jobs:
|
||||
if: needs.files-changed.outputs.markdown == 'true'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@1b916f2cf6c36510a6059943104e3c42ce6c16bc # 1.0.15
|
||||
with:
|
||||
use-quiet-mode: 'yes'
|
||||
use-verbose-mode: 'no'
|
||||
@ -51,10 +51,10 @@ jobs:
|
||||
if: needs.files-changed.outputs.documentation == 'true'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Setup Node 20
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.0.3
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
|
||||
42
.github/workflows/cleanup-cache-at-pr-closing.yml
vendored
Normal file
42
.github/workflows/cleanup-cache-at-pr-closing.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
###############################################################################
|
||||
# A Github repo has max 10 GB of cache.
|
||||
# https://github.blog/changelog/2021-11-23-github-actions-cache-size-is-now-increased-to-10gb-per-repository/
|
||||
#
|
||||
# To avoid "cache thrashing" by their cache eviction policy it is recommended
|
||||
# to apply a cache cleanup workflow at PR closing to dele cache leftovers of
|
||||
# the current branch:
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
|
||||
###############################################################################
|
||||
|
||||
name: ocelot.social cache cleanup on pr closing
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
clean-branch-cache:
|
||||
name: Cleanup branch cache
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
10
.github/workflows/deploy-documentation.yml
vendored
10
.github/workflows/deploy-documentation.yml
vendored
@ -13,10 +13,10 @@ jobs:
|
||||
documentation: ${{ steps.changes.outputs.documentation }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Check for file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
@ -27,10 +27,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Setup Node 20
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.0.3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
run: npm install && npm run docs:build
|
||||
|
||||
- name: Deploy Vuepress to Github Pages
|
||||
uses: crazy-max/ghaction-github-pages@1d6ee9b181a81033a16bd707a1401afa978daab4 # v4.0.0
|
||||
uses: crazy-max/ghaction-github-pages@df5cc2bfa78282ded844b354faee141f06b41865 # v4.0.0
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
build_dir: .vuepress/dist
|
||||
|
||||
32
.github/workflows/docker-push.yml
vendored
32
.github/workflows/docker-push.yml
vendored
@ -24,28 +24,28 @@ jobs:
|
||||
file: backend/Dockerfile
|
||||
target: production
|
||||
- name: webapp-base
|
||||
context: .
|
||||
context: webapp
|
||||
file: webapp/Dockerfile
|
||||
target: base
|
||||
- name: webapp-build
|
||||
context: .
|
||||
context: webapp
|
||||
file: webapp/Dockerfile
|
||||
target: build
|
||||
- name: webapp
|
||||
context: .
|
||||
context: webapp
|
||||
file: webapp/Dockerfile
|
||||
target: production
|
||||
- name: maintenance-base
|
||||
context: .
|
||||
file: maintenance/Dockerfile
|
||||
target: production
|
||||
context: webapp
|
||||
file: webapp/Dockerfile.maintenance
|
||||
target: base
|
||||
- name: maintenance-build
|
||||
context: .
|
||||
file: maintenance/Dockerfile
|
||||
context: webapp
|
||||
file: webapp/Dockerfile.maintenance
|
||||
target: build
|
||||
- name: maintenance
|
||||
context: .
|
||||
file: maintenance/Dockerfile
|
||||
context: webapp
|
||||
file: webapp/Dockerfile.maintenance
|
||||
target: production
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@ -59,18 +59,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
@ -83,7 +81,7 @@ jobs:
|
||||
type=sha
|
||||
- name: Build and push Docker images
|
||||
id: push
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
|
||||
with:
|
||||
context: ${{ matrix.app.context }}
|
||||
target: ${{ matrix.app.target }}
|
||||
@ -91,5 +89,3 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha,scope=${{ matrix.app.name }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.app.name }}
|
||||
|
||||
37
.github/workflows/frontend.test.build.code.yml
vendored
Normal file
37
.github/workflows/frontend.test.build.code.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: "frontend:test:build test code"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-build-code
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-build-code }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
build:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Build - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Build
|
||||
run: npm install && npm run build
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
52
.github/workflows/frontend.test.build.docker.yml
vendored
Normal file
52
.github/workflows/frontend.test.build.docker.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
name: "frontend:test:build test docker"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-build-docker
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-build-docker }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
build-production:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Build Docker Production - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Build Docker Production
|
||||
run: docker compose -f docker-compose.yml build
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
|
||||
build-development:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Build Docker Development - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Build Docker Development
|
||||
run: docker compose build
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
37
.github/workflows/frontend.test.build.docs.yml
vendored
Normal file
37
.github/workflows/frontend.test.build.docs.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: "frontend:test:build test docs"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-build-docs
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-build-docs }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
build:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Build Docs - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Build Docs
|
||||
run: npm install && npm run docs:build
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
37
.github/workflows/frontend.test.build.storybook.yml
vendored
Normal file
37
.github/workflows/frontend.test.build.storybook.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: "frontend:test:build test storybook"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-build-storybook
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-build-storybook }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
storybook:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Build Storybook - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Build Storybook
|
||||
run: npm install && npm run storybook:build
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
37
.github/workflows/frontend.test.lint.code.yml
vendored
Normal file
37
.github/workflows/frontend.test.lint.code.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: "frontend:test:lint code with defined linters"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-lint-code
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-lint-code }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
lint:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Lint - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Lint
|
||||
run: npm install && npm run test:lint
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
37
.github/workflows/frontend.test.unit.code.yml
vendored
Normal file
37
.github/workflows/frontend.test.unit.code.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: "frontend:test:unit test code with defined suites"
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# only (but most important) job from this workflow required for pull requests
|
||||
# check results serve as run conditions for all other jobs here
|
||||
files-changed:
|
||||
name: Detect File Changes - frontend-test-unit-code
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
changes: ${{ steps.changes.outputs.frontend-test-unit-code }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 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
|
||||
|
||||
unit:
|
||||
if: needs.files-changed.outputs.changes == 'true'
|
||||
name: Unit - Frontend
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WORKING_DIRECTORY: ./frontend
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Frontend | Unit
|
||||
run: npm install && npm run test:unit
|
||||
working-directory: ${{env.WORKING_DIRECTORY}}
|
||||
77
.github/workflows/maintenance-build.yml
vendored
77
.github/workflows/maintenance-build.yml
vendored
@ -1,77 +0,0 @@
|
||||
name: Maintenance Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: maintenance
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
maintenance: ${{ steps.changes.outputs.maintenance }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
|
||||
build:
|
||||
name: Build
|
||||
if: needs.files-changed.outputs.maintenance == '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: 'maintenance/.tool-versions'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: maintenance/package-lock.json
|
||||
|
||||
- name: Build UI library
|
||||
working-directory: packages/ui
|
||||
run: npm ci && npm run build
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Generate static site
|
||||
run: npx nuxt generate
|
||||
|
||||
- name: Verify build output
|
||||
run: |
|
||||
if [ ! -d ".output/public" ]; then
|
||||
echo "::error::.output/public directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f ".output/public/index.html" ]; then
|
||||
echo "::error::index.html not found in build output"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Build output verified!"
|
||||
ls -la .output/public/
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: maintenance-site
|
||||
path: maintenance/.output/public/
|
||||
retention-days: 7
|
||||
59
.github/workflows/maintenance-docker.yml
vendored
59
.github/workflows/maintenance-docker.yml
vendored
@ -1,59 +0,0 @@
|
||||
name: Maintenance Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
maintenance: ${{ steps.changes.outputs.maintenance }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
|
||||
build:
|
||||
name: Build Docker Image
|
||||
if: needs.files-changed.outputs.maintenance == '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@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Build development image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
file: ./maintenance/Dockerfile
|
||||
target: development
|
||||
push: false
|
||||
tags: ocelot-social/maintenance:development
|
||||
cache-from: type=gha,scope=maintenance-development
|
||||
cache-to: type=gha,mode=max,scope=maintenance-development
|
||||
|
||||
- name: Build production image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
file: ./maintenance/Dockerfile
|
||||
target: production
|
||||
push: false
|
||||
tags: ocelot-social/maintenance:latest
|
||||
cache-from: type=gha,scope=maintenance-production
|
||||
cache-to: type=gha,mode=max,scope=maintenance-production
|
||||
58
.github/workflows/maintenance-lint.yml
vendored
58
.github/workflows/maintenance-lint.yml
vendored
@ -1,58 +0,0 @@
|
||||
name: Maintenance Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: maintenance
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
maintenance: ${{ steps.changes.outputs.maintenance }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
|
||||
lint:
|
||||
name: ESLint
|
||||
if: needs.files-changed.outputs.maintenance == '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: 'maintenance/.tool-versions'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: maintenance/package-lock.json
|
||||
|
||||
- name: Build UI library
|
||||
working-directory: packages/ui
|
||||
run: npm ci && npm run build
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run ESLint
|
||||
run: npm run lint
|
||||
|
||||
- name: Run TypeScript type check
|
||||
run: npx nuxi typecheck
|
||||
63
.github/workflows/maintenance-test.yml
vendored
63
.github/workflows/maintenance-test.yml
vendored
@ -1,63 +0,0 @@
|
||||
name: Maintenance Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: maintenance
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
name: Detect File Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
maintenance: ${{ steps.changes.outputs.maintenance }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
filters: .github/file-filters.yml
|
||||
|
||||
test:
|
||||
name: Unit Tests
|
||||
if: needs.files-changed.outputs.maintenance == '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: 'maintenance/.tool-versions'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: maintenance/package-lock.json
|
||||
|
||||
- name: Build UI library
|
||||
working-directory: packages/ui
|
||||
run: npm ci && npm run build
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: npx vitest run --coverage
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: actions/upload-artifact@v7
|
||||
if: always()
|
||||
with:
|
||||
name: maintenance-coverage-report
|
||||
path: maintenance/coverage/
|
||||
retention-days: 7
|
||||
18
.github/workflows/publish.yml
vendored
18
.github/workflows/publish.yml
vendored
@ -14,13 +14,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 0 # Fetch full History for changelog
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Setup env
|
||||
run: |
|
||||
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
|
||||
@ -58,13 +54,9 @@ jobs:
|
||||
needs: [github_tag]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 0 # Fetch full History for changelog
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Setup env
|
||||
run: |
|
||||
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
|
||||
@ -72,7 +64,7 @@ jobs:
|
||||
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@1a91c28090489a711dd89a2424bb13d72a56e2e4 # v3.0.0
|
||||
# uses: peter-evans/repository-dispatch@6846232b0e1bfd17c14dce7ac13fd3fcefe22c0c # v3.0.0
|
||||
# with:
|
||||
# token: ${{ github.token }}
|
||||
# event-type: trigger-ocelot-build-success
|
||||
@ -80,7 +72,7 @@ jobs:
|
||||
# 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@1a91c28090489a711dd89a2424bb13d72a56e2e4 # v3.0.0
|
||||
uses: peter-evans/repository-dispatch@6846232b0e1bfd17c14dce7ac13fd3fcefe22c0c # 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
|
||||
@ -88,7 +80,7 @@ jobs:
|
||||
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@1a91c28090489a711dd89a2424bb13d72a56e2e4 # v3.0.0
|
||||
uses: peter-evans/repository-dispatch@6846232b0e1bfd17c14dce7ac13fd3fcefe22c0c # 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
|
||||
|
||||
87
.github/workflows/test-backend.yml
vendored
87
.github/workflows/test-backend.yml
vendored
@ -11,10 +11,10 @@ jobs:
|
||||
backend: ${{ steps.changes.outputs.backend }}
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Check for backend file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
@ -28,28 +28,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Neo4J | Build 'community' image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: neo4j
|
||||
file: neo4j/Dockerfile
|
||||
target: community
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/neo4j:community
|
||||
cache-from: type=gha,scope=neo4j
|
||||
cache-to: type=gha,mode=max,scope=neo4j
|
||||
|
||||
- name: Save image for test job
|
||||
run: docker save "ghcr.io/ocelot-social-community/ocelot-social/neo4j:community" > /tmp/neo4j.tar
|
||||
|
||||
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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-backend-neo4j-cache
|
||||
@ -61,28 +49,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: backend | Build 'test' image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: backend
|
||||
file: backend/Dockerfile
|
||||
target: test
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/backend:test
|
||||
cache-from: type=gha,scope=backend-test
|
||||
cache-to: type=gha,mode=max,scope=backend-test
|
||||
|
||||
- name: Save image for test job
|
||||
run: docker save "ghcr.io/ocelot-social-community/ocelot-social/backend:test" > /tmp/backend.tar
|
||||
|
||||
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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
@ -94,17 +70,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: 'backend/.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'backend/yarn.lock'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: backend | Lint
|
||||
run: cd backend && yarn --frozen-lockfile && yarn run lint
|
||||
run: cd backend && yarn && yarn run lint
|
||||
|
||||
unit_test_backend:
|
||||
name: Unit tests - Backend
|
||||
@ -115,17 +84,17 @@ jobs:
|
||||
checks: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Restore Neo4J cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # 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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
@ -139,6 +108,7 @@ jobs:
|
||||
- name: backend | copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp frontend/.env.dist frontend/.env
|
||||
cp backend/.env.template backend/.env
|
||||
|
||||
- name: backend | docker compose
|
||||
@ -153,3 +123,20 @@ jobs:
|
||||
- name: backend | Unit test incl. coverage check
|
||||
run: docker compose exec -T backend yarn test
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
if: ${{ needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true' }}
|
||||
needs: [files-changed, unit_test_backend]
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-backend-neo4j-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
KEY="${{ github.run_id }}-backend-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
|
||||
206
.github/workflows/test-e2e.yml
vendored
206
.github/workflows/test-e2e.yml
vendored
@ -8,54 +8,30 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Copy backend env file
|
||||
run: |
|
||||
cp backend/.env.test_e2e backend/.env
|
||||
cp webapp/.env.template webapp/.env
|
||||
|
||||
- name: Neo4J | Build image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: neo4j
|
||||
file: neo4j/Dockerfile
|
||||
target: community
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/neo4j:community
|
||||
cache-from: type=gha,scope=neo4j
|
||||
cache-to: type=gha,mode=max,scope=neo4j
|
||||
|
||||
- name: Backend | Build image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: backend
|
||||
file: backend/Dockerfile
|
||||
target: test
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/backend:test
|
||||
cache-from: type=gha,scope=backend-test
|
||||
cache-to: type=gha,mode=max,scope=backend-test
|
||||
|
||||
- name: Pull third-party images
|
||||
- name: Build backend and dependencies
|
||||
run: |
|
||||
docker pull quay.io/minio/minio:latest
|
||||
docker pull quay.io/minio/mc:latest
|
||||
docker pull maildev/maildev:latest
|
||||
|
||||
- name: Save all images
|
||||
run: |
|
||||
docker save "ghcr.io/ocelot-social-community/ocelot-social/backend:test" > /tmp/backend.tar
|
||||
# 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@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/tmp/backend.tar
|
||||
@ -70,27 +46,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Webapp | Build 'test' image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
file: webapp/Dockerfile
|
||||
target: test
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/webapp:test
|
||||
cache-from: type=gha,scope=webapp-test
|
||||
cache-to: type=gha,mode=max,scope=webapp-test
|
||||
|
||||
- name: Save image for test jobs
|
||||
run: docker save "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" > /tmp/webapp.tar
|
||||
|
||||
- name: Cache docker image
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.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@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-e2e-webapp-cache
|
||||
@ -99,16 +68,13 @@ jobs:
|
||||
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 # v6.0.2
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@08f58d1471bff7f3a07d167b4ad7df25d5fcfcb6 # v4.4.0
|
||||
with:
|
||||
node-version-file: 'backend/.nvmrc'
|
||||
node-version-file: 'backend/.tool-versions'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Copy env files
|
||||
@ -118,9 +84,7 @@ jobs:
|
||||
|
||||
- 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-amd64"
|
||||
echo "66a2ef158866c3ecb3d8e49a7189814a485bddca43e133e4ca5735b8d3951bf7 /opt/cucumber-json-formatter" | sha256sum -c -
|
||||
sudo chmod +x /opt/cucumber-json-formatter
|
||||
wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
|
||||
cd backend
|
||||
yarn install
|
||||
yarn build
|
||||
@ -129,7 +93,7 @@ jobs:
|
||||
|
||||
- name: Cache docker image
|
||||
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
@ -137,45 +101,29 @@ jobs:
|
||||
/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 # v6.0.2
|
||||
|
||||
- name: List feature files
|
||||
id: list
|
||||
run: |
|
||||
FEATURES=$(cd cypress && find e2e/ -name "*.feature" -printf '%P\n' | sort | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||
echo "features=$FEATURES" >> $GITHUB_OUTPUT
|
||||
|
||||
fullstack_tests:
|
||||
name: E2E | ${{ matrix.feature }}
|
||||
name: Fullstack | tests
|
||||
if: success()
|
||||
needs: [prepare_backend_environment, prepare_webapp_image, prepare_cypress, list_features]
|
||||
needs: [prepare_backend_environment, prepare_webapp_image, prepare_cypress]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
jobs: 8
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
feature: ${{ fromJson(needs.list_features.outputs.features) }}
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Delete huge unnecessary tools folder
|
||||
run: rm -rf /opt/hostedtoolcache
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@08f58d1471bff7f3a07d167b4ad7df25d5fcfcb6 # v4.4.0
|
||||
with:
|
||||
node-version-file: 'backend/.nvmrc'
|
||||
node-version-file: 'backend/.tool-versions'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Restore cypress cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
@ -185,7 +133,7 @@ jobs:
|
||||
restore-keys: ${{ github.run_id }}-e2e-cypress
|
||||
|
||||
- name: Restore backend environment cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/tmp/backend.tar
|
||||
@ -196,27 +144,15 @@ jobs:
|
||||
key: ${{ github.run_id }}-e2e-backend-environment-cache
|
||||
|
||||
- name: Restore webapp cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-e2e-webapp-cache
|
||||
|
||||
- name: Copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp backend/.env.test_e2e backend/.env
|
||||
|
||||
- name: Ensure cucumber-json-formatter exists
|
||||
run: |
|
||||
if [ ! -f /opt/cucumber-json-formatter ]; then
|
||||
sudo wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-amd64"
|
||||
echo "66a2ef158866c3ecb3d8e49a7189814a485bddca43e133e4ca5735b8d3951bf7 /opt/cucumber-json-formatter" | sha256sum -c -
|
||||
fi
|
||||
sudo chmod +x /opt/cucumber-json-formatter
|
||||
sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter
|
||||
|
||||
- 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
|
||||
@ -224,24 +160,11 @@ jobs:
|
||||
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
|
||||
|
||||
echo "Waiting for backend (max 120s)..."
|
||||
timeout 120 bash -c 'until curl -sf -X POST -H "Content-Type: application/json" -d "{\"query\":\"{__typename}\"}" http://localhost:4000 > /dev/null 2>&1; do sleep 5; done'
|
||||
echo "Backend is ready."
|
||||
|
||||
echo "Waiting for webapp (max 120s)..."
|
||||
timeout 120 bash -c 'until curl -sf http://localhost:3000 > /dev/null 2>&1; do sleep 5; done'
|
||||
echo "Webapp is ready."
|
||||
|
||||
- name: Initialize database
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn db:migrate init
|
||||
|
||||
- name: Migrate database
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn db:migrate up
|
||||
sleep 90s
|
||||
|
||||
- name: Full stack tests | run tests
|
||||
id: e2e-tests
|
||||
run: yarn run cypress:run --spec "cypress/e2e/${{ matrix.feature }}"
|
||||
run: yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
|
||||
|
||||
- name: Full stack tests | if tests failed, compile html report
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
@ -249,29 +172,32 @@ jobs:
|
||||
cd cypress/
|
||||
node create-cucumber-html-report.js
|
||||
|
||||
- name: Full stack tests | if tests failed, compute artifact name
|
||||
id: artifact-name
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
run: echo "name=e2e-report-$(echo '${{ matrix.feature }}' | tr '/' '-')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Full stack tests | if tests failed, upload report
|
||||
id: e2e-report
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ${{ steps.artifact-name.outputs.name }}
|
||||
name: ocelot-e2e-test-report-pr${{ needs.docker_preparation.outputs.pr-number }}
|
||||
path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report
|
||||
|
||||
e2e_status:
|
||||
name: E2E | Status
|
||||
if: always()
|
||||
needs: [fullstack_tests]
|
||||
cleanup_cache:
|
||||
name: Cleanup Cache
|
||||
needs: fullstack_tests
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Check E2E results
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f # v4.2.2
|
||||
|
||||
- name: Full stack tests | cleanup cache
|
||||
run: |
|
||||
if [ "${{ needs.fullstack_tests.result }}" != "success" ]; then
|
||||
echo "E2E tests failed or were cancelled (result: ${{ needs.fullstack_tests.result }})"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cacheKeys=$(gh cache list --json key --jq '.[] | select(.key | startswith("${{ github.run_id }}-e2e-")) | .key')
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeys
|
||||
do
|
||||
gh cache delete "$cacheKey"
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
73
.github/workflows/test-webapp.yml
vendored
73
.github/workflows/test-webapp.yml
vendored
@ -11,10 +11,10 @@ jobs:
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
webapp: ${{ steps.changes.outputs.webapp }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Check for frontend file changes
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
@ -28,12 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: 'webapp/.nvmrc'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Check translation files
|
||||
run: |
|
||||
@ -43,31 +38,19 @@ jobs:
|
||||
build_test_webapp:
|
||||
name: Docker Build Test - Webapp
|
||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true'
|
||||
needs: files-changed
|
||||
needs: [files-changed, prepare]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Webapp | Build 'test' image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
file: webapp/Dockerfile
|
||||
target: test
|
||||
load: true
|
||||
tags: ghcr.io/ocelot-social-community/ocelot-social/webapp:test
|
||||
cache-from: type=gha,scope=webapp-test
|
||||
cache-to: type=gha,mode=max,scope=webapp-test
|
||||
|
||||
- name: Save image for test job
|
||||
run: docker save "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" > /tmp/webapp.tar
|
||||
run: |
|
||||
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
|
||||
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
|
||||
|
||||
- name: Cache docker image
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
@ -79,17 +62,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: 'webapp/.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'webapp/yarn.lock'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: webapp | Lint
|
||||
run: cd webapp && yarn --frozen-lockfile && yarn run lint
|
||||
run: cd webapp && yarn && yarn run lint
|
||||
|
||||
unit_test_webapp:
|
||||
name: Unit Tests - Webapp
|
||||
@ -100,10 +76,10 @@ jobs:
|
||||
checks: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Restore webapp cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v4.0.2
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
@ -114,10 +90,29 @@ jobs:
|
||||
- name: Copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp frontend/.env.dist frontend/.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: backend | docker compose
|
||||
# doesn't work without the --build flag - this either means we should not load the cached images or cache the correct image
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp --build
|
||||
|
||||
- name: webapp | Unit tests incl. coverage check
|
||||
run: docker compose exec -T webapp yarn test
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
if: ${{ needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true' }}
|
||||
needs: [files-changed, unit_test_webapp]
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-webapp-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
|
||||
|
||||
5
.github/workflows/test.lint_pr.yml
vendored
5
.github/workflows/test.lint_pr.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
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@335288255954904a41ddda8947c8f2c844b8bfeb # v5.5.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@ -29,11 +29,10 @@ jobs:
|
||||
# Configure which scopes are allowed (newline delimited).
|
||||
scopes: |
|
||||
backend
|
||||
package/ui
|
||||
webapp
|
||||
frontend
|
||||
maintenance
|
||||
database
|
||||
e2e
|
||||
docu
|
||||
docker
|
||||
release
|
||||
|
||||
95
.github/workflows/ui-build.yml
vendored
95
.github/workflows/ui-build.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@v7
|
||||
with:
|
||||
name: dist
|
||||
path: packages/ui/dist/
|
||||
retention-days: 7
|
||||
121
.github/workflows/ui-compatibility.yml
vendored
121
.github/workflows/ui-compatibility.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@v7
|
||||
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@v8
|
||||
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
|
||||
59
.github/workflows/ui-docker.yml
vendored
59
.github/workflows/ui-docker.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Build development image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: ./packages/ui
|
||||
file: ./packages/ui/Dockerfile
|
||||
target: development
|
||||
push: false
|
||||
tags: ocelot-social/ui:development
|
||||
cache-from: type=gha,scope=ui-development
|
||||
cache-to: type=gha,mode=max,scope=ui-development
|
||||
|
||||
- name: Build production image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: ./packages/ui
|
||||
file: ./packages/ui/Dockerfile
|
||||
target: production
|
||||
push: false
|
||||
tags: ocelot-social/ui:latest
|
||||
cache-from: type=gha,scope=ui-production
|
||||
cache-to: type=gha,mode=max,scope=ui-production
|
||||
54
.github/workflows/ui-lint.yml
vendored
54
.github/workflows/ui-lint.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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
|
||||
65
.github/workflows/ui-release.yml
vendored
65
.github/workflows/ui-release.yml
vendored
@ -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 }}
|
||||
54
.github/workflows/ui-size.yml
vendored
54
.github/workflows/ui-size.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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
|
||||
74
.github/workflows/ui-storybook.yml
vendored
74
.github/workflows/ui-storybook.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@v7
|
||||
with:
|
||||
name: storybook-static
|
||||
path: packages/ui/storybook-static/
|
||||
retention-days: 7
|
||||
59
.github/workflows/ui-test.yml
vendored
59
.github/workflows/ui-test.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@v7
|
||||
if: always()
|
||||
with:
|
||||
name: coverage-report
|
||||
path: packages/ui/coverage/
|
||||
retention-days: 7
|
||||
51
.github/workflows/ui-verify.yml
vendored
51
.github/workflows/ui-verify.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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
|
||||
64
.github/workflows/ui-visual.yml
vendored
64
.github/workflows/ui-visual.yml
vendored
@ -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@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
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@v7
|
||||
if: failure()
|
||||
with:
|
||||
name: visual-test-results
|
||||
path: |
|
||||
packages/ui/test-results/
|
||||
packages/ui/playwright-report/
|
||||
retention-days: 7
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "styleguide"]
|
||||
path = styleguide
|
||||
url = https://github.com/Human-Connection/Nitro-Styleguide.git
|
||||
[submodule "deployment/configurations/stage.ocelot.social"]
|
||||
path = deployment/configurations/stage.ocelot.social
|
||||
url = git@github.com:Ocelot-Social-Community/stage.ocelot.social.git
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"packages/ui": "0.0.1"
|
||||
}
|
||||
1
.tool-versions
Normal file
1
.tool-versions
Normal file
@ -0,0 +1 @@
|
||||
nodejs 20.12.1
|
||||
2973
CHANGELOG.md
2973
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ SMTP_PASSWORD=
|
||||
SMTP_SECURE="false" # true for 465, false for other ports
|
||||
SMTP_DKIM_DOMAINNAME=
|
||||
SMTP_DKIM_KEYSELECTOR=
|
||||
SMTP_DKIM_PRIVATEKEY=
|
||||
SMTP_DKIM_PRIVATKEY=
|
||||
# E-Mail settings for our 'docker compose up mailserver'
|
||||
# SMTP_HOST=localhost
|
||||
# SMTP_PORT=1025
|
||||
@ -33,6 +33,8 @@ 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
|
||||
@ -43,11 +45,7 @@ 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
|
||||
S3_PUBLIC_GATEWAY=http://localhost:8000
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
MAX_PINNED_POSTS=1
|
||||
MAX_GROUP_PINNED_POSTS=1
|
||||
API_KEYS_ENABLED=false
|
||||
API_KEYS_MAX_PER_USER=5
|
||||
|
||||
@ -19,12 +19,14 @@ SMTP_PASSWORD=
|
||||
SMTP_SECURE="false" # true for 465, false for other ports
|
||||
SMTP_DKIM_DOMAINNAME=
|
||||
SMTP_DKIM_KEYSELECTOR=
|
||||
SMTP_DKIM_PRIVATEKEY=
|
||||
SMTP_DKIM_PRIVATKEY=
|
||||
|
||||
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
|
||||
@ -35,11 +37,7 @@ 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
|
||||
S3_PUBLIC_GATEWAY=http://localhost:8000
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
MAX_PINNED_POSTS=1
|
||||
MAX_GROUP_PINNED_POSTS=1
|
||||
API_KEYS_ENABLED=true
|
||||
API_KEYS_MAX_PER_USER=5
|
||||
|
||||
3
backend/.eslintignore
Normal file
3
backend/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
build/
|
||||
coverage/
|
||||
232
backend/.eslintrc.cjs
Normal file
232
backend/.eslintrc.cjs
Normal file
@ -0,0 +1,232 @@
|
||||
// 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:security/recommended-legacy',
|
||||
'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: [
|
||||
// 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',
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1 +1 @@
|
||||
v25.3.0
|
||||
v24.2.0
|
||||
9
backend/.prettierrc.cjs
Normal file
9
backend/.prettierrc.cjs
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true
|
||||
};
|
||||
1
backend/.tool-versions
Normal file
1
backend/.tool-versions
Normal file
@ -0,0 +1 @@
|
||||
nodejs 24.2.0
|
||||
@ -1,5 +1,4 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM node:25.8.2-alpine AS base
|
||||
FROM node:24.4.0-alpine AS base
|
||||
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"
|
||||
@ -29,15 +28,13 @@ 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 install --production=false --frozen-lockfile --non-interactive
|
||||
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
|
||||
ONBUILD RUN cd /build && yarn install --production=true --frozen-lockfile --non-interactive
|
||||
|
||||
FROM build AS test
|
||||
# required for the migrations
|
||||
|
||||
@ -19,16 +19,18 @@ 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/) (>= `v16.19.0`). We are using
|
||||
`v24.2.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
|
||||
# install Node
|
||||
$ cd backend
|
||||
$ nvm install
|
||||
$ nvm use
|
||||
$ nvm install v24.2.0
|
||||
$ nvm use v24.2.0
|
||||
```
|
||||
|
||||
Install node dependencies with [yarn](https://yarnpkg.com/en/):
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
import config from 'eslint-config-it4c'
|
||||
import graphql from 'eslint-config-it4c/modules/graphql'
|
||||
import jest from 'eslint-config-it4c/modules/jest'
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ['node_modules/', 'build/', 'coverage/'],
|
||||
},
|
||||
...config,
|
||||
...jest,
|
||||
// GraphQL schema linting (extend file pattern to include .gql)
|
||||
...graphql.map((c) => ({
|
||||
...c,
|
||||
files: ['**/*.graphql', '**/*.gql'],
|
||||
})),
|
||||
{
|
||||
files: ['**/*.graphql', '**/*.gql'],
|
||||
// TODO: Parser must be set explicitly because the it4c module only provides
|
||||
// plugins and rules, not languageOptions. Without this, ESLint uses the JS
|
||||
// parser for .gql files. Remove when fixed in eslint-config-it4c.
|
||||
languageOptions: {
|
||||
parser: graphql[0].plugins['@graphql-eslint'].parser,
|
||||
parserOptions: {
|
||||
graphQLConfig: {
|
||||
schema: './src/graphql/types/**/*.gql',
|
||||
documents: './src/graphql/queries/**/*.gql',
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
// Would require descriptions on every type/field/input — too noisy for now
|
||||
'@graphql-eslint/require-description': 'off',
|
||||
// camelCase operation names and _id/_ne underscores conflict with existing schema
|
||||
'@graphql-eslint/naming-convention': 'off',
|
||||
// Many types (Image, File, InviteCode, etc.) intentionally lack id: ID!
|
||||
'@graphql-eslint/strict-id-in-types': 'off',
|
||||
// Fields like groupType, queryLocations match parent type name by coincidence
|
||||
'@graphql-eslint/no-typename-prefix': 'off',
|
||||
// neo4j-graphql-js adds arguments (first, offset) at runtime not present in static schema
|
||||
'@graphql-eslint/known-argument-names': 'off',
|
||||
// TODO: operations-recommended rules must be disabled because the it4c
|
||||
// graphql module bundles both schema and operations configs together.
|
||||
// Remove when eslint-config-it4c exports them separately (e.g. graphql/schema).
|
||||
'@graphql-eslint/executable-definitions': 'off',
|
||||
// neo4j-graphql-js adds fields at runtime (_id, relations) not present in static schema
|
||||
'@graphql-eslint/fields-on-correct-type': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
// Backend-specific TypeScript overrides
|
||||
files: ['**/*.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: {
|
||||
allowDefaultProject: ['eslint.config.ts', 'jest.config.ts', 'prettier.config.ts'],
|
||||
},
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
// TypeORM compatibility: joined tables can be null but are not defined as nullable
|
||||
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||
// Allow string.match(regex) instead of regex.exec(string)
|
||||
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||
// TODO: gradually add return types to exported functions, then remove this override
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
// Allow @/* path aliases in relative parent imports
|
||||
'import-x/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Jest test file overrides
|
||||
files: ['**/*.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
// Config files: allow require() of devDependencies
|
||||
files: ['*.config.{js,mjs,cjs,ts,mts,cts}'],
|
||||
rules: {
|
||||
'n/no-unpublished-require': 'off',
|
||||
},
|
||||
},
|
||||
]
|
||||
27
backend/jest.config.cjs
Normal file
27
backend/jest.config.cjs
Normal file
@ -0,0 +1,27 @@
|
||||
/* 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: 90,
|
||||
},
|
||||
},
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
|
||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
||||
import { pathsToModuleNameMapper } from 'ts-jest'
|
||||
import { parseConfigFileTextToJson } from 'typescript'
|
||||
|
||||
// eslint-disable-next-line n/no-sync -- config files are synchronous by nature
|
||||
const tsconfigText = readFileSync('./tsconfig.json', 'utf-8')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- parseConfigFileTextToJson returns untyped config
|
||||
const { config } = parseConfigFileTextToJson('tsconfig.json', tsconfigText)
|
||||
const paths = (config as { compilerOptions: { paths: Record<string, string[]> } }).compilerOptions
|
||||
.paths
|
||||
|
||||
export default {
|
||||
verbose: true,
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'**/*.ts',
|
||||
'!**/node_modules/**',
|
||||
'!**/test/**',
|
||||
'!**/build/**',
|
||||
'!**/src/**/?(*.)+(spec|test).ts?(x)',
|
||||
'!**/src/db/**',
|
||||
'!*.config.ts',
|
||||
'!**/*.d.ts',
|
||||
'!**/gql-register.ts',
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 93,
|
||||
},
|
||||
},
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
|
||||
transform: {
|
||||
'\\.gql$': '<rootDir>/test/graphqlTransform.ts',
|
||||
'\\.tsx?$': 'ts-jest',
|
||||
},
|
||||
moduleNameMapper: pathsToModuleNameMapper(paths, { prefix: '<rootDir>/' }),
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-backend",
|
||||
"version": "3.15.1",
|
||||
"version": "3.11.0",
|
||||
"description": "GraphQL Backend for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
@ -10,111 +10,122 @@
|
||||
"scripts": {
|
||||
"start": "node build/src/",
|
||||
"build": "tsc && tsc-alias && ./scripts/build.copy.files.sh",
|
||||
"dev": "nodemon --exec tsx src/index.ts -e js,ts,gql",
|
||||
"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 .",
|
||||
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc .",
|
||||
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||
"db:reset": "tsx src/db/reset.ts",
|
||||
"db:reset:withmigrations": "tsx src/db/reset-with-migrations.ts",
|
||||
"db:seed": "tsx --require ./src/graphql/gql-register.ts src/db/seed.ts",
|
||||
"db:data:admin": "tsx src/db/admin.ts",
|
||||
"db:data:badges": "tsx src/db/badges.ts",
|
||||
"db:data:branding": "tsx src/db/data-branding.ts",
|
||||
"db:data:categories": "tsx src/db/categories.ts",
|
||||
"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": "tsx 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"
|
||||
"prod:db:data:categories": "node build/src/db/categories.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/server": "^4.11.3",
|
||||
"@aws-sdk/client-s3": "^3.1005.0",
|
||||
"@aws-sdk/lib-storage": "^3.1000.0",
|
||||
"@graphql-tools/load-files": "^7.0.0",
|
||||
"@graphql-tools/merge": "^9.0.0",
|
||||
"@sentry/node": "^5.30.0",
|
||||
"@aws-sdk/client-s3": "^3.844.0",
|
||||
"@aws-sdk/lib-storage": "^3.842.0",
|
||||
"@sentry/node": "^5.15.4",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
"bcryptjs": "~3.0.3",
|
||||
"body-parser": "^2.2.2",
|
||||
"cheerio": "~1.2.0",
|
||||
"cross-env": "~10.1.0",
|
||||
"apollo-server": "~2.14.2",
|
||||
"apollo-server-express": "^2.14.2",
|
||||
"bcryptjs": "~3.0.2",
|
||||
"body-parser": "^1.20.3",
|
||||
"cheerio": "~1.1.0",
|
||||
"cross-env": "~7.0.3",
|
||||
"dotenv": "~17.0.1",
|
||||
"email-templates": "^13.0.1",
|
||||
"express": "^4.22.1",
|
||||
"graphql": "^16.13.2",
|
||||
"graphql-middleware": "~6.1.35",
|
||||
"email-templates": "^12.0.3",
|
||||
"express": "^5.1.0",
|
||||
"graphql": "^14.6.0",
|
||||
"graphql-middleware": "~4.0.2",
|
||||
"graphql-middleware-sentry": "^3.2.1",
|
||||
"graphql-redis-subscriptions": "^2.7.0",
|
||||
"graphql-shield": "^7.6.5",
|
||||
"graphql-subscriptions": "^2.0.0",
|
||||
"graphql-shield": "~7.2.2",
|
||||
"graphql-subscriptions": "^1.1.0",
|
||||
"graphql-tag": "~2.10.3",
|
||||
"graphql-upload": "^13.0.0",
|
||||
"graphql-ws": "^5.16.2",
|
||||
"helmet": "~8.1.0",
|
||||
"ioredis": "^5.10.1",
|
||||
"ioredis": "^5.6.1",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"languagedetect": "^2.0.0",
|
||||
"linkify-html": "^4.3.2",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"lodash": "~4.17.23",
|
||||
"metascraper": "^5.50.0",
|
||||
"metascraper-author": "^5.50.0",
|
||||
"metascraper-date": "^5.50.0",
|
||||
"metascraper-description": "^5.50.0",
|
||||
"metascraper-image": "^5.50.0",
|
||||
"metascraper-lang": "^5.50.0",
|
||||
"linkify-html": "^4.3.1",
|
||||
"linkifyjs": "^4.3.1",
|
||||
"lodash": "~4.17.21",
|
||||
"merge-graphql-schemas": "^1.7.8",
|
||||
"metascraper": "^5.49.1",
|
||||
"metascraper-author": "^5.49.1",
|
||||
"metascraper-date": "^5.49.1",
|
||||
"metascraper-description": "^5.49.1",
|
||||
"metascraper-image": "^5.49.1",
|
||||
"metascraper-lang": "^5.49.1",
|
||||
"metascraper-lang-detector": "^4.10.2",
|
||||
"metascraper-logo": "^5.50.0",
|
||||
"metascraper-publisher": "^5.50.0",
|
||||
"metascraper-logo": "^5.49.1",
|
||||
"metascraper-publisher": "^5.49.1",
|
||||
"metascraper-soundcloud": "^5.34.4",
|
||||
"metascraper-title": "^5.50.0",
|
||||
"metascraper-url": "^5.50.0",
|
||||
"metascraper-video": "^5.50.0",
|
||||
"metascraper-youtube": "^5.50.0",
|
||||
"metascraper-title": "^5.49.1",
|
||||
"metascraper-url": "^5.49.1",
|
||||
"metascraper-video": "^5.49.1",
|
||||
"metascraper-youtube": "^5.49.1",
|
||||
"migrate": "^2.1.0",
|
||||
"mime-types": "^3.0.2",
|
||||
"minimatch": "^10.2.4",
|
||||
"mime-types": "^3.0.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"mustache": "^4.2.0",
|
||||
"neo4j-driver": "^4.4.11",
|
||||
"neo4j-graphql-js": "2.11.5",
|
||||
"neo4j-graphql-js": "^2.11.5",
|
||||
"neode": "^0.4.9",
|
||||
"node-fetch": "^2.7.0",
|
||||
"nodemailer": "^8.0.4",
|
||||
"nodemailer": "^7.0.5",
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
"preview-email": "^3.1.1",
|
||||
"pug": "^3.0.4",
|
||||
"sanitize-html": "~2.17.2",
|
||||
"slugify": "^1.6.8",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
"preview-email": "^3.1.0",
|
||||
"pug": "^3.0.3",
|
||||
"sanitize-html": "~2.17.0",
|
||||
"slug": "~9.1.0",
|
||||
"trunc-html": "~1.1.2",
|
||||
"tslog": "^4.10.2",
|
||||
"tslog": "^4.9.3",
|
||||
"uuid": "~9.0.1",
|
||||
"validator": "^13.15.26",
|
||||
"ws": "^8.20.0",
|
||||
"validator": "^13.15.15",
|
||||
"xregexp": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
|
||||
"@faker-js/faker": "9.9.0",
|
||||
"@types/email-templates": "^10.0.4",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/jsonwebtoken": "~8.5.1",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/request": "^2.48.13",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/request": "^2.48.12",
|
||||
"@types/slug": "^5.0.9",
|
||||
"@types/uuid": "~9.0.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-it4c": "^0.12.0",
|
||||
"jest": "^30.3.0",
|
||||
"nodemon": "~3.1.14",
|
||||
"prettier": "^3.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"apollo-server-testing": "~2.11.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-jsonc": "^2.20.1",
|
||||
"eslint-plugin-n": "^17.21.0",
|
||||
"eslint-plugin-no-catch-all": "^1.1.0",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint-plugin-promise": "^7.2.1",
|
||||
"eslint-plugin-security": "^3.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "~3.1.10",
|
||||
"prettier": "^3.6.2",
|
||||
"require-json5": "^1.3.0",
|
||||
"rosie": "^2.1.1",
|
||||
"ts-jest": "^29.4.6",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsc-alias": "^1.8.16",
|
||||
"tsx": "^4.21.0",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
@ -122,11 +133,7 @@
|
||||
"**/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",
|
||||
"neo4j-graphql-js/graphql": "^16.11.0",
|
||||
"graphql-upload/graphql": "^16.11.0"
|
||||
"**/wrap-ansi": "7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.12.1"
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export { default } from 'eslint-config-it4c/prettier'
|
||||
@ -1,17 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable import-x/no-namespace */
|
||||
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
||||
|
||||
/* eslint-disable n/no-process-env */
|
||||
import { config } from 'dotenv'
|
||||
// eslint-disable-next-line import/no-namespace
|
||||
import * as SMTPTransport from 'nodemailer/lib/smtp-pool'
|
||||
|
||||
import emails from './emails'
|
||||
import metadata from './metadata'
|
||||
|
||||
import type * as SMTPTransport from 'nodemailer/lib/smtp-pool'
|
||||
|
||||
// Load env file
|
||||
config()
|
||||
|
||||
@ -33,7 +30,6 @@ const environment = {
|
||||
: [],
|
||||
SEND_MAIL: env.NODE_ENV !== 'test',
|
||||
LOG_LEVEL: 'DEBUG',
|
||||
PROXY_S3: env.PROXY_S3,
|
||||
}
|
||||
|
||||
const server = {
|
||||
@ -51,10 +47,9 @@ const SMTP_PASSWORD = env.SMTP_PASSWORD
|
||||
const SMTP_DKIM_DOMAINNAME = env.SMTP_DKIM_DOMAINNAME
|
||||
const SMTP_DKIM_KEYSELECTOR = env.SMTP_DKIM_KEYSELECTOR
|
||||
// PEM format = https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html
|
||||
const SMTP_DKIM_PRIVATEKEY = env.SMTP_DKIM_PRIVATEKEY?.replace(/\\n/g, '\n') // replace all "\n" in .env string by real line break
|
||||
const SMTP_DKIM_PRIVATKEY = env.SMTP_DKIM_PRIVATKEY?.replace(/\\n/g, '\n') // replace all "\n" in .env string by real line break
|
||||
const SMTP_MAX_CONNECTIONS = (env.SMTP_MAX_CONNECTIONS && parseInt(env.SMTP_MAX_CONNECTIONS)) || 5
|
||||
const SMTP_MAX_MESSAGES = (env.SMTP_MAX_MESSAGES && parseInt(env.SMTP_MAX_MESSAGES)) || 100
|
||||
const SMTP_REJECT_UNAUTHORIZED = env.SMTP_REJECT_UNAUTHORIZED !== 'false' // default = true
|
||||
|
||||
const nodemailerTransportOptions: SMTPTransport.Options = {
|
||||
host: SMTP_HOST,
|
||||
@ -64,9 +59,6 @@ const nodemailerTransportOptions: SMTPTransport.Options = {
|
||||
pool: true,
|
||||
maxConnections: SMTP_MAX_CONNECTIONS,
|
||||
maxMessages: SMTP_MAX_MESSAGES,
|
||||
tls: {
|
||||
rejectUnauthorized: SMTP_REJECT_UNAUTHORIZED,
|
||||
},
|
||||
}
|
||||
if (SMTP_USERNAME && SMTP_PASSWORD) {
|
||||
nodemailerTransportOptions.auth = {
|
||||
@ -74,11 +66,11 @@ if (SMTP_USERNAME && SMTP_PASSWORD) {
|
||||
pass: SMTP_PASSWORD,
|
||||
}
|
||||
}
|
||||
if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATEKEY) {
|
||||
if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATKEY) {
|
||||
nodemailerTransportOptions.dkim = {
|
||||
domainName: SMTP_DKIM_DOMAINNAME,
|
||||
keySelector: SMTP_DKIM_KEYSELECTOR,
|
||||
privateKey: SMTP_DKIM_PRIVATEKEY,
|
||||
privateKey: SMTP_DKIM_PRIVATKEY,
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,21 +92,19 @@ const redis = {
|
||||
}
|
||||
|
||||
const required = {
|
||||
EMAIL_DEFAULT_SENDER: env.EMAIL_DEFAULT_SENDER,
|
||||
|
||||
AWS_ACCESS_KEY_ID: env.AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY,
|
||||
AWS_ENDPOINT: env.AWS_ENDPOINT,
|
||||
AWS_REGION: env.AWS_REGION,
|
||||
AWS_BUCKET: env.AWS_BUCKET,
|
||||
|
||||
IMAGOR_PUBLIC_URL: env.IMAGOR_PUBLIC_URL,
|
||||
IMAGOR_SECRET: env.IMAGOR_SECRET,
|
||||
|
||||
MAPBOX_TOKEN: env.MAPBOX_TOKEN,
|
||||
JWT_SECRET: env.JWT_SECRET,
|
||||
PRIVATE_KEY_PASSPHRASE: env.PRIVATE_KEY_PASSPHRASE,
|
||||
}
|
||||
|
||||
const S3_PUBLIC_GATEWAY = env.S3_PUBLIC_GATEWAY
|
||||
|
||||
// https://stackoverflow.com/a/53050575
|
||||
type NoUndefinedField<T> = { [P in keyof T]-?: NoUndefinedField<NonNullable<T[P]>> }
|
||||
|
||||
@ -131,6 +121,7 @@ function assertRequiredConfig(
|
||||
assertRequiredConfig(required)
|
||||
|
||||
const options = {
|
||||
EMAIL_DEFAULT_SENDER: env.EMAIL_DEFAULT_SENDER,
|
||||
SUPPORT_EMAIL: env.SUPPORT_EMAIL,
|
||||
SUPPORT_URL: emails.SUPPORT_LINK,
|
||||
APPLICATION_NAME: metadata.APPLICATION_NAME,
|
||||
@ -145,11 +136,6 @@ const options = {
|
||||
MAX_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_PINNED_POSTS))
|
||||
? 1
|
||||
: Number(process.env.MAX_PINNED_POSTS),
|
||||
MAX_GROUP_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_GROUP_PINNED_POSTS))
|
||||
? 1
|
||||
: Number(process.env.MAX_GROUP_PINNED_POSTS),
|
||||
API_KEYS_ENABLED: env.API_KEYS_ENABLED === 'true',
|
||||
API_KEYS_MAX_PER_USER: (env.API_KEYS_MAX_PER_USER && parseInt(env.API_KEYS_MAX_PER_USER)) || 5,
|
||||
}
|
||||
|
||||
const language = {
|
||||
@ -165,6 +151,7 @@ const CONFIG = {
|
||||
...redis,
|
||||
...options,
|
||||
...language,
|
||||
S3_PUBLIC_GATEWAY,
|
||||
}
|
||||
|
||||
export type Config = typeof CONFIG
|
||||
@ -175,8 +162,7 @@ export type S3Config = Pick<
|
||||
| 'AWS_ENDPOINT'
|
||||
| 'AWS_REGION'
|
||||
| 'AWS_BUCKET'
|
||||
| 'IMAGOR_SECRET'
|
||||
| 'IMAGOR_PUBLIC_URL'
|
||||
| 'S3_PUBLIC_GATEWAY'
|
||||
>
|
||||
export default CONFIG
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED'
|
||||
export const CHAT_MESSAGE_STATUS_UPDATED = 'CHAT_MESSAGE_STATUS_UPDATED'
|
||||
export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED'
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
import { getDriver, getNeode } from '@db/neo4j'
|
||||
|
||||
import type { Driver } from 'neo4j-driver'
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
|
||||
import databaseContext from '@context/database'
|
||||
import pubsubContext from '@context/pubsub'
|
||||
import CONFIG from '@src/config'
|
||||
import type { DecodedUser } from '@src/jwt/decode'
|
||||
import { decode } from '@src/jwt/decode'
|
||||
import ocelotLogger from '@src/logger'
|
||||
import type OcelotLogger from '@src/logger'
|
||||
|
||||
import type { DecodedUser } from '@src/jwt/decode'
|
||||
import type { ApolloServerExpressConfig } from 'apollo-server-express'
|
||||
|
||||
const serverDatabase = databaseContext()
|
||||
const serverPubsub = pubsubContext()
|
||||
@ -14,14 +18,14 @@ export const getContext =
|
||||
database?: ReturnType<typeof databaseContext>
|
||||
pubsub?: ReturnType<typeof pubsubContext>
|
||||
authenticatedUser: DecodedUser | null | undefined
|
||||
logger?: typeof ocelotLogger
|
||||
logger?: typeof OcelotLogger
|
||||
config: typeof CONFIG
|
||||
}) =>
|
||||
async (req: { headers: { authorization?: string } }) => {
|
||||
const {
|
||||
database = serverDatabase,
|
||||
pubsub = serverPubsub,
|
||||
authenticatedUser,
|
||||
authenticatedUser = undefined,
|
||||
logger = ocelotLogger,
|
||||
config = CONFIG,
|
||||
} = opts ?? {}
|
||||
@ -40,11 +44,18 @@ export const getContext =
|
||||
req,
|
||||
cypherParams: {
|
||||
currentUserId: user ? user.id : null,
|
||||
languageDefault: config.LANGUAGE_DEFAULT.toUpperCase(),
|
||||
},
|
||||
config,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const context: ApolloServerExpressConfig['context'] = async (options) => {
|
||||
const { connection, req } = options
|
||||
if (connection) {
|
||||
return connection.context
|
||||
} else {
|
||||
return getContext()(req)
|
||||
}
|
||||
}
|
||||
export type Context = Awaited<ReturnType<ReturnType<typeof getContext>>>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { getNeode } from './neo4j'
|
||||
import { trophies, verification } from './seed/badges'
|
||||
|
||||
// eslint-disable-next-line import-x/newline-after-import
|
||||
// eslint-disable-next-line import/newline-after-import
|
||||
;(async function () {
|
||||
const neode = getNeode()
|
||||
try {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
/* eslint-disable import-x/no-commonjs */
|
||||
// eslint-disable-next-line n/no-unpublished-require
|
||||
const tsx = require('tsx/cjs/api')
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable import/no-commonjs */
|
||||
// eslint-disable-next-line n/no-unpublished-require, @typescript-eslint/no-var-requires
|
||||
const tsNode = require('ts-node')
|
||||
// eslint-disable-next-line import/no-unassigned-import, n/no-unpublished-require
|
||||
require('tsconfig-paths/register')
|
||||
|
||||
module.exports = tsx.register
|
||||
module.exports = tsNode.register
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
/* eslint-disable @typescript-eslint/await-thenable */
|
||||
import { readdir } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
import databaseContext from '@context/database'
|
||||
|
||||
const run = async () => {
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
if (args.length !== 1) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Usage: yarn run db:func:disable-notifications <email>')
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const email = args[0]
|
||||
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(email)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error: Invalid email address format')
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const { write } = databaseContext()
|
||||
|
||||
const result = (
|
||||
await write({
|
||||
query: `
|
||||
MATCH (:EmailAddress {email: $email})-[:BELONGS_TO]->(user:User)
|
||||
SET user.emailNotificationsFollowingUsers = false
|
||||
SET user.emailNotificationsPostInGroup = false
|
||||
SET user.emailNotificationsCommentOnObservedPost = false
|
||||
SET user.emailNotificationsMention = false
|
||||
SET user.emailNotificationsChatMessage = false
|
||||
SET user.emailNotificationsGroupMemberJoined = false
|
||||
SET user.emailNotificationsGroupMemberLeft = false
|
||||
SET user.emailNotificationsGroupMemberRemoved = false
|
||||
SET user.emailNotificationsGroupMemberRoleChanged = false
|
||||
RETURN toString(count(user)) as count
|
||||
`,
|
||||
variables: {
|
||||
email,
|
||||
},
|
||||
})
|
||||
).records[0].get('count') as string
|
||||
|
||||
if (result !== '1') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`User with email address ${email} not found`)
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Notifications for User with email address ${email} disabled`)
|
||||
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
void (async function () {
|
||||
await run()
|
||||
})()
|
||||
@ -4,22 +4,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable n/no-unpublished-import */
|
||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { hashSync } from 'bcryptjs'
|
||||
import { Factory } from 'rosie'
|
||||
import slugify from 'slugify'
|
||||
import slugify from 'slug'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { generateInviteCode } from '@graphql/resolvers/inviteCodes'
|
||||
import { isUniqueFor } from '@middleware/sluggifyMiddleware'
|
||||
import uniqueSlug from '@middleware/slugify/uniqueSlug'
|
||||
import { Context } from '@src/context'
|
||||
|
||||
import { getDriver, getNeode } from './neo4j'
|
||||
|
||||
import type { Context } from '@src/context'
|
||||
|
||||
const neode = getNeode()
|
||||
|
||||
const uniqueImageUrl = (imageUrl) => {
|
||||
@ -52,14 +49,14 @@ Factory.define('category')
|
||||
.attr('id', uuid)
|
||||
.attr('icon', 'globe')
|
||||
.attr('name', 'Global Peace & Nonviolence')
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('Category', buildObject)
|
||||
})
|
||||
|
||||
Factory.define('badge')
|
||||
.attr('type', 'crowdfunding')
|
||||
.attr('status', 'permanent')
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('Badge', buildObject)
|
||||
})
|
||||
|
||||
@ -70,7 +67,7 @@ Factory.define('image')
|
||||
.attr('alt', faker.lorem.sentence)
|
||||
.attr('type', 'image/jpeg')
|
||||
.attr('url', null)
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
if (!buildObject.url) {
|
||||
buildObject.url = faker.image.urlPicsumPhotos({
|
||||
width: buildObject.width,
|
||||
@ -87,7 +84,7 @@ Factory.define('file')
|
||||
.attr('name', faker.lorem.slug)
|
||||
.attr('type', 'image/jpeg')
|
||||
.attr('url', null)
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
if (!buildObject.url) {
|
||||
buildObject.url = faker.image.urlPicsumPhotos()
|
||||
}
|
||||
@ -174,8 +171,8 @@ Factory.define('post')
|
||||
return Promise.all([Factory.build('category')])
|
||||
}) */
|
||||
.option('tagIds', [])
|
||||
.option('tags', ['tagIds'], async (tagIds) => {
|
||||
return Promise.all(tagIds.map(async (id) => neode.find('Tag', id)))
|
||||
.option('tags', ['tagIds'], (tagIds) => {
|
||||
return Promise.all(tagIds.map((id) => neode.find('Tag', id)))
|
||||
})
|
||||
.option('authorId', null)
|
||||
.option('author', ['authorId'], (authorId) => {
|
||||
@ -199,6 +196,9 @@ Factory.define('post')
|
||||
// Convert false to null
|
||||
return pinned || null
|
||||
})
|
||||
.attr('contentExcerpt', ['contentExcerpt', 'content'], (contentExcerpt, content) => {
|
||||
return contentExcerpt || content
|
||||
})
|
||||
.attr('slug', ['slug', 'title'], (slug, title) => {
|
||||
return slug || slugify(title, { lower: true })
|
||||
})
|
||||
@ -227,55 +227,6 @@ Factory.define('post')
|
||||
return post
|
||||
})
|
||||
|
||||
Factory.define('group')
|
||||
.option('ownerId', null)
|
||||
.option('owner', ['ownerId'], (ownerId) => {
|
||||
if (ownerId) return neode.find('User', ownerId)
|
||||
return Factory.build('user')
|
||||
})
|
||||
.attrs({
|
||||
id: uuid,
|
||||
name: faker.company.name,
|
||||
about: faker.lorem.sentence,
|
||||
description: faker.lorem.paragraphs,
|
||||
groupType: 'public',
|
||||
actionRadius: 'regional',
|
||||
deleted: false,
|
||||
disabled: false,
|
||||
})
|
||||
.attr('slug', ['slug', 'name'], (slug, name) => {
|
||||
return slug || slugify(name, { lower: true })
|
||||
})
|
||||
.attr(
|
||||
'descriptionExcerpt',
|
||||
['descriptionExcerpt', 'description'],
|
||||
(descriptionExcerpt, description) => {
|
||||
return descriptionExcerpt || description
|
||||
},
|
||||
)
|
||||
.after(async (buildObject, options) => {
|
||||
const [group, owner] = await Promise.all([neode.create('Group', buildObject), options.owner])
|
||||
const session = driver.session()
|
||||
try {
|
||||
await session.writeTransaction((txc) =>
|
||||
txc.run(
|
||||
`
|
||||
MATCH (owner:User {id: $ownerId}), (group:Group {id: $groupId})
|
||||
MERGE (owner)-[:CREATED]->(group)
|
||||
MERGE (owner)-[membership:MEMBER_OF]->(group)
|
||||
SET membership.createdAt = toString(datetime()),
|
||||
membership.updatedAt = toString(datetime()),
|
||||
membership.role = 'owner'
|
||||
`,
|
||||
{ ownerId: owner.get('id'), groupId: buildObject.id },
|
||||
),
|
||||
)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
return group
|
||||
})
|
||||
|
||||
Factory.define('comment')
|
||||
.option('postId', null)
|
||||
.option('post', ['postId'], (postId) => {
|
||||
@ -291,6 +242,9 @@ Factory.define('comment')
|
||||
id: uuid,
|
||||
content: faker.lorem.sentence,
|
||||
})
|
||||
.attr('contentExcerpt', ['contentExcerpt', 'content'], (contentExcerpt, content) => {
|
||||
return contentExcerpt || content
|
||||
})
|
||||
.after(async (buildObject, options) => {
|
||||
const [comment, author, post] = await Promise.all([
|
||||
neode.create('Comment', buildObject),
|
||||
@ -310,7 +264,7 @@ Factory.define('donations')
|
||||
.attr('showDonations', true)
|
||||
.attr('goal', 15000)
|
||||
.attr('progress', 7000)
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('Donations', buildObject)
|
||||
})
|
||||
|
||||
@ -321,13 +275,13 @@ const emailDefaults = {
|
||||
|
||||
Factory.define('emailAddress')
|
||||
.attrs(emailDefaults)
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('EmailAddress', buildObject)
|
||||
})
|
||||
|
||||
Factory.define('unverifiedEmailAddress')
|
||||
.attr(emailDefaults)
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('UnverifiedEmailAddress', buildObject)
|
||||
})
|
||||
|
||||
@ -340,7 +294,7 @@ const inviteCodeDefaults = {
|
||||
Factory.define('inviteCode')
|
||||
.attrs(inviteCodeDefaults)
|
||||
.option('groupId', null)
|
||||
.option('group', ['groupId'], async (groupId) => {
|
||||
.option('group', ['groupId'], (groupId) => {
|
||||
if (groupId) {
|
||||
return neode.find('Group', groupId)
|
||||
}
|
||||
@ -377,11 +331,11 @@ Factory.define('location')
|
||||
id: 'country.10743216036480410',
|
||||
type: 'country',
|
||||
})
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('Location', buildObject)
|
||||
})
|
||||
|
||||
Factory.define('report').after(async (buildObject, _options) => {
|
||||
Factory.define('report').after((buildObject, _options) => {
|
||||
return neode.create('Report', buildObject)
|
||||
})
|
||||
|
||||
@ -389,7 +343,7 @@ Factory.define('tag')
|
||||
.attrs({
|
||||
name: '#human-connection',
|
||||
})
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('Tag', buildObject)
|
||||
})
|
||||
|
||||
@ -397,7 +351,7 @@ Factory.define('socialMedia')
|
||||
.attrs({
|
||||
url: 'https://mastodon.social/@Gargron',
|
||||
})
|
||||
.after(async (buildObject, _options) => {
|
||||
.after((buildObject, _options) => {
|
||||
return neode.create('SocialMedia', buildObject)
|
||||
})
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
|
||||
import { getDriver, getNeode } from '@db/neo4j'
|
||||
|
||||
class Store {
|
||||
@ -84,7 +83,7 @@ class Store {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const { migrations } = set
|
||||
const writeTxResultPromise = session.writeTransaction(async (txc) => {
|
||||
const writeTxResultPromise = session.writeTransaction((txc) => {
|
||||
return Promise.all(
|
||||
migrations.map(async (migration) => {
|
||||
const { title, description, timestamp } = migration
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable import-x/no-extraneous-dependencies */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable promise/prefer-await-to-callbacks */
|
||||
/* eslint-disable import-x/no-deprecated */
|
||||
import { throwError, concat } from 'rxjs'
|
||||
import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators'
|
||||
|
||||
@ -72,7 +71,7 @@ export function up(next) {
|
||||
),
|
||||
)
|
||||
.subscribe({
|
||||
next: ({ user, email, _oldUser, oldEmail }) => {
|
||||
next: ({ user, email, _oldUser, oldEmail }) =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`
|
||||
Merged:
|
||||
@ -80,8 +79,7 @@ export function up(next) {
|
||||
userId: ${user.id}
|
||||
email: ${oldEmail} => ${email.email}
|
||||
=============================
|
||||
`)
|
||||
},
|
||||
`),
|
||||
complete: () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Merging of duplicate users completed')
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable import-x/no-extraneous-dependencies */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable promise/prefer-await-to-callbacks */
|
||||
/* eslint-disable import-x/no-deprecated */
|
||||
import { throwError, concat } from 'rxjs'
|
||||
import { flatMap, mergeMap, map, catchError } from 'rxjs/operators'
|
||||
|
||||
@ -66,7 +65,7 @@ export function up(next) {
|
||||
),
|
||||
)
|
||||
.subscribe({
|
||||
next: ({ updatedLocation, location }) => {
|
||||
next: ({ updatedLocation, location }) =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`
|
||||
Merged:
|
||||
@ -74,8 +73,7 @@ export function up(next) {
|
||||
locationId: ${location.id}
|
||||
updatedLocation: ${location.id} => ${updatedLocation.id}
|
||||
=============================
|
||||
`)
|
||||
},
|
||||
`),
|
||||
complete: () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Merging of duplicate locations completed')
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
'use strict'
|
||||
|
||||
export async function up(_next) {}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/await-thenable */
|
||||
|
||||
import { open } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
@ -38,38 +37,42 @@ export async function up(_next) {
|
||||
urls = urls.filter((url) => url.startsWith('/uploads'))
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('URLS uploaded:')
|
||||
for await (const url of urls) {
|
||||
const { pathname } = new URL(url, 'http://example.org')
|
||||
// TODO: find a better way to do this - this is quite a hack
|
||||
const fileLocation =
|
||||
CONFIG.NODE_ENV === 'production'
|
||||
? path.join(__dirname, `../../../../public/${pathname}`) // we're in the /build folder
|
||||
: path.join(__dirname, `../../../public/${pathname}`)
|
||||
const s3Location = `original${pathname}`
|
||||
const mimeType = lookup(fileLocation)
|
||||
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
||||
const fileHandle = await open(fileLocation)
|
||||
const params = {
|
||||
Bucket: AWS_BUCKET,
|
||||
Key: s3Location,
|
||||
ACL: ObjectCannedACL.public_read,
|
||||
ContentType: mimeType || 'image/jpeg',
|
||||
Body: fileHandle.createReadStream(),
|
||||
}
|
||||
const command = new Upload({ client: s3, params })
|
||||
await Promise.all(
|
||||
urls
|
||||
.map((url) => async () => {
|
||||
const { pathname } = new URL(url, 'http://example.org')
|
||||
// TODO: find a better way to do this - this is quite a hack
|
||||
const fileLocation =
|
||||
CONFIG.NODE_ENV === 'production'
|
||||
? path.join(__dirname, `../../../../public/${pathname}`) // we're in the /build folder
|
||||
: path.join(__dirname, `../../../public/${pathname}`)
|
||||
const s3Location = `original${pathname}`
|
||||
const mimeType = lookup(fileLocation)
|
||||
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
||||
const fileHandle = await open(fileLocation)
|
||||
const params = {
|
||||
Bucket: AWS_BUCKET,
|
||||
Key: s3Location,
|
||||
ACL: ObjectCannedACL.public_read,
|
||||
ContentType: mimeType || 'image/jpeg',
|
||||
Body: fileHandle.createReadStream(),
|
||||
}
|
||||
const command = new Upload({ client: s3, params })
|
||||
|
||||
const data = await command.done()
|
||||
const { Location: spacesUrl } = data
|
||||
const data = await command.done()
|
||||
const { Location: spacesUrl } = data
|
||||
|
||||
const updatedRecord = await transaction.run(
|
||||
'MATCH (image:Image {url: $url}) SET image.url = $spacesUrl RETURN image.url as url',
|
||||
{ url, spacesUrl },
|
||||
)
|
||||
const [updatedUrl] = updatedRecord.records.map((record) => record.get('url') as string)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(updatedUrl)
|
||||
// return updatedUrl
|
||||
}
|
||||
const updatedRecord = await transaction.run(
|
||||
'MATCH (image:Image {url: $url}) SET image.url = $spacesUrl RETURN image.url as url',
|
||||
{ url, spacesUrl },
|
||||
)
|
||||
const [updatedUrl] = updatedRecord.records.map((record) => record.get('url') as string)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(updatedUrl)
|
||||
return updatedUrl
|
||||
})
|
||||
.map((p) => p()),
|
||||
)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description = 'Delete follow relation for deleted users'
|
||||
|
||||
export async function up(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(`
|
||||
MATCH (:User {deleted: true})-[follow:FOLLOWS]-(:User)
|
||||
DELETE follow;
|
||||
`)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// cannot be rolled back
|
||||
// Implement your migration here.
|
||||
// await transaction.run(``)
|
||||
// await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description =
|
||||
'Replace global message.seen flag with per-user HAS_NOT_SEEN relationships'
|
||||
|
||||
export async function up(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Create HAS_NOT_SEEN relationships for unseen messages
|
||||
// For each message with seen=false, create a relationship for each room member
|
||||
// who is not the sender
|
||||
await transaction.run(`
|
||||
MATCH (message:Message { seen: false })-[:INSIDE]->(room:Room)<-[:CHATS_IN]-(user:User)
|
||||
WHERE NOT (user)-[:CREATED]->(message)
|
||||
CREATE (user)-[:HAS_NOT_SEEN]->(message)
|
||||
`)
|
||||
|
||||
// Remove the seen property from all messages
|
||||
await transaction.run(`
|
||||
MATCH (m:Message)
|
||||
REMOVE m.seen
|
||||
`)
|
||||
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Re-add seen property: messages with HAS_NOT_SEEN are unseen, rest are seen
|
||||
await transaction.run(`
|
||||
MATCH (m:Message)
|
||||
SET m.seen = true
|
||||
`)
|
||||
await transaction.run(`
|
||||
MATCH ()-[:HAS_NOT_SEEN]->(m:Message)
|
||||
SET m.seen = false
|
||||
`)
|
||||
|
||||
// Remove all HAS_NOT_SEEN relationships
|
||||
await transaction.run(`
|
||||
MATCH ()-[r:HAS_NOT_SEEN]->(:Message)
|
||||
DELETE r
|
||||
`)
|
||||
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description =
|
||||
'Delete empty DM rooms (no messages) that were created by the old CreateRoom mutation'
|
||||
|
||||
export async function up(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
await transaction.run(`
|
||||
MATCH (room:Room)
|
||||
WHERE NOT (room)-[:ROOM_FOR]->(:Group)
|
||||
AND NOT (room)<-[:INSIDE]-(:Message)
|
||||
DETACH DELETE room
|
||||
`)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(_next) {
|
||||
// Cannot restore deleted rooms
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description = 'Remove contentExcerpt property from Post and Comment nodes'
|
||||
|
||||
export async function up(_next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
await transaction.run(`
|
||||
MATCH (n)
|
||||
WHERE n:Post OR n:Comment
|
||||
REMOVE n.contentExcerpt
|
||||
`)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export function down(_next) {
|
||||
throw new Error(
|
||||
'Irreversible migration: contentExcerpt was removed and cannot be restored without regenerating from content',
|
||||
)
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
export default {
|
||||
id: { type: 'uuid', primary: true },
|
||||
name: { type: 'string', required: true },
|
||||
keyHash: { type: 'string', unique: 'true', required: true },
|
||||
keyPrefix: { type: 'string', required: true },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
lastUsedAt: { type: 'string', isoDate: true },
|
||||
expiresAt: { type: 'string', isoDate: true },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
owner: {
|
||||
type: 'relationship',
|
||||
relationship: 'HAS_API_KEY',
|
||||
target: 'User',
|
||||
direction: 'in',
|
||||
},
|
||||
}
|
||||
@ -10,6 +10,7 @@ export default {
|
||||
default: () => new Date().toISOString(),
|
||||
},
|
||||
content: { type: 'string', disallow: [null], min: 3 },
|
||||
contentExcerpt: { type: 'string', allow: [null] },
|
||||
deleted: { type: 'boolean', default: false },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
post: {
|
||||
|
||||
@ -13,7 +13,6 @@ export default {
|
||||
nameNL: { type: 'string' },
|
||||
namePL: { type: 'string' },
|
||||
nameRU: { type: 'string' },
|
||||
nameSQ: { type: 'string' },
|
||||
isIn: {
|
||||
type: 'relationship',
|
||||
relationship: 'IS_IN',
|
||||
|
||||
@ -18,7 +18,8 @@ export default {
|
||||
},
|
||||
title: { type: 'string', disallow: [null], min: 3 },
|
||||
slug: { type: 'string', allow: [null], unique: 'true' },
|
||||
content: { type: 'string', disallow: [null], required: true, min: 3 },
|
||||
content: { type: 'string', disallow: [null], min: 3 },
|
||||
contentExcerpt: { type: 'string', allow: [null] },
|
||||
deleted: { type: 'boolean', default: false },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
clickedCount: { type: 'int', default: 0 },
|
||||
@ -57,7 +58,6 @@ export default {
|
||||
},
|
||||
},
|
||||
pinned: { type: 'boolean', default: null, valid: [null, true] },
|
||||
groupPinned: { type: 'boolean', default: null, valid: [null, true] },
|
||||
postType: { type: 'string', default: 'Article', valid: ['Article', 'Event'] },
|
||||
observes: {
|
||||
type: 'relationship',
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable jest/valid-title */
|
||||
|
||||
import { cleanDatabase } from '@db/factories'
|
||||
import { getNeode, getDriver } from '@db/neo4j'
|
||||
@ -66,8 +65,8 @@ describe('slug', () => {
|
||||
})
|
||||
|
||||
describe('characters', () => {
|
||||
const createUser = async (attrs) => {
|
||||
return neode.create('User', attrs).then(async (user) => user.toJson())
|
||||
const createUser = (attrs) => {
|
||||
return neode.create('User', attrs).then((user) => user.toJson())
|
||||
}
|
||||
|
||||
it('-', async () => {
|
||||
@ -82,7 +81,7 @@ describe('slug', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('', async () => {
|
||||
it(' ', async () => {
|
||||
await expect(createUser({ slug: 'matt rider' })).rejects.toThrow('ERROR_VALIDATION')
|
||||
})
|
||||
|
||||
|
||||
@ -1,51 +1,36 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable n/no-missing-require */
|
||||
/* eslint-disable n/global-require */
|
||||
// NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm
|
||||
// module that is not browser-compatible. Node's `fs` module is server-side only
|
||||
//
|
||||
// We use static imports instead of dynamic require() to ensure compatibility
|
||||
// with both Node.js and Webpack (used by Cypress cucumber preprocessor).
|
||||
|
||||
import ApiKey from './ApiKey'
|
||||
import Badge from './Badge'
|
||||
import Category from './Category'
|
||||
import Comment from './Comment'
|
||||
import Donations from './Donations'
|
||||
import EmailAddress from './EmailAddress'
|
||||
import File from './File'
|
||||
import Group from './Group'
|
||||
import Image from './Image'
|
||||
import InviteCode from './InviteCode'
|
||||
import Location from './Location'
|
||||
import Migration from './Migration'
|
||||
import Post from './Post'
|
||||
import Report from './Report'
|
||||
import SocialMedia from './SocialMedia'
|
||||
import Tag from './Tag'
|
||||
import UnverifiedEmailAddress from './UnverifiedEmailAddress'
|
||||
import User from './User'
|
||||
|
||||
import type Neode from 'neode'
|
||||
|
||||
// Type assertion needed because TypeScript infers literal types from the model
|
||||
// objects (e.g., type: 'string' as literal), but Neode expects the broader
|
||||
// SchemaObject type with PropertyTypes union. The Neode type definitions are
|
||||
// incomplete/incorrect, so we use double assertion to bypass the check.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
declare let Cypress: any | undefined
|
||||
export default {
|
||||
ApiKey,
|
||||
Badge,
|
||||
Category,
|
||||
Comment,
|
||||
Donations,
|
||||
EmailAddress,
|
||||
File,
|
||||
Group,
|
||||
Image,
|
||||
InviteCode,
|
||||
Location,
|
||||
Migration,
|
||||
Post,
|
||||
Report,
|
||||
SocialMedia,
|
||||
Tag,
|
||||
UnverifiedEmailAddress,
|
||||
User,
|
||||
} as unknown as Record<string, Neode.SchemaObject>
|
||||
File: typeof Cypress !== 'undefined' ? require('./File') : require('./File').default,
|
||||
Image: typeof Cypress !== 'undefined' ? require('./Image') : require('./Image').default,
|
||||
Badge: typeof Cypress !== 'undefined' ? require('./Badge') : require('./Badge').default,
|
||||
User: typeof Cypress !== 'undefined' ? require('./User') : require('./User').default,
|
||||
Group: typeof Cypress !== 'undefined' ? require('./Group') : require('./Group').default,
|
||||
EmailAddress:
|
||||
typeof Cypress !== 'undefined' ? require('./EmailAddress') : require('./EmailAddress').default,
|
||||
UnverifiedEmailAddress:
|
||||
typeof Cypress !== 'undefined'
|
||||
? require('./UnverifiedEmailAddress')
|
||||
: require('./UnverifiedEmailAddress').default,
|
||||
SocialMedia:
|
||||
typeof Cypress !== 'undefined' ? require('./SocialMedia') : require('./SocialMedia').default,
|
||||
Post: typeof Cypress !== 'undefined' ? require('./Post') : require('./Post').default,
|
||||
Comment: typeof Cypress !== 'undefined' ? require('./Comment') : require('./Comment').default,
|
||||
Category: typeof Cypress !== 'undefined' ? require('./Category') : require('./Category').default,
|
||||
Tag: typeof Cypress !== 'undefined' ? require('./Tag') : require('./Tag').default,
|
||||
Location: typeof Cypress !== 'undefined' ? require('./Location') : require('./Location').default,
|
||||
Donations:
|
||||
typeof Cypress !== 'undefined' ? require('./Donations') : require('./Donations').default,
|
||||
Report: typeof Cypress !== 'undefined' ? require('./Report') : require('./Report').default,
|
||||
Migration:
|
||||
typeof Cypress !== 'undefined' ? require('./Migration') : require('./Migration').default,
|
||||
InviteCode:
|
||||
typeof Cypress !== 'undefined' ? require('./InviteCode') : require('./InviteCode').default,
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
/* eslint-disable import-x/no-named-as-default-member */
|
||||
import neo4j from 'neo4j-driver'
|
||||
/* eslint-disable import/no-named-as-default-member */
|
||||
import neo4j, { Driver } from 'neo4j-driver'
|
||||
import Neode from 'neode'
|
||||
|
||||
import CONFIG from '@config/index'
|
||||
import models from '@db/models/index'
|
||||
|
||||
import type { Driver } from 'neo4j-driver'
|
||||
|
||||
let driver: Driver
|
||||
const defaultOptions = {
|
||||
uri: CONFIG.NEO4J_URI,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
|
||||
/* eslint-disable n/no-process-exit */
|
||||
import CONFIG from '@config/index'
|
||||
|
||||
import { cleanDatabase } from './factories'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
|
||||
/* eslint-disable n/no-process-exit */
|
||||
import CONFIG from '@config/index'
|
||||
|
||||
import { cleanDatabase } from './factories'
|
||||
|
||||
@ -2,22 +2,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
/* eslint-disable n/no-unpublished-import */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
|
||||
|
||||
/* eslint-disable n/no-process-exit */
|
||||
import { faker } from '@faker-js/faker'
|
||||
import sample from 'lodash/sample'
|
||||
|
||||
import CONFIG from '@config/index'
|
||||
import { categories } from '@constants/categories'
|
||||
import CreateComment from '@graphql/queries/comments/CreateComment.gql'
|
||||
import ChangeGroupMemberRole from '@graphql/queries/groups/ChangeGroupMemberRole.gql'
|
||||
import CreateGroup from '@graphql/queries/groups/CreateGroup.gql'
|
||||
import JoinGroup from '@graphql/queries/groups/JoinGroup.gql'
|
||||
import CreateGroupRoom from '@graphql/queries/messaging/CreateGroupRoom.gql'
|
||||
import CreateMessage from '@graphql/queries/messaging/CreateMessage.gql'
|
||||
import CreatePost from '@graphql/queries/posts/CreatePost.gql'
|
||||
import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation'
|
||||
import { createCommentMutation } from '@graphql/queries/createCommentMutation'
|
||||
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
|
||||
import { CreateMessage } from '@graphql/queries/CreateMessage'
|
||||
import { createPostMutation } from '@graphql/queries/createPostMutation'
|
||||
import { createRoomMutation } from '@graphql/queries/createRoomMutation'
|
||||
import { joinGroupMutation } from '@graphql/queries/joinGroupMutation'
|
||||
import { createApolloTestSetup } from '@root/test/helpers'
|
||||
|
||||
import Factory from './factories'
|
||||
@ -42,7 +39,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
authenticatedUser,
|
||||
config: CONFIG,
|
||||
})
|
||||
const apolloSetup = await createApolloTestSetup({ context })
|
||||
const apolloSetup = createApolloTestSetup({ context })
|
||||
const { mutate, server, database } = apolloSetup
|
||||
const { neode } = database
|
||||
|
||||
@ -344,7 +341,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
console.log('seed', 'groups')
|
||||
authenticatedUser = await peterLustig.toJson()
|
||||
await mutate({
|
||||
mutation: CreateGroup,
|
||||
mutation: createGroupMutation(),
|
||||
variables: {
|
||||
id: 'g0',
|
||||
name: 'Investigative Journalism',
|
||||
@ -357,21 +354,21 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g0',
|
||||
userId: 'u2',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g0',
|
||||
userId: 'u4',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g0',
|
||||
userId: 'u6',
|
||||
@ -379,7 +376,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g0',
|
||||
userId: 'u2',
|
||||
@ -388,7 +385,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g0',
|
||||
userId: 'u4',
|
||||
@ -399,7 +396,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'group posts')
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p0-g0',
|
||||
groupId: 'g0',
|
||||
@ -411,7 +408,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
authenticatedUser = await bobDerBaumeister.toJson()
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p1-g0',
|
||||
groupId: 'g0',
|
||||
@ -423,7 +420,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
await mutate({
|
||||
mutation: CreateGroup,
|
||||
mutation: createGroupMutation(),
|
||||
variables: {
|
||||
id: 'g1',
|
||||
name: 'School For Citizens',
|
||||
@ -436,35 +433,35 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u1',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u2',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u5',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u6',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u7',
|
||||
@ -472,7 +469,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u1',
|
||||
@ -480,7 +477,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u5',
|
||||
@ -488,7 +485,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g1',
|
||||
userId: 'u6',
|
||||
@ -496,7 +493,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p0-g1',
|
||||
groupId: 'g1',
|
||||
@ -507,7 +504,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
authenticatedUser = await peterLustig.toJson()
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p1-g1',
|
||||
groupId: 'g1',
|
||||
@ -519,7 +516,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
authenticatedUser = await bobDerBaumeister.toJson()
|
||||
await mutate({
|
||||
mutation: CreateGroup,
|
||||
mutation: createGroupMutation(),
|
||||
variables: {
|
||||
id: 'g2',
|
||||
name: 'Yoga Practice',
|
||||
@ -531,35 +528,35 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u3',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u4',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u5',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u6',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u7',
|
||||
@ -567,7 +564,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u3',
|
||||
@ -575,7 +572,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u4',
|
||||
@ -583,7 +580,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u5',
|
||||
@ -591,7 +588,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: ChangeGroupMemberRole,
|
||||
mutation: changeGroupMemberRoleMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: 'u6',
|
||||
@ -601,7 +598,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
authenticatedUser = await louie.toJson()
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p0-g2',
|
||||
groupId: 'g2',
|
||||
@ -617,7 +614,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
const now = new Date()
|
||||
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'e0',
|
||||
title: 'Illegaler Kindergeburtstag',
|
||||
@ -632,7 +629,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'e1',
|
||||
title: 'Wir Schützen den Stuttgarter Schlossgarten',
|
||||
@ -647,7 +644,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'e2',
|
||||
title: 'IT 4 Change Treffen',
|
||||
@ -828,217 +825,17 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
)
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'api-keys')
|
||||
|
||||
// API Keys for Peter (admin) — active keys
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u1' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-peter-ci', name: 'CI Bot', keyPrefix: 'oak_peterCI',
|
||||
keyHash: 'seed-hash-peter-ci', createdAt: toString(datetime() - duration('P30D')),
|
||||
lastUsedAt: toString(datetime() - duration('PT2H')), disabled: false
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u1' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-peter-backup', name: 'Backup Script', keyPrefix: 'oak_peterBU',
|
||||
keyHash: 'seed-hash-peter-backup', createdAt: toString(datetime() - duration('P14D')),
|
||||
lastUsedAt: toString(datetime() - duration('P3D')), disabled: false
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
// Peter's revoked key
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u1' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-peter-old', name: 'Old Script', keyPrefix: 'oak_peterOL',
|
||||
keyHash: 'seed-hash-peter-old', createdAt: '2025-01-01T00:00:00.000Z',
|
||||
lastUsedAt: '2025-05-15T00:00:00.000Z',
|
||||
disabled: true, disabledAt: '2025-06-01T00:00:00.000Z'
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
// API Key for Jenny (user) — active, with expiry
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u3' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-jenny-bot', name: 'Weather Bot', keyPrefix: 'oak_jennyWB',
|
||||
keyHash: 'seed-hash-jenny-bot', createdAt: toString(datetime() - duration('P7D')),
|
||||
lastUsedAt: toString(datetime() - duration('PT30M')), disabled: false,
|
||||
expiresAt: toString(datetime() + duration('P365D'))
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
// API Key for Huey (user) — active, all his posts created via this key
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u4' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-huey-auto', name: 'Auto Publisher', keyPrefix: 'oak_hueyAUT',
|
||||
keyHash: 'seed-hash-huey-auto', createdAt: toString(datetime() - duration('P60D')),
|
||||
lastUsedAt: toString(datetime() - duration('PT5M')), disabled: false
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
// API Key for Bob (moderator) — active
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u2' })
|
||||
CREATE (u)-[:HAS_API_KEY]->(k:ApiKey {
|
||||
id: 'ak-bob-monitor', name: 'Monitoring', keyPrefix: 'oak_bobMON',
|
||||
keyHash: 'seed-hash-bob-monitor', createdAt: toString(datetime()),
|
||||
disabled: false
|
||||
})`,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
// Create some posts via API key (Peter's CI Bot)
|
||||
authenticatedUser = { ...(await peterLustig.toJson()), apiKeyId: 'ak-peter-ci' }
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'p-api-1',
|
||||
title: 'Automated Daily Report',
|
||||
content: 'This post was created automatically via API key by the CI Bot.',
|
||||
categoryIds: ['cat16'],
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'p-api-2',
|
||||
title: 'Weekly Statistics Summary',
|
||||
content: 'Automated weekly summary of community statistics.',
|
||||
categoryIds: ['cat9'],
|
||||
},
|
||||
})
|
||||
|
||||
// Jenny's Weather Bot creates a post
|
||||
authenticatedUser = { ...(await jennyRostock.toJson()), apiKeyId: 'ak-jenny-bot' }
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
variables: {
|
||||
id: 'p-api-3',
|
||||
title: 'Weather Report Paris',
|
||||
content: 'Sunny, 22°C. Perfect day for a walk along the Seine.',
|
||||
categoryIds: ['cat4'],
|
||||
},
|
||||
})
|
||||
// Jenny's bot also comments
|
||||
await mutate({
|
||||
mutation: CreateComment,
|
||||
variables: {
|
||||
id: 'c-api-1',
|
||||
postId: 'p-api-1',
|
||||
content: 'Automated cross-reference: See also the weather report.',
|
||||
},
|
||||
})
|
||||
|
||||
authenticatedUser = null
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'invitecodes')
|
||||
|
||||
// Peter invited the core users: Jenny, Bob, Huey
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'PETER1', comment: 'For Jenny' },
|
||||
{ generatedBy: peterLustig },
|
||||
{
|
||||
code: 'ABCDEF',
|
||||
},
|
||||
{
|
||||
generatedBy: jennyRostock,
|
||||
},
|
||||
)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'PETER2', comment: 'For Bob' },
|
||||
{ generatedBy: peterLustig },
|
||||
)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'PETER3', comment: 'For Huey' },
|
||||
{ generatedBy: peterLustig },
|
||||
)
|
||||
|
||||
// Jenny invited Dewey, Louie, Dagobert
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'JENNY1', comment: 'For Dewey' },
|
||||
{ generatedBy: jennyRostock },
|
||||
)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'JENNY2', comment: 'For Louie' },
|
||||
{ generatedBy: jennyRostock },
|
||||
)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'JENNY3', comment: 'For Dagobert' },
|
||||
{ generatedBy: jennyRostock },
|
||||
)
|
||||
// Jenny's shared code (used by additional users)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'ABCDEF', comment: 'Share link' },
|
||||
{ generatedBy: jennyRostock },
|
||||
)
|
||||
// Jenny's unused code (still active)
|
||||
await Factory.build('inviteCode', { code: 'JNEW01' }, { generatedBy: jennyRostock })
|
||||
// Jenny's invalidated code (was used once, then deactivated)
|
||||
await Factory.build(
|
||||
'inviteCode',
|
||||
{ code: 'JENNY0', comment: 'Old link', expiresAt: new Date().toISOString() },
|
||||
{ generatedBy: jennyRostock },
|
||||
)
|
||||
// Jenny total: JENNY1, JENNY2, JENNY3, ABCDEF, JNEW01 (5 active) + JENNY0 (1 expired) = 6 codes
|
||||
|
||||
// Create REDEEMED and INVITED relationships via Cypher
|
||||
const inviteSession = database.driver.session()
|
||||
try {
|
||||
await inviteSession.writeTransaction((txc) =>
|
||||
txc.run(`
|
||||
// Peter's invitations
|
||||
MATCH (jenny:User {id: 'u3'}), (code1:InviteCode {code: 'PETER1'}), (peter:User {id: 'u1'})
|
||||
MERGE (jenny)-[:REDEEMED {createdAt: toString(datetime())}]->(code1)
|
||||
MERGE (peter)-[:INVITED {createdAt: toString(datetime())}]->(jenny)
|
||||
MERGE (jenny)-[:FOLLOWS {createdAt: toString(datetime())}]->(peter)
|
||||
MERGE (peter)-[:FOLLOWS {createdAt: toString(datetime())}]->(jenny)
|
||||
WITH 1 AS dummy
|
||||
MATCH (bob:User {id: 'u2'}), (code2:InviteCode {code: 'PETER2'}), (peter:User {id: 'u1'})
|
||||
MERGE (bob)-[:REDEEMED {createdAt: toString(datetime())}]->(code2)
|
||||
MERGE (peter)-[:INVITED {createdAt: toString(datetime())}]->(bob)
|
||||
MERGE (bob)-[:FOLLOWS {createdAt: toString(datetime())}]->(peter)
|
||||
MERGE (peter)-[:FOLLOWS {createdAt: toString(datetime())}]->(bob)
|
||||
WITH 1 AS dummy
|
||||
MATCH (huey:User {id: 'u4'}), (code3:InviteCode {code: 'PETER3'}), (peter:User {id: 'u1'})
|
||||
MERGE (huey)-[:REDEEMED {createdAt: toString(datetime())}]->(code3)
|
||||
MERGE (peter)-[:INVITED {createdAt: toString(datetime())}]->(huey)
|
||||
MERGE (huey)-[:FOLLOWS {createdAt: toString(datetime())}]->(peter)
|
||||
MERGE (peter)-[:FOLLOWS {createdAt: toString(datetime())}]->(huey)
|
||||
WITH 1 AS dummy
|
||||
// Jenny's invitations
|
||||
MATCH (dewey:User {id: 'u5'}), (code4:InviteCode {code: 'JENNY1'}), (jenny:User {id: 'u3'})
|
||||
MERGE (dewey)-[:REDEEMED {createdAt: toString(datetime())}]->(code4)
|
||||
MERGE (jenny)-[:INVITED {createdAt: toString(datetime())}]->(dewey)
|
||||
MERGE (dewey)-[:FOLLOWS {createdAt: toString(datetime())}]->(jenny)
|
||||
MERGE (jenny)-[:FOLLOWS {createdAt: toString(datetime())}]->(dewey)
|
||||
WITH 1 AS dummy
|
||||
MATCH (louie:User {id: 'u6'}), (code5:InviteCode {code: 'JENNY2'}), (jenny:User {id: 'u3'})
|
||||
MERGE (louie)-[:REDEEMED {createdAt: toString(datetime())}]->(code5)
|
||||
MERGE (jenny)-[:INVITED {createdAt: toString(datetime())}]->(louie)
|
||||
MERGE (louie)-[:FOLLOWS {createdAt: toString(datetime())}]->(jenny)
|
||||
MERGE (jenny)-[:FOLLOWS {createdAt: toString(datetime())}]->(louie)
|
||||
WITH 1 AS dummy
|
||||
MATCH (dagobert:User {id: 'u7'}), (code6:InviteCode {code: 'JENNY3'}), (jenny:User {id: 'u3'})
|
||||
MERGE (dagobert)-[:REDEEMED {createdAt: toString(datetime())}]->(code6)
|
||||
MERGE (jenny)-[:INVITED {createdAt: toString(datetime())}]->(dagobert)
|
||||
MERGE (dagobert)-[:FOLLOWS {createdAt: toString(datetime())}]->(jenny)
|
||||
MERGE (jenny)-[:FOLLOWS {createdAt: toString(datetime())}]->(dagobert)
|
||||
`),
|
||||
)
|
||||
} finally {
|
||||
await inviteSession.close()
|
||||
}
|
||||
|
||||
authenticatedUser = await louie.toJson()
|
||||
const mention1 =
|
||||
@ -1051,7 +848,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
'The new physics of <a class="hashtag" data-hashtag-id="QuantenFlussTheorie" href="/?hashtag=QuantenFlussTheorie">#QuantenFlussTheorie</a> can explain <a class="hashtag" data-hashtag-id="QuantumGravity" href="/?hashtag=QuantumGravity">#QuantumGravity</a>! <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> got that already. ;-)'
|
||||
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p2',
|
||||
title: `Nature Philosophy Yoga`,
|
||||
@ -1060,7 +857,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p7',
|
||||
title: 'This is post #7',
|
||||
@ -1069,7 +866,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p8',
|
||||
title: `Quantum Flow Theory explains Quantum Gravity`,
|
||||
@ -1078,7 +875,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreatePost,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p12',
|
||||
title: 'This is post #12',
|
||||
@ -1102,7 +899,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
const mentionInComment2 =
|
||||
'Did <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> tell you?'
|
||||
await mutate({
|
||||
mutation: CreateComment,
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
id: 'c4',
|
||||
postId: 'p2',
|
||||
@ -1110,7 +907,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreateComment,
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
id: 'c4-1',
|
||||
postId: 'p2',
|
||||
@ -1118,7 +915,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: CreateComment,
|
||||
mutation: createCommentMutation,
|
||||
variables: {
|
||||
postId: 'p14',
|
||||
content: faker.lorem.paragraph(),
|
||||
@ -1413,164 +1210,26 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'users additional with map locations around Zwingenberg')
|
||||
|
||||
// Region Hessen (Mapbox-compatible hierarchy: place -> region -> country)
|
||||
const Hessen = await Factory.build('location', {
|
||||
id: 'region.8967011281068080',
|
||||
name: 'Hessen',
|
||||
type: 'region',
|
||||
lng: 8.6528,
|
||||
lat: 50.6521,
|
||||
nameDE: 'Hessen',
|
||||
nameEN: 'Hesse',
|
||||
nameES: 'Hesse',
|
||||
nameFR: 'Hesse',
|
||||
nameIT: 'Assia',
|
||||
namePT: 'Hessen',
|
||||
nameNL: 'Hessen',
|
||||
namePL: 'Hesja',
|
||||
nameRU: 'Гессен',
|
||||
})
|
||||
await Hessen.relateTo(Germany, 'isIn')
|
||||
|
||||
// 50 villages around Zwingenberg (64673), Zwingenberg excluded
|
||||
// Mapbox-compatible: type 'place', realistic IDs
|
||||
const zwingenbergVillages = [
|
||||
// Bergstraße (west)
|
||||
{ id: 'place.8652241', name: 'Alsbach-Hähnlein', lat: 49.7389, lng: 8.6331 },
|
||||
{ id: 'place.8652242', name: 'Bickenbach', lat: 49.7567, lng: 8.6178 },
|
||||
{ id: 'place.8652243', name: 'Seeheim-Jugenheim', lat: 49.7631, lng: 8.6506 },
|
||||
{ id: 'place.8652244', name: 'Bensheim', lat: 49.6812, lng: 8.6167 },
|
||||
{ id: 'place.8652245', name: 'Auerbach', lat: 49.7053, lng: 8.6389 },
|
||||
{ id: 'place.8652246', name: 'Heppenheim', lat: 49.6428, lng: 8.6392 },
|
||||
{ id: 'place.8652247', name: 'Lorsch', lat: 49.6539, lng: 8.5678 },
|
||||
{ id: 'place.8652248', name: 'Einhausen', lat: 49.6775, lng: 8.5578 },
|
||||
{ id: 'place.8652249', name: 'Gernsheim', lat: 49.7528, lng: 8.4906 },
|
||||
{ id: 'place.8652250', name: 'Pfungstadt', lat: 49.8056, lng: 8.6042 },
|
||||
// Odenwald (east)
|
||||
{ id: 'place.8652251', name: 'Reichenbach', lat: 49.725, lng: 8.67 },
|
||||
{ id: 'place.8652252', name: 'Lautertal', lat: 49.7253, lng: 8.6914 },
|
||||
{ id: 'place.8652253', name: 'Lindenfels', lat: 49.6836, lng: 8.7781 },
|
||||
{ id: 'place.8652254', name: 'Modautal', lat: 49.7736, lng: 8.7258 },
|
||||
{ id: 'place.8652255', name: 'Mühltal', lat: 49.8003, lng: 8.6917 },
|
||||
{ id: 'place.8652256', name: 'Ober-Ramstadt', lat: 49.8306, lng: 8.7486 },
|
||||
{ id: 'place.8652257', name: 'Reinheim', lat: 49.8289, lng: 8.8356 },
|
||||
{ id: 'place.8652258', name: 'Groß-Bieberau', lat: 49.7906, lng: 8.8281 },
|
||||
{ id: 'place.8652259', name: 'Fränkisch-Crumbach', lat: 49.745, lng: 8.8444 },
|
||||
{ id: 'place.8652260', name: 'Brensbach', lat: 49.7742, lng: 8.8819 },
|
||||
// Ried (west/southwest)
|
||||
{ id: 'place.8652261', name: 'Bürstadt', lat: 49.6433, lng: 8.4506 },
|
||||
{ id: 'place.8652262', name: 'Lampertheim', lat: 49.5978, lng: 8.47 },
|
||||
{ id: 'place.8652263', name: 'Biblis', lat: 49.6878, lng: 8.4531 },
|
||||
{ id: 'place.8652264', name: 'Groß-Rohrheim', lat: 49.7228, lng: 8.4822 },
|
||||
{ id: 'place.8652265', name: 'Riedstadt', lat: 49.835, lng: 8.4944 },
|
||||
{ id: 'place.8652266', name: 'Stockstadt am Rhein', lat: 49.8094, lng: 8.4656 },
|
||||
{ id: 'place.8652267', name: 'Biebesheim', lat: 49.7806, lng: 8.4672 },
|
||||
{ id: 'place.8652268', name: 'Trebur', lat: 49.9211, lng: 8.4081 },
|
||||
{ id: 'place.8652269', name: 'Nauheim', lat: 49.9456, lng: 8.4494 },
|
||||
{ id: 'place.8652270', name: 'Griesheim', lat: 49.8619, lng: 8.5722 },
|
||||
// Darmstadt area (north)
|
||||
{ id: 'place.8652271', name: 'Roßdorf', lat: 49.8572, lng: 8.7578 },
|
||||
{ id: 'place.8652272', name: 'Messel', lat: 49.9333, lng: 8.75 },
|
||||
{ id: 'place.8652273', name: 'Eppertshausen', lat: 49.95, lng: 8.85 },
|
||||
{ id: 'place.8652274', name: 'Münster', lat: 49.9253, lng: 8.8653 },
|
||||
{ id: 'place.8652275', name: 'Dieburg', lat: 49.8983, lng: 8.8467 },
|
||||
{ id: 'place.8652276', name: 'Babenhausen', lat: 49.965, lng: 8.9511 },
|
||||
{ id: 'place.8652277', name: 'Schaafheim', lat: 49.9244, lng: 8.9703 },
|
||||
{ id: 'place.8652278', name: 'Groß-Umstadt', lat: 49.8667, lng: 8.9333 },
|
||||
{ id: 'place.8652279', name: 'Otzberg', lat: 49.82, lng: 8.91 },
|
||||
{ id: 'place.8652280', name: 'Höchst im Odenwald', lat: 49.7994, lng: 8.9986 },
|
||||
// Further south
|
||||
{ id: 'place.8652281', name: 'Mörlenbach', lat: 49.5969, lng: 8.7378 },
|
||||
{ id: 'place.8652282', name: 'Rimbach', lat: 49.6256, lng: 8.7611 },
|
||||
{ id: 'place.8652283', name: 'Fürth', lat: 49.6522, lng: 8.7789 },
|
||||
{ id: 'place.8652284', name: 'Grasellenbach', lat: 49.6353, lng: 8.8531 },
|
||||
{ id: 'place.8652285', name: 'Wald-Michelbach', lat: 49.57, lng: 8.83 },
|
||||
{ id: 'place.8652286', name: 'Abtsteinach', lat: 49.5536, lng: 8.78 },
|
||||
{ id: 'place.8652287', name: 'Gorxheimertal', lat: 49.5322, lng: 8.7322 },
|
||||
{ id: 'place.8652288', name: 'Viernheim', lat: 49.5403, lng: 8.5783 },
|
||||
{ id: 'place.8652289', name: 'Weinheim', lat: 49.5489, lng: 8.6639 },
|
||||
{ id: 'place.8652290', name: 'Hemsbach', lat: 49.59, lng: 8.65 },
|
||||
]
|
||||
|
||||
// Create village location nodes (one per village, shared by all users in that village)
|
||||
const villageLocationNodes: (typeof Hamburg)[] = []
|
||||
for (const village of zwingenbergVillages) {
|
||||
const location = await Factory.build('location', {
|
||||
id: village.id,
|
||||
name: village.name,
|
||||
type: 'place',
|
||||
lng: village.lng,
|
||||
lat: village.lat,
|
||||
nameDE: village.name,
|
||||
nameEN: village.name,
|
||||
nameES: village.name,
|
||||
nameFR: village.name,
|
||||
nameIT: village.name,
|
||||
namePT: village.name,
|
||||
nameNL: village.name,
|
||||
namePL: village.name,
|
||||
nameRU: village.name,
|
||||
})
|
||||
await location.relateTo(Hessen, 'isIn')
|
||||
villageLocationNodes.push(location)
|
||||
}
|
||||
|
||||
// Create 1000 additional users with locations assigned during creation
|
||||
console.log('seed', 'users additional')
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const additionalUsers: any[] = []
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
if (i % 100 === 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', `additional users ${i}/1000`)
|
||||
}
|
||||
const user = await Factory.build('user')
|
||||
await jennyRostock.relateTo(user, 'following')
|
||||
await user.relateTo(jennyRostock, 'following')
|
||||
// Assign village location (round-robin across 50 villages = ~20 users per village)
|
||||
await user.relateTo(villageLocationNodes[i % villageLocationNodes.length], 'isIn')
|
||||
additionalUsers.push(user)
|
||||
|
||||
const userObj = await user.toJson()
|
||||
authenticatedUser = userObj
|
||||
|
||||
await mutate({
|
||||
mutation: JoinGroup,
|
||||
mutation: joinGroupMutation(),
|
||||
variables: {
|
||||
groupId: 'g2',
|
||||
userId: userObj.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'additional users 1000/1000 done')
|
||||
|
||||
// Jenny's first 99 additional users all redeemed code ABCDEF
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'invite redemptions for additional users')
|
||||
const jennyInviteSession = database.driver.session()
|
||||
try {
|
||||
for (let i = 0; i < Math.min(99, additionalUsers.length); i++) {
|
||||
// eslint-disable-next-line security/detect-object-injection
|
||||
const userObj = await additionalUsers[i].toJson()
|
||||
const userId = userObj.id as string
|
||||
await jennyInviteSession.writeTransaction((txc) =>
|
||||
txc.run(
|
||||
`
|
||||
MATCH (user:User {id: $userId}), (inviteCode:InviteCode {code: 'ABCDEF'}), (jenny:User {id: 'u3'})
|
||||
MERGE (user)-[:REDEEMED {createdAt: toString(datetime())}]->(inviteCode)
|
||||
MERGE (jenny)-[:INVITED {createdAt: toString(datetime())}]->(user)
|
||||
MERGE (user)-[:FOLLOWS {createdAt: toString(datetime())}]->(jenny)
|
||||
MERGE (jenny)-[:FOLLOWS {createdAt: toString(datetime())}]->(user)
|
||||
`,
|
||||
{ userId },
|
||||
),
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
await jennyInviteSession.close()
|
||||
}
|
||||
|
||||
// Jenny users
|
||||
for (let i = 0; i < 30; i++) {
|
||||
@ -1865,151 +1524,91 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
)
|
||||
}
|
||||
|
||||
// Mark all Huey's posts and comments as created via API key
|
||||
await database.write({
|
||||
query: `MATCH (u:User { id: 'u4' })-[:WROTE]->(content)
|
||||
WHERE content:Post OR content:Comment
|
||||
SET content.createdByApiKey = 'ak-huey-auto'`,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
await Factory.build('donations')
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'chat')
|
||||
// DM chat: Huey <-> Peter (first message creates room via userId)
|
||||
authenticatedUser = await huey.toJson()
|
||||
const { data: firstMsgHueyPeter } = await mutate({
|
||||
mutation: CreateMessage,
|
||||
const { data: roomHueyPeter } = await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: (await peterLustig.toJson()).id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
const roomIdHueyPeter = firstMsgHueyPeter?.CreateMessage.room.id
|
||||
|
||||
for (let i = 0; i < 29; i++) {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
authenticatedUser = await huey.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: roomHueyPeter?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
authenticatedUser = await peterLustig.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: roomIdHueyPeter, content: faker.lorem.sentence() },
|
||||
})
|
||||
authenticatedUser = await huey.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: roomIdHueyPeter, content: faker.lorem.sentence() },
|
||||
})
|
||||
}
|
||||
|
||||
// DM chat: Huey <-> Jenny (first message creates room via userId)
|
||||
authenticatedUser = await huey.toJson()
|
||||
const { data: firstMsgHueyJenny } = await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
userId: (await jennyRostock.toJson()).id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
const roomIdHueyJenny = firstMsgHueyJenny?.CreateMessage.room.id
|
||||
|
||||
for (let i = 0; i < 999; i++) {
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: roomIdHueyJenny, content: faker.lorem.sentence() },
|
||||
})
|
||||
authenticatedUser = await huey.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: roomIdHueyJenny, content: faker.lorem.sentence() },
|
||||
})
|
||||
}
|
||||
|
||||
// DM chats: Jenny <-> additionalUsers
|
||||
for (const user of additionalUsers.slice(0, 99)) {
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
const { data: firstMsg } = await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
userId: (await user.toJson()).id,
|
||||
roomId: roomHueyPeter?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
const dmRoomId = firstMsg?.CreateMessage.room.id
|
||||
}
|
||||
|
||||
for (let i = 0; i < 28; i++) {
|
||||
authenticatedUser = await user.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: dmRoomId, content: faker.lorem.sentence() },
|
||||
})
|
||||
authenticatedUser = await huey.toJson()
|
||||
const { data: roomHueyJenny } = await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: (await jennyRostock.toJson()).id,
|
||||
},
|
||||
})
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
authenticatedUser = await huey.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: roomHueyJenny?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: roomHueyJenny?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for (const user of additionalUsers.slice(0, 99)) {
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
const { data: room } = await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: (await user.toJson()).id,
|
||||
},
|
||||
})
|
||||
|
||||
for (let i = 0; i < 29; i++) {
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: { roomId: dmRoomId, content: faker.lorem.sentence() },
|
||||
variables: {
|
||||
roomId: room?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
authenticatedUser = await user.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: room?.CreateRoom.id,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('seed', 'group chat')
|
||||
|
||||
// Group g1 (School For Citizens) - active members: Jenny(owner/creator), Peter(usual), Bob(usual), Dewey(admin), Louie(owner), Dagobert(usual)
|
||||
// Create group room as Jenny (creator of g1)
|
||||
authenticatedUser = await jennyRostock.toJson()
|
||||
const { data: roomG1 } = await mutate({
|
||||
mutation: CreateGroupRoom,
|
||||
variables: { groupId: 'g1' },
|
||||
})
|
||||
const g1RoomId = roomG1?.CreateGroupRoom.id
|
||||
|
||||
// Members have a conversation
|
||||
const g1Members = [
|
||||
{ user: jennyRostock, name: 'Jenny' },
|
||||
{ user: peterLustig, name: 'Peter' },
|
||||
{ user: dewey, name: 'Dewey' },
|
||||
{ user: louie, name: 'Louie' },
|
||||
]
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const member = g1Members[i % g1Members.length]
|
||||
authenticatedUser = await member.user.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: g1RoomId,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Group g2 (Yoga Practice) - active members: Bob(owner/creator), Jenny(usual), Dewey(admin), Louie(usual), Dagobert(usual) - Huey is pending
|
||||
authenticatedUser = await bobDerBaumeister.toJson()
|
||||
const { data: roomG2 } = await mutate({
|
||||
mutation: CreateGroupRoom,
|
||||
variables: { groupId: 'g2' },
|
||||
})
|
||||
const g2RoomId = roomG2?.CreateGroupRoom.id
|
||||
|
||||
const g2Members = [
|
||||
{ user: bobDerBaumeister, name: 'Bob' },
|
||||
{ user: jennyRostock, name: 'Jenny' },
|
||||
{ user: dewey, name: 'Dewey' },
|
||||
{ user: louie, name: 'Louie' },
|
||||
{ user: dagobert, name: 'Dagobert' },
|
||||
]
|
||||
for (let i = 0; i < 25; i++) {
|
||||
const member = g2Members[i % g2Members.length]
|
||||
authenticatedUser = await member.user.toJson()
|
||||
await mutate({
|
||||
mutation: CreateMessage,
|
||||
variables: {
|
||||
roomId: g2RoomId,
|
||||
content: faker.lorem.sentence(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Group g0 (Investigative Journalism) - intentionally NO chat seeded
|
||||
} catch (err) {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.error(err)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface CategoryDbProperties {
|
||||
createdAt: string
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface CommentDbProperties {
|
||||
content: string
|
||||
contentExcerpt: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface EmailAddressDbProperties {
|
||||
createdAt: string
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { PostDbProperties } from './Post'
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
import { PostDbProperties } from './Post'
|
||||
|
||||
export interface EventDbProperties extends PostDbProperties {
|
||||
eventIsOnline: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface GroupDbProperties {
|
||||
about: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface ImageDbProperties {
|
||||
alt: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface InviteCodeDbProperties {
|
||||
code: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface LocationDbProperties {
|
||||
id: string
|
||||
@ -14,7 +14,6 @@ export interface LocationDbProperties {
|
||||
namePL: string
|
||||
namePT: string
|
||||
nameRU: string
|
||||
nameSQ: string
|
||||
type: string
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface MessageDbProperties {
|
||||
content: string
|
||||
@ -7,6 +7,7 @@ export interface MessageDbProperties {
|
||||
id: string
|
||||
indexId: number
|
||||
saved: boolean
|
||||
seen: boolean
|
||||
}
|
||||
|
||||
export type Message = Node<Integer, MessageDbProperties>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface PostDbProperties {
|
||||
clickedCount: number
|
||||
content: string
|
||||
contentExcerpt: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface ReportDbProperties {
|
||||
closed: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface TagDbProperties {
|
||||
deleted: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Integer, Node } from 'neo4j-driver'
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface UserDbProperties {
|
||||
allowEmbedIframes: boolean
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendChatMessageMail English chat_message template 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendChatMessageMail English chat_message template 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendChatMessageMail English chat_message template 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -100,13 +94,13 @@ footer {
|
||||
<p>you have received a new chat message from <a class="user" href="http://webapp:3000/profile/chatSender/chatsender">chatSender</a>.
|
||||
</p><a class="button" href="http://webapp:3000/chat">Show Chat</a>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p><br>
|
||||
<p class="no-margin-top-bottom">PS: If you don't want to receive e-mails anymore, change your <a class="settings" href="http://webapp:3000/settings/notifications">notification settings</a>!</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p><br>
|
||||
<p>PS: If you don't want to receive e-mails anymore, change your <a class="settings" href="http://webapp:3000/settings/notifications">notification settings</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -135,20 +129,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "chatReceiver",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendChatMessageMail German chat_message template 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -158,10 +146,9 @@ exports[`sendChatMessageMail German chat_message template 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -169,10 +156,9 @@ exports[`sendChatMessageMail German chat_message template 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -183,17 +169,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -204,9 +184,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -214,6 +195,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -222,7 +207,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -242,13 +227,13 @@ footer {
|
||||
<p>du hast eine neue Chat-Nachricht von <a class="user" href="http://webapp:3000/profile/chatSender/chatsender">chatSender</a> erhalten.
|
||||
</p><a class="button" href="http://webapp:3000/chat">Chat anzeigen</a>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p><br>
|
||||
<p class="no-margin-top-bottom">PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine <a class="settings" href="http://webapp:3000/settings/notifications">Benachrichtigungseinstellungen</a>!</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p><br>
|
||||
<p>PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine <a class="settings" href="http://webapp:3000/settings/notifications">Benachrichtigungseinstellungen</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -277,9 +262,6 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "chatReceiver",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendEmailVerification English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendEmailVerification English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendEmailVerification English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -101,12 +95,12 @@ footer {
|
||||
<p>If you don't want to change your e-mail address feel free to ignore this message. </p>
|
||||
<p>If the above button doesn't work, you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -138,20 +132,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "User",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendEmailVerification German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -161,10 +149,9 @@ exports[`sendEmailVerification German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -172,10 +159,9 @@ exports[`sendEmailVerification German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -186,17 +172,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -207,9 +187,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -217,6 +198,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -225,7 +210,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -246,12 +231,12 @@ footer {
|
||||
<p>Falls du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese Nachricht einfach ignorieren. </p>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -283,9 +268,6 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "User",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendRegistrationMail with invite code English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendRegistrationMail with invite code English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendRegistrationMail with invite code English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -97,19 +91,19 @@ footer {
|
||||
<h2>Welcome to ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:</p><a class="button" href="http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&inviteCode=welcome&method=invite-code">Confirm your e-mail address</a>
|
||||
<p>Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:</p><a class="button" href="http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code">Confirm your e-mail address</a>
|
||||
<p>If the above button doesn't work, you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<p>However, this only works if you have registered through our website.</p>
|
||||
<p>If you didn't sign up for <a>ocelot.social</a> we recommend you to check it out! It's a social network from people for people who want to connect and change the world together.
|
||||
</p>
|
||||
<p>PS: If you ignore this e-mail we will not create an account for you. ;)</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -125,7 +119,7 @@ just one tiny step missing before we can start shaping the world together …
|
||||
Please confirm your e-mail address by clicking the button below:
|
||||
|
||||
Confirm your e-mail address
|
||||
[http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&inviteCode=welcome&method=invite-code]
|
||||
[http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code]
|
||||
|
||||
If the above button doesn't work, you can also copy the following code into your
|
||||
browser window: 123456
|
||||
@ -147,20 +141,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail with invite code German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -170,10 +158,9 @@ exports[`sendRegistrationMail with invite code German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -181,10 +168,9 @@ exports[`sendRegistrationMail with invite code German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -195,17 +181,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -216,9 +196,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -226,6 +207,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -234,7 +219,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -251,19 +236,19 @@ footer {
|
||||
<h2>Willkommen bei ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige deine E-Mail Adresse:</p><a class="button" href="http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&inviteCode=welcome&method=invite-code">Bestätige deine E-Mail Adresse</a>
|
||||
<p>Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige deine E-Mail Adresse:</p><a class="button" href="http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code">Bestätige deine E-Mail Adresse</a>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<p>Das funktioniert allerdings nur, wenn du dich über unsere Website registriert hast.</p>
|
||||
<p>Falls du dich nicht selbst bei <a>ocelot.social</a> angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.
|
||||
</p>
|
||||
<p>PS: Wenn du keinen Account bei uns möchtest, kannst du diese E-Mail einfach ignorieren. ;)</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -279,7 +264,7 @@ fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können
|
||||
… Bitte bestätige deine E-Mail Adresse:
|
||||
|
||||
Bestätige deine E-Mail Adresse
|
||||
[http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&inviteCode=welcome&method=invite-code]
|
||||
[http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code]
|
||||
|
||||
Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in
|
||||
dein Browserfenster kopieren: 123456
|
||||
@ -302,20 +287,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail without invite code English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -325,10 +304,9 @@ exports[`sendRegistrationMail without invite code English renders correctly 1`]
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -336,10 +314,9 @@ exports[`sendRegistrationMail without invite code English renders correctly 1`]
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -350,17 +327,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -371,9 +342,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -381,6 +353,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -389,7 +365,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -406,19 +382,19 @@ footer {
|
||||
<h2>Welcome to ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:</p><a class="button" href="http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&method=invite-mail">Confirm your e-mail address</a>
|
||||
<p>Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:</p><a class="button" href="http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail">Confirm your e-mail address</a>
|
||||
<p>If the above button doesn't work, you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<p>However, this only works if you have registered through our website.</p>
|
||||
<p>If you didn't sign up for <a>ocelot.social</a> we recommend you to check it out! It's a social network from people for people who want to connect and change the world together.
|
||||
</p>
|
||||
<p>PS: If you ignore this e-mail we will not create an account for you. ;)</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -434,7 +410,7 @@ just one tiny step missing before we can start shaping the world together …
|
||||
Please confirm your e-mail address by clicking the button below:
|
||||
|
||||
Confirm your e-mail address
|
||||
[http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&method=invite-mail]
|
||||
[http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail]
|
||||
|
||||
If the above button doesn't work, you can also copy the following code into your
|
||||
browser window: 123456
|
||||
@ -456,20 +432,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail without invite code German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -479,10 +449,9 @@ exports[`sendRegistrationMail without invite code German renders correctly 1`] =
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -490,10 +459,9 @@ exports[`sendRegistrationMail without invite code German renders correctly 1`] =
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -504,17 +472,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -525,9 +487,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -535,6 +498,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -543,7 +510,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -560,19 +527,19 @@ footer {
|
||||
<h2>Willkommen bei ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige deine E-Mail Adresse:</p><a class="button" href="http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&method=invite-mail">Bestätige deine E-Mail Adresse</a>
|
||||
<p>Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige deine E-Mail Adresse:</p><a class="button" href="http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail">Bestätige deine E-Mail Adresse</a>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<p>Das funktioniert allerdings nur, wenn du dich über unsere Website registriert hast.</p>
|
||||
<p>Falls du dich nicht selbst bei <a>ocelot.social</a> angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.
|
||||
</p>
|
||||
<p>PS: Wenn du keinen Account bei uns möchtest, kannst du diese E-Mail einfach ignorieren. ;)</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -588,7 +555,7 @@ fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können
|
||||
… Bitte bestätige deine E-Mail Adresse:
|
||||
|
||||
Bestätige deine E-Mail Adresse
|
||||
[http://webapp:3000/registration?email=moderator%40example.org&nonce=123456&method=invite-mail]
|
||||
[http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail]
|
||||
|
||||
Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in
|
||||
dein Browserfenster kopieren: 123456
|
||||
@ -611,9 +578,6 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendResetPasswordMail English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendResetPasswordMail English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendResetPasswordMail English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -101,12 +95,12 @@ footer {
|
||||
<p>If you didn't request a new password feel free to ignore this e-mail.</p>
|
||||
<p>If the above button doesn't work you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -137,20 +131,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendResetPasswordMail German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -160,10 +148,9 @@ exports[`sendResetPasswordMail German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -171,10 +158,9 @@ exports[`sendResetPasswordMail German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -185,17 +171,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -206,9 +186,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -216,6 +197,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -224,7 +209,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -245,12 +230,12 @@ footer {
|
||||
<p>Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.</p>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -282,9 +267,6 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendWrongEmail English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendWrongEmail English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendWrongEmail English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -101,12 +95,12 @@ footer {
|
||||
<p>If you don't have an account at <a>ocelot.social</a> yet or if you didn't want to reset your password, please ignore this e-mail.
|
||||
</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -135,20 +129,14 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendWrongEmail German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -158,10 +146,9 @@ exports[`sendWrongEmail German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -169,10 +156,9 @@ exports[`sendWrongEmail German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -183,17 +169,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -204,9 +184,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -214,6 +195,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -222,7 +207,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -243,12 +228,12 @@ footer {
|
||||
<p>Wenn du noch keinen Account bei <a>ocelot.social</a> hast oder dein Password gar nicht ändern willst, kannst du diese E-Mail einfach ignorieren!
|
||||
</p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:devops@ocelot.social">devops@ocelot.social</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -277,9 +262,6 @@ devops@ocelot.social [devops@ocelot.social]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "moderator@example.org",
|
||||
"name": "Bob &"?@\\ Baumeister",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendResetPasswordMail with support English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -16,10 +13,9 @@ exports[`sendResetPasswordMail with support English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -27,10 +23,9 @@ exports[`sendResetPasswordMail with support English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -41,17 +36,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -62,9 +51,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -72,6 +62,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -80,7 +74,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -101,12 +95,12 @@ footer {
|
||||
<p>If you didn't request a new password feel free to ignore this e-mail.</p>
|
||||
<p>If the above button doesn't work you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">If you have questions or problems, feel free to contact our support: <a href="mailto:support@example.org">support@example.org</a></p>
|
||||
<p>If you have questions or problems, feel free to contact our support: <a href="mailto:support@example.org">support@example.org</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -137,20 +131,14 @@ support@example.org [support@example.org]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendResetPasswordMail with support German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -160,10 +148,9 @@ exports[`sendResetPasswordMail with support German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -171,10 +158,9 @@ exports[`sendResetPasswordMail with support German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -185,17 +171,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -206,9 +186,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -216,6 +197,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -224,7 +209,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -245,12 +230,12 @@ footer {
|
||||
<p>Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.</p>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="support">
|
||||
<p class="no-margin-top-bottom">Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:support@example.org">support@example.org</a></p>
|
||||
<p>Wenn du Fragen oder Probleme hast, kannst du dich gerne an den Support wenden: <a href="mailto:support@example.org">support@example.org</a></p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
@ -282,20 +267,14 @@ support@example.org [support@example.org]
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendResetPasswordMail without support English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -305,10 +284,9 @@ exports[`sendResetPasswordMail without support English renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -316,10 +294,9 @@ exports[`sendResetPasswordMail without support English renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -330,17 +307,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -351,9 +322,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -361,6 +333,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -369,7 +345,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -390,8 +366,8 @@ footer {
|
||||
<p>If you didn't request a new password feel free to ignore this e-mail.</p>
|
||||
<p>If the above button doesn't work you can also copy the following code into your browser window: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– The ocelot.social Team</p>
|
||||
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
@ -420,20 +396,14 @@ See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendResetPasswordMail without support German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": {
|
||||
"address": "devops@ocelot.social",
|
||||
"name": "ocelot.social",
|
||||
},
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
@ -443,10 +413,9 @@ exports[`sendResetPasswordMail without support German renders correctly 1`] = `
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
line-height: 24px;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
@ -454,10 +423,9 @@ exports[`sendResetPasswordMail without support German renders correctly 1`] = `
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 24px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@ -468,17 +436,11 @@ h2 {
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 30%;
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
p.no-margin-top-bottom {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -489,9 +451,10 @@ a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 9px 18px;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
@ -499,6 +462,10 @@ a.button {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
@ -507,7 +474,7 @@ a.button {
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
@ -528,8 +495,8 @@ footer {
|
||||
<p>Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.</p>
|
||||
<p>Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in dein Browserfenster kopieren: <span>123456</span></p>
|
||||
<div class="text-block">
|
||||
<p class="no-margin-top-bottom">Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p class="no-margin-top-bottom">– Dein ocelot.social Team</p>
|
||||
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
@ -559,9 +526,6 @@ Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": {
|
||||
"address": "user@example.org",
|
||||
"name": "Jenny Rostock",
|
||||
},
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
{
|
||||
"notification": "Notificación",
|
||||
"subjects": {
|
||||
"changedGroupMemberRole": "Rol en el grupo cambiado",
|
||||
"chatMessage": "Nuevo mensaje de chat",
|
||||
"commentedOnPost": "Nuevo comentario en una publicación",
|
||||
"followedUserPosted": "Nueva publicación de un usuario seguido",
|
||||
"mentionedInComment": "Mencionado en un comentario",
|
||||
"mentionedInPost": "Mencionado en una publicación",
|
||||
"newEmail": "Nueva dirección de correo electrónico",
|
||||
"removedUserFromGroup": "Eliminado del grupo",
|
||||
"postInGroup": "Nueva publicación en el grupo",
|
||||
"resetPassword": "Restablecer contraseña",
|
||||
"userJoinedGroup": "Un usuario se unió al grupo",
|
||||
"userLeftGroup": "Un usuario abandonó el grupo",
|
||||
"wrongEmail": "¿Correo electrónico incorrecto?"
|
||||
},
|
||||
"registration": {
|
||||
"introduction": "Gracias por registrarte – nos alegra tenerte con nosotros. Solo falta un pequeño paso antes de que podamos cambiar el mundo juntos … Por favor, confirma tu dirección de correo electrónico haciendo clic en el botón de abajo:",
|
||||
"codeHint": "Si el botón de arriba no funciona, también puedes copiar el siguiente código en la ventana de tu navegador: ",
|
||||
"codeHintException": "Sin embargo, esto solo funciona si te has registrado a través de nuestro sitio web.",
|
||||
"notYouStart": "Si no te has registrado en ",
|
||||
"notYouEnd": " te recomendamos que lo visites. Es una red social de personas para personas que quieren conectarse y cambiar el mundo juntas.",
|
||||
"ps": "PD: Si ignoras este correo electrónico, no crearemos una cuenta para ti. ;)"
|
||||
},
|
||||
"emailVerification": {
|
||||
"codeHint": "Si el botón de arriba no funciona, también puedes copiar el siguiente código en la ventana de tu navegador: ",
|
||||
"introduction": "¿Quieres cambiar tu dirección de correo electrónico? ¡No hay problema! Simplemente haz clic en el botón de abajo para verificar tu nueva dirección:",
|
||||
"doNotChange": "Si no quieres cambiar tu dirección de correo electrónico, simplemente ignora este mensaje. "
|
||||
},
|
||||
"support": "Si tienes preguntas o problemas, no dudes en contactar con nuestro soporte: ",
|
||||
"buttons": {
|
||||
"confirmEmail": "Confirma tu dirección de correo electrónico",
|
||||
"resetPassword": "Restablecer contraseña",
|
||||
"tryAgain": "Probar con otro correo electrónico",
|
||||
"verifyEmail": "Verificar dirección de correo electrónico",
|
||||
"viewChat": "Ver chat",
|
||||
"viewComment": "Ver comentario",
|
||||
"viewGroup": "Ver grupo",
|
||||
"viewPost": "Ver publicación"
|
||||
},
|
||||
"general": {
|
||||
"greeting": "Hola",
|
||||
"seeYou": "¡Hasta pronto en ",
|
||||
"yourTeam": "– El equipo de {team}",
|
||||
"settingsHint": "PD: Si no quieres recibir más correos electrónicos, cambia tu ",
|
||||
"settingsName": "configuración de notificaciones",
|
||||
"welcome": "Bienvenido a"
|
||||
},
|
||||
"resetPassword": {
|
||||
"codeHint": "Si el botón de arriba no funciona, también puedes copiar el siguiente código en la ventana de tu navegador: ",
|
||||
"ignore": "Si no has solicitado una nueva contraseña, simplemente ignora este correo electrónico.",
|
||||
"introduction": "¿Has olvidado tu contraseña? ¡No hay problema! Simplemente haz clic en el botón de abajo para restablecerla en las próximas 24 horas:"
|
||||
},
|
||||
"wrongEmail": {
|
||||
"ignoreEnd": " o si no querías restablecer tu contraseña, simplemente ignora este correo electrónico.",
|
||||
"ignoreStart": "Si no tienes una cuenta en ",
|
||||
"introduction": "Has solicitado un restablecimiento de contraseña, pero lamentablemente no hemos encontrado ninguna cuenta asociada a tu dirección de correo electrónico. ¿Te registraste quizás con otra dirección?"
|
||||
},
|
||||
"changedGroupMemberRole": "tu rol en el grupo «{groupName}» ha sido cambiado. Haz clic en el botón para ver este grupo:",
|
||||
"chatMessageStart": "has recibido un nuevo mensaje de chat de ",
|
||||
"chatMessageEnd": ".",
|
||||
"commentedOnPost": " ha comentado en una publicación que sigues con el título «{postTitle}». Haz clic en el botón para ver este comentario:",
|
||||
"followedUserPosted": ", un usuario al que sigues, ha escrito una nueva publicación con el título «{postTitle}». Haz clic en el botón para ver esta publicación:",
|
||||
"mentionedInComment": " te ha mencionado en un comentario de la publicación con el título «{postTitle}». Haz clic en el botón para ver este comentario:",
|
||||
"mentionedInPost": " te ha mencionado en una publicación con el título «{postTitle}». Haz clic en el botón para ver esta publicación:",
|
||||
"removedUserFromGroup": "has sido eliminado del grupo «{groupName}».",
|
||||
"postInGroup": "alguien ha escrito una nueva publicación con el título «{postTitle}» en uno de tus grupos. Haz clic en el botón para ver esta publicación:",
|
||||
"userJoinedGroup": " se ha unido al grupo «{groupName}». Haz clic en el botón para ver este grupo:",
|
||||
"userLeftGroup": " ha abandonado el grupo «{groupName}». Haz clic en el botón para ver este grupo:"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user