diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ea7761d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +.gitignore +README.md \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..c32e1ec --- /dev/null +++ b/.env @@ -0,0 +1 @@ +WEEK1_FIRST_DAY=2024-09-09 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c32e1ec --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +WEEK1_FIRST_DAY=2024-09-09 diff --git a/.gitignore b/.gitignore index 6c9351f..93f789b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.DS_Store /XJTLU e-Bridge.html -/test.ics \ No newline at end of file +/test.ics +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f8c9744 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM denoland/deno:1.33.2 + +# 设置工作目录 +WORKDIR /app + +# 复制依赖文件 +COPY deps.ts . + +# 缓存依赖 +RUN deno cache deps.ts + +# 复制其余源代码 +COPY . . + +# 编译应用 +RUN deno cache server.ts + +# 暴露端口 +EXPOSE 8000 + +# 运行应用 +CMD ["run", "--allow-net", "server.ts"] \ No newline at end of file diff --git a/README.md b/README.md index 3eda023..1e97139 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,26 @@ console.log(calendar.toLines()); Deno.writeTextFileSync('test.ics', calendar.toString()); ``` + +### Build Docker Image & Run + +build + +```bash +docker build -t ebridge-timetable-api . +``` + +run + +```bash +docker run -p 8000:8000 ebridge-timetable-api +``` + +Now api service will run on . +You can also use docker compose by typing `docker-compose up`. + +### Api Test + +```bash +curl -X POST -H "Content-Type: text/plain" --data-binary @"XJTLU e-Bridge.html" http://localhost:8000/parse-and-generate > result.json +``` \ No newline at end of file diff --git a/deps.ts b/deps.ts index 6e047e2..9790426 100644 --- a/deps.ts +++ b/deps.ts @@ -10,3 +10,7 @@ export type { } from "https://deno.land/x/simple_ics@0.1.0/mod.ts"; export { Calendar, Event } from "https://deno.land/x/simple_ics@0.0.11/mod.ts"; + +export { config } from "https://deno.land/x/dotenv/mod.ts"; + +export { Application, Router } from "https://deno.land/x/oak@v11.1.0/mod.ts"; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ba9bf03 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + api: + build: . + ports: + - "8000:8000" \ No newline at end of file diff --git a/main.ts b/main.ts index 0c0ad45..086dd8b 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,12 @@ import { Calendar, Day, Event, EventConfig, RecurrenceRule } from "./deps.ts"; import { Lesson } from "./parser.ts"; +import { config } from "./deps.ts"; -const WEEK1_FIRST_DAY = [2023, 1, 13]; +await config(); // 加载 .env 文件并设置环境变量 + +const week1FirstDayEnv = Deno.env.get("WEEK1_FIRST_DAY"); + +const WEEK1_FIRST_DAY = week1FirstDayEnv ? week1FirstDayEnv.split("-").map((item, index) => index === 1 ? parseInt(item) - 1 : parseInt(item)) : [2024, 8, 9]; const getMondayOfWeek = (n: number) => { const [year, month, date] = WEEK1_FIRST_DAY; @@ -24,6 +29,8 @@ const getDayCode = (day: Day) => { return 5; case "SU": return 6; + default: + throw new Error('invalid day code') } }; @@ -60,7 +67,7 @@ export function genCalendar(lessons: { [title: string]: Lesson }) { until: weekDuration.length >= 2 ? new Date( firstMonday.getTime() + - 86400 * 7e3 * (weekDuration[1] - weekDuration[0] + 1), + 86400 * 7e3 * (weekDuration[1] - weekDuration[0] + 1), ) : new Date(firstMonday.getTime() + 86400 * 7e3), }; diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..fdba77f --- /dev/null +++ b/server.ts @@ -0,0 +1,45 @@ +import { Application, Router } from "./deps.ts"; +import { DOMParser, genCalendar, parseTimetable } from "./mod.ts"; + +const app = new Application(); +const router = new Router(); + +router.post("/calendar-parse", async (ctx) => { + const body = ctx.request.body(); + + if (body.type !== "text") { + ctx.response.status = 400; + ctx.response.body = { error: "请求体必须是文本格式的HTML" }; + return; + } + + const htmlContent = await body.value; + + const parser = new DOMParser(); + const document = parser.parseFromString(htmlContent, "text/html"); + + if (!document) { + ctx.response.status = 400; + ctx.response.body = { error: "无法解析HTML内容" }; + return; + } + + try { + const lessons = parseTimetable(document); + const calendar = genCalendar(lessons); + + ctx.response.body = { + lessons: lessons, + ics: calendar.toString(), + }; + } catch (error) { + ctx.response.status = 500; + ctx.response.body = { error: "处理课程表时出错: " + error.message }; + } +}); + +app.use(router.routes()); +app.use(router.allowedMethods()); + +console.log("服务器运行在 http://localhost:8000"); +await app.listen({ port: 8000 }); \ No newline at end of file