Skip to content

Commit

Permalink
Merge pull request #300 from caorushizi/dev-web
Browse files Browse the repository at this point in the history
feat: ✨  dev web
  • Loading branch information
caorushizi authored Sep 30, 2024
2 parents 89bec2c + ca42e09 commit 72fc9a5
Show file tree
Hide file tree
Showing 32 changed files with 602 additions and 110 deletions.
8 changes: 8 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# FIXME: APP_ 开头的会暴漏给前端
APP_NAME=mediago
APP_ID=mediago.ziying.site
APP_COPYRIGHT=caorushizi
APP_VERSION=3.0.0-beta.2

# FIXME: 没有 APP_ 前缀的不会暴漏给前端(包括主进程)
# 会将变量注入到 backend
MYSQL_HOST=127.0.0.1
MYSQL_USER=root
MYSQL_PASSWORD=123456
MYSQL_DATABASE=mediago
12 changes: 5 additions & 7 deletions packages/backend/Dockerfile → Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ FROM m.daocloud.io/docker.io/library/node:20
# 设置工作目录
WORKDIR /app

# 复制打包产物到工作目录
COPY ./packages/backend/dist /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./
COPY ./packages/backend/package*.json ./

# 安装依赖
RUN npm install --registry=https://registry.npmmirror.com

# 复制项目文件
COPY . .

RUN npm run build

# 暴露应用运行的端口
EXPOSE 3000

# 启动应用
CMD ["node", "dist/inedx.js"]
CMD ["node", "server/index.js"]
3 changes: 0 additions & 3 deletions packages/backend/docker-compose.yml → docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ services:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
NODE_ENV: development
MYSQL_HOST: mysql
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
"scripts": {
"dev": "tsx scripts/dev.ts && pnpm --parallel -F \"./packages/*\" run dev",
"dev:web": "pnpm -F backend run mysql && pnpm --parallel -F \"./packages/*\" run dev:web",
"start:web": "pnpm -F backend run mysql && pnpm -F backend start",
"build": "tsx scripts/build.ts",
"build:plugin": "pnpm -F plugin run build",
"build:main": "pnpm -F main run build",
"build:web": "pnpm --parallel -F \"./packages/*\" run build:web",
"build:renderer": "pnpm -F renderer run build",
"build:mobile": "pnpm -F mobile run build",
"docker:start": "docker-compose up --build",
"beta": "pnpm run build && pnpm -F mediago run pack",
"release": "pnpm run build && pnpm -F main run release",
"docs:dev": "vitepress dev docs",
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/nodemon.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"watch": ["dist"],
"ext": "js"
"ext": "js",
"exec": "node dist/server/index.js"
}
6 changes: 5 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
"type": "module",
"main": "dist/index.js",
"scripts": {
"start": "nodemon dist/index.js",
"start": "nodemon",
"dev:web": "cross-env NODE_ENV=development gulp dev",
"build": "cross-env NODE_ENV=production gulp build",
"build:web": "cross-env NODE_ENV=production gulp build",
"mysql": "docker-compose up -d mysql",
"mysql:stop": "docker-compose stop mysql",
"types": "tsc",
Expand All @@ -28,6 +29,7 @@
"@types/gulp": "^4.0.17",
"@types/koa": "^2.15.0",
"@types/koa-bodyparser": "^4.3.12",
"@types/koa-static": "^4.0.4",
"@types/koa__cors": "^5.0.0",
"@types/koa__router": "^12.0.4",
"@types/lodash": "^4.17.4",
Expand Down Expand Up @@ -63,13 +65,15 @@
"inversify": "^6.0.2",
"koa": "^2.15.3",
"koa-bodyparser": "^4.4.1",
"koa-static": "^5.0.0",
"lint-staged": "^15.2.5",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"mysql2": "^3.11.0",
"nanoid": "^5.0.7",
"node-pty": "^1.0.0",
"reflect-metadata": "^0.2.2",
"socket.io": "^4.8.0",
"strip-ansi": "^7.1.0",
"typeorm": "0.3.20",
"winston": "^3.14.2"
Expand Down
15 changes: 7 additions & 8 deletions packages/backend/scripts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ import esbuild from "esbuild";

export function buildOptions(): esbuild.BuildOptions {
const getDefine = (): Record<string, string> => {
const vars: Record<string, string> = {
"process.env.NODE_ENV": isDev ? '"development"' : '"production"',
};
if (isDev) {
return {
__bin__: `"${mainResolve("bin").replace(/\\/g, "\\\\")}"`,
};
vars.__bin__ = `"${mainResolve("bin").replace(/\\/g, "\\\\")}"`;
Object.assign(vars, Env.getInstance().loadDotEnvDefined());
}

return {
...Env.getInstance().loadDotEnvDefined(),
"process.env.NODE_ENV": '"production"',
};
return vars;
};

return {
entryPoints: [mainResolve("src/index.ts")],
bundle: true,
sourcemap: process.env.NODE_ENV === "development",
define: getDefine(),
outdir: mainResolve("dist"),
outdir: mainResolve("dist/server"),
loader: { ".png": "file" },
minify: process.env.NODE_ENV === "production",
packages: "external",
Expand Down
22 changes: 11 additions & 11 deletions packages/backend/scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import gulp from "gulp";
import * as esbuild from "esbuild";
import consola from "consola";
import { buildOptions } from "./config";
// import fs from "fs";
import fs from "fs";

const env = Env.getInstance();
env.loadDotEnvRuntime();

async function clean() {
return deleteSync([mainResolve("dist")]);
return deleteSync([mainResolve("dist/server")]);
}

// async function copyBin() {
// const source = mainResolve("bin", process.platform);
// const target = mainResolve("app/bin");
// fs.cpSync(source, target, { recursive: true });
// }
async function copyBin() {
const source = mainResolve("../main/bin", process.platform);
const target = mainResolve("dist/server/bin");
fs.cpSync(source, target, { recursive: true });
}

// const copy = gulp.parallel(copyBin);
const copy = gulp.parallel(copyBin);

async function watchTask() {
const main = await esbuild.context(buildOptions());
Expand All @@ -32,7 +32,7 @@ async function watchTask() {
.on("change", async () => {
await main.rebuild();
})
.on("error", (error) => {
.on("error", (error: any) => {
consola.error(error);
});
return Promise.resolve();
Expand All @@ -44,6 +44,6 @@ async function buildTask() {

// 开发环境
// TODO 暂时不拷贝 bin 文件夹
export const dev = gulp.series(clean, watchTask);
export const dev = gulp.series(clean, copy, watchTask);
// 构建打包
export const build = gulp.series(clean, buildTask);
export const build = gulp.series(clean, copy, buildTask);
1 change: 0 additions & 1 deletion packages/backend/scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export class Env {

loadDotEnvDefined() {
return Object.keys(this.env).reduce<Record<string, string>>((prev, cur) => {
if (!cur.startsWith("APP_")) return prev;
prev[`process.env.${[cur]}`] = JSON.stringify(this.env[cur]);
return prev;
}, {});
Expand Down
35 changes: 31 additions & 4 deletions packages/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import Koa from "koa";
import cors from "@koa/cors";
import bodyParser from "koa-bodyparser";
import Logger from "./vendor/Logger.ts";
import http from "http";
import SocketIO from "./vendor/SocketIO.ts";
import VideoRepository from "./repository/VideoRepository.ts";
import { DownloadStatus } from "./interfaces.ts";
import serve from "koa-static";
import { STATIC_DIR } from "./const.ts";

@injectable()
export default class ElectronApp extends Koa {
Expand All @@ -16,6 +22,10 @@ export default class ElectronApp extends Koa {
private readonly db: TypeORM,
@inject(TYPES.Logger)
private readonly logger: Logger,
@inject(TYPES.SocketIO)
private readonly socket: SocketIO,
@inject(TYPES.VideoRepository)
private readonly videoRepository: VideoRepository,
) {
super();
}
Expand All @@ -30,12 +40,29 @@ export default class ElectronApp extends Koa {
// vendor
await this.vendorInit();

this.use(cors());
this.use(bodyParser());
this.use(this.router.routes()).use(this.router.allowedMethods());
this.use(cors())
.use(bodyParser())
.use(this.router.routes())
.use(this.router.allowedMethods());
this.use(serve(STATIC_DIR));

this.listen(3000, () => {
const server = http.createServer(this.callback());

this.socket.initSocketIO(server);

server.listen(3000, () => {
this.logger.info("Server running on port 3000");
});
}

// 如果重启后还有正在下载的视频,就将状态改成下载失败
async resetDownloadStatus(): Promise<void> {
// 重启后如果还有 downloading 状态的数据, 全部重置为失败
const videos = await this.videoRepository.findWattingAndDownloadingVideos();
const videoIds = videos.map((video) => video.id);
await this.videoRepository.changeVideoStatus(
videoIds,
DownloadStatus.Failed,
);
}
}
11 changes: 11 additions & 0 deletions packages/backend/src/const.ts
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
import os from "os";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export const API_PREFIX = "/api";
export const HOME_DIR = os.homedir();
export const DOWNLOAD_DIR = `${HOME_DIR}/mediago`;
export const STATIC_DIR = resolve(__dirname, "../app");
export const BIN_DIR = resolve(__dirname, "./bin");
81 changes: 51 additions & 30 deletions packages/backend/src/controller/DownloadController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,27 @@ import { inject, injectable } from "inversify";
import {
DownloadItem,
DownloadItemPagination,
DownloadStatus,
Task,
type Controller,
} from "../interfaces.ts";
import { TYPES } from "../types.ts";
import FavoriteRepository from "../repository/FavoriteRepository.ts";
import { get, post } from "../helper/index.ts";
import Logger from "../vendor/Logger.ts";
import VideoRepository from "../repository/VideoRepository.ts";
import { Context } from "koa";
import ConfigService from "../services/ConfigService.ts";
import DownloaderService from "../services/DownloaderService.ts";
import DownloadService from "../services/DownloadService.ts";
import Logger from "../vendor/Logger.ts";

@injectable()
export default class DownloadController implements Controller {
constructor(
@inject(TYPES.FavoriteRepository)
private readonly favoriteRepository: FavoriteRepository,
@inject(TYPES.Logger)
private readonly logger: Logger,
@inject(TYPES.VideoRepository)
private readonly videoRepository: VideoRepository,
@inject(TYPES.ConfigService)
private readonly store: ConfigService,
@inject(TYPES.DownloaderService)
private readonly downloaderService: DownloaderService,
@inject(TYPES.DownloadService)
private readonly downloadService: DownloadService,
@inject(TYPES.Logger)
private readonly logger: Logger,
) {}

@get("/")
Expand All @@ -45,8 +40,7 @@ export default class DownloadController implements Controller {
@post("add-download-items")
async addDownloadItems(ctx: Context) {
const videos = ctx.request.body as DownloadItem[];
const items = await this.videoRepository.addVideos(videos);
return items;
return this.downloaderService.addDownloadItems(videos);
}

@post("get-download-items")
Expand All @@ -59,23 +53,50 @@ export default class DownloadController implements Controller {
@post("start-download")
async startDownload(ctx: Context) {
const { vid } = ctx.request.body as { vid: number };
// 查找将要下载的视频
const video = await this.videoRepository.findVideo(vid);
const { name, url, headers, type } = video;
const { local, deleteSegments } = await this.store.getConfig();
await this.downloaderService.startDownload(vid);
}

@post("delete-download-item")
async deleteDownloadItem(ctx: Context) {
const { id } = ctx.request.body as { id: number };
await this.videoRepository.deleteDownloadItem(id);
}

@post("download-now")
async downloadNow(ctx: Context) {
const video = ctx.request.body as Omit<DownloadItem, "id">;
await this.downloaderService.downloadNow(video);
}

@post("download-items-now")
async downloadItemsNow(ctx: Context) {
const videos = ctx.request.body as Omit<DownloadItem, "id">[];
// 添加下载项
const items = await this.downloaderService.addDownloadItems(videos);
// 开始下载
items.forEach((item) => this.downloaderService.startDownload(item.id));
return items;
}

@post("edit-download-now")
async editDownloadNow(ctx: Context) {
const video = ctx.request.body as DownloadItem;
const item = await this.downloaderService.editDownloadItem(video);
await this.downloaderService.startDownload(item.id);
return item;
}

@post("edit-download-item")
async editDownloadItem(ctx: Context) {
const video = ctx.request.body as DownloadItem;
this.logger.info("editDownloadItem", video);
return this.downloaderService.editDownloadItem(video);
}

@post("stop-download")
async stopDownload(ctx: Context) {
const { id } = ctx.request.body as { id: number };

const task: Task = {
id: vid,
params: {
url,
type,
local,
name,
headers,
deleteSegments,
},
};
await this.videoRepository.changeVideoStatus(vid, DownloadStatus.Watting);
this.downloadService.addTask(task);
this.downloadService.stopTask(id);
}
}
Loading

0 comments on commit 72fc9a5

Please sign in to comment.