Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
.gitignore
README.md
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WEEK1_FIRST_DAY=2024-09-09
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WEEK1_FIRST_DAY=2024-09-09
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.DS_Store
/XJTLU e-Bridge.html
/test.ics
/test.ics
.env
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <http://localhost:8000>.
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
```
4 changes: 4 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
6 changes: 6 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3'
services:
api:
build: .
ports:
- "8000:8000"
11 changes: 9 additions & 2 deletions main.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,6 +29,8 @@ const getDayCode = (day: Day) => {
return 5;
case "SU":
return 6;
default:
throw new Error('invalid day code')
}
};

Expand Down Expand Up @@ -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),
};
Expand Down
45 changes: 45 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
@@ -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 });