├── cron ├── Dockerfile ├── entrypoint.sh └── run-cron.sh ├── compose.yaml ├── README.md └── .github └── workflows └── docker-publish.yml /cron/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3 2 | 3 | RUN apk add bash dcron 4 | RUN mkdir -p /var/logs 5 | 6 | # 20 minute interval 7 | ENV EXECUTION_CRON_EXPRESSION */20 * * * * 8 | 9 | COPY entrypoint.sh / 10 | COPY run-cron.sh / 11 | RUN chmod +x /entrypoint.sh 12 | RUN chmod +x /run-cron.sh 13 | ENTRYPOINT ["/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | volumes: 2 | file_storage: 3 | 4 | services: 5 | server: 6 | build: build/ 7 | restart: unless-stopped 8 | volumes: 9 | - file_storage:/var/www/dl 10 | ports: 11 | - 127.0.0.1:8023:80 12 | cron: 13 | build: cron/ 14 | restart: unless-stopped 15 | volumes_from: 16 | - server 17 | -------------------------------------------------------------------------------- /cron/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | trap 'exit 0' SIGTERM | tee -a /var/logs/cron.log 4 | 5 | cat > crontab.tmp << EOF 6 | $EXECUTION_CRON_EXPRESSION /run-cron.sh $@ | tee -a /var/logs/cron.log 7 | # An empty line is required at the end of this file for a valid cron file. 8 | EOF 9 | 10 | crontab "crontab.tmp" 11 | rm -f "crontab.tmp" 12 | /usr/sbin/crond -L /var/logs/cron.log 13 | tail -n 5000 -f /var/logs/cron.log 14 | -------------------------------------------------------------------------------- /cron/run-cron.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | 3 | DL_DIR=/var/www/dl 4 | 5 | if [ ! -d ${DL_DIR} ]; then 6 | exit 0 7 | fi 8 | 9 | CURRENT_TS=$(date '+%s') 10 | find "${DL_DIR}" -type f -name "expires-at-*" | while read expires_file; do 11 | # /var/www/dl/2f6d5c47b014c1973f2d4f846303349d/expires-at-1585729013 12 | TS=$(basename ${expires_file} | sed -E 's/^expires-at-(\d+)$/\1/g') 13 | if [ $(echo ${TS} | grep -q '^\d\+$') -gt 0 ]; then 14 | continue; 15 | fi 16 | if (( CURRENT_TS > TS )); then 17 | echo -en $(date -d @${CURRENT_TS}) 18 | echo -en " > " 19 | echo -en $(date -d @${TS}) 20 | echo " >> DELETING $(dirname ${expires_file})" 21 | rm -r $(dirname ${expires_file}) 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # file-upload 2 | most simple file upload container 3 | to be used via curl 4 | `curl -F file=@somefile https://your-url` 5 | 6 | supports expiration by adding `?exp=..` to the querystring, like so: 7 | `curl -F file=@somefile https://your-url?exp=1w` 8 | 9 | the duration expressions supported are: 10 | * h - hours 11 | * d - days 12 | * w - weeks 13 | * m - months 14 | * y - years 15 | 16 | they can not combined as of now, only one may be used, the default is to not expire the upload, like before the addition of this feature. 17 | 18 | the marker for expiration is simply a file adhering to a naming convention indiciating the unix timestamp of expiry. 19 | 20 | a cron container has also been added, it will find expired uploads every 20 minutes and delete them. 21 | 22 | the project is meant to be used behind a reverse proxy, which is not in scope, I personally run a non-docker reverse proxy in front of all my projects, that's why this is bound to localhost by default. 23 | 24 | there are many projects out there incorporating TLS and all kinds of nice features, this one for example: https://github.com/linuxserver/docker-letsencrypt it could easily be added to the compose file and you're good to go 25 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | push: 10 | branches: [ "master" ] 11 | # Publish semver tags as releases. 12 | tags: [ 'v*.*.*' ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | env: 17 | # Use docker.io for Docker Hub if empty 18 | REGISTRY: ghcr.io 19 | # github.repository as / 20 | IMAGE_NAME: ${{ github.repository }} 21 | 22 | 23 | jobs: 24 | build: 25 | 26 | runs-on: ubuntu-latest 27 | permissions: 28 | contents: read 29 | packages: write 30 | # This is used to complete the identity challenge 31 | # with sigstore/fulcio when running outside of PRs. 32 | id-token: write 33 | 34 | strategy: 35 | matrix: 36 | image: 37 | - context: build 38 | suffix: build 39 | - context: cron 40 | suffix: cron 41 | 42 | steps: 43 | - name: Checkout repository 44 | uses: actions/checkout@v4 45 | 46 | # Install the cosign tool except on PR 47 | # https://github.com/sigstore/cosign-installer 48 | - name: Install cosign 49 | if: github.event_name != 'pull_request' 50 | uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 51 | with: 52 | cosign-release: 'v2.2.4' 53 | 54 | # Set up BuildKit Docker container builder to be able to build 55 | # multi-platform images and export cache 56 | # https://github.com/docker/setup-buildx-action 57 | - name: Set up Docker Buildx 58 | uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 59 | 60 | # Login against a Docker registry except on PR 61 | # https://github.com/docker/login-action 62 | - name: Log into registry ${{ env.REGISTRY }} 63 | if: github.event_name != 'pull_request' 64 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 65 | with: 66 | registry: ${{ env.REGISTRY }} 67 | username: ${{ github.actor }} 68 | password: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | # Extract metadata (tags, labels) for Docker 71 | # https://github.com/docker/metadata-action 72 | - name: Extract Docker metadata for ${{ matrix.image.suffix }} 73 | id: meta 74 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 75 | with: 76 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 77 | flavor: | 78 | suffix=-${{ matrix.image.suffix }},onlatest=true 79 | 80 | # Build and push Docker image with Buildx (don't push on PR) 81 | # https://github.com/docker/build-push-action 82 | - name: Build and push Docker image for ${{ matrix.image.suffix }} 83 | id: build-and-push 84 | uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 85 | with: 86 | context: ${{ matrix.image.context }}/ 87 | push: ${{ github.event_name != 'pull_request' }} 88 | tags: ${{ steps.meta.outputs.tags }} 89 | labels: ${{ steps.meta.outputs.labels }} 90 | cache-from: type=gha,scope=${{ matrix.image.suffix }} 91 | cache-to: type=gha,mode=max,scope=${{ matrix.image.suffix }} 92 | 93 | # Sign the resulting Docker image digest except on PRs. 94 | # This will only write to the public Rekor transparency log when the Docker 95 | # repository is public to avoid leaking data. If you would like to publish 96 | # transparency data even for private images, pass --force to cosign below. 97 | # https://github.com/sigstore/cosign 98 | - name: Sign the published Docker image for ${{ matrix.image.suffix }} 99 | if: ${{ github.event_name != 'pull_request' }} 100 | env: 101 | # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable 102 | TAGS: ${{ steps.meta.outputs.tags }} 103 | DIGEST: ${{ steps.build-and-push.outputs.digest }} 104 | # This step uses the identity token to provision an ephemeral certificate 105 | # against the sigstore community Fulcio instance. 106 | run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} 107 | --------------------------------------------------------------------------------