Merge branch 'master' of github.com:Ocelot-Social-Community/Ocelot-Social into brand-reformer-network-first-step
# Conflicts: # webapp/constants/links.js # webapp/locales/de.json # webapp/locales/en.json
9
.github/dependabot.yml
vendored
@ -57,19 +57,10 @@ updates:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "*apollo-server*"
|
||||
babel:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "@babel*"
|
||||
metascraper:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "metascraper*"
|
||||
typescript:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "ts*"
|
||||
- "*types?"
|
||||
|
||||
# webapp
|
||||
- package-ecosystem: docker
|
||||
|
||||
143
.github/workflows/test-e2e.yml
vendored
@ -3,8 +3,62 @@ name: ocelot.social end-to-end test CI
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
docker_preparation:
|
||||
name: Fullstack test preparation
|
||||
prepare_neo4j_image:
|
||||
name: Fullstack | prepare neo4j image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
|
||||
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
|
||||
|
||||
- name: Cache docker image
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-e2e-neo4j-cache
|
||||
|
||||
prepare_backend_image:
|
||||
name: Fullstack | prepare backend image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Build docker image
|
||||
run: |
|
||||
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
|
||||
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
|
||||
|
||||
- name: Cache docker image
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-e2e-backend-cache
|
||||
|
||||
prepare_webapp_image:
|
||||
name: Fullstack | prepare webapp image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Build docker image
|
||||
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@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-e2e-webapp-cache
|
||||
|
||||
prepare_cypress:
|
||||
name: Fullstack | prepare cypress
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@ -13,18 +67,7 @@ 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: Build docker images
|
||||
run: |
|
||||
mkdir /tmp/images
|
||||
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
|
||||
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/images/neo4j.tar
|
||||
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
|
||||
docker save "ocelotsocialnetwork/backend:test" > /tmp/images/backend.tar
|
||||
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
|
||||
docker save "ocelotsocialnetwork/webapp:test" > /tmp/images/webapp.tar
|
||||
cp backend/.env.test_e2e backend/.env
|
||||
|
||||
- name: Install cypress requirements
|
||||
run: |
|
||||
@ -35,21 +78,20 @@ jobs:
|
||||
cd ..
|
||||
yarn install
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache
|
||||
- name: Cache docker image
|
||||
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
/home/runner/.cache/Cypress
|
||||
/home/runner/work/Ocelot-Social/Ocelot-Social
|
||||
/tmp/images/
|
||||
key: ${{ github.run_id }}-e2e-preparation-cache
|
||||
key: ${{ github.run_id }}-e2e-cypress
|
||||
|
||||
fullstack_tests:
|
||||
name: Fullstack tests
|
||||
name: Fullstack | tests
|
||||
if: success()
|
||||
needs: docker_preparation
|
||||
needs: [prepare_neo4j_image, prepare_backend_image, prepare_webapp_image, prepare_cypress]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
jobs: 8
|
||||
@ -58,26 +100,42 @@ jobs:
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Restore cache
|
||||
- name: Restore cypress cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
/home/runner/.cache/Cypress
|
||||
/home/runner/work/Ocelot-Social/Ocelot-Social
|
||||
/tmp/images/
|
||||
key: ${{ github.run_id }}-e2e-preparation-cache
|
||||
fail-on-cache-miss: true
|
||||
key: ${{ github.run_id }}-e2e-cypress
|
||||
restore-keys: ${{ github.run_id }}-e2e-cypress
|
||||
|
||||
- name: Restore neo4j cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-e2e-neo4j-cache
|
||||
|
||||
- name: Restore backend cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-e2e-backend-cache
|
||||
|
||||
- name: Restore webapp cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2
|
||||
with:
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-e2e-webapp-cache
|
||||
|
||||
- name: Boot up test system | docker compose
|
||||
run: |
|
||||
chmod +x /opt/cucumber-json-formatter
|
||||
sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter
|
||||
docker load < /tmp/images/neo4j.tar
|
||||
docker load < /tmp/images/backend.tar
|
||||
docker load < /tmp/images/webapp.tar
|
||||
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend --build
|
||||
docker load < /tmp/neo4j.tar
|
||||
docker load < /tmp/backend.tar
|
||||
docker load < /tmp/webapp.tar
|
||||
docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --detach --no-deps webapp neo4j backend mailserver
|
||||
sleep 90s
|
||||
|
||||
- name: Full stack tests | run tests
|
||||
@ -98,17 +156,24 @@ jobs:
|
||||
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
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
needs: [docker_preparation, fullstack_tests]
|
||||
cleanup_cache:
|
||||
name: Cleanup Cache
|
||||
needs: fullstack_tests
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
|
||||
|
||||
- name: Full stack tests | cleanup cache
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-e2e-preparation-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
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 }}
|
||||
2
.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@04501d43b574e4c1d23c629ffe4dcec27acfdeff # v5.5.3
|
||||
- uses: amannn/action-semantic-pull-request@335288255954904a41ddda8947c8f2c844b8bfeb # v5.5.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
81
CHANGELOG.md
@ -4,8 +4,63 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [3.5.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.2...3.5.3)
|
||||
|
||||
- fix(backend): correct email from [`#8501`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8501)
|
||||
- refactor(backend): types for global config [`#8485`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8485)
|
||||
- fix warning in workflow for lower case as [`#8494`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8494)
|
||||
|
||||
#### [3.5.2](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.1...3.5.2)
|
||||
|
||||
> 6 May 2025
|
||||
|
||||
- v3.5.2 [`#8498`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8498)
|
||||
- fix emails2 [`#8497`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8497)
|
||||
|
||||
#### [3.5.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.0...3.5.1)
|
||||
|
||||
> 6 May 2025
|
||||
|
||||
- v3.5.1 [`#8496`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8496)
|
||||
- fix emails in production [`#8493`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8493)
|
||||
|
||||
#### [3.5.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.4.0...3.5.0)
|
||||
|
||||
> 6 May 2025
|
||||
|
||||
- v3.5.0 [`#8492`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8492)
|
||||
- feat(webapp): user teaser popover [`#8450`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8450)
|
||||
- feat(backend): signup email localized [`#8459`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8459)
|
||||
- lint json [`#8472`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8472)
|
||||
- refactor(other): cypress: simplify cucumber preprocessor imports and some linting [`#8489`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8489)
|
||||
- fix backend node23 [`#8488`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8488)
|
||||
- refactor(workflow): parallelize e2e preparation [`#8481`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8481)
|
||||
- refactor(backend): types for context + `slug` [`#8486`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8486)
|
||||
- feat(backend): emails for notifications [`#8435`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8435)
|
||||
- remove some dependabot groups & no alpine version to allow update [`#8475`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8475)
|
||||
- build(deps-dev): bump the babel group with 3 updates [`#8478`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8478)
|
||||
- build(deps-dev): bump @types/node from 22.15.2 to 22.15.3 in /backend [`#8479`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8479)
|
||||
- refactor(backend): refactor context [`#8434`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8434)
|
||||
- build(deps): bump amannn/action-semantic-pull-request [`#8480`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8480)
|
||||
- build(deps-dev): bump eslint-plugin-prettier in /webapp [`#8332`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8332)
|
||||
- remove all helpers on src/helpers [`#8469`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8469)
|
||||
- move models into database folder [`#8471`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8471)
|
||||
- also lint cjs files [`#8467`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8467)
|
||||
- refactor(backend): refactor badges [`#8465`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8465)
|
||||
- refactor(backend): move resolvers into graphql folder [`#8470`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8470)
|
||||
- refactor(webapp): remove unused packages [`#8468`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8468)
|
||||
- refactor(backend): remove unused packages [`#8466`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8466)
|
||||
- fix(backend): fix backend dev and dev:debug command [`#8439`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8439)
|
||||
- move distanceToMe onto Location [`#8464`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8464)
|
||||
- feat(backend): distanceToMe [`#8462`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8462)
|
||||
- fix(webapp): fixed padding for mobile in basic layout [`#8455`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8455)
|
||||
- Fix ocelot.social link for imprint and donation [`#8461`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8461)
|
||||
|
||||
#### [3.4.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.3.0...3.4.0)
|
||||
|
||||
> 28 April 2025
|
||||
|
||||
- v3.4.0 [`#8454`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8454)
|
||||
- fix(webapp): fix badge focus [`#8452`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8452)
|
||||
- feat(backend): branding middlewares [`#8429`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8429)
|
||||
- refactor(webapp): make login, registration, password-reset layout brandable [`#8440`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8440)
|
||||
@ -1383,15 +1438,31 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
- updated CHANGELOG.md [`9d9075f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9d9075f2117b2eb4b607e7d59ab18c7e655c6ea7)
|
||||
|
||||
#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...0.6.4)
|
||||
#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.4...0.6.4)
|
||||
|
||||
> 8 February 2021
|
||||
|
||||
- regenerated `CHANGELOG.md` [`ee688ec`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/ee688ece24cf592b3989e83340701ca8772e876e)
|
||||
- fetch full history [`5ecee4d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5ecee4d73a92d2e5c5ae971d79848ed27f65a72c)
|
||||
- don't fail if tag exists (release) [`39c82fc`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/39c82fcb37d5c8e7e78a79288e1ef6280f8d0892)
|
||||
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
||||
- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a)
|
||||
- - deploy structure image [`a60400b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a60400b4fe6f59bbb80e1073db4def3ba205e1a7)
|
||||
|
||||
#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...0.6.3)
|
||||
#### [v0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...v0.6.4)
|
||||
|
||||
> 9 February 2021
|
||||
|
||||
- chore(release): 0.6.4 [`8b7570d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8b7570dc35d0ea431f673a711ac051f1e1320acb)
|
||||
- change user roles is working, test fails [`8c3310a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8c3310abaf87c0e5597fec4f93fb37d27122c9e7)
|
||||
- change user role: tests are working [`f10da4b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f10da4b09388fe1e2b85abd53f6ffc67c785d4c1)
|
||||
|
||||
#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.3...0.6.3)
|
||||
|
||||
> 8 February 2021
|
||||
|
||||
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
||||
- - fixed changelog [`cf70b12`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/cf70b12ed74011924ea788ab932fc9d7ac0e6bd9)
|
||||
- - yarn install to allow yarn auto-changelog [`fc496aa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/fc496aa04cb7e804da4335da0cb5cda26f874ea2)
|
||||
|
||||
#### [v0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...v0.6.3)
|
||||
|
||||
> 8 February 2021
|
||||
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
coverage:
|
||||
range: "60...100"
|
||||
41
backend/.env.test_e2e
Normal file
@ -0,0 +1,41 @@
|
||||
DEBUG=true
|
||||
|
||||
NEO4J_URI=bolt://localhost:7687
|
||||
NEO4J_USERNAME=neo4j
|
||||
NEO4J_PASSWORD=letmein
|
||||
GRAPHQL_URI=http://localhost:4000
|
||||
CLIENT_URI=http://localhost:3000
|
||||
|
||||
# E-Mail default settings
|
||||
EMAIL_SUPPORT="devops@ocelot.social"
|
||||
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
|
||||
SMTP_HOST=mailserver
|
||||
SMTP_PORT=1025
|
||||
SMTP_IGNORE_TLS=true
|
||||
SMTP_MAX_CONNECTIONS=5
|
||||
SMTP_MAX_MESSAGES=Infinity
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_SECURE="false" # true for 465, false for other ports
|
||||
SMTP_DKIM_DOMAINNAME=
|
||||
SMTP_DKIM_KEYSELECTOR=
|
||||
SMTP_DKIM_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
|
||||
INVITE_REGISTRATION=true
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_ENDPOINT=
|
||||
AWS_REGION=
|
||||
AWS_BUCKET=
|
||||
|
||||
CATEGORIES_ACTIVE=false
|
||||
@ -133,7 +133,7 @@ module.exports = {
|
||||
'error',
|
||||
{ allowModules: ['apollo-server-testing', 'rosie', '@faker-js/faker', 'ts-jest'] },
|
||||
], // part of n/recommended
|
||||
// 'n/no-unpublished-require': 'error', // 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
|
||||
@ -204,7 +204,6 @@ module.exports = {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
// this is to properly reference the referenced project database without requirement of compiling it
|
||||
// eslint-disable-next-line camelcase
|
||||
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||
},
|
||||
},
|
||||
@ -224,5 +223,10 @@ module.exports = {
|
||||
'jest/unbound-method': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: ['plugin:jsonc/recommended-with-jsonc'],
|
||||
files: ['*.json', '*.json5', '*.jsonc'],
|
||||
parser: 'jsonc-eslint-parser',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"schemaPath": "./src/schema.graphql"
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
FROM node:20.12.1-alpine3.19 AS base
|
||||
FROM node:23.11.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"
|
||||
@ -10,7 +10,7 @@ LABEL maintainer="devops@ocelot.social"
|
||||
ENV NODE_ENV="production"
|
||||
ENV PORT="4000"
|
||||
EXPOSE ${PORT}
|
||||
RUN apk --no-cache add git python3 make g++ bash
|
||||
RUN apk --no-cache add git python3 make g++ bash linux-headers
|
||||
RUN mkdir -p /app
|
||||
WORKDIR /app
|
||||
CMD ["/bin/bash", "-c", "yarn run start"]
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "10"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-throw-expressions"
|
||||
]
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const { pathsToModuleNameMapper } = require('ts-jest')
|
||||
const requireJSON5 = require('require-json5')
|
||||
const { pathsToModuleNameMapper } = require('ts-jest')
|
||||
|
||||
const { compilerOptions } = requireJSON5('./tsconfig.json')
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-backend",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.3",
|
||||
"description": "GraphQL Backend for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
@ -10,9 +10,9 @@
|
||||
"scripts": {
|
||||
"start": "node build/src/",
|
||||
"build": "tsc && tsc-alias && ./scripts/build.copy.files.sh",
|
||||
"dev": "nodemon --exec ts-node --require tsconfig-paths/register src/ -e js,ts,gql",
|
||||
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,ts,gql",
|
||||
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts .",
|
||||
"dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql",
|
||||
"dev:debug": "nodemon --exec node --inspect=0.0.0.0:9229 build/src/index.js -e js,ts,gql",
|
||||
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc .",
|
||||
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||
"db:reset": "ts-node --require tsconfig-paths/register src/db/reset.ts",
|
||||
"db:reset:withmigrations": "ts-node --require tsconfig-paths/register src/db/reset-with-migrations.ts",
|
||||
@ -27,30 +27,16 @@
|
||||
"prod:db:data:branding": "node build/src/db/data-branding.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "~7.27.0",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/node": "~7.26.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.25.9",
|
||||
"@babel/preset-env": "~7.26.9",
|
||||
"@babel/register": "^7.23.7",
|
||||
"@sentry/node": "^5.15.4",
|
||||
"apollo-cache-inmemory": "~1.6.6",
|
||||
"apollo-client": "~2.6.10",
|
||||
"apollo-link-context": "~1.0.20",
|
||||
"apollo-link-http": "~1.5.17",
|
||||
"apollo-server": "~2.14.2",
|
||||
"apollo-server-express": "^2.14.2",
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"babel-core": "~7.0.0-0",
|
||||
"babel-eslint": "~10.1.0",
|
||||
"babel-jest": "~29.7.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"bcryptjs": "~3.0.2",
|
||||
"body-parser": "^1.20.3",
|
||||
"cheerio": "~1.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"cross-env": "~7.0.3",
|
||||
"dotenv": "~16.5.0",
|
||||
"email-templates": "^12.0.2",
|
||||
"express": "^5.1.0",
|
||||
"graphql": "^14.6.0",
|
||||
"graphql-middleware": "~4.0.2",
|
||||
@ -92,10 +78,11 @@
|
||||
"node-fetch": "^2.7.0",
|
||||
"nodemailer": "^6.10.1",
|
||||
"nodemailer-html-to-text": "^3.2.0",
|
||||
"preview-email": "^3.1.0",
|
||||
"pug": "^3.0.3",
|
||||
"request": "~2.88.2",
|
||||
"sanitize-html": "~2.16.0",
|
||||
"slug": "~9.1.0",
|
||||
"subscriptions-transport-ws": "^0.9.19",
|
||||
"trunc-html": "~1.1.2",
|
||||
"uuid": "~9.0.1",
|
||||
"validator": "^13.15.0",
|
||||
@ -104,9 +91,11 @@
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
|
||||
"@faker-js/faker": "9.7.0",
|
||||
"@types/email-templates": "^10.0.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/node": "^22.15.2",
|
||||
"@types/node": "^22.15.3",
|
||||
"@types/slug": "^5.0.9",
|
||||
"@types/uuid": "~9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
@ -117,6 +106,7 @@
|
||||
"eslint-import-resolver-typescript": "^4.3.4",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jest": "^28.11.0",
|
||||
"eslint-plugin-jsonc": "^2.20.0",
|
||||
"eslint-plugin-n": "^17.17.0",
|
||||
"eslint-plugin-no-catch-all": "^1.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
@ -135,7 +125,10 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"**/**/fs-capacitor": "^6.2.0",
|
||||
"**/graphql-upload": "^11.0.0"
|
||||
"**/graphql-upload": "^11.0.0",
|
||||
"**/strip-ansi": "6.0.1",
|
||||
"**/string-width": "4.2.0",
|
||||
"**/wrap-ansi": "7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.12.1"
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
<path
|
||||
fill="#333"
|
||||
d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"
|
||||
id="path1" />
|
||||
id="path1"
|
||||
style="fill:#868383;fill-opacity:1" />
|
||||
<g
|
||||
fill="#ffffff"
|
||||
id="g2"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -1 +1,45 @@
|
||||
<svg width="513" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"/><path d="M35.01 367.726c-.08-21.169-.205-53.162 21.257-71.332 3.817-3.253 9.93-7.497 17.321-9.224 2.575-.523 4.956-.756 7.262-.979 4.438-.431 8.27-.804 12.054-2.9l4.954-2.846c9.87-5.655 19.194-10.996 28.226-17.377 5.085-3.632 6.726-15.73 6.095-25.428-.214-2.792-1.893-5.7-3.67-8.777-1.097-1.901-2.232-3.867-3.065-5.916l-.073-.199a56.976 56.976 0 0 1-.422-1.443c-1.195-4.205-1.933-6.378-2.386-7.476-7.029-.944-11.8-8.647-12.888-21.006l-.031-.557c-.645-12.785.808-16.13 2.316-17.716.24-.254.505-.475.783-.666-1.754-16.051 3.115-32.521 13.358-44.704 9.314-11.079 21.955-17.18 35.592-17.18 3.73 0 7.55.458 11.355 1.362 25.63 6.228 41.679 30.27 40.062 59.227.53.251 1.018.61 1.44 1.066 2.752 2.964 2.47 10.97 2.22 14.276l-.024.41c-.335 5.236-.684 10.65-3.052 15.73-1.739 3.918-4.405 6.242-6.76 8.29-2.396 2.089-4.288 3.735-5.294 6.885-.7 2.416-1.645 4.866-2.559 7.235-1.752 4.538-3.407 8.827-3.54 13.244-.427 10.222 1.17 18.391 4.172 21.359 5.097 5.163 13.003 9.391 19.978 13.121 1.6.855 3.166 1.692 4.654 2.517 9.28 5.052 16.07 7.915 25.309 8.557 9.118.849 18.056 5.193 24.754 11.97.736.641 1.82 1.744 3.694 3.648 4.416 4.492 4.416 4.492 4.426 5.852l.007.758c10.783 17.702 11.14 40.656 11.415 58.169l.05 3.28-3.278.028c-42.05.363-84.058.677-126.058.993-42.12.314-84.232.632-126.367.994l-3.273.029-.014-3.274zM329.011 135.763a5.232 5.232 0 0 0-5.223 5.23 5.232 5.232 0 0 0 5.223 5.23 5.236 5.236 0 0 0 5.231-5.23 5.236 5.236 0 0 0-5.23-5.23m0 40.237C309.705 176 294 160.297 294 140.993 294 121.698 309.706 106 329.011 106 348.303 106 364 121.698 364 140.993 364 160.297 348.303 176 329.011 176" fill="#FFF"/><path d="M330.511 101C308.173 101 290 119.164 290 141.492 290 163.828 308.173 182 330.511 182 352.836 182 371 163.828 371 141.492 371 119.164 352.836 101 330.511 101m0 51.022c5.823 0 10.531-4.716 10.531-10.53a10.517 10.517 0 0 0-10.53-10.529 10.51 10.51 0 0 0-10.523 10.529c0 5.814 4.7 10.53 10.522 10.53m0-40.496c16.563 0 29.963 13.406 29.963 29.966 0 16.555-13.4 29.982-29.963 29.982-16.555 0-29.985-13.427-29.985-29.982 0-16.56 13.43-29.966 29.985-29.966" fill="#AD245D"/><path d="M331 106.209c-20.305 0-36.825 16.06-36.825 35.799 0 19.747 16.52 35.813 36.825 35.813 20.306 0 36.827-16.066 36.827-35.813 0-19.74-16.521-35.8-36.827-35.8zM314.287 215l-4.11-21.345c-.324-.129-.648-.265-.972-.404l-18.012 12.169-23.607-23.609 12.186-18.009a63.31 63.31 0 0 1-.403-.968L258 158.712v-33.383l21.361-4.13c.131-.327.267-.652.407-.979l-12.18-18.025 23.608-23.612 18.015 12.198c.322-.137.643-.27.964-.4L314.287 69h33.416l4.13 21.387c.319.13.638.26.956.396l18.024-12.2 23.608 23.612-12.186 18.031c.139.324.273.648.402.971L404 125.33v33.381l-21.37 4.124c-.13.32-.262.64-.398.96l12.19 18.017-23.606 23.609-18.021-12.171c-.32.137-.642.27-.964.402L347.701 215h-33.414z" fill="#FFF"/><path d="M330 171.448c-17.342 0-31.45-13.656-31.45-30.44 0-16.778 14.108-30.427 31.45-30.427 17.341 0 31.449 13.649 31.449 30.426 0 16.785-14.108 30.441-31.45 30.441zM350.979 63h-41.97l-1.64 8.517-1.953 10.156-8.55-5.788-7.18-4.862-6.132 6.132-17.399 17.4-6.126 6.126 4.85 7.178 5.8 8.583-10.173 1.966-8.506 1.646v41.932l8.51 1.643 10.16 1.962-5.787 8.553-4.858 7.179 6.13 6.13 17.399 17.399 6.126 6.126 7.177-4.85 8.56-5.782 1.954 10.141 1.64 8.513H350.975l1.645-8.507 1.964-10.15 8.566 5.787 7.178 4.846 6.124-6.124 17.4-17.399 6.13-6.13-4.858-7.18-5.788-8.554 10.153-1.96 8.51-1.643v-41.932l-8.507-1.646-10.165-1.965 5.8-8.584 4.85-7.178-6.125-6.126-17.4-17.4-6.13-6.13-7.18 4.858-8.558 5.792-1.964-10.166L350.98 63zm-20.98 118.948c23.176 0 41.95-18.318 41.95-40.94 0-22.607-18.774-40.927-41.95-40.927-23.174 0-41.948 18.32-41.948 40.926 0 22.623 18.774 40.941 41.949 40.941zM342.313 73.5l3.855 19.963a47.184 47.184 0 0 1 6.037 2.502l16.824-11.386 17.4 17.4-11.362 16.818a53.171 53.171 0 0 1 2.502 6.066l19.932 3.855v24.601l-19.932 3.848a56.644 56.644 0 0 1-2.502 6.066l11.362 16.795-17.4 17.4-16.824-11.364a44.931 44.931 0 0 1-6.037 2.504l-3.855 19.932H317.68l-3.84-19.932a43.821 43.821 0 0 1-6.043-2.504l-16.818 11.364-17.4-17.4 11.364-16.795a53.759 53.759 0 0 1-2.51-6.066l-19.933-3.848v-24.601l19.933-3.855a50.617 50.617 0 0 1 2.51-6.066l-11.364-16.818 17.4-17.4 16.818 11.386a45.957 45.957 0 0 1 6.043-2.502l3.84-19.963h24.632z" fill="#AD245D"/></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="513"
|
||||
height="444"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5" />
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g5">
|
||||
<path
|
||||
fill="#ad245d"
|
||||
d="M 384.5,0.297 512.325,221.9 384.325,443.602 128.5,443.704 0.675,222.1 128.675,0.4 Z"
|
||||
id="path1"
|
||||
style="display:inline;fill:#e67919;fill-opacity:1" />
|
||||
<path
|
||||
d="m 83.051688,348.226 c -0.08,-21.169 -0.205,-53.162 21.257002,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.367002,0.994 l -3.273,0.029 z"
|
||||
id="path2-5"
|
||||
style="display:inline;fill:#ffffff" />
|
||||
<path
|
||||
d="m 351.97006,182.43811 c -2.88526,0.005 -5.2219,2.34474 -5.223,5.23 0.001,2.88526 2.33774,5.22504 5.223,5.23 2.88747,-0.003 5.22769,-2.34253 5.231,-5.23 -0.003,-2.88708 -2.34292,-5.22669 -5.23,-5.23 m 0,40.237 c -19.307,0 -35.012,-15.703 -35.012,-35.007 0,-19.295 15.706,-34.993 35.011,-34.993 19.292,0 34.989,15.698 34.989,34.993 0,19.304 -15.697,35.007 -34.989,35.007"
|
||||
fill="#ffffff"
|
||||
id="path2"
|
||||
style="display:inline" />
|
||||
<path
|
||||
d="m 351.97006,147.67511 c -22.338,0 -40.511,18.164 -40.511,40.492 0,22.336 18.173,40.508 40.511,40.508 22.325,0 40.489,-18.172 40.489,-40.508 0,-22.328 -18.164,-40.492 -40.489,-40.492 m 0,51.022 c 5.823,0 10.531,-4.716 10.531,-10.53 a 10.517,10.517 0 0 0 -10.53,-10.529 10.51,10.51 0 0 0 -10.523,10.529 c 0,5.814 4.7,10.53 10.522,10.53 m 0,-40.496 c 16.563,0 29.963,13.406 29.963,29.966 0,16.555 -13.4,29.982 -29.963,29.982 -16.555,0 -29.985,-13.427 -29.985,-29.982 0,-16.56 13.43,-29.966 29.985,-29.966"
|
||||
fill="#ad245d"
|
||||
id="path3"
|
||||
style="display:inline;fill:#e67919;fill-opacity:1" />
|
||||
<path
|
||||
d="m 351.95906,152.88411 c -20.305,0 -36.825,16.06 -36.825,35.799 0,19.747 16.52,35.813 36.825,35.813 20.306,0 36.827,-16.066 36.827,-35.813 0,-19.74 -16.521,-35.8 -36.827,-35.8 z m -16.713,108.791 -4.11,-21.345 c -0.324,-0.129 -0.648,-0.265 -0.972,-0.404 l -18.012,12.169 -23.607,-23.609 12.186,-18.009 a 63.31,63.31 0 0 1 -0.403,-0.968 l -21.369,-4.122 v -33.383 l 21.361,-4.13 c 0.131,-0.327 0.267,-0.652 0.407,-0.979 l -12.18,-18.025 23.608,-23.612 18.015,12.198 c 0.322,-0.137 0.643,-0.27 0.964,-0.4 l 4.112,-21.381 h 33.416 l 4.13,21.387 c 0.319,0.13 0.638,0.26 0.956,0.396 l 18.024,-12.2 23.608,23.612 -12.186,18.031 c 0.139,0.324 0.273,0.648 0.402,0.971 l 21.363,4.133 v 33.381 l -21.37,4.124 c -0.13,0.32 -0.262,0.64 -0.398,0.96 l 12.19,18.017 -23.606,23.609 -18.021,-12.171 c -0.32,0.137 -0.642,0.27 -0.964,0.402 l -4.13,21.348 z"
|
||||
fill="#ffffff"
|
||||
id="path4"
|
||||
style="display:inline" />
|
||||
<path
|
||||
d="m 351.95956,218.12311 c -17.342,0 -31.45,-13.656 -31.45,-30.44 0,-16.778 14.108,-30.427 31.45,-30.427 17.341,0 31.449,13.649 31.449,30.426 0,16.785 -14.108,30.441 -31.45,30.441 z m 20.979,-108.448 h -41.97 l -1.64,8.517 -1.953,10.156 -8.55,-5.788 -7.18,-4.862 -6.132,6.132 -17.399,17.4 -6.126,6.126 4.85,7.178 5.8,8.583 -10.173,1.966 -8.506,1.646 v 41.932 l 8.51,1.643 10.16,1.962 -5.787,8.553 -4.858,7.179 6.13,6.13 17.399,17.399 6.126,6.126 7.177,-4.85 8.56,-5.782 1.954,10.141 1.64,8.513 h 41.964 l 1.645,-8.507 1.964,-10.15 8.566,5.787 7.178,4.846 6.124,-6.124 17.4,-17.399 6.13,-6.13 -4.858,-7.18 -5.788,-8.554 10.153,-1.96 8.51,-1.643 v -41.932 l -8.507,-1.646 -10.165,-1.965 5.8,-8.584 4.85,-7.178 -6.125,-6.126 -17.4,-17.4 -6.13,-6.13 -7.18,4.858 -8.558,5.792 -1.964,-10.166 -1.64,-8.509 z m -20.98,118.948 c 23.176,0 41.95,-18.318 41.95,-40.94 0,-22.607 -18.774,-40.927 -41.95,-40.927 -23.174,0 -41.948,18.32 -41.948,40.926 0,22.623 18.774,40.941 41.949,40.941 z m 12.314,-108.448 3.855,19.963 a 47.184,47.184 0 0 1 6.037,2.502 l 16.824,-11.386 17.4,17.4 -11.362,16.818 a 53.171,53.171 0 0 1 2.502,6.066 l 19.932,3.855 v 24.601 l -19.932,3.848 a 56.644,56.644 0 0 1 -2.502,6.066 l 11.362,16.795 -17.4,17.4 -16.824,-11.364 a 44.931,44.931 0 0 1 -6.037,2.504 l -3.855,19.932 h -24.633 l -3.84,-19.932 a 43.821,43.821 0 0 1 -6.043,-2.504 l -16.818,11.364 -17.4,-17.4 11.364,-16.795 a 53.759,53.759 0 0 1 -2.51,-6.066 l -19.933,-3.848 v -24.601 l 19.933,-3.855 a 50.617,50.617 0 0 1 2.51,-6.066 l -11.364,-16.818 17.4,-17.4 16.818,11.386 a 45.957,45.957 0 0 1 6.043,-2.502 l 3.84,-19.963 h 24.632 z"
|
||||
fill="#ad245d"
|
||||
id="path5"
|
||||
style="display:inline;fill:#e67919;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.7 KiB |
@ -1 +1,38 @@
|
||||
<svg width="513" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"/><g fill="#FFF"><path d="M35.01 367.726c-.08-21.169-.205-53.162 21.257-71.332 3.817-3.253 9.93-7.497 17.321-9.224 2.575-.523 4.956-.756 7.262-.979 4.438-.431 8.27-.804 12.054-2.9l4.954-2.846c9.87-5.655 19.194-10.996 28.226-17.377 5.085-3.632 6.726-15.73 6.095-25.428-.214-2.792-1.893-5.7-3.67-8.777-1.097-1.901-2.232-3.867-3.065-5.916l-.073-.199a56.976 56.976 0 0 1-.422-1.443c-1.195-4.205-1.933-6.378-2.386-7.476-7.029-.944-11.8-8.647-12.888-21.006l-.031-.557c-.645-12.785.808-16.13 2.316-17.716.24-.254.505-.475.783-.666-1.754-16.051 3.115-32.521 13.358-44.704 9.314-11.079 21.955-17.18 35.592-17.18 3.73 0 7.55.458 11.355 1.362 25.63 6.228 41.679 30.27 40.062 59.227.53.251 1.018.61 1.44 1.066 2.752 2.964 2.47 10.97 2.22 14.276l-.024.41c-.335 5.236-.684 10.65-3.052 15.73-1.739 3.918-4.405 6.242-6.76 8.29-2.396 2.089-4.288 3.735-5.294 6.885-.7 2.416-1.645 4.866-2.559 7.235-1.752 4.538-3.407 8.827-3.54 13.244-.427 10.222 1.17 18.391 4.172 21.359 5.097 5.163 13.003 9.391 19.978 13.121 1.6.855 3.166 1.692 4.654 2.517 9.28 5.052 16.07 7.915 25.309 8.557 9.118.849 18.056 5.193 24.754 11.97.736.641 1.82 1.744 3.694 3.648 4.416 4.492 4.416 4.492 4.426 5.852l.007.758c10.783 17.702 11.14 40.656 11.415 58.169l.05 3.28-3.278.028c-42.05.363-84.058.677-126.058.993-42.12.314-84.232.632-126.367.994l-3.273.029-.014-3.274z"/><text font-family="Impact" font-size="118" font-style="condensed" font-weight="700" transform="translate(1 -1)"><tspan x="256" y="208"></></tspan></text></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="513"
|
||||
height="444"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs3" />
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g3">
|
||||
<path
|
||||
fill="#AD245D"
|
||||
d="M384.5.297L512.325 221.9l-128 221.702-255.825.102L.675 222.1 128.675.4z"
|
||||
id="path1"
|
||||
style="fill:#e67919;fill-opacity:1" />
|
||||
<path
|
||||
d="m 80.877371,348.226 c -0.08,-21.169 -0.205,-53.162 21.256999,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.366999,0.994 l -3.273,0.029 z"
|
||||
id="path2-5"
|
||||
style="display:inline;fill:#ffffff" />
|
||||
<text
|
||||
font-family="Impact"
|
||||
font-size="118px"
|
||||
font-style="condensed"
|
||||
font-weight="700"
|
||||
id="text2"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;font-family:Monospace;-inkscape-font-specification:Monospace;display:inline;fill:#ffffff"
|
||||
x="6.8672509"
|
||||
y="17"><tspan
|
||||
x="262.86725"
|
||||
y="225"
|
||||
id="tspan2"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:96px;font-family:Monospace;-inkscape-font-specification:Monospace;fill:#ffffff"></></tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.6 KiB |
@ -1 +1,29 @@
|
||||
<svg width="512" height="444" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#AD245D" d="M384 .297L511.392 221.65l-128 221.702-255.392.352L.608 222.35 128.608.65z"/><g fill="#FFF"><path d="M34.944 367.726c-.081-21.169-.205-53.162 21.215-71.332 3.81-3.253 9.91-7.497 17.288-9.224 2.57-.523 4.946-.756 7.247-.979 4.43-.431 8.254-.804 12.03-2.9l4.945-2.846c9.851-5.655 19.157-10.996 28.171-17.377 5.075-3.632 6.713-15.73 6.082-25.428-.213-2.792-1.888-5.7-3.662-8.777-1.095-1.901-2.228-3.867-3.059-5.916l-.073-.199a57.061 57.061 0 0 1-.42-1.443c-1.194-4.205-1.93-6.378-2.382-7.476-7.015-.944-11.778-8.647-12.864-21.006l-.03-.557c-.644-12.785.806-16.13 2.31-17.716.241-.254.505-.475.783-.666-1.75-16.051 3.11-32.521 13.331-44.704 9.296-11.079 21.912-17.18 35.524-17.18 3.722 0 7.535.458 11.332 1.362 25.58 6.228 41.597 30.27 39.983 59.227.53.251 1.016.61 1.439 1.066 2.745 2.964 2.464 10.97 2.215 14.276l-.024.41c-.335 5.236-.683 10.65-3.046 15.73-1.736 3.918-4.397 6.242-6.747 8.29-2.391 2.089-4.28 3.735-5.284 6.885-.698 2.416-1.642 4.866-2.554 7.235-1.749 4.538-3.4 8.827-3.534 13.244-.425 10.222 1.17 18.391 4.165 21.359 5.087 5.163 12.977 9.391 19.939 13.121 1.597.855 3.16 1.692 4.645 2.517 9.262 5.052 16.038 7.915 25.259 8.557 9.1.849 18.02 5.193 24.706 11.97.735.641 1.817 1.744 3.687 3.648 4.408 4.492 4.408 4.492 4.417 5.852l.007.758c10.762 17.702 11.12 40.656 11.392 58.169l.05 3.28-3.271.028c-41.968.363-83.894.677-125.812.993-42.038.314-84.067.632-126.12.994l-3.267.029-.013-3.274zM332.387 115.763h15.318v86.734h-15.318v.513l-22.127-17.64v.12h-10.21V211h-22.128v-25.51h-20.424v-52.722h52.762v-.508l22.127-17.065v.568zm34.313 72.093l-7.988-4.591c15.803-27.453 1.717-46.642 1.106-47.443l7.304-5.607c.774.993 18.547 24.675-.422 57.641zm27.361 21.216l-13.866-7.975c27.437-47.66 2.98-80.973 1.918-82.36L394.795 109c1.343 1.723 32.2 42.839-.734 100.072z"/></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="444"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs3" />
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g3">
|
||||
<path
|
||||
fill="#AD245D"
|
||||
d="M384 .297L511.392 221.65l-128 221.702-255.392.352L.608 222.35 128.608.65z"
|
||||
id="path1"
|
||||
style="fill:#e67919;fill-opacity:1" />
|
||||
<path
|
||||
d="m 85.310542,348.226 c -0.08,-21.169 -0.205,-53.162 21.256998,-71.332 3.817,-3.253 9.93,-7.497 17.321,-9.224 2.575,-0.523 4.956,-0.756 7.262,-0.979 4.438,-0.431 8.27,-0.804 12.054,-2.9 l 4.954,-2.846 c 9.87,-5.655 19.194,-10.996 28.226,-17.377 5.085,-3.632 6.726,-15.73 6.095,-25.428 -0.214,-2.792 -1.893,-5.7 -3.67,-8.777 -1.097,-1.901 -2.232,-3.867 -3.065,-5.916 l -0.073,-0.199 a 56.976,56.976 0 0 1 -0.422,-1.443 c -1.195,-4.205 -1.933,-6.378 -2.386,-7.476 -7.029,-0.944 -11.8,-8.647 -12.888,-21.006 l -0.031,-0.557 c -0.645,-12.785 0.808,-16.13 2.316,-17.716 0.24,-0.254 0.505,-0.475 0.783,-0.666 -1.754,-16.051 3.115,-32.521 13.358,-44.704 9.314,-11.079 21.955,-17.18 35.592,-17.18 3.73,0 7.55,0.458 11.355,1.362 25.63,6.228 41.679,30.27 40.062,59.227 0.53,0.251 1.018,0.61 1.44,1.066 2.752,2.964 2.47,10.97 2.22,14.276 l -0.024,0.41 c -0.335,5.236 -0.684,10.65 -3.052,15.73 -1.739,3.918 -4.405,6.242 -6.76,8.29 -2.396,2.089 -4.288,3.735 -5.294,6.885 -0.7,2.416 -1.645,4.866 -2.559,7.235 -1.752,4.538 -3.407,8.827 -3.54,13.244 -0.427,10.222 1.17,18.391 4.172,21.359 5.097,5.163 13.003,9.391 19.978,13.121 1.6,0.855 3.166,1.692 4.654,2.517 9.28,5.052 16.07,7.915 25.309,8.557 9.118,0.849 18.056,5.193 24.754,11.97 0.736,0.641 1.82,1.744 3.694,3.648 4.416,4.492 4.416,4.492 4.426,5.852 l 0.007,0.758 c 10.783,17.702 11.14,40.656 11.415,58.169 l 0.05,3.28 -3.278,0.028 c -42.05,0.363 -84.058,0.677 -126.058,0.993 -42.12,0.314 -84.232,0.632 -126.366998,0.994 l -3.273,0.029 z"
|
||||
id="path2-5"
|
||||
style="display:inline;fill:#ffffff" />
|
||||
<path
|
||||
d="m 349.88573,158.65792 h 15.318 v 86.734 h -15.318 v 0.513 l -22.127,-17.64 v 0.12 h -10.21 v 25.51 h -22.128 v -25.51 h -20.424 v -52.722 h 52.762 v -0.508 l 22.127,-17.065 z m 34.313,72.093 -7.988,-4.591 c 15.803,-27.453 1.717,-46.642 1.106,-47.443 l 7.304,-5.607 c 0.774,0.993 18.547,24.675 -0.422,57.641 z m 27.361,21.216 -13.866,-7.975 c 27.437,-47.66 2.98,-80.973 1.918,-82.36 l 12.682,-9.737 c 1.343,1.723 32.2,42.839 -0.734,100.072 z"
|
||||
id="path2-1"
|
||||
style="display:inline;fill:#ffffff" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.5 KiB |
@ -1,257 +1,234 @@
|
||||
[
|
||||
{
|
||||
"provider_name": "Codepen",
|
||||
"provider_url": "https:\/\/codepen.io",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/codepen.io\/*",
|
||||
"https:\/\/codepen.io\/*"
|
||||
],
|
||||
"url": "http:\/\/codepen.io\/api\/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "DTube",
|
||||
"provider_url": "https:\/\/d.tube\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/d.tube\/v\/*"
|
||||
],
|
||||
"url": "https:\/\/api.d.tube\/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Facebook (Post)",
|
||||
"provider_url": "https:\/\/www.facebook.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/www.facebook.com\/*\/posts\/*",
|
||||
"https:\/\/www.facebook.com\/photos\/*",
|
||||
"https:\/\/www.facebook.com\/*\/photos\/*",
|
||||
"https:\/\/www.facebook.com\/photo.php*",
|
||||
"https:\/\/www.facebook.com\/photo.php",
|
||||
"https:\/\/www.facebook.com\/*\/activity\/*",
|
||||
"https:\/\/www.facebook.com\/permalink.php",
|
||||
"https:\/\/www.facebook.com\/media\/set?set=*",
|
||||
"https:\/\/www.facebook.com\/questions\/*",
|
||||
"https:\/\/www.facebook.com\/notes\/*\/*\/*"
|
||||
],
|
||||
"url": "https:\/\/www.facebook.com\/plugins\/post\/oembed.json",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Facebook (Video)",
|
||||
"provider_url": "https:\/\/www.facebook.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/www.facebook.com\/*\/videos\/*",
|
||||
"https:\/\/www.facebook.com\/video.php"
|
||||
],
|
||||
"url": "https:\/\/www.facebook.com\/plugins\/video\/oembed.json",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Flickr",
|
||||
"provider_url": "https:\/\/www.flickr.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/*.flickr.com\/photos\/*",
|
||||
"http:\/\/flic.kr\/p\/*",
|
||||
"https:\/\/*.flickr.com\/photos\/*",
|
||||
"https:\/\/flic.kr\/p\/*"
|
||||
],
|
||||
"url": "https:\/\/www.flickr.com\/services\/oembed\/",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "GIPHY",
|
||||
"provider_url": "https:\/\/giphy.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/giphy.com\/gifs\/*",
|
||||
"http:\/\/gph.is\/*",
|
||||
"https:\/\/media.giphy.com\/media\/*\/giphy.gif"
|
||||
],
|
||||
"url": "https:\/\/giphy.com\/services\/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Instagram",
|
||||
"provider_url": "https:\/\/instagram.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/instagram.com\/p\/*",
|
||||
"http:\/\/instagr.am\/p\/*",
|
||||
"http:\/\/www.instagram.com\/p\/*",
|
||||
"http:\/\/www.instagr.am\/p\/*",
|
||||
"https:\/\/instagram.com\/p\/*",
|
||||
"https:\/\/instagr.am\/p\/*",
|
||||
"https:\/\/www.instagram.com\/p\/*",
|
||||
"https:\/\/www.instagr.am\/p\/*"
|
||||
],
|
||||
"url": "https:\/\/api.instagram.com\/oembed",
|
||||
"formats": [
|
||||
"json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Meetup",
|
||||
"provider_url": "http:\/\/www.meetup.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/meetup.com\/*",
|
||||
"https:\/\/www.meetup.com\/*",
|
||||
"https:\/\/meetup.com\/*",
|
||||
"http:\/\/meetu.ps\/*"
|
||||
],
|
||||
"url": "https:\/\/api.meetup.com\/oembed",
|
||||
"formats": [
|
||||
"json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "MixCloud",
|
||||
"provider_url": "https:\/\/mixcloud.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/www.mixcloud.com\/*\/*\/",
|
||||
"https:\/\/www.mixcloud.com\/*\/*\/"
|
||||
],
|
||||
"url": "https:\/\/www.mixcloud.com\/oembed\/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Reddit",
|
||||
"provider_url": "https:\/\/reddit.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/reddit.com\/r\/*\/comments\/*\/*",
|
||||
"https:\/\/www.reddit.com\/r\/*\/comments\/*\/*"
|
||||
],
|
||||
"url": "https:\/\/www.reddit.com\/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "SlideShare",
|
||||
"provider_url": "http:\/\/www.slideshare.net\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/www.slideshare.net\/*\/*",
|
||||
"http:\/\/fr.slideshare.net\/*\/*",
|
||||
"http:\/\/de.slideshare.net\/*\/*",
|
||||
"http:\/\/es.slideshare.net\/*\/*",
|
||||
"http:\/\/pt.slideshare.net\/*\/*"
|
||||
],
|
||||
"url": "http:\/\/www.slideshare.net\/api\/oembed\/2",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "SoundCloud",
|
||||
"provider_url": "http:\/\/soundcloud.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/soundcloud.com\/*",
|
||||
"https:\/\/soundcloud.com\/*"
|
||||
],
|
||||
"url": "https:\/\/soundcloud.com\/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Twitch",
|
||||
"provider_url": "https:\/\/www.twitch.tv",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http:\/\/clips.twitch.tv\/*",
|
||||
"https:\/\/clips.twitch.tv\/*",
|
||||
"http:\/\/www.twitch.tv\/*",
|
||||
"https:\/\/www.twitch.tv\/*",
|
||||
"http:\/\/twitch.tv\/*",
|
||||
"https:\/\/twitch.tv\/*"
|
||||
],
|
||||
"url": "https:\/\/api.twitch.tv\/v4\/oembed",
|
||||
"formats": [
|
||||
"json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Twitter",
|
||||
"provider_url": "http:\/\/www.twitter.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/twitter.com\/*\/status\/*",
|
||||
"https:\/\/*.twitter.com\/*\/status\/*"
|
||||
],
|
||||
"url": "https:\/\/publish.twitter.com\/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Vimeo",
|
||||
"provider_url": "https:\/\/vimeo.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/vimeo.com\/*",
|
||||
"https:\/\/vimeo.com\/album\/*\/video\/*",
|
||||
"https:\/\/vimeo.com\/channels\/*\/*",
|
||||
"https:\/\/vimeo.com\/groups\/*\/videos\/*",
|
||||
"https:\/\/vimeo.com\/ondemand\/*\/*",
|
||||
"https:\/\/player.vimeo.com\/video\/*"
|
||||
],
|
||||
"url": "https:\/\/vimeo.com\/api\/oembed.{format}",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "YouTube",
|
||||
"provider_url": "https:\/\/www.youtube.com\/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https:\/\/*.youtube.com\/watch*",
|
||||
"https:\/\/*.youtube.com\/v\/*",
|
||||
"https:\/\/youtu.be\/*"
|
||||
],
|
||||
"url": "https:\/\/www.youtube.com\/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
{
|
||||
"provider_name": "Codepen",
|
||||
"provider_url": "https://codepen.io",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["http://codepen.io/*", "https://codepen.io/*"],
|
||||
"url": "http://codepen.io/api/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "DTube",
|
||||
"provider_url": "https://d.tube/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["https://d.tube/v/*"],
|
||||
"url": "https://api.d.tube/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Facebook (Post)",
|
||||
"provider_url": "https://www.facebook.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https://www.facebook.com/*/posts/*",
|
||||
"https://www.facebook.com/photos/*",
|
||||
"https://www.facebook.com/*/photos/*",
|
||||
"https://www.facebook.com/photo.php*",
|
||||
"https://www.facebook.com/photo.php",
|
||||
"https://www.facebook.com/*/activity/*",
|
||||
"https://www.facebook.com/permalink.php",
|
||||
"https://www.facebook.com/media/set?set=*",
|
||||
"https://www.facebook.com/questions/*",
|
||||
"https://www.facebook.com/notes/*/*/*"
|
||||
],
|
||||
"url": "https://www.facebook.com/plugins/post/oembed.json",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Facebook (Video)",
|
||||
"provider_url": "https://www.facebook.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["https://www.facebook.com/*/videos/*", "https://www.facebook.com/video.php"],
|
||||
"url": "https://www.facebook.com/plugins/video/oembed.json",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Flickr",
|
||||
"provider_url": "https://www.flickr.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http://*.flickr.com/photos/*",
|
||||
"http://flic.kr/p/*",
|
||||
"https://*.flickr.com/photos/*",
|
||||
"https://flic.kr/p/*"
|
||||
],
|
||||
"url": "https://www.flickr.com/services/oembed/",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "GIPHY",
|
||||
"provider_url": "https://giphy.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https://giphy.com/gifs/*",
|
||||
"http://gph.is/*",
|
||||
"https://media.giphy.com/media/*/giphy.gif"
|
||||
],
|
||||
"url": "https://giphy.com/services/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Instagram",
|
||||
"provider_url": "https://instagram.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http://instagram.com/p/*",
|
||||
"http://instagr.am/p/*",
|
||||
"http://www.instagram.com/p/*",
|
||||
"http://www.instagr.am/p/*",
|
||||
"https://instagram.com/p/*",
|
||||
"https://instagr.am/p/*",
|
||||
"https://www.instagram.com/p/*",
|
||||
"https://www.instagr.am/p/*"
|
||||
],
|
||||
"url": "https://api.instagram.com/oembed",
|
||||
"formats": ["json"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Meetup",
|
||||
"provider_url": "http://www.meetup.com",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http://meetup.com/*",
|
||||
"https://www.meetup.com/*",
|
||||
"https://meetup.com/*",
|
||||
"http://meetu.ps/*"
|
||||
],
|
||||
"url": "https://api.meetup.com/oembed",
|
||||
"formats": ["json"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "MixCloud",
|
||||
"provider_url": "https://mixcloud.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["http://www.mixcloud.com/*/*/", "https://www.mixcloud.com/*/*/"],
|
||||
"url": "https://www.mixcloud.com/oembed/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Reddit",
|
||||
"provider_url": "https://reddit.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https://reddit.com/r/*/comments/*/*",
|
||||
"https://www.reddit.com/r/*/comments/*/*"
|
||||
],
|
||||
"url": "https://www.reddit.com/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "SlideShare",
|
||||
"provider_url": "http://www.slideshare.net/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http://www.slideshare.net/*/*",
|
||||
"http://fr.slideshare.net/*/*",
|
||||
"http://de.slideshare.net/*/*",
|
||||
"http://es.slideshare.net/*/*",
|
||||
"http://pt.slideshare.net/*/*"
|
||||
],
|
||||
"url": "http://www.slideshare.net/api/oembed/2",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "SoundCloud",
|
||||
"provider_url": "http://soundcloud.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["http://soundcloud.com/*", "https://soundcloud.com/*"],
|
||||
"url": "https://soundcloud.com/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Twitch",
|
||||
"provider_url": "https://www.twitch.tv",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"http://clips.twitch.tv/*",
|
||||
"https://clips.twitch.tv/*",
|
||||
"http://www.twitch.tv/*",
|
||||
"https://www.twitch.tv/*",
|
||||
"http://twitch.tv/*",
|
||||
"https://twitch.tv/*"
|
||||
],
|
||||
"url": "https://api.twitch.tv/v4/oembed",
|
||||
"formats": ["json"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Twitter",
|
||||
"provider_url": "http://www.twitter.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": ["https://twitter.com/*/status/*", "https://*.twitter.com/*/status/*"],
|
||||
"url": "https://publish.twitter.com/oembed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "Vimeo",
|
||||
"provider_url": "https://vimeo.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https://vimeo.com/*",
|
||||
"https://vimeo.com/album/*/video/*",
|
||||
"https://vimeo.com/channels/*/*",
|
||||
"https://vimeo.com/groups/*/videos/*",
|
||||
"https://vimeo.com/ondemand/*/*",
|
||||
"https://player.vimeo.com/video/*"
|
||||
],
|
||||
"url": "https://vimeo.com/api/oembed.{format}",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider_name": "YouTube",
|
||||
"provider_url": "https://www.youtube.com/",
|
||||
"endpoints": [
|
||||
{
|
||||
"schemes": [
|
||||
"https://*.youtube.com/watch*",
|
||||
"https://*.youtube.com/v/*",
|
||||
"https://youtu.be/*"
|
||||
],
|
||||
"url": "https://www.youtube.com/oembed",
|
||||
"discovery": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -3,15 +3,12 @@
|
||||
# public
|
||||
cp -r public/ build/public/
|
||||
|
||||
# html files
|
||||
mkdir -p build/src/middleware/helpers/email/templates/
|
||||
cp -r src/middleware/helpers/email/templates/*.html build/src/middleware/helpers/email/templates/
|
||||
# email files
|
||||
mkdir -p build/src/emails/templates/
|
||||
cp -r src/emails/templates/ build/src/emails/
|
||||
|
||||
mkdir -p build/src/middleware/helpers/email/templates/en/
|
||||
cp -r src/middleware/helpers/email/templates/en/*.html build/src/middleware/helpers/email/templates/en/
|
||||
|
||||
mkdir -p build/src/middleware/helpers/email/templates/de/
|
||||
cp -r src/middleware/helpers/email/templates/de/*.html build/src/middleware/helpers/email/templates/de/
|
||||
mkdir -p build/src/emails/locales/
|
||||
cp -r src/emails/locales/ build/src/emails/
|
||||
|
||||
# gql files
|
||||
mkdir -p build/src/graphql/types/
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
/* 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'
|
||||
@ -13,18 +15,20 @@ config()
|
||||
// Use Cypress env or process.env
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
declare let Cypress: any | undefined
|
||||
const env = typeof Cypress !== 'undefined' ? Cypress.env() : process.env
|
||||
const env = (typeof Cypress !== 'undefined' ? Cypress.env() : process.env) as typeof process.env
|
||||
|
||||
const environment = {
|
||||
NODE_ENV: env.NODE_ENV || process.env.NODE_ENV,
|
||||
NODE_ENV: env.NODE_ENV ?? process.env.NODE_ENV,
|
||||
DEBUG: env.NODE_ENV !== 'production' && env.DEBUG,
|
||||
TEST: env.NODE_ENV === 'test',
|
||||
PRODUCTION: env.NODE_ENV === 'production',
|
||||
// used for staging enviroments if 'PRODUCTION=true' and 'PRODUCTION_DB_CLEAN_ALLOW=true'
|
||||
PRODUCTION_DB_CLEAN_ALLOW: env.PRODUCTION_DB_CLEAN_ALLOW === 'true' || false, // default = false
|
||||
DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string)
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV!)
|
||||
? (env.DISABLED_MIDDLEWARES?.split(',') ?? [])
|
||||
: [],
|
||||
SEND_MAIL: env.NODE_ENV !== 'test',
|
||||
}
|
||||
|
||||
const required = {
|
||||
@ -34,32 +38,51 @@ const required = {
|
||||
}
|
||||
|
||||
const server = {
|
||||
CLIENT_URI: env.CLIENT_URI || 'http://localhost:3000',
|
||||
GRAPHQL_URI: env.GRAPHQL_URI || 'http://localhost:4000',
|
||||
JWT_EXPIRES: env.JWT_EXPIRES || '2y',
|
||||
CLIENT_URI: env.CLIENT_URI ?? 'http://localhost:3000',
|
||||
GRAPHQL_URI: env.GRAPHQL_URI ?? 'http://localhost:4000',
|
||||
JWT_EXPIRES: env.JWT_EXPIRES ?? '2y',
|
||||
}
|
||||
|
||||
const hasDKIMData = env.SMTP_DKIM_DOMAINNAME && env.SMTP_DKIM_KEYSELECTOR && env.SMTP_DKIM_PRIVATKEY
|
||||
const SMTP_HOST = env.SMTP_HOST
|
||||
const SMTP_PORT = (env.SMTP_PORT && parseInt(env.SMTP_PORT)) || undefined
|
||||
const SMTP_IGNORE_TLS = env.SMTP_IGNORE_TLS !== 'false' // default = true
|
||||
const SMTP_SECURE = env.SMTP_SECURE === 'true'
|
||||
const SMTP_USERNAME = env.SMTP_USERNAME
|
||||
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_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 = {
|
||||
SMTP_HOST: env.SMTP_HOST,
|
||||
SMTP_PORT: env.SMTP_PORT,
|
||||
SMTP_IGNORE_TLS: env.SMTP_IGNORE_TLS !== 'false', // default = true
|
||||
SMTP_SECURE: env.SMTP_SECURE === 'true',
|
||||
SMTP_USERNAME: env.SMTP_USERNAME,
|
||||
SMTP_PASSWORD: env.SMTP_PASSWORD,
|
||||
SMTP_DKIM_DOMAINNAME: hasDKIMData && env.SMTP_DKIM_DOMAINNAME,
|
||||
SMTP_DKIM_KEYSELECTOR: hasDKIMData && env.SMTP_DKIM_KEYSELECTOR,
|
||||
// PEM format: https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html
|
||||
SMTP_DKIM_PRIVATKEY: hasDKIMData && env.SMTP_DKIM_PRIVATKEY.replace(/\\n/g, '\n'), // replace all "\n" in .env string by real line break
|
||||
SMTP_MAX_CONNECTIONS: env.SMTP_MAX_CONNECTIONS || 5,
|
||||
SMTP_MAX_MESSAGES: env.SMTP_MAX_MESSAGES || 100,
|
||||
const nodemailerTransportOptions: SMTPTransport.Options = {
|
||||
host: SMTP_HOST,
|
||||
port: SMTP_PORT,
|
||||
ignoreTLS: SMTP_IGNORE_TLS,
|
||||
secure: SMTP_SECURE, // true for 465, false for other ports
|
||||
pool: true,
|
||||
maxConnections: SMTP_MAX_CONNECTIONS,
|
||||
maxMessages: SMTP_MAX_MESSAGES,
|
||||
}
|
||||
if (SMTP_USERNAME && SMTP_PASSWORD) {
|
||||
nodemailerTransportOptions.auth = {
|
||||
user: SMTP_USERNAME,
|
||||
pass: SMTP_PASSWORD,
|
||||
}
|
||||
}
|
||||
if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATKEY) {
|
||||
nodemailerTransportOptions.dkim = {
|
||||
domainName: SMTP_DKIM_DOMAINNAME,
|
||||
keySelector: SMTP_DKIM_KEYSELECTOR,
|
||||
privateKey: SMTP_DKIM_PRIVATKEY,
|
||||
}
|
||||
}
|
||||
|
||||
const neo4j = {
|
||||
NEO4J_URI: env.NEO4J_URI || 'bolt://localhost:7687',
|
||||
NEO4J_USERNAME: env.NEO4J_USERNAME || 'neo4j',
|
||||
NEO4J_PASSWORD: env.NEO4J_PASSWORD || 'neo4j',
|
||||
NEO4J_URI: env.NEO4J_URI ?? 'bolt://localhost:7687',
|
||||
NEO4J_USERNAME: env.NEO4J_USERNAME ?? 'neo4j',
|
||||
NEO4J_PASSWORD: env.NEO4J_PASSWORD ?? 'neo4j',
|
||||
}
|
||||
|
||||
const sentry = {
|
||||
@ -69,7 +92,7 @@ const sentry = {
|
||||
|
||||
const redis = {
|
||||
REDIS_DOMAIN: env.REDIS_DOMAIN,
|
||||
REDIS_PORT: env.REDIS_PORT,
|
||||
REDIS_PORT: (env.REDIS_PORT && parseInt(env.REDIS_PORT)) || undefined,
|
||||
REDIS_PASSWORD: env.REDIS_PASSWORD,
|
||||
}
|
||||
|
||||
@ -109,10 +132,11 @@ export default {
|
||||
...environment,
|
||||
...server,
|
||||
...required,
|
||||
...smtp,
|
||||
...neo4j,
|
||||
...sentry,
|
||||
...redis,
|
||||
...s3,
|
||||
...options,
|
||||
}
|
||||
|
||||
export { nodemailerTransportOptions }
|
||||
|
||||
3
backend/src/constants/subscriptions.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED'
|
||||
export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED'
|
||||
49
backend/src/context/database.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { getDriver, getNeode } from '@db/neo4j'
|
||||
|
||||
import type { Driver } from 'neo4j-driver'
|
||||
|
||||
export const query =
|
||||
(driver: Driver) =>
|
||||
async ({ query, variables = {} }: { driver; query: string; variables: object }) => {
|
||||
const session = driver.session()
|
||||
|
||||
const result = session.readTransaction(async (transaction) => {
|
||||
const response = await transaction.run(query, variables)
|
||||
return response
|
||||
})
|
||||
|
||||
try {
|
||||
return await result
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export const mutate =
|
||||
(driver: Driver) =>
|
||||
async ({ query, variables = {} }: { driver; query: string; variables: object }) => {
|
||||
const session = driver.session()
|
||||
|
||||
const result = session.writeTransaction(async (transaction) => {
|
||||
const response = await transaction.run(query, variables)
|
||||
return response
|
||||
})
|
||||
|
||||
try {
|
||||
return await result
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
query: query(driver),
|
||||
mutate: mutate(driver),
|
||||
}
|
||||
}
|
||||
25
backend/src/context/pubsub.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { RedisPubSub } from 'graphql-redis-subscriptions'
|
||||
import { PubSub } from 'graphql-subscriptions'
|
||||
import Redis from 'ioredis'
|
||||
|
||||
import CONFIG from '@config/index'
|
||||
|
||||
export default () => {
|
||||
const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG
|
||||
if (!(REDIS_DOMAIN && REDIS_PORT && REDIS_PASSWORD)) {
|
||||
return new PubSub()
|
||||
}
|
||||
|
||||
const options = {
|
||||
host: REDIS_DOMAIN,
|
||||
port: REDIS_PORT,
|
||||
password: REDIS_PASSWORD,
|
||||
retryStrategy: (times) => {
|
||||
return Math.min(times * 50, 2000)
|
||||
},
|
||||
}
|
||||
return new RedisPubSub({
|
||||
publisher: new Redis(options),
|
||||
subscriber: new Redis(options),
|
||||
})
|
||||
}
|
||||
@ -10,7 +10,7 @@ import { Factory } from 'rosie'
|
||||
import slugify from 'slug'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import generateInviteCode from '@schema/resolvers/helpers/generateInviteCode'
|
||||
import generateInviteCode from '@graphql/resolvers/helpers/generateInviteCode'
|
||||
|
||||
import { getDriver, getNeode } from './neo4j'
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import { throwError, concat } from 'rxjs'
|
||||
import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators'
|
||||
|
||||
import { getDriver } from '@db/neo4j'
|
||||
import normalizeEmail from '@schema/resolvers/helpers/normalizeEmail'
|
||||
import normalizeEmail from '@graphql/resolvers/helpers/normalizeEmail'
|
||||
|
||||
export const description = `
|
||||
This migration merges duplicate :User and :EmailAddress nodes. It became
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable security/detect-non-literal-fs-filename */
|
||||
import https from 'https'
|
||||
import { existsSync, createReadStream } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { S3 } from 'aws-sdk'
|
||||
import mime from 'mime-types'
|
||||
|
||||
import s3Configs from '@config/index'
|
||||
import { getDriver } from '@db/neo4j'
|
||||
|
||||
export const description = `
|
||||
Upload all image files to a S3 compatible object storage in order to reduce
|
||||
load on our backend.
|
||||
`
|
||||
|
||||
export async function up(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
const agent = new https.Agent({
|
||||
maxSockets: 5,
|
||||
})
|
||||
|
||||
const {
|
||||
AWS_ENDPOINT: endpoint,
|
||||
AWS_REGION: region,
|
||||
AWS_BUCKET: Bucket,
|
||||
S3_CONFIGURED,
|
||||
} = s3Configs
|
||||
|
||||
if (!S3_CONFIGURED) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('No S3 given, cannot upload image files')
|
||||
return
|
||||
}
|
||||
|
||||
const s3 = new S3({ region, endpoint, httpOptions: { agent } })
|
||||
try {
|
||||
// Implement your migration here.
|
||||
const { records } = await transaction.run('MATCH (image:Image) RETURN image.url as url')
|
||||
let urls = records.map((r) => r.get('url'))
|
||||
urls = urls.filter((url) => url.startsWith('/uploads'))
|
||||
const locations = await Promise.all(
|
||||
urls
|
||||
.map((url) => {
|
||||
return async () => {
|
||||
const { pathname } = new URL(url, 'http://example.org')
|
||||
const fileLocation = path.join(__dirname, `../../../public/${pathname}`)
|
||||
const s3Location = `original${pathname}`
|
||||
// eslint-disable-next-line n/no-sync
|
||||
if (existsSync(fileLocation)) {
|
||||
const mimeType = mime.lookup(fileLocation)
|
||||
const params = {
|
||||
Bucket,
|
||||
Key: s3Location,
|
||||
ACL: 'public-read',
|
||||
ContentType: mimeType || 'image/jpeg',
|
||||
Body: createReadStream(fileLocation),
|
||||
}
|
||||
|
||||
const data = await s3.upload(params).promise()
|
||||
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'))
|
||||
return updatedUrl
|
||||
}
|
||||
}
|
||||
})
|
||||
.map((p) => p()),
|
||||
)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('this is locations', locations)
|
||||
await transaction.commit()
|
||||
next()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(``)
|
||||
await transaction.commit()
|
||||
next()
|
||||
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
} finally {
|
||||
await session.close()
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* 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 '@models/index'
|
||||
import models from '@db/models/index'
|
||||
|
||||
let driver: Driver
|
||||
const defaultOptions = {
|
||||
|
||||
@ -26,6 +26,8 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
|
||||
throw new Error(`You cannot seed the database in a non-staging and real production environment!`)
|
||||
}
|
||||
|
||||
CONFIG.SEND_MAIL = true
|
||||
|
||||
const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
;(async function () {
|
||||
|
||||
@ -9,155 +9,155 @@ export const trophies = async () => {
|
||||
trophyBear: await Factory.build('badge', {
|
||||
id: 'trophy_bear',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Bear',
|
||||
description: 'Has earned a Bear',
|
||||
icon: '/img/badges/trophy_blue_bear.svg',
|
||||
}),
|
||||
trophyPanda: await Factory.build('badge', {
|
||||
id: 'trophy_panda',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Panda',
|
||||
description: 'Has earned a Panda',
|
||||
icon: '/img/badges/trophy_blue_panda.svg',
|
||||
}),
|
||||
trophyRabbit: await Factory.build('badge', {
|
||||
id: 'trophy_rabbit',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Rabbit',
|
||||
description: 'Has earned a Rabbit',
|
||||
icon: '/img/badges/trophy_blue_rabbit.svg',
|
||||
}),
|
||||
trophyRacoon: await Factory.build('badge', {
|
||||
id: 'trophy_racoon',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Racoon',
|
||||
description: 'Has earned a Racoon',
|
||||
icon: '/img/badges/trophy_blue_racoon.svg',
|
||||
}),
|
||||
trophyRhino: await Factory.build('badge', {
|
||||
id: 'trophy_rhino',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Rhino',
|
||||
description: 'Has earned a Rhino',
|
||||
icon: '/img/badges/trophy_blue_rhino.svg',
|
||||
}),
|
||||
trophyTiger: await Factory.build('badge', {
|
||||
id: 'trophy_tiger',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Tiger',
|
||||
description: 'Has earned a Tiger',
|
||||
icon: '/img/badges/trophy_blue_tiger.svg',
|
||||
}),
|
||||
trophyTurtle: await Factory.build('badge', {
|
||||
id: 'trophy_turtle',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Turtle',
|
||||
description: 'Has earned a Turtle',
|
||||
icon: '/img/badges/trophy_blue_turtle.svg',
|
||||
}),
|
||||
trophyWhale: await Factory.build('badge', {
|
||||
id: 'trophy_whale',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Whale',
|
||||
description: 'Has earned a Whale',
|
||||
icon: '/img/badges/trophy_blue_whale.svg',
|
||||
}),
|
||||
trophyWolf: await Factory.build('badge', {
|
||||
id: 'trophy_wolf',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Wolf',
|
||||
description: 'Has earned a Wolf',
|
||||
icon: '/img/badges/trophy_blue_wolf.svg',
|
||||
}),
|
||||
// Green Transports
|
||||
trophyAirship: await Factory.build('badge', {
|
||||
id: 'trophy_airship',
|
||||
type: 'trophy',
|
||||
description: 'You earned an Airship',
|
||||
description: 'Has earned an Airship',
|
||||
icon: '/img/badges/trophy_green_airship.svg',
|
||||
}),
|
||||
trophyAlienship: await Factory.build('badge', {
|
||||
id: 'trophy_alienship',
|
||||
type: 'trophy',
|
||||
description: 'You earned an Alienship',
|
||||
description: 'Has earned an Alienship',
|
||||
icon: '/img/badges/trophy_green_alienship.svg',
|
||||
}),
|
||||
trophyBalloon: await Factory.build('badge', {
|
||||
id: 'trophy_balloon',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Balloon',
|
||||
description: 'Has earned a Balloon',
|
||||
icon: '/img/badges/trophy_green_balloon.svg',
|
||||
}),
|
||||
trophyBigballoon: await Factory.build('badge', {
|
||||
id: 'trophy_bigballoon',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Big Balloon',
|
||||
description: 'Has earned a Big Balloon',
|
||||
icon: '/img/badges/trophy_green_bigballoon.svg',
|
||||
}),
|
||||
trophyCrane: await Factory.build('badge', {
|
||||
id: 'trophy_crane',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Crane',
|
||||
description: 'Has earned a Crane',
|
||||
icon: '/img/badges/trophy_green_crane.svg',
|
||||
}),
|
||||
trophyGlider: await Factory.build('badge', {
|
||||
id: 'trophy_glider',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Glider',
|
||||
description: 'Has earned a Glider',
|
||||
icon: '/img/badges/trophy_green_glider.svg',
|
||||
}),
|
||||
trophyHelicopter: await Factory.build('badge', {
|
||||
id: 'trophy_helicopter',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Helicopter',
|
||||
description: 'Has earned a Helicopter',
|
||||
icon: '/img/badges/trophy_green_helicopter.svg',
|
||||
}),
|
||||
// Green Animals
|
||||
trophyBee: await Factory.build('badge', {
|
||||
id: 'trophy_bee',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Bee',
|
||||
description: 'Has earned a Bee',
|
||||
icon: '/img/badges/trophy_green_bee.svg',
|
||||
}),
|
||||
trophyButterfly: await Factory.build('badge', {
|
||||
id: 'trophy_butterfly',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Butterfly',
|
||||
description: 'Has earned a Butterfly',
|
||||
icon: '/img/badges/trophy_green_butterfly.svg',
|
||||
}),
|
||||
// Green Plants
|
||||
trophyFlower: await Factory.build('badge', {
|
||||
id: 'trophy_flower',
|
||||
type: 'trophy',
|
||||
description: 'You earned a Flower',
|
||||
description: 'Has earned a Flower',
|
||||
icon: '/img/badges/trophy_green_flower.svg',
|
||||
}),
|
||||
trophyLifetree: await Factory.build('badge', {
|
||||
id: 'trophy_lifetree',
|
||||
type: 'trophy',
|
||||
description: 'You earned the tree of life',
|
||||
description: 'Has earned the tree of life',
|
||||
icon: '/img/badges/trophy_green_lifetree.svg',
|
||||
}),
|
||||
// Green Misc
|
||||
trophyDoublerainbow: await Factory.build('badge', {
|
||||
id: 'trophy_doublerainbow',
|
||||
type: 'trophy',
|
||||
description: 'You earned the Double Rainbow',
|
||||
description: 'Has earned the Double Rainbow',
|
||||
icon: '/img/badges/trophy_green_doublerainbow.svg',
|
||||
}),
|
||||
trophyEndrainbow: await Factory.build('badge', {
|
||||
id: 'trophy_endrainbow',
|
||||
type: 'trophy',
|
||||
description: 'You earned the End of the Rainbow',
|
||||
description: 'Has earned the End of the Rainbow',
|
||||
icon: '/img/badges/trophy_green_endrainbow.svg',
|
||||
}),
|
||||
trophyMagicrainbow: await Factory.build('badge', {
|
||||
id: 'trophy_magicrainbow',
|
||||
type: 'trophy',
|
||||
description: 'You earned the Magic Rainbow',
|
||||
description: 'Has earned the Magic Rainbow',
|
||||
icon: '/img/badges/trophy_green_magicrainbow.svg',
|
||||
}),
|
||||
trophyStarter: await Factory.build('badge', {
|
||||
id: 'trophy_starter',
|
||||
type: 'trophy',
|
||||
description: 'You earned the Starter Badge',
|
||||
description: 'Has earned the Starter Badge',
|
||||
icon: '/img/badges/trophy_green_starter.svg',
|
||||
}),
|
||||
trophySuperfounder: await Factory.build('badge', {
|
||||
id: 'trophy_superfounder',
|
||||
type: 'trophy',
|
||||
description: 'You earned the Super Founder Badge',
|
||||
description: 'Has earned the Super Founder Badge',
|
||||
icon: '/img/badges/trophy_green_superfounder.svg',
|
||||
}),
|
||||
}
|
||||
@ -169,19 +169,19 @@ export const verification = async () => {
|
||||
verificationModerator: await Factory.build('badge', {
|
||||
id: 'verification_moderator',
|
||||
type: 'verification',
|
||||
description: 'You are a Moderator',
|
||||
description: 'Is a Moderator',
|
||||
icon: '/img/badges/verification_red_moderator.svg',
|
||||
}),
|
||||
verificationAdmin: await Factory.build('badge', {
|
||||
id: 'verification_admin',
|
||||
type: 'verification',
|
||||
description: 'You are an Administrator',
|
||||
description: 'Is an Administrator',
|
||||
icon: '/img/badges/verification_red_admin.svg',
|
||||
}),
|
||||
verificationDeveloper: await Factory.build('badge', {
|
||||
id: 'verification_developer',
|
||||
type: 'verification',
|
||||
description: 'You are a Developer',
|
||||
description: 'Is a Developer',
|
||||
icon: '/img/badges/verification_red_developer.svg',
|
||||
}),
|
||||
}
|
||||
|
||||
32
backend/src/db/types/User.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Integer, Node } from 'neo4j-driver'
|
||||
|
||||
export interface UserDbProperties {
|
||||
allowEmbedIframes: boolean
|
||||
awaySince?: string
|
||||
createdAt: string
|
||||
deleted: boolean
|
||||
disabled: boolean
|
||||
emailNotificationsChatMessage?: boolean
|
||||
emailNotificationsCommentOnObservedPost?: boolean
|
||||
emailNotificationsFollowingUsers?: boolean
|
||||
emailNotificationsGroupMemberJoined?: boolean
|
||||
emailNotificationsGroupMemberLeft?: boolean
|
||||
emailNotificationsGroupMemberRemoved?: boolean
|
||||
emailNotificationsGroupMemberRoleChanged?: boolean
|
||||
emailNotificationsMention?: boolean
|
||||
emailNotificationsPostInGroup?: boolean
|
||||
encryptedPassword: string
|
||||
id: string
|
||||
lastActiveAt?: string
|
||||
lastOnlineStatus?: string
|
||||
locale: string
|
||||
name: string
|
||||
role: string
|
||||
showShoutsPublicly: boolean
|
||||
slug: string
|
||||
termsAndConditionsAgreedAt: string
|
||||
termsAndConditionsAgreedVersion: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type User = Node<Integer, UserDbProperties>
|
||||
@ -0,0 +1,255 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendChatMessageMail English chat_message template 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hello chatReceiver,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>you have received a new chat message from <a class="user" href="http://webapp:3000/user/chatSender/chatsender">chatSender</a>.
|
||||
</p><a class="button" href="http://webapp:3000/chat">Show Chat</a>
|
||||
<div class="text-block">
|
||||
<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>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "ocelot.social – Notification: New chat message",
|
||||
"text": "HELLO CHATRECEIVER,
|
||||
|
||||
you have received a new chat message from chatSender
|
||||
[http://webapp:3000/user/chatSender/chatsender].
|
||||
|
||||
Show Chat [http://webapp:3000/chat]
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
PS: If you don't want to receive e-mails anymore, change your notification
|
||||
settings [http://webapp:3000/settings/notifications]!
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendChatMessageMail German chat_message template 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hallo chatReceiver,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>du hast eine neue Chat-Nachricht von <a class="user" href="http://webapp:3000/user/chatSender/chatsender">chatSender</a> erhalten.
|
||||
</p><a class="button" href="http://webapp:3000/chat">Chat anzeigen</a>
|
||||
<div class="text-block">
|
||||
<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>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "ocelot.social – Benachrichtigung: Neue Chat Nachricht",
|
||||
"text": "HALLO CHATRECEIVER,
|
||||
|
||||
du hast eine neue Chat-Nachricht von chatSender
|
||||
[http://webapp:3000/user/chatSender/chatsender] erhalten.
|
||||
|
||||
Chat anzeigen [http://webapp:3000/chat]
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine
|
||||
Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]!
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,261 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendEmailVerification English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hello User,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>So, you want to change your e-mail? No problem! Just click the button below to verify your new address:</p><a class="button" href="http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456">Verify e-mail address</a>
|
||||
<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>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "New E-Mail Address ocelot.social",
|
||||
"text": "HELLO USER,
|
||||
|
||||
So, you want to change your e-mail? No problem! Just click the button below to
|
||||
verify your new address:
|
||||
|
||||
Verify e-mail address
|
||||
[http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456]
|
||||
|
||||
If you don't want to change your e-mail address feel free to ignore this
|
||||
message.
|
||||
|
||||
If the above button doesn't work, you can also copy the following code into your
|
||||
browser window: 123456
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendEmailVerification German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hallo User,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button kannst Du Deine neue E-Mail Adresse bestätigen:</p><a class="button" href="http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456">E-Mail Adresse bestätigen</a>
|
||||
<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>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Neue E-Mail Addresse ocelot.social",
|
||||
"text": "HALLO USER,
|
||||
|
||||
Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button
|
||||
kannst Du Deine neue E-Mail Adresse bestätigen:
|
||||
|
||||
E-Mail Adresse bestätigen
|
||||
[http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456]
|
||||
|
||||
Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese
|
||||
Nachricht einfach ignorieren.
|
||||
|
||||
Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in
|
||||
Dein Browserfenster kopieren: 123456
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
2281
backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap
Normal file
@ -0,0 +1,559 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendRegistrationMail with invite code English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<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=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>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Welcome to ocelot.social",
|
||||
"text": "WELCOME TO OCELOT.SOCIAL!
|
||||
|
||||
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:
|
||||
|
||||
Confirm your e-mail address
|
||||
[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
|
||||
|
||||
However, this only works if you have registered through our website.
|
||||
|
||||
If you didn't sign up for ocelot.social 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.
|
||||
|
||||
PS: If you ignore this e-mail we will not create an account for you. ;)
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail with invite code German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<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=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>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Willkommen bei ocelot.social",
|
||||
"text": "WILLKOMMEN BEI OCELOT.SOCIAL!
|
||||
|
||||
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:
|
||||
|
||||
Bestätige Deine E-Mail Adresse
|
||||
[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
|
||||
|
||||
Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert
|
||||
hast.
|
||||
|
||||
Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal
|
||||
vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.
|
||||
|
||||
PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach
|
||||
ignorieren. ;)
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail without invite code English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<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=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>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Welcome to ocelot.social",
|
||||
"text": "WELCOME TO OCELOT.SOCIAL!
|
||||
|
||||
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:
|
||||
|
||||
Confirm your e-mail address
|
||||
[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
|
||||
|
||||
However, this only works if you have registered through our website.
|
||||
|
||||
If you didn't sign up for ocelot.social 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.
|
||||
|
||||
PS: If you ignore this e-mail we will not create an account for you. ;)
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendRegistrationMail without invite code German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<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=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>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Willkommen bei ocelot.social",
|
||||
"text": "WILLKOMMEN BEI OCELOT.SOCIAL!
|
||||
|
||||
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:
|
||||
|
||||
Bestätige Deine E-Mail Adresse
|
||||
[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
|
||||
|
||||
Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert
|
||||
hast.
|
||||
|
||||
Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal
|
||||
vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.
|
||||
|
||||
PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach
|
||||
ignorieren. ;)
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,260 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendResetPasswordMail English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hello Jenny Rostock,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>So, you forgot your password? No problem! Just click the button below to reset it within the next 24 hours:</p><a class="button" href="http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456">Confirm your e-mail address</a>
|
||||
<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>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Reset Password ocelot.social",
|
||||
"text": "HELLO JENNY ROSTOCK,
|
||||
|
||||
So, you forgot your password? No problem! Just click the button below to reset
|
||||
it within the next 24 hours:
|
||||
|
||||
Confirm your e-mail address
|
||||
[http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456]
|
||||
|
||||
If you didn't request a new password feel free to ignore this e-mail.
|
||||
|
||||
If the above button doesn't work you can also copy the following code into your
|
||||
browser window: 123456
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendResetPasswordMail German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Hallo Jenny Rostock,</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen:</p><a class="button" href="http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456">Bestätige Deine E-Mail Adresse</a>
|
||||
<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>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Neues Passwort ocelot.social",
|
||||
"text": "HALLO JENNY ROSTOCK,
|
||||
|
||||
Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button
|
||||
kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen:
|
||||
|
||||
Bestätige Deine E-Mail Adresse
|
||||
[http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456]
|
||||
|
||||
Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach
|
||||
ignorieren.
|
||||
|
||||
Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in
|
||||
Dein Browserfenster kopieren: 123456
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
255
backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap
Normal file
@ -0,0 +1,255 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`sendWrongEmail English renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Welcome to ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address. Did you maybe use another one when you signed up?</p><a class="button" href="http://webapp:3000/password-reset/request">Try a different e-mail</a>
|
||||
<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>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– The ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Wrong E-mail? ocelot.social",
|
||||
"text": "WELCOME TO OCELOT.SOCIAL!
|
||||
|
||||
You requested a password reset but unfortunately we couldn't find an account
|
||||
associated with your e-mail address. Did you maybe use another one when you
|
||||
signed up?
|
||||
|
||||
Try a different e-mail [http://webapp:3000/password-reset/request]
|
||||
|
||||
If you don't have an account at ocelot.social yet or if you didn't want to reset
|
||||
your password, please ignore this e-mail.
|
||||
|
||||
See you soon on ocelot.social [https://ocelot.social]!
|
||||
|
||||
– The ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`sendWrongEmail German renders correctly 1`] = `
|
||||
{
|
||||
"attachments": [],
|
||||
"from": "ocelot.social <devops@ocelot.social>",
|
||||
"html": "<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||
<style>body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||
</div>
|
||||
</header>
|
||||
<h2>Willkommen bei ocelot.social!</h2>
|
||||
<div class="wrapper">
|
||||
<div class="content"></div>
|
||||
<p>Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer anderen Adresse bei uns angemeldet bist?</p><a class="button" href="http://webapp:3000/password-reset/request">Versuch' es mit einer anderen E-Mail</a>
|
||||
<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>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||
<p>– Dein ocelot.social Team</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>",
|
||||
"subject": "Falsche Mailaddresse? ocelot.social",
|
||||
"text": "WILLKOMMEN BEI OCELOT.SOCIAL!
|
||||
|
||||
Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen
|
||||
Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer
|
||||
anderen Adresse bei uns angemeldet bist?
|
||||
|
||||
Versuch' es mit einer anderen E-Mail [http://webapp:3000/password-reset/request]
|
||||
|
||||
Wenn du noch keinen Account bei ocelot.social hast oder dein Password gar nicht
|
||||
ändern willst, kannst du diese E-Mail einfach ignorieren!
|
||||
|
||||
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||
|
||||
– Dein ocelot.social Team
|
||||
|
||||
|
||||
ocelot.social Community [https://ocelot.social]",
|
||||
"to": "user@example.org",
|
||||
}
|
||||
`;
|
||||
71
backend/src/emails/locales/de.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"notification": "Benachrichtigung",
|
||||
"subjects": {
|
||||
"changedGroupMemberRole": "Rolle in Gruppe geändert",
|
||||
"chatMessage": "Neue Chat Nachricht",
|
||||
"commentedOnPost": "Neuer Kommentar zu Beitrag",
|
||||
"followedUserPosted": "Neuer Beitrag von gefolgtem Nutzer",
|
||||
"mentionedInComment": "Erwähnung in Kommentar",
|
||||
"mentionedInPost": "Erwähnung in Beitrag",
|
||||
"newEmail": "Neue E-Mail Addresse",
|
||||
"removedUserFromGroup": "Aus Gruppe entfernt",
|
||||
"postInGroup": "Neuer Beitrag in Gruppe",
|
||||
"resetPassword": "Neues Passwort",
|
||||
"userJoinedGroup": "Nutzer tritt Gruppe bei",
|
||||
"userLeftGroup": "Nutzer verlässt Gruppe",
|
||||
"wrongEmail": "Falsche Mailaddresse?"
|
||||
},
|
||||
"registration": {
|
||||
"introduction": "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:",
|
||||
"codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ",
|
||||
"codeHintException": "Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert hast.",
|
||||
"notYouStart": "Falls Du Dich nicht selbst bei ",
|
||||
"notYouEnd": " angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.",
|
||||
"ps": "PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach ignorieren. ;)"
|
||||
},
|
||||
"emailVerification": {
|
||||
"codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ",
|
||||
"introduction": "Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button kannst Du Deine neue E-Mail Adresse bestätigen:",
|
||||
"doNotChange": "Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese Nachricht einfach ignorieren. "
|
||||
},
|
||||
"buttons": {
|
||||
"confirmEmail": "Bestätige Deine E-Mail Adresse",
|
||||
"resetPassword": "Passwort zurücksetzen",
|
||||
"tryAgain": "Versuch' es mit einer anderen E-Mail",
|
||||
"verifyEmail": "E-Mail Adresse bestätigen",
|
||||
"viewChat": "Chat anzeigen",
|
||||
"viewComment": "Kommentar ansehen",
|
||||
"viewGroup": "Gruppe ansehen",
|
||||
"viewPost": "Beitrag ansehen"
|
||||
},
|
||||
"general": {
|
||||
"greeting": "Hallo",
|
||||
"seeYou": "Bis bald bei ",
|
||||
"yourTeam": "– Dein {team} Team",
|
||||
"settingsHint": "PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine ",
|
||||
"settingsName": "Benachrichtigungseinstellungen",
|
||||
"welcome": "Willkommen bei"
|
||||
},
|
||||
"resetPassword": {
|
||||
"codeHint": "Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in Dein Browserfenster kopieren: ",
|
||||
"ignore": "Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.",
|
||||
"introduction": "Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen:"
|
||||
},
|
||||
"wrongEmail": {
|
||||
"codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ",
|
||||
"ignoreEnd": " hast oder dein Password gar nicht ändern willst, kannst du diese E-Mail einfach ignorieren!",
|
||||
"ignoreStart": "Wenn du noch keinen Account bei ",
|
||||
"introduction": "Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer anderen Adresse bei uns angemeldet bist?"
|
||||
},
|
||||
"changedGroupMemberRole": "deine Rolle in der Gruppe „{groupName}“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:",
|
||||
"chatMessageStart": "du hast eine neue Chat-Nachricht von ",
|
||||
"chatMessageEnd": " erhalten.",
|
||||
"commentedOnPost": " hat einen Beitrag den du beobachtest mit dem Titel „{postTitle}“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen:",
|
||||
"followedUserPosted": ", ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „{postTitle}“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:",
|
||||
"mentionedInComment": " hat dich in einem Kommentar zu dem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen:",
|
||||
"mentionedInPost": " hat Dich in einem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen:",
|
||||
"postInGroup": "jemand hat einen neuen Beitrag mit dem Titel „{postTitle}“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:",
|
||||
"removedUserFromGroup": "du wurdest aus der Gruppe „{groupName}“ entfernt.",
|
||||
"userJoinedGroup": " ist der Gruppe „{groupName}“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen:",
|
||||
"userLeftGroup": " hat die Gruppe „{groupName}“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen:"
|
||||
}
|
||||
70
backend/src/emails/locales/en.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"notification": "Notification",
|
||||
"subjects": {
|
||||
"changedGroupMemberRole": "Role in group changed",
|
||||
"chatMessage": "New chat message",
|
||||
"commentedOnPost": "New comment on post",
|
||||
"followedUserPosted": "New post by followd user",
|
||||
"mentionedInComment": "Mentioned in comment",
|
||||
"mentionedInPost": "Mentioned in post",
|
||||
"newEmail": "New E-Mail Address",
|
||||
"removedUserFromGroup": "Removed from group",
|
||||
"postInGroup": "New post in group",
|
||||
"resetPassword": "Reset Password",
|
||||
"userJoinedGroup": "User joined group",
|
||||
"userLeftGroup": "User left group",
|
||||
"wrongEmail": "Wrong E-mail?"
|
||||
},
|
||||
"registration": {
|
||||
"introduction": "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:",
|
||||
"codeHint": "If the above button doesn't work, you can also copy the following code into your browser window: ",
|
||||
"codeHintException": "However, this only works if you have registered through our website.",
|
||||
"notYouStart": "If you didn't sign up for ",
|
||||
"notYouEnd": " 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.",
|
||||
"ps": "PS: If you ignore this e-mail we will not create an account for you. ;)"
|
||||
},
|
||||
"emailVerification": {
|
||||
"codeHint": "If the above button doesn't work, you can also copy the following code into your browser window: ",
|
||||
"introduction": "So, you want to change your e-mail? No problem! Just click the button below to verify your new address:",
|
||||
"doNotChange": "If you don't want to change your e-mail address feel free to ignore this message. "
|
||||
},
|
||||
"buttons": {
|
||||
"confirmEmail": "Confirm your e-mail address",
|
||||
"resetPassword": "Reset password",
|
||||
"tryAgain": "Try a different e-mail",
|
||||
"verifyEmail": "Verify e-mail address",
|
||||
"viewChat": "Show Chat",
|
||||
"viewComment": "View comment",
|
||||
"viewGroup": "View group",
|
||||
"viewPost": "View post"
|
||||
},
|
||||
"general": {
|
||||
"greeting": "Hello",
|
||||
"seeYou": "See you soon on ",
|
||||
"yourTeam": "– The {team} Team",
|
||||
"settingsHint": "PS: If you don't want to receive e-mails anymore, change your ",
|
||||
"settingsName": "notification settings",
|
||||
"welcome": "Welcome to"
|
||||
},
|
||||
"resetPassword": {
|
||||
"codeHint": "If the above button doesn't work you can also copy the following code into your browser window: ",
|
||||
"ignore": "If you didn't request a new password feel free to ignore this e-mail.",
|
||||
"introduction": "So, you forgot your password? No problem! Just click the button below to reset it within the next 24 hours:"
|
||||
},
|
||||
"wrongEmail": {
|
||||
"ignoreEnd": " yet or if you didn't want to reset your password, please ignore this e-mail.",
|
||||
"ignoreStart": "If you don't have an account at ",
|
||||
"introduction": "You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address. Did you maybe use another one when you signed up?"
|
||||
},
|
||||
"changedGroupMemberRole": "your role in the group “{groupName}” has been changed. Click on the button to view this group:",
|
||||
"chatMessageStart": "you have received a new chat message from ",
|
||||
"chatMessageEnd": ".",
|
||||
"commentedOnPost": " commented on a post that you are observing with the title “{postTitle}”. Click on the button to view this comment:",
|
||||
"followedUserPosted": ", a user you are following, wrote a new post with the title “{postTitle}”. Click on the button to view this post:",
|
||||
"mentionedInComment": " mentioned you in a comment to the post with the title “{postTitle}”. Click on the button to view this comment:",
|
||||
"mentionedInPost": " mentioned you in a post with the title “{postTitle}”. Click on the button to view this post:",
|
||||
"removedUserFromGroup": "you have been removed from the group “{groupName}”.",
|
||||
"postInGroup": "someone wrote a new post with the title “{postTitle}” in one of your groups. Click on the button to view this post:",
|
||||
"userJoinedGroup": " joined the group “{groupName}”. Click on the button to view this group:",
|
||||
"userLeftGroup": " left the group “{groupName}”. Click on the button to view this group:"
|
||||
}
|
||||
87
backend/src/emails/sendChatMessageMail.spec.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { sendChatMessageMail } from './sendEmail'
|
||||
|
||||
const senderUser = {
|
||||
allowEmbedIframes: false,
|
||||
createdAt: '2025-04-30T00:16:49.610Z',
|
||||
deleted: false,
|
||||
disabled: false,
|
||||
emailNotificationsChatMessage: true,
|
||||
emailNotificationsCommentOnObservedPost: true,
|
||||
emailNotificationsFollowingUsers: true,
|
||||
emailNotificationsGroupMemberJoined: true,
|
||||
emailNotificationsGroupMemberLeft: true,
|
||||
emailNotificationsGroupMemberRemoved: true,
|
||||
emailNotificationsGroupMemberRoleChanged: true,
|
||||
emailNotificationsMention: true,
|
||||
emailNotificationsPostInGroup: true,
|
||||
encryptedPassword: '$2b$10$n.WujXapJrvn498lS97MD.gn8QwjWI9xlf8ckEYYtMTOPadMidcbG',
|
||||
id: 'chatSender',
|
||||
locale: 'en',
|
||||
name: 'chatSender',
|
||||
role: 'user',
|
||||
showShoutsPublicly: false,
|
||||
slug: 'chatsender',
|
||||
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
|
||||
termsAndConditionsAgreedVersion: '0.0.1',
|
||||
updatedAt: '2025-04-30T00:16:49.610Z',
|
||||
}
|
||||
|
||||
const recipientUser = {
|
||||
allowEmbedIframes: false,
|
||||
createdAt: '2025-04-30T00:16:49.716Z',
|
||||
deleted: false,
|
||||
disabled: false,
|
||||
emailNotificationsChatMessage: true,
|
||||
emailNotificationsCommentOnObservedPost: true,
|
||||
emailNotificationsFollowingUsers: true,
|
||||
emailNotificationsGroupMemberJoined: true,
|
||||
emailNotificationsGroupMemberLeft: true,
|
||||
emailNotificationsGroupMemberRemoved: true,
|
||||
emailNotificationsGroupMemberRoleChanged: true,
|
||||
emailNotificationsMention: true,
|
||||
emailNotificationsPostInGroup: true,
|
||||
encryptedPassword: '$2b$10$KOrCHvEB5CM7D.P3VcX2z.pSSBZKZhPqHW/QKym6V1S6fiG..xtBq',
|
||||
id: 'chatReceiver',
|
||||
locale: 'en',
|
||||
name: 'chatReceiver',
|
||||
role: 'user',
|
||||
showShoutsPublicly: false,
|
||||
slug: 'chatreceiver',
|
||||
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
|
||||
termsAndConditionsAgreedVersion: '0.0.1',
|
||||
updatedAt: '2025-04-30T00:16:49.716Z',
|
||||
}
|
||||
|
||||
describe('sendChatMessageMail', () => {
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
recipientUser.locale = 'en'
|
||||
})
|
||||
|
||||
it('chat_message template', async () => {
|
||||
await expect(
|
||||
sendChatMessageMail({
|
||||
email: 'user@example.org',
|
||||
senderUser,
|
||||
recipientUser,
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
recipientUser.locale = 'de'
|
||||
})
|
||||
|
||||
it('chat_message template', async () => {
|
||||
await expect(
|
||||
sendChatMessageMail({
|
||||
email: 'user@example.org',
|
||||
senderUser,
|
||||
recipientUser,
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
321
backend/src/emails/sendEmail.ts
Normal file
@ -0,0 +1,321 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
import path from 'node:path'
|
||||
|
||||
import Email from 'email-templates'
|
||||
import { createTransport } from 'nodemailer'
|
||||
|
||||
// import type Email as EmailType from '@types/email-templates'
|
||||
|
||||
import CONFIG, { nodemailerTransportOptions } from '@config/index'
|
||||
import logosWebapp from '@config/logos'
|
||||
import metadata from '@config/metadata'
|
||||
import { UserDbProperties } from '@db/types/User'
|
||||
|
||||
const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI)
|
||||
const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI)
|
||||
|
||||
const defaultParams = {
|
||||
welcomeImageUrl,
|
||||
APPLICATION_NAME: CONFIG.APPLICATION_NAME,
|
||||
ORGANIZATION_NAME: metadata.ORGANIZATION_NAME,
|
||||
ORGANIZATION_URL: CONFIG.ORGANIZATION_URL,
|
||||
supportUrl: CONFIG.SUPPORT_URL,
|
||||
settingsUrl,
|
||||
renderSettingsUrl: true,
|
||||
}
|
||||
|
||||
const from = `${CONFIG.APPLICATION_NAME} <${CONFIG.EMAIL_DEFAULT_SENDER}>`
|
||||
|
||||
const transport = createTransport(nodemailerTransportOptions)
|
||||
|
||||
const email = new Email({
|
||||
message: {
|
||||
from,
|
||||
},
|
||||
transport,
|
||||
i18n: {
|
||||
locales: ['en', 'de'],
|
||||
defaultLocale: 'en',
|
||||
retryInDefaultLocale: false,
|
||||
directory: path.join(__dirname, 'locales'),
|
||||
updateFiles: false,
|
||||
objectNotation: true,
|
||||
mustacheConfig: {
|
||||
tags: ['{', '}'],
|
||||
disable: false,
|
||||
},
|
||||
},
|
||||
send: CONFIG.SEND_MAIL,
|
||||
preview: false,
|
||||
// This is very useful to see the emails sent by the unit tests
|
||||
/*
|
||||
preview: {
|
||||
open: {
|
||||
app: 'brave-browser',
|
||||
},
|
||||
},
|
||||
*/
|
||||
})
|
||||
|
||||
interface OriginalMessage {
|
||||
to: string
|
||||
from: string
|
||||
attachments: string[]
|
||||
subject: string
|
||||
html: string
|
||||
text: string
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const sendNotificationMail = async (notification: any): Promise<OriginalMessage> => {
|
||||
const locale = notification?.to?.locale
|
||||
const to = notification?.email
|
||||
const name = notification?.to?.name
|
||||
const template = notification?.reason
|
||||
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', template),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale,
|
||||
name,
|
||||
postTitle:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? notification?.from?.post?.title
|
||||
: notification?.from?.title,
|
||||
postUrl: new URL(
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}`
|
||||
: `/post/${notification?.from?.id}/${notification?.from?.slug}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
),
|
||||
postAuthorName:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? undefined
|
||||
: notification?.from?.author?.name,
|
||||
postAuthorUrl:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? undefined
|
||||
: new URL(
|
||||
`user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
),
|
||||
commenterName:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? notification?.from?.author?.name
|
||||
: undefined,
|
||||
commenterUrl:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? new URL(
|
||||
`/user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
)
|
||||
: undefined,
|
||||
commentUrl:
|
||||
notification?.from?.__typename === 'Comment'
|
||||
? new URL(
|
||||
`/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
)
|
||||
: undefined,
|
||||
// chattingUser: 'SR-71',
|
||||
// chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
|
||||
groupUrl:
|
||||
notification?.from?.__typename === 'Group'
|
||||
? new URL(
|
||||
`/group/${notification?.from?.id}/${notification?.from?.slug}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
)
|
||||
: undefined,
|
||||
groupName:
|
||||
notification?.from?.__typename === 'Group' ? notification?.from?.name : undefined,
|
||||
groupRelatedUserName:
|
||||
notification?.from?.__typename === 'Group' ? notification?.relatedUser?.name : undefined,
|
||||
groupRelatedUserUrl:
|
||||
notification?.from?.__typename === 'Group'
|
||||
? new URL(
|
||||
`/user/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`,
|
||||
CONFIG.CLIENT_URI,
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatMessageEmailInput {
|
||||
senderUser: UserDbProperties
|
||||
recipientUser: UserDbProperties
|
||||
email: string
|
||||
}
|
||||
|
||||
export const sendChatMessageMail = async (
|
||||
data: ChatMessageEmailInput,
|
||||
): Promise<OriginalMessage> => {
|
||||
const { senderUser, recipientUser } = data
|
||||
const to = data.email
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', 'chat_message'),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale: recipientUser.locale,
|
||||
name: recipientUser.name,
|
||||
chattingUser: senderUser.name,
|
||||
chattingUserUrl: new URL(`/user/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI),
|
||||
chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
interface VerifyMailInput {
|
||||
email: string
|
||||
nonce: string
|
||||
locale: string
|
||||
}
|
||||
|
||||
interface RegistrationMailInput extends VerifyMailInput {
|
||||
inviteCode?: string
|
||||
}
|
||||
|
||||
export const sendRegistrationMail = async (
|
||||
data: RegistrationMailInput,
|
||||
): Promise<OriginalMessage> => {
|
||||
const { nonce, locale, inviteCode } = data
|
||||
const to = data.email
|
||||
const actionUrl = new URL('/registration', CONFIG.CLIENT_URI)
|
||||
actionUrl.searchParams.set('email', to)
|
||||
actionUrl.searchParams.set('nonce', nonce)
|
||||
if (inviteCode) {
|
||||
actionUrl.searchParams.set('inviteCode', inviteCode)
|
||||
actionUrl.searchParams.set('method', 'invite-code')
|
||||
} else {
|
||||
actionUrl.searchParams.set('method', 'invite-mail')
|
||||
}
|
||||
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', 'registration'),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale,
|
||||
actionUrl,
|
||||
nonce,
|
||||
renderSettingsUrl: false,
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
interface EmailVerificationInput extends VerifyMailInput {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const sendEmailVerification = async (
|
||||
data: EmailVerificationInput,
|
||||
): Promise<OriginalMessage> => {
|
||||
const { nonce, locale, name } = data
|
||||
const to = data.email
|
||||
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
|
||||
actionUrl.searchParams.set('email', to)
|
||||
actionUrl.searchParams.set('nonce', nonce)
|
||||
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', 'emailVerification'),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale,
|
||||
actionUrl,
|
||||
nonce,
|
||||
name,
|
||||
renderSettingsUrl: false,
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export const sendResetPasswordMail = async (
|
||||
data: EmailVerificationInput,
|
||||
): Promise<OriginalMessage> => {
|
||||
const { nonce, locale, name } = data
|
||||
const to = data.email
|
||||
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI)
|
||||
actionUrl.searchParams.set('email', to)
|
||||
actionUrl.searchParams.set('nonce', nonce)
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', 'resetPassword'),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale,
|
||||
actionUrl,
|
||||
nonce,
|
||||
name,
|
||||
renderSettingsUrl: false,
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export const sendWrongEmail = async (data: {
|
||||
locale: string
|
||||
email: string
|
||||
}): Promise<OriginalMessage> => {
|
||||
const { locale } = data
|
||||
const to = data.email
|
||||
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)
|
||||
try {
|
||||
const { originalMessage } = await email.send({
|
||||
template: path.join(__dirname, 'templates', 'wrongEmail'),
|
||||
message: {
|
||||
to,
|
||||
},
|
||||
locals: {
|
||||
...defaultParams,
|
||||
locale,
|
||||
actionUrl,
|
||||
renderSettingsUrl: false,
|
||||
},
|
||||
})
|
||||
return originalMessage as OriginalMessage
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
35
backend/src/emails/sendEmailVerification.spec.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { sendEmailVerification } from './sendEmail'
|
||||
|
||||
describe('sendEmailVerification', () => {
|
||||
const data: {
|
||||
email: string
|
||||
nonce: string
|
||||
locale: string
|
||||
name: string
|
||||
} = {
|
||||
email: 'user@example.org',
|
||||
nonce: '123456',
|
||||
locale: 'en',
|
||||
name: 'User',
|
||||
}
|
||||
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'en'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendEmailVerification(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'de'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendEmailVerification(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
475
backend/src/emails/sendNotificationMail.spec.ts
Normal file
@ -0,0 +1,475 @@
|
||||
import { sendNotificationMail } from './sendEmail'
|
||||
|
||||
describe('sendNotificationMail', () => {
|
||||
let locale = 'en'
|
||||
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
locale = 'en'
|
||||
})
|
||||
|
||||
it('followed_user_posted template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'followed_user_posted',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('post_in_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'post_in_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('mentioned_in_post template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'mentioned_in_post',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('commented_on_post template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'commented_on_post',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c1',
|
||||
slug: 'new-comment',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
post: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('mentioned_in_comment template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'mentioned_in_comment',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c1',
|
||||
slug: 'new-comment',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
post: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('changed_group_member_role template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'changed_group_member_role',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('user_joined_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'user_joined_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
relatedUser: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('user_left_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'user_left_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
relatedUser: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('removed_user_from_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'removed_user_from_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
locale = 'de'
|
||||
})
|
||||
|
||||
it('followed_user_posted template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'followed_user_posted',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('post_in_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'post_in_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('mentioned_in_post template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'mentioned_in_post',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('commented_on_post template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'commented_on_post',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c1',
|
||||
slug: 'new-comment',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
post: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('mentioned_in_comment template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'mentioned_in_comment',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Comment',
|
||||
id: 'c1',
|
||||
slug: 'new-comment',
|
||||
author: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
post: {
|
||||
id: 'p1',
|
||||
slug: 'new-post',
|
||||
title: 'New Post',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('changed_group_member_role template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'changed_group_member_role',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('user_joined_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'user_joined_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
relatedUser: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('user_left_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'user_left_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
relatedUser: {
|
||||
id: 'u2',
|
||||
name: 'Peter Lustig',
|
||||
slug: 'peter-lustig',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('removed_user_from_group template', async () => {
|
||||
await expect(
|
||||
sendNotificationMail({
|
||||
reason: 'removed_user_from_group',
|
||||
email: 'user@example.org',
|
||||
to: {
|
||||
name: 'Jenny Rostock',
|
||||
id: 'u1',
|
||||
slug: 'jenny-rostock',
|
||||
locale,
|
||||
},
|
||||
from: {
|
||||
__typename: 'Group',
|
||||
id: 'g1',
|
||||
slug: 'the-group',
|
||||
name: 'The Group',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
63
backend/src/emails/sendRegistrationMail.spec.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { sendRegistrationMail } from './sendEmail'
|
||||
|
||||
describe('sendRegistrationMail', () => {
|
||||
const data: {
|
||||
email: string
|
||||
nonce: string
|
||||
locale: string
|
||||
inviteCode?: string
|
||||
} = {
|
||||
email: 'user@example.org',
|
||||
nonce: '123456',
|
||||
locale: 'en',
|
||||
inviteCode: 'welcome',
|
||||
}
|
||||
|
||||
describe('with invite code', () => {
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'en'
|
||||
data.inviteCode = 'welcome'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'de'
|
||||
data.inviteCode = 'welcome'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('without invite code', () => {
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'en'
|
||||
delete data.inviteCode
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'de'
|
||||
delete data.inviteCode
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
35
backend/src/emails/sendResetPasswordMail.spec.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { sendResetPasswordMail } from './sendEmail'
|
||||
|
||||
describe('sendResetPasswordMail', () => {
|
||||
const data: {
|
||||
email: string
|
||||
nonce: string
|
||||
locale: string
|
||||
name: string
|
||||
} = {
|
||||
email: 'user@example.org',
|
||||
nonce: '123456',
|
||||
locale: 'en',
|
||||
name: 'Jenny Rostock',
|
||||
}
|
||||
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'en'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendResetPasswordMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'de'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendResetPasswordMail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
31
backend/src/emails/sendWrongEmail.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { sendWrongEmail } from './sendEmail'
|
||||
|
||||
describe('sendWrongEmail', () => {
|
||||
const data: {
|
||||
email: string
|
||||
locale: string
|
||||
} = {
|
||||
email: 'user@example.org',
|
||||
locale: 'en',
|
||||
}
|
||||
|
||||
describe('English', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'en'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendWrongEmail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('German', () => {
|
||||
beforeEach(() => {
|
||||
data.locale = 'de'
|
||||
})
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await expect(sendWrongEmail(data)).resolves.toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,7 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
- var groupUrl = groupUrl
|
||||
p= t('changedGroupMemberRole', { groupName })
|
||||
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.changedGroupMemberRole')}`
|
||||
8
backend/src/emails/templates/chat_message/html.pug
Normal file
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('chatMessageStart')
|
||||
a.user(href=chattingUserUrl)= chattingUser
|
||||
= t('chatMessageEnd')
|
||||
a.button(href=chatUrl)= t('buttons.viewChat')
|
||||
1
backend/src/emails/templates/chat_message/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.chatMessage')}`
|
||||
8
backend/src/emails/templates/commented_on_post/html.pug
Normal file
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=commenterUrl)= commenterName
|
||||
= t('commentedOnPost', { postTitle})
|
||||
a.button(href=commentUrl)= t('buttons.viewComment')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.commentedOnPost')}`
|
||||
10
backend/src/emails/templates/emailVerification/html.pug
Normal file
@ -0,0 +1,10 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('emailVerification.introduction')
|
||||
a.button(href=actionUrl)= t('buttons.verifyEmail')
|
||||
p= t('emailVerification.doNotChange')
|
||||
|
||||
p= t('emailVerification.codeHint')
|
||||
span= nonce
|
||||
@ -0,0 +1 @@
|
||||
= `${t('subjects.newEmail')} ${APPLICATION_NAME}`
|
||||
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=postAuthorUrl)= postAuthorName
|
||||
= t('followedUserPosted', { postTitle })
|
||||
a.button(href=postUrl)= t('buttons.viewPost')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.followedUserPosted')}`
|
||||
5
backend/src/emails/templates/includes/footer.pug
Normal file
@ -0,0 +1,5 @@
|
||||
footer
|
||||
.footer
|
||||
- var organizationUrl = ORGANIZATION_URL
|
||||
- var organizationName = ORGANIZATION_NAME
|
||||
a(href=organizationUrl)= organizationName
|
||||
17
backend/src/emails/templates/includes/greeting.pug
Normal file
@ -0,0 +1,17 @@
|
||||
//- This sets the greeting at the end of every e-mail
|
||||
.text-block
|
||||
- var organizationUrl = ORGANIZATION_URL
|
||||
- var team = APPLICATION_NAME
|
||||
- var settingsUrl = settingsUrl
|
||||
- var renderSettingsUrl = renderSettingsUrl
|
||||
p= t('general.seeYou')
|
||||
a.organization(href=organizationUrl)= team
|
||||
| !
|
||||
p= t('general.yourTeam', { team })
|
||||
|
||||
if renderSettingsUrl
|
||||
br
|
||||
p= t('general.settingsHint')
|
||||
a.settings(href=settingsUrl)= t('general.settingsName')
|
||||
| !
|
||||
|
||||
9
backend/src/emails/templates/includes/header.pug
Normal file
@ -0,0 +1,9 @@
|
||||
header
|
||||
.head
|
||||
- var img = welcomeImageUrl
|
||||
img.head-logo(
|
||||
alt="Welcome Image"
|
||||
loading="lazy"
|
||||
src=img
|
||||
)
|
||||
|
||||
1
backend/src/emails/templates/includes/salutation.pug
Normal file
@ -0,0 +1 @@
|
||||
h2= `${t('general.greeting')} ${name},`
|
||||
69
backend/src/emails/templates/includes/webflow.css
Normal file
@ -0,0 +1,69 @@
|
||||
body{
|
||||
display: block;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 17px;
|
||||
text-align: left;
|
||||
text-align: -webkit-left;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
font-size: 25px;
|
||||
font-weight: normal;
|
||||
line-height: 22px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.head-logo {
|
||||
width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background: #17b53e;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 15px;
|
||||
text-decoration: none;
|
||||
text-align:center;
|
||||
padding: 13px 17px;
|
||||
color: #ffffff;
|
||||
display: table;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #17b53e;
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 20px;
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
color: #888888;
|
||||
}
|
||||
1
backend/src/emails/templates/includes/welcome.pug
Normal file
@ -0,0 +1 @@
|
||||
h2= `${t('general.welcome')} ${APPLICATION_NAME}!`
|
||||
30
backend/src/emails/templates/layout.pug
Normal file
@ -0,0 +1,30 @@
|
||||
doctype html
|
||||
html(lang=locale)
|
||||
head
|
||||
meta(
|
||||
content="multipart/html; charset=UTF-8"
|
||||
http-equiv="content-type"
|
||||
)
|
||||
meta(
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
)
|
||||
style.
|
||||
.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}
|
||||
style
|
||||
include includes/webflow.css
|
||||
|
||||
- var name = name
|
||||
body
|
||||
div.container
|
||||
include includes/header.pug
|
||||
if name
|
||||
include includes/salutation.pug
|
||||
else
|
||||
include includes/welcome.pug
|
||||
|
||||
.wrapper
|
||||
block content
|
||||
include includes/greeting.pug
|
||||
|
||||
include includes/footer.pug
|
||||
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=commenterUrl)= commenterName
|
||||
= t('mentionedInComment', { postTitle})
|
||||
a.button(href=commentUrl)= t('buttons.viewComment')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInComment')}`
|
||||
8
backend/src/emails/templates/mentioned_in_post/html.pug
Normal file
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=postAuthorUrl)= postAuthorName
|
||||
= t('mentionedInPost', { postTitle })
|
||||
a.button(href=postUrl)= t('buttons.viewPost')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInPost')}`
|
||||
7
backend/src/emails/templates/post_in_group/html.pug
Normal file
@ -0,0 +1,7 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
- var postUrl = postUrl
|
||||
p= t('postInGroup', { postTitle})
|
||||
a.button(href=postUrl)= t('buttons.viewPost')
|
||||
1
backend/src/emails/templates/post_in_group/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.postInGroup')}`
|
||||
15
backend/src/emails/templates/registration/html.pug
Normal file
@ -0,0 +1,15 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('registration.introduction')
|
||||
a.button(href=actionUrl)= t('buttons.confirmEmail')
|
||||
p= t('registration.codeHint')
|
||||
span= nonce
|
||||
p= t('registration.codeHintException')
|
||||
|
||||
p= t('registration.notYouStart')
|
||||
a(href=ORGANIZATION_LINK)= APPLICATION_NAME
|
||||
= t('registration.notYouEnd')
|
||||
|
||||
p= t('registration.ps')
|
||||
1
backend/src/emails/templates/registration/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${t('general.welcome')} ${APPLICATION_NAME}`
|
||||
@ -0,0 +1,5 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('removedUserFromGroup', { groupName })
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.removedUserFromGroup')}`
|
||||
9
backend/src/emails/templates/resetPassword/html.pug
Normal file
@ -0,0 +1,9 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('resetPassword.introduction')
|
||||
a.button(href=actionUrl)= t('buttons.confirmEmail')
|
||||
p= t('resetPassword.ignore')
|
||||
p= t('resetPassword.codeHint')
|
||||
span= nonce
|
||||
1
backend/src/emails/templates/resetPassword/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${t('subjects.resetPassword')} ${APPLICATION_NAME}`
|
||||
8
backend/src/emails/templates/user_joined_group/html.pug
Normal file
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=groupRelatedUserUrl)= groupRelatedUserName
|
||||
= t('userJoinedGroup', { groupName })
|
||||
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userJoinedGroup')}`
|
||||
8
backend/src/emails/templates/user_left_group/html.pug
Normal file
@ -0,0 +1,8 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p
|
||||
a.user(href=groupRelatedUserUrl)= groupRelatedUserName
|
||||
= t('userLeftGroup', { groupName })
|
||||
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||
1
backend/src/emails/templates/user_left_group/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userLeftGroup')}`
|
||||
10
backend/src/emails/templates/wrongEmail/html.pug
Normal file
@ -0,0 +1,10 @@
|
||||
extend ../layout.pug
|
||||
|
||||
block content
|
||||
.content
|
||||
p= t('wrongEmail.introduction')
|
||||
a.button(href=actionUrl)= t('buttons.tryAgain')
|
||||
|
||||
p= t('wrongEmail.ignoreStart')
|
||||
a(href=ORGANIZATION_LINK)= APPLICATION_NAME
|
||||
= t('wrongEmail.ignoreEnd')
|
||||
1
backend/src/emails/templates/wrongEmail/subject.pug
Normal file
@ -0,0 +1 @@
|
||||
= `${t('subjects.wrongEmail')} ${APPLICATION_NAME}`
|
||||