├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── appwrite-function ├── code.tar.gz └── index.js ├── composer.json ├── composer.lock ├── cover.png ├── docker-compose.yml └── src └── index.php /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor 2 | appwrite-function -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | WEBHOOK_PROXY_APPWRITE_ENDPOINT=http://[YOUR_HOST]/v1 2 | WEBHOOK_PROXY_APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID] 3 | WEBHOOK_PROXY_APPWRITE_API_KEY=[YOUR_API_KEY] 4 | WEBHOOK_PROXY_APPWRITE_FUNCTION_ID=[YOUR_FUNCTION_ID] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | appwrite-function/node_modules 3 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM phpswoole/swoole:4.8-php7.4-alpine 2 | 3 | COPY . /usr/src/myapp 4 | WORKDIR /usr/src/myapp 5 | 6 | RUN composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist 7 | 8 | EXPOSE 4444 9 | 10 | CMD [ "php", "./src/index.php" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Cover image](cover.png) 2 | 3 | # Appwrite Webhook Proxy 4 | 5 | A simple HTTP server behaving as proxy between webhooks and Appwrite Functions, allowing for instance Stripe payments integration into Appwrite. 6 | 7 | **🚨 This is unofficial 3rd party exception for self-hosted Appwrite server 🚨** 8 | 9 | _Maintained by core team member, tho 😛_ 10 | 11 | ## Usage 12 | 13 | 0. Make sure to have Appwrite function that can print `APPWRITE_FUNCTION_DATA` for you, so you can see it working. You can use `appwrite-function.tar.gz` from this repo as example function. Make sure to use `NodeJS` runtime, and set command to `node index.js`. 14 | 15 | 1. Add webhook proxy container to Appwrite's docker stack. Locate `docker-compose.yml` file inside your `appwrite` folder, and add following section: 16 | 17 | ```yml 18 | version: "3" 19 | 20 | services: 21 | appwrite-webhook-proxy: 22 | image: meldiron/appwrite-webhook-proxy:v0.0.5 23 | container_name: appwrite-webhook-proxy 24 | restart: unless-stopped 25 | labels: 26 | - "traefik.enable=true" 27 | - "traefik.constraint-label-stack=appwrite" 28 | - "traefik.docker.network=appwrite" 29 | - "traefik.http.services.appwrite_webhook_proxy.loadbalancer.server.port=4444" 30 | # http 31 | - traefik.http.routers.appwrite_webhook_proxy_http.entrypoints=appwrite_web 32 | - traefik.http.routers.appwrite_webhook_proxy_http.rule=PathPrefix(`/v1/webhook-proxy`) 33 | - traefik.http.routers.appwrite_webhook_proxy_http.service=appwrite_webhook_proxy 34 | # https 35 | - traefik.http.routers.appwrite_webhook_proxy_https.entrypoints=appwrite_websecure 36 | - traefik.http.routers.appwrite_webhook_proxy_https.rule=PathPrefix(`/v1/webhook-proxy`) 37 | - traefik.http.routers.appwrite_webhook_proxy_https.service=appwrite_webhook_proxy 38 | - traefik.http.routers.appwrite_webhook_proxy_https.tls=true 39 | - traefik.http.routers.appwrite_webhook_proxy_https.tls.certresolver=dns 40 | networks: 41 | - appwrite 42 | depends_on: 43 | - appwrite 44 | environment: 45 | - WEBHOOK_PROXY_APPWRITE_ENDPOINT 46 | - WEBHOOK_PROXY_APPWRITE_PROJECT_ID 47 | - WEBHOOK_PROXY_APPWRITE_API_KEY 48 | - WEBHOOK_PROXY_APPWRITE_FUNCTION_ID 49 | # ... 50 | # ... 51 | ``` 52 | 53 | 2. Add webhook proxy configuration into Appwrite's stack. Locate hidden `.env` file inside your `appwrite` folder, and add following variables: 54 | 55 | ``` 56 | WEBHOOK_PROXY_APPWRITE_ENDPOINT=http://[YOUR_IP]/v1 57 | WEBHOOK_PROXY_APPWRITE_PROJECT_ID=stripeTest 58 | WEBHOOK_PROXY_APPWRITE_API_KEY=cb61e6bc3...abafcbb02 59 | WEBHOOK_PROXY_APPWRITE_FUNCTION_ID=onStripeWebhook 60 | ``` 61 | 62 | > Make sure to replace values with your credentials and information. If your endpoint is `localhost`, set the IP to your LAN IP address. 63 | 64 | 3. Restart Appwrite stack using `docker-compose up -d` 65 | 66 | That's it! You can now visit `http://localhost/v1/webhook-proxy` (replace localhost with domain of Appwrite instance), and it will execute your function. You can see headers, endpoint, body, and pretty much all request information in the execution log, if you used our function tag from step 0. 67 | 68 | Environment variables provided in `.env` are only considered as a fallback, if no other value is provided. That means, you can change any of `endpoint`, `projectId`, `apiKey` and `functionId`, simply by providing it in the request **query parameters**. For example: 69 | 70 | ``` 71 | http://localhost/v1/webhook-proxy?functionId=createPayment 72 | http://localhost/v1/webhook-proxy?functionId=deletepayment 73 | 74 | http://localhost/v1/webhook-proxy?functionId=deleteUser&projectId=awesomeProject&apiKey=abcd....wxyz 75 | ``` 76 | 77 | ## Contribution 78 | 79 | Setup: 80 | 81 | ```bash 82 | docker run --rm --interactive --tty \ 83 | --volume $PWD:/app \ 84 | composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist 85 | ``` 86 | 87 | Run local version: 88 | 89 | ```bash 90 | docker-compose up --build --force-recreate 91 | ``` 92 | 93 | > Use command above to also restart script after doing any changes. 94 | 95 | ## Release version 96 | 97 | 1. `docker login` 98 | 2. `docker build -t meldiron/appwrite-webhook-proxy:v0.0.2 .` 99 | 3. `docker push meldiron/appwrite-webhook-proxy:v0.0.2` 100 | 101 | > Make sure to change version number with releasing new version 102 | -------------------------------------------------------------------------------- /appwrite-function/code.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meldiron/appwrite-webhook-proxy/f4e46ca52d683f7e31d7a43864f89d8fccc8a93d/appwrite-function/code.tar.gz -------------------------------------------------------------------------------- /appwrite-function/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async (req, res) => { 2 | res.json({ payload: req.payload }) 3 | } 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meldiron/appwrite-webhook-proxy", 3 | "description": "A simple HTTP server behaving as proxy between webhooks and Appwrite Functions, allowing for instance Stripe payments integration into Appwrite.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Matej Bačo", 8 | "email": "matej@appwrite.io" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=7.4", 13 | "ext-swoole": ">=4.6" 14 | }, 15 | "require-dev": { 16 | "swoole/ide-helper": "4.8.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "2b5b30fdf37befeaa33d9b1f1d2316c4", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "swoole/ide-helper", 12 | "version": "4.8.3", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/swoole/ide-helper.git", 16 | "reference": "3ac4971814273889933b871e03b2a6b340e58f79" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/swoole/ide-helper/zipball/3ac4971814273889933b871e03b2a6b340e58f79", 21 | "reference": "3ac4971814273889933b871e03b2a6b340e58f79", 22 | "shasum": "" 23 | }, 24 | "type": "library", 25 | "notification-url": "https://packagist.org/downloads/", 26 | "license": [ 27 | "Apache-2.0" 28 | ], 29 | "authors": [ 30 | { 31 | "name": "Team Swoole", 32 | "email": "team@swoole.com" 33 | } 34 | ], 35 | "description": "IDE help files for Swoole.", 36 | "support": { 37 | "issues": "https://github.com/swoole/ide-helper/issues", 38 | "source": "https://github.com/swoole/ide-helper/tree/4.8.3" 39 | }, 40 | "funding": [ 41 | { 42 | "url": "https://gitee.com/swoole/swoole?donate=true", 43 | "type": "custom" 44 | }, 45 | { 46 | "url": "https://github.com/swoole", 47 | "type": "github" 48 | } 49 | ], 50 | "time": "2021-12-01T08:11:40+00:00" 51 | } 52 | ], 53 | "aliases": [], 54 | "minimum-stability": "stable", 55 | "stability-flags": [], 56 | "prefer-stable": false, 57 | "prefer-lowest": false, 58 | "platform": { 59 | "php": ">=7.4", 60 | "ext-swoole": ">=4.6" 61 | }, 62 | "platform-dev": [], 63 | "plugin-api-version": "2.2.0" 64 | } 65 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meldiron/appwrite-webhook-proxy/f4e46ca52d683f7e31d7a43864f89d8fccc8a93d/cover.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | appwrite-webhook-proxy: 4 | build: . 5 | ports: 6 | - 4444:4444 7 | volumes: 8 | - ./:/usr/src/myapp 9 | environment: 10 | - WEBHOOK_PROXY_APPWRITE_FUNCTION_ID 11 | - WEBHOOK_PROXY_APPWRITE_API_KEY 12 | - WEBHOOK_PROXY_APPWRITE_PROJECT_ID 13 | - WEBHOOK_PROXY_APPWRITE_ENDPOINT 14 | -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | handle('/', function(Swoole\Http\Request $swooleRequest, Swoole\Http\Response $swooleResponse) 12 | { 13 | try { 14 | $endpoint = $swooleRequest->get['endpoint'] ?? \getenv('WEBHOOK_PROXY_APPWRITE_ENDPOINT'); 15 | $apiKey = $swooleRequest->get['apiKey'] ?? \getenv('WEBHOOK_PROXY_APPWRITE_API_KEY'); 16 | $projectId = $swooleRequest->get['projectId'] ?? \getenv('WEBHOOK_PROXY_APPWRITE_PROJECT_ID'); 17 | $functionId = $swooleRequest->get['functionId'] ?? \getenv('WEBHOOK_PROXY_APPWRITE_FUNCTION_ID'); 18 | 19 | $requestBody = [ 20 | 'data' => \json_encode([ 21 | 'method' => $swooleRequest->getMethod(), 22 | 'body' => $swooleRequest->getContent(), 23 | 'headers' => $swooleRequest->header, 24 | 'params' => $swooleRequest->get 25 | ]), 26 | 'async' => false 27 | ]; 28 | 29 | $ch = \curl_init(); 30 | 31 | $optArray = array( 32 | CURLOPT_URL => $endpoint . '/functions/' . $functionId . '/executions', 33 | CURLOPT_RETURNTRANSFER => true, 34 | CURLOPT_POST => true, 35 | CURLOPT_POSTFIELDS => \json_encode($requestBody), 36 | CURLOPT_HEADEROPT => \CURLHEADER_UNIFIED, 37 | CURLOPT_HTTPHEADER => array( 38 | 'Content-Type: application/json', 39 | 'X-Appwrite-Project: ' . $projectId, 40 | 'X-Appwrite-Key: ' . $apiKey 41 | ) 42 | ); 43 | 44 | \curl_setopt_array($ch, $optArray); 45 | 46 | $result = \json_decode(curl_exec($ch)); 47 | 48 | $response = curl_getinfo($ch, \CURLINFO_HTTP_CODE); 49 | 50 | $responseObject = [ 51 | 'code' => $response, 52 | 'error' => \curl_error($ch), 53 | 'body' => $result 54 | ]; 55 | 56 | \curl_close($ch); 57 | 58 | $swooleResponse->setStatusCode($response); 59 | $swooleResponse->end(\json_encode($responseObject)); 60 | } catch(\Throwable $err) { 61 | \var_dump($err); 62 | $swooleResponse->setStatusCode(500); 63 | $swooleResponse->end(\json_encode([ 64 | 'code' => 500, 65 | 'error' => 'Unexpected server error.', 66 | 'body' => [] 67 | ])); 68 | } 69 | }); 70 | 71 | $server->start(); 72 | }); --------------------------------------------------------------------------------