parcelvoy / platform

Parcelvoy: Open source multi-channel marketing automation platform. Send data-driven emails, sms, push notifications and more!
https://parcelvoy.com
Other
209 stars 31 forks source link

Uploaded image not found (error 404) #381

Closed mattes3 closed 5 months ago

mattes3 commented 6 months ago

When I upload an image inside the email template editor, it lands in my ./data/uploads directory correctly. However, when I want to preview the email, the image is not found. The server returns HTTP 404 (not found).

In my .env file, next to the docker compose file, I have this setting:

STORAGE_DRIVER=local
STORAGE_BASE_URL=https://my-domain.com/uploads

In the docker-compose file, the volume declaration looks like this:

volumes:
  mysql_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: ./data/mysql

  redis_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: ./data/redis

  uploads:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: ./data/uploads

Upload works fine, the image is in the correct directory!

These are the permissions of the directory:

drwxr-xr-x 2             1000             1000 4096 Mar 10 15:38 uploads

What needs to be done here?

mattes3 commented 6 months ago

Oh, how stupid I was! I could solve this problem inside my own reverse proxy configuration. Because I use Caddy server as a reverse proxy anyway, a few additional lines of config did the trick. I share all lines here so you understand my assumptions.

First, some Docker commands:

docker network create parcelvoy-network
docker volume create parcelvoy-files

This creates a network and a volume which can both be shared between Parcelvoy and Caddy. I need to do this because they have separate docker-compose files.

Then, in Caddy's docker-compose.yml file I write:

version: '3.3'
services:
  caddy:
    # ...
    volumes:
      - parcelvoy-files:/srv/parcelvoy/files:ro
    networks:
      - parcelvoy-network
#
networks:
  parcelvoy-network:
    external: true
#
volumes:
  parcelvoy-files:
    external: true

Then, in Caddyfile:

my-domain.com {
    route {
        file_server /files/* {
            root /srv/parcelvoy
        }

        reverse_proxy parcelvoy-ui:3000
    }
}

Then, in Parcelvoy's docker-compose.yml file:

services:
  api:
    # ...
    networks:
      - parcelvoy-network
    volumes:
      - parcelvoy-files:/usr/src/app/public/uploads

  parcelvoy-ui:
    # ...
    networks:
      - parcelvoy-network

networks:
  parcelvoy-network:
    external: true

volumes:
  parcelvoy-files:
    external: true

And, in Parcelvoy's .env file:

STORAGE_DRIVER=local
STORAGE_BASE_URL=https://my-domain.com/files

All this works just fine.

pushchris commented 6 months ago

Glad you were able to get this resolved! In general the problem was most likely the directory you are specifying (./data/uploads) as those files are then not automatically statically served by the API but ./public/uploads is. Looks like you came around to this in your updated comment!

mattes3 commented 5 months ago

Yes, I thought my volume mapping would already do the trick, but it did not:

services:
  api:
    volumes:
      - parcelvoy-files:/usr/src/app/public/uploads

The weird thing is that the API uploads the files into that place but does not serve them from that place.

pushchris commented 5 months ago

The other piece to the puzzle may be the Nginx config for the frontend. By default the API endpoints are served under the /api namespace which lets you have both under a single domain for cookie purposes. The Nginx config proxies some paths over (such as the images one) so that you don't need it to be /api/uploads for example but it would require the folder where you are serving the images from to be at that strict /public/uploads (the STORAGE_BASE_URL that is). That env variable is mostly for use if you are putting a CDN in front of the images and want to rewrite the URL in some way

    location ~ ^\/(uploads|unsubscribe|.well-known|(?:c$|c\/)|(?:o$|o\/)) { 
        rewrite ^\/(.*)$ /api/$1 break;
        proxy_pass http://api:3001;
    }
pushchris commented 5 months ago

Closing this for now, if you have any further issues please dont hesitate to reach back out!