diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46d80241f..1740c09fe 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -329,19 +329,16 @@ jobs: - name: backend | docker-compose run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend - name: cypress | Fullstack tests + id: e2e-tests run: | yarn install yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} ) ########################################################################## - # UPLOAD SCREENSHOTS & VIDEO ############################################# + # UPLOAD SCREENSHOTS - IF TESTS FAIL ##################################### ########################################################################## - - name: Upload Artifact + - name: Full stack tests | if any test failed, upload screenshots + if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} uses: actions/upload-artifact@v3 with: name: cypress-screenshots path: cypress/screenshots/ - - name: Upload Artifact - uses: actions/upload-artifact@v3 - with: - name: cypress-videos - path: cypress/videos/ diff --git a/README.md b/README.md index 8ddf97fe5..06e8d4929 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,8 @@ Prepare database once before you start by running the following command in a sec ```bash # in main folder while docker-compose is up -$ docker-compose exec backend yarn run db:migrate init +$ docker compose exec backend yarn run db:migrate init +$ docker compose exec backend yarn run db:migrate up ``` Then clear and seed database by running the following command as well in the second terminal: diff --git a/backend/README.md b/backend/README.md index 03724ce54..98b0e7218 100644 --- a/backend/README.md +++ b/backend/README.md @@ -81,8 +81,7 @@ More details about our GraphQL playground and how to use it with ocelot.social c ### Database Indexes and Constraints -Database indexes and constraints need to be created when the database and the -backend is running: +Database indexes and constraints need to be created and upgraded when the database and the backend are running: {% tabs %} {% tab title="Docker" %} @@ -98,6 +97,11 @@ $ docker compose exec backend yarn prod:migrate init $ docker compose exec backend /bin/sh -c "yarn prod:migrate init" ``` +```bash +# in main folder with docker compose running +$ docker exec backend yarn run db:migrate up +``` + {% endtab %} {% tab title="Without Docker" %} @@ -107,6 +111,11 @@ $ docker compose exec backend /bin/sh -c "yarn prod:migrate init" yarn run db:migrate init ``` +```bash +# in backend/ with database running (In docker or local) +yarn run db:migrate up +``` + {% endtab %} {% endtabs %} @@ -134,6 +143,8 @@ $ docker exec backend yarn run db:reset $ docker-compose down -v # if container is not running, run this command to set up your database indexes and constraints $ docker exec backend yarn run db:migrate init +# And then upgrade the indexes and const +$ docker exec backend yarn run db:migrate up ``` {% endtab %} diff --git a/backend/src/db/factories.js b/backend/src/db/factories.js index ecc792736..44af5bbea 100644 --- a/backend/src/db/factories.js +++ b/backend/src/db/factories.js @@ -23,6 +23,7 @@ export const cleanDatabase = async (options = {}) => { return transaction.run( ` MATCH (everything) + WHERE NOT 'Migration' IN labels(everything) DETACH DELETE everything `, ) diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index 4a13dcc88..5ec1700b9 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -16,6 +16,7 @@ export default { Group: async (_object, params, context, _resolveInfo) => { const { isMember, id, slug, first, offset } = params let pagination = '' + const orderBy = 'ORDER BY group.createdAt DESC' if (first !== undefined && offset !== undefined) pagination = `SKIP ${offset} LIMIT ${first}` const matchParams = { id, slug } removeUndefinedNullValuesFromObject(matchParams) @@ -29,6 +30,7 @@ export default { WITH group, membership WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner']) RETURN group {.*, myRole: membership.role} + ${orderBy} ${pagination} ` } else { @@ -39,6 +41,7 @@ export default { WITH group WHERE group.groupType IN ['public', 'closed'] RETURN group {.*, myRole: NULL} + ${orderBy} ${pagination} ` } else { @@ -48,6 +51,7 @@ export default { WITH group, membership WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner']) RETURN group {.*, myRole: membership.role} + ${orderBy} ${pagination} ` } diff --git a/cypress/cypress.json b/cypress/cypress.json index dbe8691fa..de323f736 100644 --- a/cypress/cypress.json +++ b/cypress/cypress.json @@ -4,6 +4,7 @@ "ignoreTestFiles": "*.js", "chromeWebSecurity": false, "baseUrl": "http://localhost:3000", + "video":false, "retries": { "runMode": 2, "openMode": 0 diff --git a/deployment/DOCKER_MORE_CLOSELY.md b/deployment/DOCKER_MORE_CLOSELY.md new file mode 100644 index 000000000..67488fe81 --- /dev/null +++ b/deployment/DOCKER_MORE_CLOSELY.md @@ -0,0 +1,33 @@ +# Docker + +## Apple M1 Platform + +***Attention:** For using Docker commands in Apple M1 environments!* + +```bash +# set env variable for your shell +$ export DOCKER_DEFAULT_PLATFORM=linux/amd64 +``` + +For even more informations, see [Docker More Closely](#docker-more-closely) + +### Docker Compose Override File For Apple M1 Platform + +For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform: + +```bash +# in main folder + +# for production +$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up + +# for production testing Docker images from DockerHub +$ docker compose -f docker-compose.ocelotsocial-branded.yml -f docker-compose.apple-m1.override.yml up + +# only once: init admin user and create indexes and constraints in Neo4j database +$ docker compose exec backend /bin/sh -c "yarn prod:migrate init" +``` + +## Docker More Closely In Main Code + +To get more informations about the Apple M1 platform and to analyze the Docker builds etc. you find our documentation in our main code, [here](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/DOCKER_MORE_CLOSELY.md). diff --git a/deployment/src/kubernetes/dns.values.template.yaml b/deployment/configurations/example/kubernetes/dns.values.template.yaml similarity index 100% rename from deployment/src/kubernetes/dns.values.template.yaml rename to deployment/configurations/example/kubernetes/dns.values.template.yaml diff --git a/deployment/scripts/cluster.reseed.sh b/deployment/scripts/cluster.reseed.sh new file mode 100755 index 000000000..705e1880a --- /dev/null +++ b/deployment/scripts/cluster.reseed.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +# configuration +CONFIGURATION=${CONFIGURATION:-"example"} +KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} + +# clean & seed +kubectl --kubeconfig=${KUBECONFIG} -n default exec -it $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "node --experimental-repl-await dist/db/clean.js && node --experimental-repl-await dist/db/seed.js" \ No newline at end of file diff --git a/deployment/scripts/secret.generate.sh b/deployment/scripts/secret.generate.sh new file mode 100755 index 000000000..dba958c34 --- /dev/null +++ b/deployment/scripts/secret.generate.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# generate a secret and store it in the SECRET file. +# Note that this overwrites the existing file + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +# configuration +CONFIGURATION=${CONFIGURATION:-"example"} +SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET + +openssl rand -base64 32 > ${SECRET_FILE} \ No newline at end of file diff --git a/deployment/scripts/secrets.decrypt.sh b/deployment/scripts/secrets.decrypt.sh new file mode 100755 index 000000000..283768ad0 --- /dev/null +++ b/deployment/scripts/secrets.decrypt.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# decrypt secrets in the selected configuration +# Note that existing decrypted files will be overwritten + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +# configuration +CONFIGURATION=${CONFIGURATION:-"example"} +SECRET=${SECRET} +SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET +FILES=(\ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/.env" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/dns.values.yaml" \ + ) + +# Load SECRET from file if it is not set explicitly +if [ -z ${SECRET} ] && [ -f "${SECRET_FILE}" ]; then + SECRET=$(<${SECRET_FILE}) +fi + +# exit when there is no SECRET set +if [ -z ${SECRET} ]; then + echo "No SECRET provided and no SECRET-File found." + exit 1 +fi + +# decrypt +for file in "${FILES[@]}" +do + if [ -f "${file}.enc" ]; then + #gpg --symmetric --batch --passphrase="${SECRET}" --cipher-algo AES256 --output ${file}.enc ${file} + gpg --quiet --batch --yes --decrypt --passphrase="${SECRET}" --output ${file} ${file}.enc + echo "Decrypted ${file}" + fi +done + +echo "DONE" +# gpg --quiet --batch --yes --decrypt --passphrase="${SECRET}" \ +# --output $HOME/secrets/my_secret.json my_secret.json.gpg diff --git a/deployment/scripts/secrets.encrypt.sh b/deployment/scripts/secrets.encrypt.sh new file mode 100755 index 000000000..ef6c87e85 --- /dev/null +++ b/deployment/scripts/secrets.encrypt.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# encrypt secrets in the selected configuration +# Note that existing encrypted files will be overwritten + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) + +# configuration +CONFIGURATION=${CONFIGURATION:-"example"} +SECRET=${SECRET} +SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET +FILES=(\ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/.env" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml" \ + "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/dns.values.yaml" \ + ) + +# Load SECRET from file if it is not set explicitly +if [ -z ${SECRET} ] && [ -f "${SECRET_FILE}" ]; then + SECRET=$(<${SECRET_FILE}) +fi + +# exit when there is no SECRET set +if [ -z ${SECRET} ]; then + echo "No SECRET provided and no SECRET-File found." + exit 1 +fi + +# encrypt +for file in "${FILES[@]}" +do + if [ -f "${file}" ]; then + gpg --symmetric --batch --yes --passphrase="${SECRET}" --cipher-algo AES256 --output ${file}.enc ${file} + echo "Encrypted ${file}" + fi +done + +echo "DONE" diff --git a/deployment/src/kubernetes/.gitignore b/deployment/src/kubernetes/.gitignore deleted file mode 100644 index e0473b0fd..000000000 --- a/deployment/src/kubernetes/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/dns.values.yaml -/nginx.values.yaml -/values.yaml diff --git a/deployment/src/kubernetes/nginx.values.template.yaml b/deployment/src/kubernetes/nginx.values.yaml similarity index 100% rename from deployment/src/kubernetes/nginx.values.template.yaml rename to deployment/src/kubernetes/nginx.values.yaml diff --git a/neo4j/README.md b/neo4j/README.md index df3b5fde6..1ea625d89 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -44,7 +44,7 @@ for development, spin up a [hosted Neo4j Sandbox instance](https://neo4j.com/download/), run Neo4j in one of the [many cloud options](https://neo4j.com/developer/guide-cloud-deployment/), [spin up Neo4j in a Docker container](https://neo4j.com/developer/docker/), -on Archlinux you can install [neo4j-community from AUR](https://aur.archlinux.org/packages/neo4j-community/) +on Arch linux you can install [neo4j-community from AUR](https://aur.archlinux.org/packages/neo4j-community/) or on Debian-based systems install [Neo4j from the Debian Repository](http://debian.neo4j.org/). Just be sure to update the Neo4j connection string and credentials accordingly in `backend/.env`. diff --git a/webapp/components/Category/index.vue b/webapp/components/Category/index.vue index 8e4014086..ebbaae584 100644 --- a/webapp/components/Category/index.vue +++ b/webapp/components/Category/index.vue @@ -30,6 +30,7 @@ export default { } } .filterActive { - background-color: $color-success-active; + color: $color-primary-inverse; + background-color: $color-primary-active; } diff --git a/webapp/components/FilterMenu/HeaderButton.vue b/webapp/components/FilterMenu/HeaderButton.vue new file mode 100644 index 000000000..780e29ade --- /dev/null +++ b/webapp/components/FilterMenu/HeaderButton.vue @@ -0,0 +1,57 @@ + + + + {{ title }} + + + + + + diff --git a/webapp/components/MasonryGrid/MasonryGrid.vue b/webapp/components/MasonryGrid/MasonryGrid.vue index 00afa31af..f95c0e16e 100644 --- a/webapp/components/MasonryGrid/MasonryGrid.vue +++ b/webapp/components/MasonryGrid/MasonryGrid.vue @@ -30,7 +30,7 @@ export default { /* dirty fix to override broken styleguide inline-styles */ .ds-grid { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)) !important; - gap: 16px !important; + gap: 32px 16px !important; grid-auto-rows: 20px; } diff --git a/webapp/components/NotificationsTable/NotificationsTable.spec.js b/webapp/components/NotificationsTable/NotificationsTable.spec.js index e48610034..0d3560787 100644 --- a/webapp/components/NotificationsTable/NotificationsTable.spec.js +++ b/webapp/components/NotificationsTable/NotificationsTable.spec.js @@ -59,8 +59,8 @@ describe('NotificationsTable.vue', () => { wrapper = Wrapper() }) - it('renders a table', () => { - expect(wrapper.find('.ds-table').exists()).toBe(true) + it('renders a grid table', () => { + expect(wrapper.find('.notification-grid').exists()).toBe(true) }) describe('renders 4 columns', () => { @@ -84,7 +84,7 @@ describe('NotificationsTable.vue', () => { describe('Post', () => { let firstRowNotification beforeEach(() => { - firstRowNotification = wrapper.findAll('tbody tr').at(0) + firstRowNotification = wrapper.findAll('.notification-grid-row').at(0) }) it('renders the author', () => { @@ -117,7 +117,7 @@ describe('NotificationsTable.vue', () => { describe('Comment', () => { let secondRowNotification beforeEach(() => { - secondRowNotification = wrapper.findAll('tbody tr').at(1) + secondRowNotification = wrapper.findAll('.notification-grid-row').at(1) }) it('renders the author', () => { diff --git a/webapp/components/NotificationsTable/NotificationsTable.vue b/webapp/components/NotificationsTable/NotificationsTable.vue index 07db4f9a2..7065b2624 100644 --- a/webapp/components/NotificationsTable/NotificationsTable.vue +++ b/webapp/components/NotificationsTable/NotificationsTable.vue @@ -1,62 +1,108 @@ - - - - - - - - - - - - - {{ $t(`notifications.reason.${scope.row.reason}`) }} - - - - + + + + + {{ field.label }} + + + + - {{ scope.row.from.title || scope.row.from.post.title | truncate(50) }} - - - - - {{ scope.row.from.contentExcerpt | removeHtml }} - - - + + + + + + + + + + + + + + + + + + + + + {{ $t(`notifications.reason.${notification.reason}`) }} + + + + + + + + + + + + + {{ notification.from.title || notification.from.post.title | truncate(50) }} + + + + + + + + {{ notification.from.contentExcerpt | removeHtml }} + + + + + + + + +