优化docker
This commit is contained in:
@@ -13,6 +13,7 @@ ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN mkdir -p public/audio
|
||||
RUN npm run build
|
||||
|
||||
FROM ${NODE_IMAGE} AS runner
|
||||
@@ -23,6 +24,7 @@ ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
ENV PORT=3000
|
||||
ENV AUDIO_DIRECTORY=/app/public/audio
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm config set registry ${NPM_REGISTRY} && npm ci --omit=dev && npm cache clean --force
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { existsSync, mkdirSync } from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { EdgeTTS } from "edge-tts-universal";
|
||||
import { getAudioFilePath, getAudioPublicPath } from "@/lib/audio";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -14,14 +14,14 @@ export async function POST(request: NextRequest) {
|
||||
const rate = String(body.rate ?? "-10%");
|
||||
const text = String(body.text ?? "Hello, welcome to English PK.");
|
||||
|
||||
const audioDirectory = path.join(process.cwd(), "public", "audio", "preview");
|
||||
const audioDirectory = getAudioFilePath("preview");
|
||||
if (!existsSync(audioDirectory)) {
|
||||
mkdirSync(audioDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
const fileName = `tts-preview-${Date.now()}.mp3`;
|
||||
const filePath = path.join(audioDirectory, fileName);
|
||||
const publicPath = `/audio/preview/${fileName}`;
|
||||
const filePath = getAudioFilePath("preview", fileName);
|
||||
const publicPath = getAudioPublicPath(`preview/${fileName}`);
|
||||
|
||||
const tts = new EdgeTTS(text, voice, { rate });
|
||||
const result = await tts.synthesize();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { existsSync, mkdirSync } from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { EdgeTTS } from "edge-tts-universal";
|
||||
import { getAudioDirectory, getAudioFilePath, getAudioPublicPath } from "@/lib/audio";
|
||||
import { findWord, getSettings, updateWordAudio } from "@/lib/db";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
@@ -22,15 +22,15 @@ export async function POST(_request: NextRequest, context: RouteContext) {
|
||||
return NextResponse.json({ message: "词库不存在" }, { status: 404 });
|
||||
}
|
||||
|
||||
const audioDirectory = path.join(process.cwd(), "public", "audio");
|
||||
const audioDirectory = getAudioDirectory();
|
||||
if (!existsSync(audioDirectory)) {
|
||||
mkdirSync(audioDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
const safeEnglish = word.english.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
||||
const fileName = `${word.id}-${safeEnglish || "word"}.mp3`;
|
||||
const filePath = path.join(audioDirectory, fileName);
|
||||
const publicPath = `/audio/${fileName}`;
|
||||
const filePath = getAudioFilePath(fileName);
|
||||
const publicPath = getAudioPublicPath(fileName);
|
||||
|
||||
const settings = getSettings();
|
||||
const tts = new EdgeTTS(word.english, settings.ttsVoice, {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { stat, readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { resolveAudioFilePath } from "@/lib/audio";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
type RouteContext = {
|
||||
params: Promise<{ path: string[] }>;
|
||||
};
|
||||
|
||||
export async function GET(_request: NextRequest, context: RouteContext) {
|
||||
const { path: audioPath } = await context.params;
|
||||
const filePath = resolveAudioFilePath(audioPath);
|
||||
|
||||
if (!filePath || path.extname(filePath).toLowerCase() !== ".mp3") {
|
||||
return new NextResponse("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
try {
|
||||
const fileStat = await stat(filePath);
|
||||
if (!fileStat.isFile()) {
|
||||
return new NextResponse("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
const file = await readFile(filePath);
|
||||
return new NextResponse(new Uint8Array(file), {
|
||||
headers: {
|
||||
"Content-Type": "audio/mpeg",
|
||||
"Content-Length": String(fileStat.size),
|
||||
"Cache-Control": "public, max-age=31536000, immutable",
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
return new NextResponse("Not found", { status: 404 });
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ services:
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
AUDIO_DIRECTORY: /app/public/audio
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./public/audio:/app/public/audio
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import path from "node:path";
|
||||
|
||||
const audioPathPrefix = "/audio";
|
||||
|
||||
export function getAudioDirectory() {
|
||||
return process.env.AUDIO_DIRECTORY || path.join(/*turbopackIgnore: true*/ process.cwd(), "public", "audio");
|
||||
}
|
||||
|
||||
export function getAudioPublicPath(fileName: string) {
|
||||
return `${audioPathPrefix}/${fileName}`;
|
||||
}
|
||||
|
||||
export function getAudioFilePath(...segments: string[]) {
|
||||
return path.join(getAudioDirectory(), ...segments);
|
||||
}
|
||||
|
||||
export function resolveAudioFilePath(segments: string[]) {
|
||||
const audioDirectory = path.resolve(/*turbopackIgnore: true*/ getAudioDirectory());
|
||||
const filePath = path.resolve(audioDirectory, ...segments);
|
||||
|
||||
if (filePath !== audioDirectory && !filePath.startsWith(`${audioDirectory}${path.sep}`)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
Generated
-7821
File diff suppressed because it is too large
Load Diff
+1
-3
@@ -7,8 +7,7 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"tauri": "tauri"
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.17",
|
||||
@@ -25,7 +24,6 @@
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.0.0",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/react": "^19.0.1",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
|
||||
Generated
+4939
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
allowBuilds:
|
||||
sharp: set this to true or false
|
||||
onlyBuiltDependencies:
|
||||
- sharp
|
||||
- unrs-resolver
|
||||
Reference in New Issue
Block a user