{"id":13621834,"url":"https://github.com/rafikk/halfshell","last_synced_at":"2025-04-07T15:10:50.746Z","repository":{"id":14960408,"uuid":"17685194","full_name":"rafikk/halfshell","owner":"rafikk","description":"A proxy server for processing images on the fly.","archived":false,"fork":false,"pushed_at":"2016-05-17T18:24:46.000Z","size":507,"stargazers_count":363,"open_issues_count":8,"forks_count":39,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-31T14:11:15.483Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"brewster/vcardigan","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rafikk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-12T21:43:48.000Z","updated_at":"2025-02-11T12:05:07.000Z","dependencies_parsed_at":"2022-08-30T07:51:01.869Z","dependency_job_id":null,"html_url":"https://github.com/rafikk/halfshell","commit_stats":null,"previous_names":["oysterbooks/halfshell"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafikk%2Fhalfshell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafikk%2Fhalfshell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafikk%2Fhalfshell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafikk%2Fhalfshell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rafikk","download_url":"https://codeload.github.com/rafikk/halfshell/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247675607,"owners_count":20977378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:11.039Z","updated_at":"2025-04-07T15:10:50.722Z","avatar_url":"https://github.com/rafikk.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Halfshell\n\nHalfshell is a proxy server for processing images on the fly. It allows you to dynamically resize (and apply effects to) images hosted on S3, a local filesystem or an http source via query parameters. It supports creating “families” of images which can read from distinct image sources and enable different configuration values for image processing and retrieval. See the [introduction blog post](http://engineering.oysterbooks.com/post/79458380259/resizing-images-on-the-fly-with-go).\n\nCurrent version: `0.1.1`\n\n## Architecture\n\nHalfshell was architected to be extensible from the beginning. The system is composed of a few components with their own configuration and simple interfaces.\n\n### Sources\n\nSources are repositories from which an “original” image can be loaded. They return an image given a path. Currently, sources for downloading images from S3, a local filesystem and http are included.\n\n### Processors\n\nProcessors perform all image manipulation. They accept an image and a set of options and return a modified image. Out of the box, the default processor supports resizing and blurring images. Each processor can be configured with maximum and default image dimensions and enable/disable certain features.\n\n### Routes\n\nRoutes bind URL rules (regular expressions) with a source and a processor. Halfshell supports setting up an arbitrary number of routes, and sources and processors do not need to correspond 1-1 with routes.\n\nWhen Halfshell receives a request, it determines the matching route, retrieves the image from its source, and processes the image using its processor.\n\nThis simple architecture has allowed us to serve images from multiple S3 buckets and maintain isolated configuration settings for each family of images.\n\n## Usage and Configuration\n\nHalfshell uses a JSON file for configuration. An example is shown below:\n\n```json\n{\n    \"server\": {\n        \"port\": 8080,\n        \"read_timeout\": 5,\n        \"write_timeout\": 30\n    },\n    \"sources\": {\n        \"default\": {\n            \"type\": \"s3\",\n            \"s3_access_key\": \"\u003cS3_ACCESS_KEY\u003e\",\n            \"s3_secret_key\": \"\u003cS3_SECRET_KEY\u003e\"\n        },\n        \"blog-post-images\": {\n            \"s3_bucket\": \"my-company-blog-post-images\"\n        },\n        \"profile-photos\": {\n            \"s3_bucket\": \"my-company-profile-photos\"\n        }\n    },\n    \"processors\": {\n        \"default\": {\n            \"image_compression_quality\": 85,\n            \"default_scale_mode\": \"aspect_fit\",\n            \"max_blur_radius_percentage\": 0,\n            \"max_image_height\": 0,\n            \"max_image_width\": 1000\n\n        },\n        \"profile-photos\": {\n            \"default_image_width\": 120\n        }\n    },\n    \"routes\": {\n        \"^/blog(?P\u003cimage_path\u003e/.*)$\": {\n            \"name\": \"blog-post-images\",\n            \"source\": \"blog-post-images\",\n            \"processor\": \"default\",\n            \"cache_control\": \"no-transform,public,max-age=2592000,s-maxage=31104000\"\n        },\n        \"^/users(?P\u003cimage_path\u003e/.*)$\": {\n            \"name\": \"profile-photos\",\n            \"source\": \"profile-photos\",\n            \"processor\": \"profile-photos\"\n        }\n    }\n}\n```\n\nTo start the server, pass configuration file path as an argument.\n\n```bash\n$ ./bin/halfshell config.json\n```\n\nThis will start the server on port 8080, and service requests whose path begins with /users/ or /blog/, e.g.:\n\n    http://localhost:8080/users/joe/default.jpg?w=100\u0026h=100\n    http://localhost:8080/blog/posts/announcement.jpg?w=600\u0026h=200\n\nThe image_host named group in the route pattern match (e.g., `^/users(?P\u003cimage_path\u003e/.*)$`) gets extracted as the request path for the source. In this instance, the file “joe/default.jpg” is requested from the “my-company-profile-photos” S3 bucket. The processor resizes the image to a width and height of 100.\n\n### Server\n\nThe `server` configuration block accepts the following settings:\n\n##### port\n\nThe port to run the server on.\n\n##### read_timeout\n\nThe timeout in seconds for reading the initial data from the connection.\n\n##### write_timeout\n\nThe timeout in seconds for writing the image data backto the connection.\n\n### Sources\n\nThe `sources` block is a mapping of source names to source configuration values.\nValues from a source named `default` will be inherited by all other sources.\n\n##### type\n\nThe type of image source. Currently `s3` or `filesystem`.\n\n##### s3_access_key\n\nFor the S3 source type, the access key to read from S3.\n\n##### s3_secret_key\n\nFor the S3 source type, the secret key to read from S3.\n\n##### s3_bucket\n\nFor the S3 source type, the bucket to request images from.\n\n##### directory\n\nFor the Filesystem source type, the local directory to request images from. Required.\nFor the S3 source type, `directory` corresponds to an optional base directory in the S3 bucket.\n\n### Processors\n\nThe `processors` block is a mapping of processor names to processor configuration values.\nValues from a processor named `default` will be inherited by all other processors.\n\n##### image_compression_quality\n\nThe compression quality to use for JPEG images.\n\n##### maintain_aspect_ratio\n\nDEPRECATED: Use the `aspect_fit` `scale_mode` instead.\n\nIf this is set to true, the resized images will always maintain the original\naspect ratio. When set to false, the image will be stretched to fit the width\nand height requested.\n\n##### default_scale_mode\n\nWhen changing the dimensions of an image, you may want to crop edges or\nconstrain proportions. Use the `default_scale_mode` setting to define these\nrules (`scale_mode` as a URL query parameter).\n\nA value of `aspect_fit` will change the image size to fit in the given\ndimensions while retaining original proportions. No part of the image will be\ncut away.\n\nA value of `aspect_fill` will change the image size to at least fit the given\ndimensions while retaining original proportions. No part of the image will be\ncut away.\n\nA value of `aspect_crop` will change the image size to fit in the given\ndimensions while retaining original proportions. Edges that do not fit in the\ngiven dimensions will be cut off.\n\nThe default behavior is to `fill`, which changes the image size to fit the given\ndimensions and will NOT retain the original proportions.\n\nCheat Sheet:\n\n    Image dimensions: 500x800\n    Requested dimensions: 400x400\n\n    Scale mode: fill\n    New dimensions: 400x400\n    Maintain aspect ratio: NO\n    Cropping: NO\n\n    Scale mode: aspect_fit\n    New dimensions: 250x400\n    Maintain aspect ratio: YES\n    Cropping: NO\n\n    Scale mode: aspect_fill\n    New dimensions: 400x640\n    Maintain aspect ratio: YES\n    Cropping: NO\n\n    Scale mode: aspect_crop\n    New dimensions: 400x400\n    Maintain aspect ratio: YES\n    Cropping: YES\n\n##### default_image_width\n\nIn the absence of a width parameter in the request, use this as image width. A\nvalue of `0` sets no default.\n##### default_image_height\n\nIn the absence of a height parameter in the request, use this as image height.\nA value of `0` sets no default.\n\n##### max_image_width\n\nSet a maximum image width. A value of `0` specifies no maximum.\n\n##### max_image_height\n\nSet a maximum image height. A value of `0` specifies no maximum.\n\n##### max_blur_radius_percentage\n\nSet a maximum blur radius percentage. A value of `0` disables blurring images.\nFor Gaussian blur, the radius used is this value * the image width. This allows\nyou to use a blur parameter (from 0-1) which will apply the same proportion of\nblurring to each image size.\n\n##### auto_orient\n\nIf set to true, the image processor will respect EXIF rotation data. A common\ncase are photos taken with a camera (eg: iPhone, digital camera) in landscape\nmode. The built-in gyroscope will embed rotation data in the image via EXIF.\n\nDisabled by default.\n\n##### formats\n\n```\nformats: {\n    \"large\": { \"width\": 1280, \"height\": 768, \"blur\": 0 },\n    \"medium\": { \"width\": 640, \"height\": 480, \"blur\": 0 }\n}\n```\n\nIf specified, the `w`, `h` and `blur` parameters will be ignored from the\nrequest. Instead will only be read the `format` parameter.\n\n### Routes\n\nThe `routes` block is a mapping of route patterns to route configuration values.\n\nThe route pattern is a regular expression with a captured group for `image_path`.\nThe subexpression match is the path that is requested from the image source.\n\n##### name\n\nThe name to use for the route. This is currently used in logging and StatsD key\nnames.\n\n##### source\n\nThe name of the source to use for the route.\n\n##### processor\n\nThe name of the processor to use for the route.\n\n##### cache_control\n\nThe Cache-Control response header to set. If left empty or unspecified, `no-transform,public,max-age=86400,s-maxage=2592000` will be set.\n\n### Health Checks\n\nYou can check the server health at `/healthcheck` and `/health`. If the server\nis up and running, the HTTP client will receive a response with status code\n`200`.\n\n## Adopters\n\n- [Oyster](https://www.oysterbooks.com)\n- [Storehouse](https://www.storehouse.co)\n\nIf your organization is using Halfshell, consider adding a link and sending us a pull request!\n\n## Contributing\n\nContributions are welcome.\n\n### Building\n\nThere's a Vagrant file set up to ease development. After you have the\nVagrant box set up, cd to the /vagrant directory and run `make`.\n\n### Notes\n\nRun `make format` before sending any pull requests.\n\n### Questions?\n\nFile an issue or send an email to rafik@oysterbooks.com.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafikk%2Fhalfshell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frafikk%2Fhalfshell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafikk%2Fhalfshell/lists"}