Skip to content

Commit

Permalink
Merge pull request #16 from magicpages/develop
Browse files Browse the repository at this point in the history
v1.2.2
  • Loading branch information
betschki authored Jul 1, 2024
2 parents 1484793 + 43570c8 commit 8c843c6
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 59 deletions.
47 changes: 29 additions & 18 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
name: Build and Push Docker image
name: Docker Builds

on:
push:
branches:
- develop
tags:
- 'v*'

jobs:
build-and-push:
dev-build-and-push:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- name: Check out the code
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Log in to Docker Hub
uses: docker/login-action@v1
- uses: actions/checkout@v2
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: magicpages/bunnycdn-perma-cache-purger:dev
platforms: linux/amd64,linux/arm64

- name: Extract version from tag
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV

release-build-and-push:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: magicpages/bunnycdn-perma-cache-purger:${{ env.VERSION }},magicpages/bunnycdn-perma-cache-purger:latest
build-args: VERSION=${{ env.VERSION }}
platforms: linux/amd64,linux/arm64

14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ghost-bunnycdn-middleware",
"version": "1.2.0",
"version": "1.2.1",
"description": "A middleware between a Ghost CMS instance and BunnyCDN, supposed to run in a Docker stack, alongside a Ghost container.",
"main": "dist/index.js",
"author": "Jannis Fedoruk-Betschki <[email protected]>",
Expand All @@ -13,19 +13,21 @@
"devDependencies": {
"@types/express": "4.17.21",
"@types/node": "20.14.8",
"typescript": "5.5.2",
"tsc-watch": "6.2.0"
"tsc-watch": "6.2.0",
"typescript": "5.5.2"
},
"dependencies": {
"body-parser": "1.20.2",
"@types/multer": "^1.4.11",
"body-parser": "^1.20.2",
"dotenv": "16.4.5",
"express": "4.19.2",
"form-data": "^4.0.0",
"multer": "^1.4.5-lts.1",
"node-fetch": "3.3.2"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsc-watch --noClear -p ./tsconfig.json --onSuccess \"node dist/index.js\"",
"ship": "sh ./ship.sh"
"dev": "tsc-watch --noClear -p ./tsconfig.json --onSuccess \"node dist/index.js\""
}
}
15 changes: 0 additions & 15 deletions ship.sh

This file was deleted.

76 changes: 61 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import express from 'express';
import fetch, { Headers, Response as FetchResponse } from 'node-fetch';
import FormData from 'form-data';
import dotenv from 'dotenv';
import bodyParser from 'body-parser';
import http from 'http';
import https from 'https';
import path from 'path';
import { deleteStorageZoneFile, getPullZoneName, listStorageZoneFiles } from './util.js';
import multer from 'multer';

dotenv.config();

const app = express();

// Set up multer for handling multipart/form-data requests
const upload = multer({ storage: multer.memoryStorage() });

// Set up body-parser for handling JSON requests
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));


const PORT = process.env.PORT || 3000;
const GHOST_URL = process.env.GHOST_URL?.replace(/\/$/, '');
Expand Down Expand Up @@ -154,8 +163,8 @@ const purgeCache = async (): Promise<void> => {
app.use((req, res, next) => {
const reqPath = req.path;

// Paths that should not have a trailing slash appended
const excludedPaths = ['/r/', '/api/'];
// Define paths that should not have a trailing slash appended.
const excludedPaths = ['/r/', '/ghost/api/'];

// Check if the path ends with a slash or has a file extension or is an excluded path
if (
Expand All @@ -166,9 +175,30 @@ app.use((req, res, next) => {
return next();
}

// If the path is not in the excluded list and does not end with a slash, append one
const query = req.url.slice(reqPath.length);
res.redirect(301, `${reqPath}/${query}`);
// If the path does not end with a slash, append one but exclude specific paths
if (!reqPath.endsWith('/') && !excludedPaths.some(ep => reqPath.startsWith(ep))) {
const query = req.url.slice(reqPath.length);
res.redirect(301, `${reqPath}/${query}`);
} else {
next();
}
});


app.use((req, res, next) => {
const contentType = req.headers['content-type'] || '';
if (contentType.includes('multipart/form-data')) {
upload.any()(req, res, (err) => {
if (err) {
return res
.status(500)
.json({ error: 'Failed to parse multipart/form-data.' });
}
next();
});
} else {
next();
}
});

app.use(async (req, res) => {
Expand All @@ -182,17 +212,36 @@ app.use(async (req, res) => {
headers.set('Cookie', req.headers.cookie);
}

console.info(
`${new Date().toISOString()} - Proxying request to ${currentUrl} with method ${
req.method
}`
);
let body;
if (req.is('multipart/form-data') && req.files) {
const formData = new FormData();
const files = req.files as Express.Multer.File[];

files.forEach((file) => {
formData.append(file.fieldname, file.buffer, file.originalname);
});

Object.keys(req.body).forEach((key) => {
formData.append(key, req.body[key]);
});

body = formData;
const formHeaders = formData.getHeaders();
Object.entries(formHeaders).forEach(([key, value]) => {
headers.set(key, value);
});
} else {
body = req.method !== 'GET' ? JSON.stringify(req.body) : undefined;
if (body) headers.set('Content-Type', 'application/json');
}

console.info(`Proxying request to ${currentUrl} with method ${req.method}`);

try {
const response = await fetch(currentUrl, {
method: req.method,
headers: Object.fromEntries(headers),
body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
body,
redirect: 'manual',
agent: currentUrl.startsWith('https') ? httpsAgent : httpAgent,
});
Expand Down Expand Up @@ -232,10 +281,7 @@ app.use(async (req, res) => {
res.end();
}
} catch (error) {
console.error(
`${new Date().toISOString()} - Error proxying request to ${currentUrl}:`,
error
);
console.error(`Error proxying request to ${currentUrl}: ${error}`);
res.status(500).send({
statusCode: 500,
error: 'Internal Server Error',
Expand Down
Loading

0 comments on commit 8c843c6

Please sign in to comment.