feat: added dockerfile, made the guide closable
This commit is contained in:
parent
99882bb80a
commit
1ac80e2a56
5 changed files with 58 additions and 7 deletions
5
.dockerignore
Normal file
5
.dockerignore
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
uploads
|
||||||
|
.env
|
||||||
|
.git
|
||||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Build stage: compile TypeScript
|
||||||
|
FROM node:22-alpine AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Runtime stage: production deps only
|
||||||
|
FROM node:22-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
COPY --from=build /app/dist ./dist
|
||||||
|
COPY public ./public
|
||||||
|
RUN mkdir -p uploads && chown node:node uploads
|
||||||
|
USER node
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "dist/app.js"]
|
||||||
14
package.json
14
package.json
|
|
@ -1,15 +1,25 @@
|
||||||
{
|
{
|
||||||
"name": "send-to-ereader",
|
"name": "send-to-ereader",
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"description": "Convert web articles to EPUB and email them to your e-reader, or upload a file (EPUB, PDF, etc.) directly.",
|
"description": "Convert web articles to EPUB and email them to your e-reader, or upload a file (EPUB, PDF, etc.) directly.",
|
||||||
"homepage": "https://atanasov.fi",
|
"homepage": "https://atanasov.fi",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.atanasov.fi/atanasov/send-to-ereader.git"
|
||||||
|
},
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx watch ./src/app.ts",
|
"dev": "tsx watch ./src/app.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/app.js",
|
"start": "node dist/app.js",
|
||||||
"prod": "npm run build && npm run start"
|
"prod": "npm run build && npm run start",
|
||||||
|
"build-prod-push": "docker buildx build --push --platform linux/amd64 -t $(npm pkg get config.docker.registry | xargs)/$(npm pkg get name | xargs):$(npm pkg get version | xargs) -t $(npm pkg get config.docker.registry | xargs)/$(npm pkg get name | xargs):latest ."
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"docker": {
|
||||||
|
"registry": "git.atanasov.fi/atanasov"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Stefan Atanasov",
|
"author": "Stefan Atanasov",
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,16 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setup-info summary {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-info summary h2 {
|
||||||
|
display: inline;
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.setup-info code {
|
.setup-info code {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
|
|
@ -209,8 +219,8 @@
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<article class="container">
|
<article class="container">
|
||||||
<section class="setup-info">
|
<details class="setup-info">
|
||||||
<h2>guide</h2>
|
<summary><h2>guide</h2></summary>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
whitelist <code>send@read.atanasov.fi</code> on your device — one-time setup:
|
whitelist <code>send@read.atanasov.fi</code> on your device — one-time setup:
|
||||||
|
|
@ -275,7 +285,7 @@
|
||||||
<code>localStorage</code> for future use; nothing is stored on the server
|
<code>localStorage</code> for future use; nothing is stored on the server
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</details>
|
||||||
|
|
||||||
<section class="send-section">
|
<section class="send-section">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
@ -344,7 +354,7 @@
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p id="credits">
|
<p id="credits">
|
||||||
v 0.2.0 •
|
v 0.2.2 •
|
||||||
<a
|
<a
|
||||||
href="https://atanasov.fi"
|
href="https://atanasov.fi"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -353,7 +363,7 @@
|
||||||
>
|
>
|
||||||
•
|
•
|
||||||
<a
|
<a
|
||||||
href="https://codeberg.org/atanasoff/send-to-ereader"
|
href="https://git.atanasov.fi/atanasov/send-to-ereader"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>source</a
|
>source</a
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { JSDOM } from "jsdom";
|
||||||
import { Readability } from "@mozilla/readability";
|
import { Readability } from "@mozilla/readability";
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import os from "os";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import sanitizeFilename from "sanitize-filename";
|
import sanitizeFilename from "sanitize-filename";
|
||||||
import { EpubOptions, EPub } from "@lesjoursfr/html-to-epub";
|
import { EpubOptions, EPub } from "@lesjoursfr/html-to-epub";
|
||||||
|
|
@ -151,6 +152,10 @@ async function createEpub(article: ArticleContent): Promise<string> {
|
||||||
img { max-width: 100%; height: auto; }
|
img { max-width: 100%; height: auto; }
|
||||||
`,
|
`,
|
||||||
verbose: false,
|
verbose: false,
|
||||||
|
// Write scratch files to the system temp dir instead of the library's
|
||||||
|
// default inside node_modules, which is not writable by the non-root
|
||||||
|
// user in the Docker image.
|
||||||
|
tempDir: os.tmpdir(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const epub = new EPub(options, epubPath);
|
const epub = new EPub(options, epubPath);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue