├── .gitignore
├── Dockerfile
├── README.md
├── config.js
├── docker-compose.yml
├── ecosystem.config.js
├── index.js
├── package-lock.json
├── package.json
├── pics
├── moon.jpg
└── webp_server.png
└── webp.service
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # next.js build output
79 | .next
80 |
81 | # nuxt.js build output
82 | .nuxt
83 |
84 | # gatsby files
85 | .cache/
86 | public
87 |
88 | # vuepress build output
89 | .vuepress/dist
90 |
91 | # Serverless directories
92 | .serverless/
93 |
94 | # FuseBox cache
95 | .fusebox/
96 |
97 | # DynamoDB Local files
98 | .dynamodb/
99 |
100 | # TernJS port file
101 | .tern-port
102 |
103 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10
2 |
3 | # Create app directory
4 | WORKDIR /usr/src/app
5 |
6 | COPY package*.json ./
7 |
8 | RUN npm ci
9 |
10 | COPY . .
11 |
12 | EXPOSE 3333
13 |
14 | ENV DOCKER=tsuki
15 | CMD ["node","index.js"]
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | **THIS PROJECT IS UNDER DEVELOPMENT, DON'T USE IT ON PRODUCTION ENVIRONMENT.**
6 |
7 | This is a NodeJS Server based on Express and cwebp, which allows you to serve WebP images on the fly.
8 |
9 | > e.g When you visit `https://a.com/1.jpg`,it will serve as `image/webp` without changing the URL.
10 | >
11 | > For Safari and Opera users, the original image will be used.
12 |
13 | ## Usage(Docker)
14 |
15 | If you've docker installed, you can try build the package by doing as follows(don't worry, it's simple):
16 |
17 | 1. Download the `docker-compose.yml` file to a folder you like, let's say `/home/nova/server1/`.
18 | 2. Edit the `docker-compose.yml`, at line 10 `./INTAKE` to the directory which contains your images.
19 | 3. run `docker-compose up -d` in that folder, this will run WebP Server with some parameters written in the `docker-compose.yml` and create the related folders.
20 | 4. Configure a NGINX server to reverse proxy it for public use.
21 |
22 | That't it, the supervision and fail-over is handled by Docker Daemon, no more PM2s or NPMs are needed, Yay!
23 |
24 | > Or, if you like the traditional way, see below.
25 |
26 | ## Usage(Traditional)
27 |
28 | Make sure you've installed `node` on your system, and the version of it shall be greater than 10, it will convert `jpg,jpeg,png` files by default, this can be customized by editing the `config.js`.
29 |
30 | 1. Clone the repo and run `npm install` in it.
31 | 2. Make Sure you've install pm2, if not, use `npm install pm2 -g`
32 | 3. Define your pics folder on `config.js` (for instance there is a `1.jpg` in the related pic folder):
33 | ```
34 | IMG_PATH: "/PATH/TO/pics",
35 | ```
36 | 4. Run the APP with `pm2 start ecosystem.config.js --env production`, as this is a temporary solution to suppress errors, this should be fixed on later commits.
37 | 5. Let Nginx to `proxy_pass http://localhost:3333/;`
38 |
39 | ## Detailed deploy instruction on a systemd based distros
40 | The following examples is tested under Ubuntu 18.04.
41 | ### 1. Install node
42 | ```shell script
43 | curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
44 | sudo apt-get install -y nodejs
45 | ```
46 | ### 2. Clone and install deps
47 | Refer to the previous usage section.
48 | ### 3. Add users
49 | ```shell script
50 | useradd -s /usr/sbin/nologin webp
51 | groupadd webp
52 | chown webp:webp -R /opt/webp_server/
53 | ```
54 | ### 4. systemd service
55 | ```shell script
56 | cp ./webp.service /lib/systemd/systemd/
57 | systemctl daemon-reload
58 | systemctl enable webp.service
59 | systemctl start webp.service
60 | ```
61 | ### 5. Nginx configuration
62 | Suppose you're using Wordpress and would like to serve Media images locate and edit your nginx configuration as follows
63 | ```
64 | location ^~ /wp-content/uploads/ {
65 | proxy_pass http://127.0.0.1:3333;
66 | }
67 | ```
68 | where `wp-content/uploads` is your image path.
69 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | HOST: '127.0.0.1',
3 | PORT: 3333,
4 | IMG_PATH: "pics",
5 | ALLOWED_TYPES: ['jpg','png','jpeg','webp']
6 | };
7 |
8 | module.exports = config;
9 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 |
3 | services:
4 |
5 | webp_server:
6 | image: n0vad3v/webp_server
7 | restart: always
8 | volumes:
9 | - ./EXHAUST:/usr/src/app/webp
10 | - ./INTAKE:/usr/src/app/pics
11 | environment:
12 | NODE_ENV: production
13 | ports:
14 | - 3000:3333
15 |
--------------------------------------------------------------------------------
/ecosystem.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps : [{
3 | name: 'WebP Server',
4 | script: 'index.js',
5 |
6 | // Options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/
7 | args: '',
8 | instances: 1,
9 | autorestart: true,
10 | watch: false,
11 | max_memory_restart: '1G',
12 | env: {
13 | NODE_ENV: 'development'
14 | },
15 | env_production: {
16 | NODE_ENV: 'production'
17 | }
18 | }],
19 | };
20 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 |
4 | const express = require('express');
5 | let webp = require('webp-converter');
6 |
7 | // Load config files here
8 | const config = require('./config');
9 | const PORT = config.PORT;
10 | const ALLOWED_TYPES = config.ALLOWED_TYPES;
11 | // Docker specified config
12 | const IMG_PATH = process.env.IMG_PATH || config.IMG_PATH;
13 | const HOST = process.env.DOCKER ? "0.0.0.0" : config.HOST;
14 |
15 | if (process.argv.length === 3)
16 | IMG_PATH = process.argv[2];
17 |
18 | const app = express();
19 |
20 | // Define the catch-all interface
21 | app.get('/*', (req, res) => {
22 | // Get requested URL and concatenate absolute path and URL
23 | let requested_img_path = path.join(IMG_PATH, req.originalUrl); // Will get /PATH/TO/pics/images/tsuki.jpg
24 |
25 | // Check allowed file extensions
26 | let file_extension = path.extname(req.originalUrl).split('.')[1].toLowerCase();
27 | if (!ALLOWED_TYPES.includes(file_extension)) {
28 | res.status(500).send("File Not Allowed.");
29 | }
30 |
31 | let cache_path = path.join(__dirname, "webp/" + path.dirname(req.originalUrl)); // Will get /PATH/TO/webp_server/webp/images
32 | let cache_img_path = path.join(__dirname, "webp/" + req.originalUrl.split(".")[0] + ".webp"); // Will get /PATH/TO/webp_server/webp/images/tsuki.webp
33 |
34 | // Check the original image for existence
35 | if (!fs.existsSync(requested_img_path)) {
36 | // The original image doesn't exist, check the webp image, delete if processed.
37 | if (fs.existsSync(cache_img_path)) {
38 | fs.unlink(cache_img_path);
39 | }
40 | res.status(404).send("Not Found.");
41 | }
42 |
43 | // Check if its Safari, and send the original image
44 | let ua = req.headers['user-agent'];
45 | console.log("UA is " + ua);
46 | if (ua.includes('Safari') && !ua.includes('Chrome') && !ua.includes('Firefox')) {
47 | console.log("Gotcha Safari!");
48 | res.sendFile(requested_img_path);
49 | return;
50 | }
51 |
52 | // Make Cache path for the processed images
53 | if (!fs.existsSync(cache_path)) {
54 | fs.mkdirSync(cache_path, {
55 | recursive: true
56 | });
57 | }
58 |
59 | // Hit Cache, then just send the processed images
60 | if (fs.existsSync(cache_img_path)) {
61 | console.log("Sending");
62 | res.sendFile(cache_img_path);
63 | return;
64 | }
65 |
66 | let webp_image = webp.cwebp(requested_img_path, cache_img_path, "-q 80", function (status, error) {
67 | res.sendFile(cache_img_path);
68 | console.log("Convert" + requested_img_path);
69 | console.log(status, error);
70 | })
71 | });
72 |
73 |
74 | app.listen(PORT, HOST, () =>
75 | console.log(`WebP Server is listening on http://${HOST}:${PORT} with ${IMG_PATH}`));
76 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webp_server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.7",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
11 | "requires": {
12 | "mime-types": "~2.1.24",
13 | "negotiator": "0.6.2"
14 | }
15 | },
16 | "array-flatten": {
17 | "version": "1.1.1",
18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
20 | },
21 | "body-parser": {
22 | "version": "1.19.0",
23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
24 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
25 | "requires": {
26 | "bytes": "3.1.0",
27 | "content-type": "~1.0.4",
28 | "debug": "2.6.9",
29 | "depd": "~1.1.2",
30 | "http-errors": "1.7.2",
31 | "iconv-lite": "0.4.24",
32 | "on-finished": "~2.3.0",
33 | "qs": "6.7.0",
34 | "raw-body": "2.4.0",
35 | "type-is": "~1.6.17"
36 | }
37 | },
38 | "bytes": {
39 | "version": "3.1.0",
40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
41 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
42 | },
43 | "content-disposition": {
44 | "version": "0.5.3",
45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
46 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
47 | "requires": {
48 | "safe-buffer": "5.1.2"
49 | }
50 | },
51 | "content-type": {
52 | "version": "1.0.4",
53 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
54 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
55 | },
56 | "cookie": {
57 | "version": "0.4.0",
58 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
59 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
60 | },
61 | "cookie-signature": {
62 | "version": "1.0.6",
63 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
64 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
65 | },
66 | "debug": {
67 | "version": "2.6.9",
68 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
69 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
70 | "requires": {
71 | "ms": "2.0.0"
72 | }
73 | },
74 | "depd": {
75 | "version": "1.1.2",
76 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
77 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
78 | },
79 | "destroy": {
80 | "version": "1.0.4",
81 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
82 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
83 | },
84 | "ee-first": {
85 | "version": "1.1.1",
86 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
87 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
88 | },
89 | "encodeurl": {
90 | "version": "1.0.2",
91 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
92 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
93 | },
94 | "escape-html": {
95 | "version": "1.0.3",
96 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
97 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
98 | },
99 | "etag": {
100 | "version": "1.8.1",
101 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
102 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
103 | },
104 | "express": {
105 | "version": "4.17.1",
106 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
107 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
108 | "requires": {
109 | "accepts": "~1.3.7",
110 | "array-flatten": "1.1.1",
111 | "body-parser": "1.19.0",
112 | "content-disposition": "0.5.3",
113 | "content-type": "~1.0.4",
114 | "cookie": "0.4.0",
115 | "cookie-signature": "1.0.6",
116 | "debug": "2.6.9",
117 | "depd": "~1.1.2",
118 | "encodeurl": "~1.0.2",
119 | "escape-html": "~1.0.3",
120 | "etag": "~1.8.1",
121 | "finalhandler": "~1.1.2",
122 | "fresh": "0.5.2",
123 | "merge-descriptors": "1.0.1",
124 | "methods": "~1.1.2",
125 | "on-finished": "~2.3.0",
126 | "parseurl": "~1.3.3",
127 | "path-to-regexp": "0.1.7",
128 | "proxy-addr": "~2.0.5",
129 | "qs": "6.7.0",
130 | "range-parser": "~1.2.1",
131 | "safe-buffer": "5.1.2",
132 | "send": "0.17.1",
133 | "serve-static": "1.14.1",
134 | "setprototypeof": "1.1.1",
135 | "statuses": "~1.5.0",
136 | "type-is": "~1.6.18",
137 | "utils-merge": "1.0.1",
138 | "vary": "~1.1.2"
139 | }
140 | },
141 | "finalhandler": {
142 | "version": "1.1.2",
143 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
144 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
145 | "requires": {
146 | "debug": "2.6.9",
147 | "encodeurl": "~1.0.2",
148 | "escape-html": "~1.0.3",
149 | "on-finished": "~2.3.0",
150 | "parseurl": "~1.3.3",
151 | "statuses": "~1.5.0",
152 | "unpipe": "~1.0.0"
153 | }
154 | },
155 | "forwarded": {
156 | "version": "0.1.2",
157 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
158 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
159 | },
160 | "fresh": {
161 | "version": "0.5.2",
162 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
163 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
164 | },
165 | "http-errors": {
166 | "version": "1.7.2",
167 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
168 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
169 | "requires": {
170 | "depd": "~1.1.2",
171 | "inherits": "2.0.3",
172 | "setprototypeof": "1.1.1",
173 | "statuses": ">= 1.5.0 < 2",
174 | "toidentifier": "1.0.0"
175 | }
176 | },
177 | "iconv-lite": {
178 | "version": "0.4.24",
179 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
180 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
181 | "requires": {
182 | "safer-buffer": ">= 2.1.2 < 3"
183 | }
184 | },
185 | "inherits": {
186 | "version": "2.0.3",
187 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
188 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
189 | },
190 | "ipaddr.js": {
191 | "version": "1.9.0",
192 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
193 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
194 | },
195 | "media-typer": {
196 | "version": "0.3.0",
197 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
198 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
199 | },
200 | "merge-descriptors": {
201 | "version": "1.0.1",
202 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
203 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
204 | },
205 | "methods": {
206 | "version": "1.1.2",
207 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
208 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
209 | },
210 | "mime": {
211 | "version": "1.6.0",
212 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
213 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
214 | },
215 | "mime-db": {
216 | "version": "1.40.0",
217 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
218 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
219 | },
220 | "mime-types": {
221 | "version": "2.1.24",
222 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
223 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
224 | "requires": {
225 | "mime-db": "1.40.0"
226 | }
227 | },
228 | "ms": {
229 | "version": "2.0.0",
230 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
231 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
232 | },
233 | "negotiator": {
234 | "version": "0.6.2",
235 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
236 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
237 | },
238 | "on-finished": {
239 | "version": "2.3.0",
240 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
241 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
242 | "requires": {
243 | "ee-first": "1.1.1"
244 | }
245 | },
246 | "parseurl": {
247 | "version": "1.3.3",
248 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
249 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
250 | },
251 | "path-to-regexp": {
252 | "version": "0.1.7",
253 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
254 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
255 | },
256 | "proxy-addr": {
257 | "version": "2.0.5",
258 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
259 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
260 | "requires": {
261 | "forwarded": "~0.1.2",
262 | "ipaddr.js": "1.9.0"
263 | }
264 | },
265 | "qs": {
266 | "version": "6.7.0",
267 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
268 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
269 | },
270 | "range-parser": {
271 | "version": "1.2.1",
272 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
273 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
274 | },
275 | "raw-body": {
276 | "version": "2.4.0",
277 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
278 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
279 | "requires": {
280 | "bytes": "3.1.0",
281 | "http-errors": "1.7.2",
282 | "iconv-lite": "0.4.24",
283 | "unpipe": "1.0.0"
284 | }
285 | },
286 | "safe-buffer": {
287 | "version": "5.1.2",
288 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
289 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
290 | },
291 | "safer-buffer": {
292 | "version": "2.1.2",
293 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
294 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
295 | },
296 | "send": {
297 | "version": "0.17.1",
298 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
299 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
300 | "requires": {
301 | "debug": "2.6.9",
302 | "depd": "~1.1.2",
303 | "destroy": "~1.0.4",
304 | "encodeurl": "~1.0.2",
305 | "escape-html": "~1.0.3",
306 | "etag": "~1.8.1",
307 | "fresh": "0.5.2",
308 | "http-errors": "~1.7.2",
309 | "mime": "1.6.0",
310 | "ms": "2.1.1",
311 | "on-finished": "~2.3.0",
312 | "range-parser": "~1.2.1",
313 | "statuses": "~1.5.0"
314 | },
315 | "dependencies": {
316 | "ms": {
317 | "version": "2.1.1",
318 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
319 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
320 | }
321 | }
322 | },
323 | "serve-static": {
324 | "version": "1.14.1",
325 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
326 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
327 | "requires": {
328 | "encodeurl": "~1.0.2",
329 | "escape-html": "~1.0.3",
330 | "parseurl": "~1.3.3",
331 | "send": "0.17.1"
332 | }
333 | },
334 | "setprototypeof": {
335 | "version": "1.1.1",
336 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
337 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
338 | },
339 | "statuses": {
340 | "version": "1.5.0",
341 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
342 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
343 | },
344 | "toidentifier": {
345 | "version": "1.0.0",
346 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
347 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
348 | },
349 | "type-is": {
350 | "version": "1.6.18",
351 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
352 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
353 | "requires": {
354 | "media-typer": "0.3.0",
355 | "mime-types": "~2.1.24"
356 | }
357 | },
358 | "unpipe": {
359 | "version": "1.0.0",
360 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
361 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
362 | },
363 | "utils-merge": {
364 | "version": "1.0.1",
365 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
366 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
367 | },
368 | "vary": {
369 | "version": "1.1.2",
370 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
371 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
372 | },
373 | "webp-converter": {
374 | "version": "2.2.3",
375 | "resolved": "https://registry.npmjs.org/webp-converter/-/webp-converter-2.2.3.tgz",
376 | "integrity": "sha512-ROjdJNNJlqYX5cDQ0XO7ae9AD9asmYAR2nX2Y7ix1SA/hsWbdEAoL61ApqOPvoGZHldHq+yOhmRYlrTNBBcAMA=="
377 | }
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webp_server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.17.1",
13 | "webp-converter": "^2.2.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pics/moon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webp-sh/webp_server_node/7e57e2662981e6c03b626af69b21eef0fa1803ad/pics/moon.jpg
--------------------------------------------------------------------------------
/pics/webp_server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webp-sh/webp_server_node/7e57e2662981e6c03b626af69b21eef0fa1803ad/pics/webp_server.png
--------------------------------------------------------------------------------
/webp.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=A Webp middle convert server
3 | After=network.target network-online.target nss-lookup.target
4 |
5 | [Service]
6 | User=webp
7 | Group=webp
8 |
9 | Environment="NODE_ENV=production"
10 | Restart=on-failure
11 | Type=simple
12 | WorkingDirectory=/opt/webp_server
13 | ExecStart=/usr/bin/node /opt/webp_server/index.js
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------