diff --git a/.github/workflows/deploy-acceptance-api.yml b/.github/workflows/deploy-acceptance-api.yml index 05c85a7b7..ab0a3d924 100644 --- a/.github/workflows/deploy-acceptance-api.yml +++ b/.github/workflows/deploy-acceptance-api.yml @@ -23,21 +23,18 @@ jobs: - name: Build binary run: make build-server working-directory: ./server - - name: Rename binary - run: mv server cl-api-acceptance - working-directory: ./server - name: Install SSH key uses: benoitchantre/setup-ssh-authentication-action@1.0.1 with: - private-key: ${{ secrets.VPS1_PRIVKEY }} - known-hosts: ${{ secrets.VPS1_KNOWNHOSTS }} + private-key: ${{ secrets.VPS2_PRIVKEY }} + private-key-name: id_ed25519 + known-hosts: ${{ secrets.VPS2_KNOWNHOSTS }} - name: Send binary to vps and restart service run: | - rsync -az --delete ./server/cl-api-acceptance admin@vpsnode1.vps.webdock.cloud:/home/admin/ - ssh admin@vpsnode1.vps.webdock.cloud "\ - sudo rm /home/clothingloop/opt/api-acceptance; \ - sudo cp /home/admin/cl-api-acceptance /home/clothingloop/opt/api-acceptance; \ - sudo chown root:root /home/clothingloop/opt/api-acceptance; \ - sudo chmod 0775 /home/clothingloop/opt/api-acceptance; \ - sudo systemctl restart cl-api-acceptance" + rsync -az --delete ./server/server admin@vps2.vps.webdock.cloud:/home/admin/acc.api.clothingloop.org/server.next + ssh admin@vps2.vps.webdock.cloud "\ + task-services stop my_api_acc; \ + sudo mv /home/admin/acc.api.clothingloop.org/server.next /home/admin/acc.api.clothingloop.org/server; \ + sudo chmod +x /home/admin/acc.api.clothingloop.org/server; \ + task-services start my_api_acc;" echo "done" diff --git a/.github/workflows/deploy-acceptance-app.yml b/.github/workflows/deploy-acceptance-app.yml index 43e5c5080..cc8897dfd 100644 --- a/.github/workflows/deploy-acceptance-app.yml +++ b/.github/workflows/deploy-acceptance-app.yml @@ -28,13 +28,13 @@ jobs: - name: Install SSH key uses: benoitchantre/setup-ssh-authentication-action@1.0.1 with: - private-key: ${{ secrets.VPS1_PRIVKEY }} - known-hosts: ${{ secrets.VPS1_KNOWNHOSTS }} + private-key: ${{ secrets.VPS2_PRIVKEY }} + private-key-name: id_ed25519 + known-hosts: ${{ secrets.VPS2_KNOWNHOSTS }} - name: Send public files to vps run: | - ssh admin@vpsnode1.vps.webdock.cloud "mkdir -p /home/admin/cl-app-acceptance" - rsync -az --delete ./app/build/ admin@vpsnode1.vps.webdock.cloud:/home/admin/cl-app-acceptance/ - ssh admin@vpsnode1.vps.webdock.cloud "\ - sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/cl-app-acceptance/ /var/caddy/acc.app.clothingloop.org/; \ + rsync -az --delete ./app/build/ admin@vps2.vps.webdock.cloud:/home/admin/acc.app.clothingloop.org/ + ssh admin@vps2.vps.webdock.cloud "\ + sudo chmod -R 0775 /home/admin/acc.app.clothingloop.org/; \ sudo systemctl reload caddy" echo "done" diff --git a/.github/workflows/deploy-acceptance-fe.yml b/.github/workflows/deploy-acceptance-fe.yml index 7d6cde829..8661eee69 100644 --- a/.github/workflows/deploy-acceptance-fe.yml +++ b/.github/workflows/deploy-acceptance-fe.yml @@ -28,13 +28,13 @@ jobs: - name: Install SSH key uses: benoitchantre/setup-ssh-authentication-action@1.0.1 with: - private-key: ${{ secrets.VPS1_PRIVKEY }} - known-hosts: ${{ secrets.VPS1_KNOWNHOSTS }} + private-key: ${{ secrets.VPS2_PRIVKEY }} + private-key-name: id_ed25519 + known-hosts: ${{ secrets.VPS2_KNOWNHOSTS }} - name: Send public files to vps run: | - ssh admin@vpsnode1.vps.webdock.cloud "mkdir -p /home/admin/cl-fe-acceptance" - rsync -az --delete ./frontend/build/ admin@vpsnode1.vps.webdock.cloud:/home/admin/cl-fe-acceptance/ - ssh admin@vpsnode1.vps.webdock.cloud "\ - sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/cl-fe-acceptance/ /var/caddy/acc.clothingloop.org/; \ + rsync -az --delete ./frontend/build/ admin@vps2.vps.webdock.cloud:/home/admin/acc.clothingloop.org/ + ssh admin@vps2.vps.webdock.cloud "\ + sudo chmod -R 0775 /home/admin/acc.clothingloop.org/; \ sudo systemctl reload caddy" echo "done" diff --git a/app/src/components/AddressList/AddressListItem.tsx b/app/src/components/AddressList/AddressListItem.tsx index d92c5caf2..6e483b2c2 100644 --- a/app/src/components/AddressList/AddressListItem.tsx +++ b/app/src/components/AddressList/AddressListItem.tsx @@ -13,6 +13,7 @@ export interface AddressListItemProps { number: number; routerLink: string | undefined; isUserPaused: boolean; + isChainAdmin: boolean; } export default function AddressListItem({ user, @@ -23,6 +24,7 @@ export default function AddressListItem({ number, routerLink, isUserPaused, + isChainAdmin, }: AddressListItemProps) { return ( {isUserPaused ? ( - {t("paused")} + isChainAdmin ? ( + {user.address} + ) : null ) : isAddressPrivate ? (   ) : ( diff --git a/app/src/components/Bags/SelectUserModal.tsx b/app/src/components/Bags/SelectUserModal.tsx index 507a8810b..517b484f2 100644 --- a/app/src/components/Bags/SelectUserModal.tsx +++ b/app/src/components/Bags/SelectUserModal.tsx @@ -20,12 +20,15 @@ import { IonSearchbar, IonRadioGroup, IonRadio, + IonIcon, } from "@ionic/react"; import { RefObject, useContext, useState, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { bagPut } from "../../api/bag"; import { UID } from "../../api/types"; import { StoreContext } from "../../stores/Store"; +import IsPaused from "../../utils/is_paused"; +import { pauseCircleSharp } from "ionicons/icons"; const MIN_USERS_FOR_SEARCH = 15; @@ -40,7 +43,7 @@ export default function SelectUserModal({ modal: RefObject; didDismiss?: (e: IonModalCustomEvent>) => void; }) { - const { chain, chainUsers, route, authUser, setChain } = + const { chain, chainUsers, route, authUser, setChain, isChainAdmin } = useContext(StoreContext); const { t, i18n } = useTranslation(); @@ -166,11 +169,23 @@ export default function SelectUserModal({ const isSelected = selected === user.uid; const isMe = user.uid === authUser?.uid; + const isPaused = IsPaused(user, chain?.uid); return ( - - {`#${ - i + 1 - }`} + +
+ {isPaused ? ( + + ) : ( + {`#${i + 1}`} + )} +

- {user.paused_until ? ( + {isPaused ? ( {user.name} diff --git a/app/src/components/Settings/RoutePrivacyExample.tsx b/app/src/components/Settings/RoutePrivacyExample.tsx index 541ba8257..1f3bfb3ae 100644 --- a/app/src/components/Settings/RoutePrivacyExample.tsx +++ b/app/src/components/Settings/RoutePrivacyExample.tsx @@ -92,6 +92,7 @@ export default function RoutePrivacyExample(props: { isUserPaused={user.paused_until !== null} number={i + 5} routerLink={undefined} + isChainAdmin={true} /> ); diff --git a/app/src/pages/AddressItem.tsx b/app/src/pages/AddressItem.tsx index f32c86697..a967b6cad 100644 --- a/app/src/pages/AddressItem.tsx +++ b/app/src/pages/AddressItem.tsx @@ -15,7 +15,7 @@ import { useContext, useMemo } from "react"; import { RouteComponentProps } from "react-router"; import UserCard from "../components/UserCard"; import { StoreContext } from "../stores/Store"; -import isPaused from "../utils/is_paused"; +import IsPaused from "../utils/is_paused"; import { t } from "i18next"; import Badges from "../components/SizeBadge"; import AddressBagCard from "../components/Bags/AddressBagCard"; @@ -29,7 +29,7 @@ export default function AddressItem({ let userUID = match.params.uid; return chainUsers.find((u) => u.uid === userUID) || null; }, [match.params.uid, chainUsers]); - const isUserPaused = isPaused(user, chain?.uid); + const isUserPaused = IsPaused(user, chain?.uid); const userBags = useMemo(() => { return bags.filter((b) => b.user_uid === user?.uid); diff --git a/app/src/pages/AddressList.tsx b/app/src/pages/AddressList.tsx index 45eef500d..27dbbbdff 100644 --- a/app/src/pages/AddressList.tsx +++ b/app/src/pages/AddressList.tsx @@ -29,7 +29,7 @@ import AddressListItem, { } from "../components/AddressList/AddressListItem"; import wrapIndex from "../utils/wrap_index"; import { useDebounce } from "@uidotdev/usehooks"; -import isPaused from "../utils/is_paused"; +import IsPaused from "../utils/is_paused"; export default function AddressList() { const { @@ -102,7 +102,7 @@ export default function AddressList() { const isPrivate = IsPrivate(user.email); const isAddressPrivate = IsPrivate(user.address); - const isUserPaused = isPaused(user, chain.uid); + const isUserPaused = IsPaused(user, chain.uid); arr.push({ user, @@ -113,6 +113,7 @@ export default function AddressList() { number: i + 1, isUserPaused, routerLink: isPrivate ? undefined : "/address/" + user.uid, + isChainAdmin, }); } diff --git a/app/src/pages/BagsList.tsx b/app/src/pages/BagsList.tsx index 15ceda64f..474108561 100644 --- a/app/src/pages/BagsList.tsx +++ b/app/src/pages/BagsList.tsx @@ -61,6 +61,7 @@ import UserLink from "../components/Bags/UserLink"; import SelectUserModal from "../components/Bags/SelectUserModal"; import { useDebounce } from "@uidotdev/usehooks"; import dayjs from "../dayjs"; +import IsPaused from "../utils/is_paused"; type State = { bag_id?: number } | undefined; @@ -408,6 +409,7 @@ export default function BagsList() { {bagsCard.map((bag) => { const user = chainUsers.find((u) => u.uid === bag.user_uid); if (!user) return null; + const isPaused = IsPaused(user, chain?.uid); let routeIndex = route.indexOf(user.uid); if (routeIndex === -1) return null; const { bagUpdatedAt, isBagTooOldMe, isBagTooOldHost } = @@ -458,7 +460,11 @@ export default function BagsList() { routerLink={"/address/" + user.uid} className="tw-py-3 tw-px-2 tw-block tw-bg-light" > - + @@ -472,6 +478,7 @@ export default function BagsList() { const { bagUpdatedAt, isBagTooOldMe, isBagTooOldHost } = useBagTooOld(authUser, isChainAdmin, bag); let isOpen = openCard == bag.id; + const isPaused = IsPaused(user, chain?.uid); return ( - + )} {isChainAdmin ? ( diff --git a/app/src/pages/HelpItem.tsx b/app/src/pages/HelpItem.tsx index 38d2186a4..492dbd045 100644 --- a/app/src/pages/HelpItem.tsx +++ b/app/src/pages/HelpItem.tsx @@ -8,7 +8,7 @@ import { IonButtons, IonIcon, } from "@ionic/react"; -import { useContext, useMemo } from "react"; +import { Fragment, useContext, useMemo } from "react"; import { useTranslation } from "react-i18next"; import type { TOptionsBase } from "i18next"; import { RouteComponentProps } from "react-router"; @@ -84,9 +84,14 @@ export default function HelpItem({ > {item.title}

- {item.content.split("\n").map((s, i) => ( -

{s}

- ))} +

+ {item.content.split("\n").map((s, i) => ( + + {s === "" ? null : {s}} +
+
+ ))} +

@@ -260,6 +262,12 @@ export default function Login(props: { isLoggedIn: boolean }) {
) : null} +
+ {VERSION} +
diff --git a/app/src/pages/Settings.tsx b/app/src/pages/Settings.tsx index 46d15f2a4..2541b0d00 100644 --- a/app/src/pages/Settings.tsx +++ b/app/src/pages/Settings.tsx @@ -53,7 +53,7 @@ import { warningOutline, } from "ionicons/icons"; import dayjs from "../dayjs"; -import isPaused from "../utils/is_paused"; +import IsPaused from "../utils/is_paused"; import Badges from "../components/SizeBadge"; import { Share } from "@capacitor/share"; import { Clipboard } from "@capacitor/clipboard"; @@ -201,7 +201,7 @@ export default function Settings() { Share.share({ url }); } - let isUserPaused = isPaused(authUser, chain?.uid); + let isUserPaused = IsPaused(authUser, chain?.uid); let pausedDayjs = isUserPaused ? dayjs(authUser!.paused_until) : null; let showExpandButton = (chain?.description.length || 0) > 200; let emptyDescription = (chain?.description.length || 0) == 0; diff --git a/app/src/utils/is_paused.test.ts b/app/src/utils/is_paused.test.ts index e47e34f72..f6191cb00 100644 --- a/app/src/utils/is_paused.test.ts +++ b/app/src/utils/is_paused.test.ts @@ -1,9 +1,9 @@ import { expect, test } from "vitest"; -import isPaused from "./is_paused"; +import IsPaused from "./is_paused"; import dayjs from "../dayjs"; function TestIsPausedByDate(date: string | null) { - return isPaused({ chains: [], paused_until: date } as any, ""); + return IsPaused({ chains: [], paused_until: date } as any, ""); } test("should is pause be a date in the past then return true", () => { @@ -24,7 +24,7 @@ test("should is pause be null then return false", () => { test("should be paused when the current user chain is paused", () => { expect( - isPaused( + IsPaused( { chains: [{ chain_uid: "test", is_paused: true }] } as any, "test", ), diff --git a/app/src/utils/is_paused.ts b/app/src/utils/is_paused.ts index 2309789ef..3a2498bd5 100644 --- a/app/src/utils/is_paused.ts +++ b/app/src/utils/is_paused.ts @@ -1,7 +1,7 @@ import { UID, User } from "../api/types"; import dayjs from "../dayjs"; -export default function isPaused( +export default function IsPaused( user: User | null, currentChainUID: UID | undefined, ): boolean { diff --git a/devops/.gitignore b/devops/.gitignore index 5228746d6..02fd0c9b1 100644 --- a/devops/.gitignore +++ b/devops/.gitignore @@ -1,4 +1,4 @@ -/group_vars/all.yml -/templates/maint/style.min.css -/templates/imageproxy -/images \ No newline at end of file +/vps1/group_vars/all.yml +/vps1/templates/maint/style.min.css +/vps1/templates/imageproxy +/*/images diff --git a/devops/vps1/DEPRECATED b/devops/vps1/DEPRECATED new file mode 100644 index 000000000..e69de29bb diff --git a/devops/README.md b/devops/vps1/README.md similarity index 100% rename from devops/README.md rename to devops/vps1/README.md diff --git a/devops/ansible.cfg b/devops/vps1/ansible.cfg similarity index 100% rename from devops/ansible.cfg rename to devops/vps1/ansible.cfg diff --git a/devops/group_vars/all.yml.example b/devops/vps1/group_vars/all.yml.example similarity index 100% rename from devops/group_vars/all.yml.example rename to devops/vps1/group_vars/all.yml.example diff --git a/devops/hosts b/devops/vps1/hosts similarity index 100% rename from devops/hosts rename to devops/vps1/hosts diff --git a/devops/lint-caddy-files.sh b/devops/vps1/lint-caddy-files.sh similarity index 100% rename from devops/lint-caddy-files.sh rename to devops/vps1/lint-caddy-files.sh diff --git a/devops/maint-start.yml b/devops/vps1/maint-start.yml similarity index 100% rename from devops/maint-start.yml rename to devops/vps1/maint-start.yml diff --git a/devops/maint-stop.yml b/devops/vps1/maint-stop.yml similarity index 100% rename from devops/maint-stop.yml rename to devops/vps1/maint-stop.yml diff --git a/devops/setup-1-firewall.yml b/devops/vps1/setup-1-firewall.yml similarity index 100% rename from devops/setup-1-firewall.yml rename to devops/vps1/setup-1-firewall.yml diff --git a/devops/setup-2-caddy.yml b/devops/vps1/setup-2-caddy.yml similarity index 100% rename from devops/setup-2-caddy.yml rename to devops/vps1/setup-2-caddy.yml diff --git a/devops/setup-3-mariadb.yml b/devops/vps1/setup-3-mariadb.yml similarity index 100% rename from devops/setup-3-mariadb.yml rename to devops/vps1/setup-3-mariadb.yml diff --git a/devops/setup-4-api.yml b/devops/vps1/setup-4-api.yml similarity index 100% rename from devops/setup-4-api.yml rename to devops/vps1/setup-4-api.yml diff --git a/devops/setup-5-cron.yml b/devops/vps1/setup-5-cron.yml similarity index 100% rename from devops/setup-5-cron.yml rename to devops/vps1/setup-5-cron.yml diff --git a/devops/setup-6-phpmyadmin.yml b/devops/vps1/setup-6-phpmyadmin.yml similarity index 100% rename from devops/setup-6-phpmyadmin.yml rename to devops/vps1/setup-6-phpmyadmin.yml diff --git a/devops/setup-7-imageserver.yml b/devops/vps1/setup-7-imageserver.yml similarity index 100% rename from devops/setup-7-imageserver.yml rename to devops/vps1/setup-7-imageserver.yml diff --git a/devops/setup-8-docker.yml b/devops/vps1/setup-8-docker.yml similarity index 100% rename from devops/setup-8-docker.yml rename to devops/vps1/setup-8-docker.yml diff --git a/devops/setup-9-mailpit.yml b/devops/vps1/setup-9-mailpit.yml similarity index 100% rename from devops/setup-9-mailpit.yml rename to devops/vps1/setup-9-mailpit.yml diff --git a/devops/status.yml b/devops/vps1/status.yml similarity index 100% rename from devops/status.yml rename to devops/vps1/status.yml diff --git a/devops/sync-images.yml b/devops/vps1/sync-images.yml similarity index 100% rename from devops/sync-images.yml rename to devops/vps1/sync-images.yml diff --git a/devops/templates/Caddyfile b/devops/vps1/templates/Caddyfile similarity index 100% rename from devops/templates/Caddyfile rename to devops/vps1/templates/Caddyfile diff --git a/devops/templates/Caddyfile.maint b/devops/vps1/templates/Caddyfile.maint similarity index 100% rename from devops/templates/Caddyfile.maint rename to devops/vps1/templates/Caddyfile.maint diff --git a/devops/templates/api.service.j2 b/devops/vps1/templates/api.service.j2 similarity index 100% rename from devops/templates/api.service.j2 rename to devops/vps1/templates/api.service.j2 diff --git a/devops/templates/config.yml.j2 b/devops/vps1/templates/config.yml.j2 similarity index 100% rename from devops/templates/config.yml.j2 rename to devops/vps1/templates/config.yml.j2 diff --git a/devops/templates/maint/index.html b/devops/vps1/templates/maint/index.html similarity index 100% rename from devops/templates/maint/index.html rename to devops/vps1/templates/maint/index.html diff --git a/devops/templates/maint/src/build.sh b/devops/vps1/templates/maint/src/build.sh similarity index 100% rename from devops/templates/maint/src/build.sh rename to devops/vps1/templates/maint/src/build.sh diff --git a/devops/templates/maint/src/style.min.css b/devops/vps1/templates/maint/src/style.min.css similarity index 100% rename from devops/templates/maint/src/style.min.css rename to devops/vps1/templates/maint/src/style.min.css diff --git a/devops/templates/maint/src/tailwind.config.js b/devops/vps1/templates/maint/src/tailwind.config.js similarity index 100% rename from devops/templates/maint/src/tailwind.config.js rename to devops/vps1/templates/maint/src/tailwind.config.js diff --git a/devops/templates/service.j2 b/devops/vps1/templates/service.j2 similarity index 100% rename from devops/templates/service.j2 rename to devops/vps1/templates/service.j2 diff --git a/devops/vps2/.gitignore b/devops/vps2/.gitignore new file mode 100644 index 000000000..0e3c81454 --- /dev/null +++ b/devops/vps2/.gitignore @@ -0,0 +1,5 @@ +/bin +/configs/config.acc.yml +/configs/config.prod.yml +/configs/*.tmp +/configs/.env-caddy \ No newline at end of file diff --git a/devops/vps2/INSTALL_HISTORY.md b/devops/vps2/INSTALL_HISTORY.md new file mode 100644 index 000000000..9675dc778 --- /dev/null +++ b/devops/vps2/INSTALL_HISTORY.md @@ -0,0 +1,85 @@ +# History of all installed programs + +## Used ports + +| Port | Description | +|-----:|----------------| +| 80 | Caddy | +| 443 | Caddy | +| 3306 | MySQL | +| 8081 | api_prod | +| 8082 | api_acc | +| 8083 | imageproxy | +| 8084 | mailpit | +| 8085 | mailpit ui | + +## Installed packages + +``` +# golang +sudo snap install --classic go + +# imageproxy +CGO_ENABLED=0 go install willnorris.com/go/imageproxy/cmd/imageproxy@latest +sudo cp ~/go/bin/imageproxy /usr/local/bin/ + +# mailpit +curl -fsSL https://raw.githubusercontent.com/axllent/mailpit/develop/install.sh -o /tmp/mailpit_install.sh +sudo bash /tmp/mailpit_install.sh + +# taskfile +curl -fsSL https://taskfile.dev/install.sh -o /tmp/taskfile_install.sh +sudo bash /tmp/taskfile_install.sh -- -d -b /usr/local/bin + +# caddy +sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update +sudo apt install caddy + +mkdir -p /home/admin/api.clothingloop.org \ + /home/admin/acc.api.clothingloop.org \ + /home/admin/www.clothingloop.org \ + /home/admin/acc.clothingloop.org \ + /home/admin/images.clothingloop.org \ + /home/admin/app.clothingloop.org \ + /home/admin/acc.app.clothingloop.org + +sudo -u www-data mkdir -p /var/www/acc.clothingloop.org \ + /var/www/www.clothingloop.org \ + /var/www/app.clothingloop.org \ + /var/www/acc.app.clothingloop.org + +# docker +curl -fsSL https://get.docker.com -o /tmp/get-docker.sh +sudo sh /tmp/get-docker.sh + +# task-services +curl -fsSL https://codeberg.org/lil5/task-services/releases/download/v1.0.0/task-services_linux_amd64 -o /tmp/task-services +sudo mv /tmp/task-services /usr/local/bin/task-services +sudo chmod +x /usr/local/bin/task-services + +# phpmyadmin +sudo apt install phpmyadmin -y + +# mattermost +# https://docs.mattermost.com/install/installing-ubuntu-2004-LTS.html +wget releases.mattermost.com/9.7.2/mattermost-9.7.2-linux-amd64.tar.gz +tar -xvzf mattermost*.gz +sudo mv mattermost /opt +sudo mkdir /opt/mattermost/data +sudo useradd --system --user-group mattermost +sudo chown -R mattermost:mattermost /opt/mattermost +sudo chmod -R g+w /opt/mattermost +sudo touch /lib/systemd/system/mattermost.service +sudo systemctl daemon-reload + + +# setup services +sudo systemctl enable mattermost +sudo systemctl disable nginx +sudo systemctl stop nginx +sudo systemctl enable caddy +sudo systemctl start caddy +``` \ No newline at end of file diff --git a/devops/vps2/Taskfile.server.yml b/devops/vps2/Taskfile.server.yml new file mode 100644 index 000000000..ebc41c954 --- /dev/null +++ b/devops/vps2/Taskfile.server.yml @@ -0,0 +1,18 @@ +version: "3" + +tasks: + service:caddy:reload: sudo systemctl reload caddy + service:caddy:restart: sudo systemctl restart caddy + service:mm:restart: sudo systemctl restart mattermost + +services: + my_imageproxy: /usr/local/bin/imageproxy -cache memory:200:8h -addr localhost:8083 -allowHosts images.clothingloop.org -baseURL https://images.clothingloop.org/original/ + my_mailpit: /usr/local/bin/mailpit --smtp 127.0.0.1:8084 --listen 127.0.0.1:8085 --smtp-auth-allow-insecure --smtp-auth-accept-any --webroot /mailpit/ + my_api_prod: + cmd: /home/admin/api.clothingloop.org/server + dir: /home/admin/api.clothingloop.org/ + env: SERVER_ENV=production + my_api_acc: + cmd: /home/admin/acc.api.clothingloop.org/server + dir: /home/admin/acc.api.clothingloop.org/ + env: SERVER_ENV=acceptance diff --git a/devops/vps2/Taskfile.yml b/devops/vps2/Taskfile.yml new file mode 100644 index 000000000..cae5211cb --- /dev/null +++ b/devops/vps2/Taskfile.yml @@ -0,0 +1,62 @@ +version: "3" + +tasks: + publish:taskfileserver: + - rsync -av ./Taskfile.server.yml vps2.clothingloop.org:/home/admin/Taskfile.yml + + publish:imageproxy:sync: + - rsync -av vps2.clothingloop.org:/home/admin/images.clothingloop.org/ ./images/ + - rsync -av ./images/ vps2.clothingloop.org:/home/admin/images.clothingloop.org/ + publish:imageproxy:restart: + - ssh vps2.clothingloop.org /bin/bash -c "cd /home/admin; task-services restart my_imageproxy" + + publish:config:prod: + - rsync -av ./configs/config.prod.yml vps2.clothingloop.org:/home/admin/api.clothingloop.org/config.prod.yml + publish:config:acc: + - rsync -av ./configs/config.acc.yml vps2.clothingloop.org:/home/admin/acc.api.clothingloop.org/config.acc.yml + publish:caddy: + dotenv: + - "configs/.env-caddy" + cmds: + - rm configs/Caddyfile.tmp || true + - cp configs/Caddyfile configs/Caddyfile.tmp + - sed -i '' -e 's/TUFJTFBJVF9QQVNTV09SRA==/'"$MAILPIT_PASSWORD_BCRYPT"'/' configs/Caddyfile.tmp + - sed -i '' -e 's/PRERENDER_TOKEN/'"$PRERENDER_TOKEN"'/' configs/Caddyfile.tmp + - caddy validate --config ./configs/Caddyfile.tmp || exit 1 + - rsync -av ./configs/Caddyfile.tmp vps2.clothingloop.org:/home/admin/ + - ssh -t vps2.clothingloop.org "sudo chown caddy:sudo /home/admin/Caddyfile.tmp" + - ssh -t vps2.clothingloop.org "sudo mv /home/admin/Caddyfile.tmp /etc/caddy/Caddyfile" + - ssh -t vps2.clothingloop.org "sudo systemctl reload caddy" + + publish:api:prod: + - cd ../../server; GOOS=linux GOARCH=amd64 CGO_ENABLED=0 make build-server + - rsync -av ../../server/server vps2.clothingloop.org:/home/admin/api.clothingloop.org/server + - ssh vps2.clothingloop.org /bin/bash -c "cd /home/admin; task-services stop my_api_prod || true" + - ssh vps2.clothingloop.org /bin/bash -c "cd /home/admin; task-services start my_api_prod || true" + publish:api:acc: + - cd ../../server; GOOS=linux GOARCH=amd64 CGO_ENABLED=0 make build-server + - ssh vps2.clothingloop.org /bin/bash -c "cd /home/admin; task-services stop my_api_acc || true" + - rsync -av ../../server/server vps2.clothingloop.org:/home/admin/acc.api.clothingloop.org/server + - ssh vps2.clothingloop.org /bin/bash -c "cd /home/admin; task-services start my_api_acc || true" + + publish:fe:prod: + - cd ../../frontend; npm run build:production + - rsync -av --delete ../../frontend/build/ vps2.clothingloop.org:/home/admin/www.clothingloop.org/ + - ssh vps2.clothingloop.org "sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/www.clothingloop.org/ /var/www/www.clothingloop.org/" + - ssh vps2.clothingloop.org "sudo systemctl reload caddy" + publish:fe:acc: + - cd ../../frontend; npm run build:acceptance + - rsync -av --delete ../../frontend/build/ vps2.clothingloop.org:/home/admin/acc.clothingloop.org/ + - ssh vps2.clothingloop.org "sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/acc.clothingloop.org/ /var/www/acc.clothingloop.org/" + - ssh vps2.clothingloop.org "sudo systemctl reload caddy" + + publish:app:prod: + - cd ../../app; npm run build:production + - rsync -av ../../app/build/ vps2.clothingloop.org:/home/admin/app.clothingloop.org/ + - ssh vps2.clothingloop.org "sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/app.clothingloop.org/ /var/www/app.clothingloop.org/" + - ssh vps2.clothingloop.org "sudo systemctl reload caddy" + publish:app:acc: + - cd ../../app; npm run build:acceptance + - rsync -av --delete ../../app/build/ vps2.clothingloop.org:/home/admin/acc.app.clothingloop.org/ + - ssh vps2.clothingloop.org "sudo rsync -z -rlt --chown=caddy:caddy --chmod=0775 --delete /home/admin/acc.app.clothingloop.org/ /var/www/acc.app.clothingloop.org/" + - ssh vps2.clothingloop.org "sudo systemctl reload caddy" diff --git a/devops/vps2/configs/Caddyfile b/devops/vps2/configs/Caddyfile new file mode 100644 index 000000000..e4f6103d0 --- /dev/null +++ b/devops/vps2/configs/Caddyfile @@ -0,0 +1,228 @@ +(hsts) { + header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" +} + +(astro_url) { + # /event/:uid/ -> /event/?event= + @oldevent path_regexp eventp ^/(\w{2}/)?events/([\w-]{36})/?$ + redir @oldevent /{re.eventp.1}events/details/?event={re.eventp.2} 302 + + # /loops/:uid/members -> /loops/members?chain= + @oldmembers path_regexp membersp ^/(\w{2}/)?loops/([\w-]{36})/members/?$ + redir @oldmembers /{re.membersp.1}loops/members/?chain={re.membersp.2} 302 + + # /loops/:uid/users/signup -> /loops/users/signup/?chain= + @oldusersignup path_regexp userssignupp ^/(\w{2}/)?loops/([\w-]{36})/users/signup/?$ + redir @oldusersignup /{re.userssignupp.1}loops/users/signup/?chain={re.userssignupp.2} 302 + + # /loops/:uid/users/login -> /loops/users/login/?chain= + @olduserslogin path_regexp usersloginp ^/(\w{2}/)?loops/([\w-]{36})/users/login/?$ + redir @olduserslogin /{re.usersloginp.1}loops/users/login/?chain={re.usersloginp.2} 302 + + # /users/login/validate?apiKey= -> /users/login/validate/?apiKey= + # https://acc.clothingloop.org/users/login/validate?apiKey=74932015&u=aG9zdEBleGFtcGxlLmNvbQ%3D%3D + @oldusersvalidp path_regexp usersvalidp ^/(\w{2}/)?users/login/validate$ + redir @oldusersvalidp /{re.usersvalidp.1}users/login/validate/?{query} 302 + + file_server + try_files {path} {path}/ =404 + encode zstd gzip +} + + +# clothingloop.org, www.clothingloop.be, clothingloop.be { +# import hsts +# redir https://www.clothingloop.org{uri} +# } + +# www.clothingloop.org { +# import hsts +# log { +# output file /var/log/caddy/api.clothingloop.org-error.log +# format console +# level ERROR +# } + +# @searchbot { +# header User-Agent googlebot +# header User-Agent bingbot +# header User-Agent yandex +# header User-Agent baiduspider +# header User-Agent facebookexternalhit +# header User-Agent twitterbot +# header User-Agent rogerbot +# header User-Agent linkedinbot +# header User-Agent embedly +# header User-Agent "quora link preview" +# header User-Agent showyoubot +# header User-Agent outbrain +# header User-Agent pinterest\/0\. +# header User-Agent pinterestbot +# header User-Agent slackbot +# header User-Agent vkShare +# header User-Agent W3C_Validator +# header User-Agent whatsapp +# header User-Agent redditbot +# header User-Agent applebot +# header User-Agent flipboard +# header User-Agent tumblr +# header User-Agent bitlybot +# header User-Agent skypeuripreview +# header User-Agent nuzzel +# header User-Agent discordbot +# header User-Agent "google page speed" +# header User-Agent qwantify +# header User-Agent "bitrix link preview" +# header User-Agent xing-contenttabreceiver +# header User-Agent chrome-lighthouse +# header User-Agent telegrambot +# not path_regexp .*?(\.js|\.css|\.xml|\.less|\.png|\.jpg|\.jpeg|\.gif|\.pdf|\.doc|\.txt|\.ico|\.rss|\.zip|\.mp3|\.rar|\.exe|\.wmv|\.doc|\.avi|\.ppt|\.mpg|\.mpeg|\.tif|\.wav|\.mov|\.psd|\.ai|\.xls|\.mp4|\.m4a|\.swf|\.dat|\.dmg|\.iso|\.flv|\.m4v|\.torrent|\.ttf|\.woff|\.svg) +# } +# @assets { +# path /fonts/* /icons/* /images/* +# } + +# header @assets Cache-Control "max-age=604800 must-revalidate" +# header ?Cache-Control no-cache + +# handle_path /api/* { +# reverse_proxy 127.0.0.1:8081 +# encode zstd gzip +# } + +# handle_path /toolkit { +# redir https://drive.google.com/drive/folders/1iMJzIcBxgApKx89hcaHhhuP5YAs_Yb27 +# } + +# handle @searchbot { +# request_header X-Prerender-Token PRERENDER_TOKEN +# rewrite * /{scheme}://{host}{uri} +# reverse_proxy https://service.prerender.io +# } + +# handle { +# import astro_url +# root * /var/www/clothingloop.org +# } +# } + + +vps2.vps.webdock.cloud { + respond "Hello, world!" +} + +# admin.clothingloop.org { +# import hsts +# log { +# output file /var/log/caddy/phpmyadmin-error.log +# format console +# level ERROR +# } +# php_fastcgi unix//run/php/php-fpm.sock +# root * /usr/share/phpmyadmin +# file_server +# try_files {path} /index.php +# } + +acc.clothingloop.org { + import hsts + log { + output file /var/log/caddy/acc.clothingloop.org-error.log + format console + level ERROR + } + + header X-Robots-Tag noindex + + route { + header Cache-Control no-cache + } + + handle_path /api/* { + reverse_proxy 127.0.0.1:8082 + } + + redir /mailpit /mailpit/ + handle_path /mailpit/* { + basicauth bcrypt { + hello@clothingloop.org TUFJTFBJVF9QQVNTV09SRA== + } + rewrite * /mailpit{path} + reverse_proxy 127.0.0.1:8085 + } + + handle { + import astro_url + root * /var/www/acc.clothingloop.org + } +} + +acc.app.clothingloop.org { + import hsts + log { + output file /var/log/caddy/acc.clothingloop.org-error.log + format console + level ERROR + } + + header X-Robots-Tag noindex + + route { + header Cache-Control no-cache + } + + handle_path /api/* { + reverse_proxy 127.0.0.1:8082 + } + + handle { + root * /var/www/acc.app.clothingloop.org + file_server + encode zstd gzip + try_files {path} /index.html + } +} + +mm.clothingloop.org { + import hsts + + header Cache-Control no-cache + reverse_proxy 127.0.0.1:8065 +} + +# images.clothingloop.org { +# import hsts +# header Cache-Control max-age=1209600 + +# handle_path /original/* { +# root * /var/www/images.clothingloop.org +# file_server +# } +# handle { +# reverse_proxy 127.0.0.1:8083 +# } +# } + +# app.clothingloop.org { +# import hsts +# header X-Robots-Tag noindex + +# handle_path /api/* { +# @options { +# method OPTIONS +# } +# respond @options 200 + +# reverse_proxy 127.0.0.1:8081 +# header Access-Control-Allow-Origin "*" +# header Access-Control-Allow-Methods "*" +# header Access-Control-Allow-Headers "Content-Type, Origin, Authorization, X-Requested-With, Accept" +# } + +# handle { +# root * /var/www/app.clothingloop.org +# file_server +# encode zstd gzip +# try_files {path} /index.html +# } +# } diff --git a/frontend/public/locales/ar/translation.json b/frontend/public/locales/ar/translation.json index 427e84267..e9f8f03ec 100644 --- a/frontend/public/locales/ar/translation.json +++ b/frontend/public/locales/ar/translation.json @@ -173,7 +173,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/ca/translation.json b/frontend/public/locales/ca/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/ca/translation.json +++ b/frontend/public/locales/ca/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/da/translation.json b/frontend/public/locales/da/translation.json index 427e84267..e9f8f03ec 100644 --- a/frontend/public/locales/da/translation.json +++ b/frontend/public/locales/da/translation.json @@ -173,7 +173,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/de/translation.json b/frontend/public/locales/de/translation.json index 35f85d3e0..68e38b4da 100644 --- a/frontend/public/locales/de/translation.json +++ b/frontend/public/locales/de/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Lies unseren Impact Report", "thankYou": "Vielen Dank!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "Du bist die besondere Art von Person, die Menschen aufmuntert und die Welt zu einem besseren Ort macht. Sei Dir sicher, dass wir bei jeder Spende einen kleinen Tanz in unserem (Haus-)Büro machen werden. Wir haben noch viel Arbeit vor uns, und Deine Spende hat es viel einfacher gemacht, die Dinge zu erledigen und uns in Richtung einer nachhaltigeren Welt voranzubringen. Vielen Dank für Deinen Wunsch dazu beizutragen, dass The Clothing Loop erfolgreich wird.", - "byDonatingYouAgreeWithOur": "Indem Du fortfährst, akzeptierst Du unsere ", + "byDonatingYouAgreeWithOur": "Indem Du fortfährst, akzeptierst Du unsere <1>Privacy Policy.", "accessTokenNotConfigured": "Zugriffstoken nicht konfiguriert", "dontHaveACreditCardChooseSepa": "Hast Du keine Kreditkarte? Wähle im nächsten Schritt \"SEPA Direct Debit\" aus.", "donateToTheClothingLoop": "Spende an The Clothing Loop", diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/es/translation.json b/frontend/public/locales/es/translation.json index 61c490fed..115525fc8 100644 --- a/frontend/public/locales/es/translation.json +++ b/frontend/public/locales/es/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Lea nuestro informe de impacto", "thankYou": "¡Gracias!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "Usted es el tipo especial de persona que eleva a la gente y hace del mundo un lugar mejor. Estén seguros de que cada donación nos hace bailar un poco en nuestra oficina (en casa). Tenemos mucho trabajo por delante, y su donación ha hecho que sea mucho más fácil hacer las cosas y impulsarnos hacia un mundo más sostenible. Muchas gracias por su deseo de ayudar a que The Clothing Loop sea un éxito.", - "byDonatingYouAgreeWithOur": "Al proceder, aceptas nuestra ", + "byDonatingYouAgreeWithOur": "Al proceder, aceptas nuestra <1>Privacy Policy.", "accessTokenNotConfigured": "Token de acceso no creado", "dontHaveACreditCardChooseSepa": "¿No tiene tarjeta de crédito? Elija 'Débito Directo SEPA' en el siguiente paso.", "donateToTheClothingLoop": "Donar a The Clothing Loop", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index 5e2d2f314..a0fc18066 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Lire notre rapport d'impact", "thankYou": "Merci !", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "Vous êtes ce genre de personne spéciale qui soulève les autres et qui embellit le monde. Soyez assurés que chaque don nous incite à faire une petite danse dans notre bureau (à domicile). Il y a beaucoup de travail à accomplir. Vos dons nous ont permis d'aller de l'avant et de progresser vers un monde plus vert et vivable. Merci beaucoup pour votre soutien à la réussite de The Clothing Loop.", - "byDonatingYouAgreeWithOur": "En faisant un don, vous acceptez nos ", + "byDonatingYouAgreeWithOur": "En faisant un don, vous acceptez nos <1>Privacy Policy.", "accessTokenNotConfigured": "Clé d'accès non configurée", "dontHaveACreditCardChooseSepa": "Vous n'avez pas de carte de crédit ? Dans l'étape suivante, choisissez \"SEPA Direct Debit\".", "donateToTheClothingLoop": "Faire un don à The Clothing Loop", diff --git a/frontend/public/locales/he/translation.json b/frontend/public/locales/he/translation.json index 471848cb3..b7b854413 100644 --- a/frontend/public/locales/he/translation.json +++ b/frontend/public/locales/he/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "קראו את דו\"ח ההשפעה שלנו", "thankYou": "תודה רבה!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "אתם מסוג האנשים המיוחדים שמעודדים אחרים ועושים את העולם מקום טוב יותר. היו סמוכים ובטוחים שכל תרומה גורמת לנו לעשות ריקוד קטן במשרד (הבית) שלנו. יש לנו עוד הרבה עבודה לפנינו, והתרומה שלכם הפכה את זה להרבה יותר קל להשגה, זה דוחף אותנו קדימה לעבר עולם יציב יותר. תודה רבה על רצונכם לעזור להצלחת The Clothing Loop.", - "byDonatingYouAgreeWithOur": "על-ידי התרומה אתם מסכימים עם ", + "byDonatingYouAgreeWithOur": "על-ידי התרומה אתם מסכימים עם <1>Privacy Policy.", "accessTokenNotConfigured": "גישת התוקן אינה נקבעה", "dontHaveACreditCardChooseSepa": "אין ברשותכם כרטיס אשראי? אנחנו בחרו 'SEPA Direct Debit' בשלב הבא.", "donateToTheClothingLoop": "תירמו ל The Clothing Loop", diff --git a/frontend/public/locales/it/translation.json b/frontend/public/locales/it/translation.json index fbd59807c..3971a960a 100644 --- a/frontend/public/locales/it/translation.json +++ b/frontend/public/locales/it/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/ja/translation.json b/frontend/public/locales/ja/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/ja/translation.json +++ b/frontend/public/locales/ja/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/ko/translation.json b/frontend/public/locales/ko/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/ko/translation.json +++ b/frontend/public/locales/ko/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/nl/translation.json b/frontend/public/locales/nl/translation.json index 57222fb52..beca2009c 100644 --- a/frontend/public/locales/nl/translation.json +++ b/frontend/public/locales/nl/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Lees ons impactrapport", "thankYou": "Dankjewel!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "Jij bent diegene die de wereld een betere plek maakt. En je kan er zeker vanuit gaan dat wij bij elke donatie een kein dansje doen in ons (thuis) kantoor. We hebben nog veel werk voor de boeg en door middel van uw donatie help je ons mee om de wereld een duurzamere plek te maken. Heel erg bedankt voor je wens om de Clothing Loop te helpen slagen.", - "byDonatingYouAgreeWithOur": "Door te doneren gaat u akkoord met onze ", + "byDonatingYouAgreeWithOur": "Door te doneren gaat u akkoord met onze <1>Privacy Policy.", "accessTokenNotConfigured": "Toegangstoken is niet geconfigureerd", "dontHaveACreditCardChooseSepa": "Heb je geen creditcard? Kies in de volgende stap voor 'SEPA Direct Debit''.", "donateToTheClothingLoop": "Doneer aan de Clothing Loop", diff --git a/frontend/public/locales/pl/translation.json b/frontend/public/locales/pl/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/pl/translation.json +++ b/frontend/public/locales/pl/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/pt/translation.json b/frontend/public/locales/pt/translation.json index 8344ae21a..31195fd11 100644 --- a/frontend/public/locales/pt/translation.json +++ b/frontend/public/locales/pt/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/public/locales/sv/translation.json b/frontend/public/locales/sv/translation.json index 812a19f17..750facc53 100644 --- a/frontend/public/locales/sv/translation.json +++ b/frontend/public/locales/sv/translation.json @@ -222,7 +222,7 @@ "readOurImpactReport": "Läs vår impact report", "thankYou": "Tack!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "Du är den speciella typen av person som lyfter upp människor och gör världen till en bättre plats. Var säker på att varje donation får oss att göra en liten dans på vårt (hemma)kontor. Vi har mycket arbete framför oss, och din donation har gjort det så mycket lättare att få saker gjorda och drivit oss framåt mot en mer hållbar värld. Tack så mycket för din önskan att hjälpa The Clothing Loop att lyckas.", - "byDonatingYouAgreeWithOur": "Genom att donera godkänner du vår ", + "byDonatingYouAgreeWithOur": "Genom att donera godkänner du vår <1>Privacy Policy.", "accessTokenNotConfigured": "Åtkomsttoken inte konfigurerad", "dontHaveACreditCardChooseSepa": "Har du inget kreditkort? Välj \"SEPA Autogiro\" i nästa steg.", "donateToTheClothingLoop": "Skänka till The Clothing Loop", diff --git a/frontend/public/locales/zh/translation.json b/frontend/public/locales/zh/translation.json index 427e84267..e9f8f03ec 100644 --- a/frontend/public/locales/zh/translation.json +++ b/frontend/public/locales/zh/translation.json @@ -173,7 +173,7 @@ "readOurImpactReport": "Read our impact report", "thankYou": "Thank you!", "youAretheSpecialTypeOfPersonThatLiftsPeopleUp": "You are the special type of person that lifts people up, and makes the world a better place. Rest assured that every donation has us doing a little dance in our (home) office. We've got a lot of work ahead of us, and your donation has made it that much easier to get things done and thrust us forward towards a more sustainable world. Thank you so much for your desire to help The Clothing Loop succeed.", - "byDonatingYouAgreeWithOur": "By donating you agree with our ", + "byDonatingYouAgreeWithOur": "By donating you agree with our <1>Privacy Policy.", "accessTokenNotConfigured": "Access token not configured", "dontHaveACreditCardChooseSepa": "Don't have a credit card? Choose 'SEPA Direct Debit' in the next step.", "donateToTheClothingLoop": "Donate to The Clothing Loop", diff --git a/frontend/src/components/react/pages/DonationForm.tsx b/frontend/src/components/react/pages/DonationForm.tsx index 117871280..dcc26182a 100644 --- a/frontend/src/components/react/pages/DonationForm.tsx +++ b/frontend/src/components/react/pages/DonationForm.tsx @@ -6,7 +6,7 @@ import { paymentInitiate, priceIDs } from "../../../api/payment"; import { GinParseErrors } from "../util/gin-errors"; import useForm from "../util/form.hooks"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { addToastError } from "../../../stores/toast"; import useLocalizePath from "../util/localize_path.hooks"; @@ -312,15 +312,18 @@ function DonationFormContent() {

- {t("byDonatingYouAgreeWithOur")} - - {t("privacyPolicy")} - - . + + ), + }} + />

    @@ -343,20 +346,61 @@ export default function DonationForm() { const stripePromise = loadStripe(stripePublicKey); return ( -
    -

    - {t("donateToTheClothingLoop")} -

    -
    - - - - +
    +
    +

    + {t("donateToTheClothingLoop")} +

    +
    + + + + +
    +
    +
    + + anbi logo + + +
      +
    • + An ANBI does not pay donation tax over donations that are used + for general interest. The maximum amount of the exemption for + donation tax in 2021 is € 3.244. +
    • +
    • + An ANBI does not pay inheritance tax over inheritances that are + used as contributions to the mission of the organization. The + maximum amount of the exemption in inheritance tax is € 2.244. +
    • +
    • An ANBI can be entitled to a return of the energy tax.
    • +
    • + The work done by volunteers for an institution with an + ANBI-status can be seen as a gift under certain conditions for + the tax assessment. +
    • +
    • + Donors that make donations to an ANBI can - if the donation is + recorded - deduct that donation in their assessment income- or + partnership tax. +
    • +
    +
    +
    ); } diff --git a/server/go.mod b/server/go.mod index 310d11bd7..a63f199f5 100644 --- a/server/go.mod +++ b/server/go.mod @@ -23,7 +23,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/stripe/stripe-go/v73 v73.16.0 github.com/wneessen/go-mail v0.3.9 - golang.org/x/net v0.20.0 + golang.org/x/net v0.23.0 gopkg.in/guregu/null.v3 v3.5.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.1 @@ -76,11 +76,11 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.design/x/go2generics v0.0.1 golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/image v0.10.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/oauth2 v0.9.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/server/go.sum b/server/go.sum index fc796fd28..0aed8340b 100644 --- a/server/go.sum +++ b/server/go.sum @@ -365,8 +365,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -439,8 +439,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -497,8 +497,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=