Completed
Push — master ( 022795...5e0f69 )
by Equim
57s
created

app.js (4 issues)

1
/* TODO
2
 * 完善异常处理
3
 * 建立用户名-Cookie数据库
4
 */
5
6
'use strict';
7
8
const
9
    express    = require('express'),
10
    superagent = require('superagent'),
11
    cheerio    = require('cheerio'),
12
    escaper    = require('true-html-escape'),
13
    colors     = require('colors'),
14
    program    = require('commander'),
15
    moment     = require('moment'),
16
    access     = require('./lib/access.js');
17
18
program
19
    .option('-h, --help')
20
    .option('-v, --version')
21
    .option('-p, --port [value]', parseInt)
22
    .option('-f, --fullLog')
23
    .parse(process.argv);
24
25
// 别问我为什么这里逻辑这么奇怪……测试的结果确实是这样的啊hhh
26
if (!program.help || !program.version) {
27
    console.log(
28
`${`CSUEMS API v${require('./package').version}
29
by The Liberators`.rainbow}${program.help ? '' :
30
`
31
32
Preparation:
33
  ${'(This section is WIP)'.grey}
34
35
Usage:
36
  npm start [-- <options...>]
37
38
Options:
39
  -h, --help          print this message and exit.
40
  -v, --version       print the version and exit.
41
  -f, --fullLog       enable full log, by default only errors are logged.
42
  -p, --port [value]  specify a port to listen, process.env.PORT || 2333 by default.
43
44
Examples:
45
  $ npm start -- -p 43715               # listening to 43715
46
  $ forever start app.js                # deploy with forever as daemon (root access recommended)
47
  $ pm2 start -i 0 -n "csuapi" app.js   # deploy with pm2 as daemon  (root access recommended)`}`);
48
    process.exit(0);
49
}
50
51
const
52
    timeStamp = () => moment().format('[[]YY-MM-DD HH:mm:ss[]]'),
53
    // 以系统时间获取当前学期
54
    getSem = () => {
55
        let now = new Date();
56
        let month = now.getMonth();
57
        let year = now.getFullYear();
58
        if (month === 0) {
59
            return (year - 1) + '-' + year + '-1';
60
        } else if (month <= 6) {
61
            return (year - 1) + '-' + year + '-2';
62
        } else {
63
            return year + '-' + (year + 1) + '-1';
64
        }
65
    },
66
    port = program.port || process.env.PORT || 2333,
67
    fullLog = !!program.fullLog,
68
    app = express();
69
70
// 获取文档
71
app.get('/doc', function (req, res) {
72
    res.sendFile(__dirname + '/doc/API.html');
73
});
74
75 View Code Duplication
// 查成绩API,通过GET传入用户名和密码
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
76
app.get(/^\/g(?:|rades)$/, function (req, res, next) {
77
    if (!req.query.id || !req.query.pwd || (req.query.sem && !(/^20\d{2}-20\d{2}-[1-2]$/).test(req.query.sem))) {
78
        res.status(404).send({ error: "参数不正确" });
79
        return;
80
    }
81
    if (fullLog) {
82
        var start = new Date();
83
        console.log(`${timeStamp()} Started to query the grades: `.cyan + req.query.id.yellow);
84
    }
85
    access.login(req.query.id, req.query.pwd, res, function (headers) {
86
        if (fullLog) {
87
            console.log(`${timeStamp()} Successfully logged in.`.green);
88
        }
89
90
        // 实际上xnxq01id为空的时候和GET这个URL的效果是一样的,都是查询所有学期
91
        superagent
92
            .post('http://csujwc.its.csu.edu.cn/jsxsd/kscj/yscjcx_list')
93
            .set(headers)
94
            .type('form')
95
            .send({
96
                xnxq01id: req.query.sem
97
            })
98
            .end(function (err, iires) {
99
                if (err) {
100
                    console.log(`${timeStamp()} Failed to get grades page\n${err.stack}`.red);
101
                    res.status(404).send({ error: '无法进入成绩页面' });
102
                    return next(err);
103
                }
104
                if (fullLog) {
105
                    console.log(`${timeStamp()} Successfully entered grades page.`.green);
106
                }
107
108
                let $ = cheerio.load(iires.text);
109
110
                let result = {
111
                    name: escaper.unescape($('#Top1_divLoginName').text().match(/\s.+\(/)[0].replace(/\s|\(/g, '')),
112
                    id: escaper.unescape($('#Top1_divLoginName').text().match(/\(.+\)/)[0].replace(/\(|\)/g, '')),
113
                    grades: {},
114
                    'subject-count': 0,
115
                    failed: {},
116
                    'failed-count': 0,
117
                };
118
119
                // 获取成绩列表
120
                $('#dataList tr').each(function (index) {
121
                    if (index === 0) {
122
                        return;
123
                    }
124
                    let element = $(this).find('td');
125
126
                    let title = escaper.unescape(element.eq(3).text().match(/].+$/)[0].substring(1));
127
128
                    let item = {
129
                        sem: escaper.unescape(element.eq(2).text()),
130
                        reg: escaper.unescape(element.eq(4).text()),
131
                        exam: escaper.unescape(element.eq(5).text()),
132
                        overall: escaper.unescape(element.eq(6).text())
133
                    };
134
                    if (req.query.details) {
135
                        item.id = escaper.unescape(element.eq(3).text().match(/\[.+\]/)[0].replace(/\[|\]/g, ''));
136
                        item.attr = escaper.unescape(element.eq(8).text());
137
                        item.genre = escaper.unescape(element.eq(9).text());
138
                        item.credit = escaper.unescape(element.eq(7).text());
139
                    }
140
141
                    // 如果有补考记录,则以最高分的为准
142
                    // 这段代码是拿可读性换了时间复杂度……
143
                    if (title in result.grades) {
144
                        // 暂不考虑NaN
145
                        if (item.overall < result.grades[title].overall) {
146
                            return;
147
                        }
148
                        if (!element.eq(6).css('color')) {
149
                            delete result.failed[title];
150
                        }
151
                    } else if (element.eq(6).css('color')) {
152
                        result.failed[title] = item;
153
                    }
154
155
                    result.grades[title] = item;
156
                });
157
158
                result['subject-count'] = Object.keys(result.grades).length;
159
                result['failed-count'] = Object.keys(result.failed).length;
160
161
                access.logout(headers, res, function() {
162
                    // 返回JSON
163 View Code Duplication
                    res.send(JSON.stringify(result));
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
164
                    if (fullLog) {
165
                        console.log(`${timeStamp()} Successfully logged out: `.green +
166
                            req.query.id.yellow +
167
                            ` (processed in ${new Date() - start} ms)`.green);
168
                    }
169
                });
170
            });
171
    });
172
});
173
174
// 查考试API,通过GET传入用户名和密码
175
app.get(/^\/e(?:|xams)$/, function (req, res, next) {
176
    if (!req.query.id || !req.query.pwd || (req.query.sem && !(/^20\d{2}-20\d{2}-[1-2]$/).test(req.query.sem))) {
177
        res.status(404).send({ error: "参数不正确" });
178
        return;
179
    }
180
    if (fullLog) {
181
        var start = new Date();
182
        console.log(`${timeStamp()} Started to query the exams: `.cyan + req.query.id.yellow);
183
    }
184
    access.login(req.query.id, req.query.pwd, res, function (headers) {
185
        if (fullLog) {
186
            console.log(`${timeStamp()} Successfully logged in.`.green);
187
        }
188
        superagent
189
            .post('http://csujwc.its.csu.edu.cn/jsxsd/xsks/xsksap_list')
190
            .set(headers)
191
            .type('form')
192
            .send({
193
                xqlbmc: '',
194
                xnxqid: req.query.sem || getSem(),
195
                xqlb: ''
196
            })
197
            .end(function (err, iires) {
198
                if (err) {
199
                    console.log(`${timeStamp()} Failed to reach exams page\n${err.stack}`.red);
200
                    res.status(404).send({ error: '获取成绩失败' });
201
                    return next(err);
202
                }
203
                if (fullLog) {
204
                    console.log(`${timeStamp()} Successfully entered exams page.`.green);
205
                }
206
207
                let $ = cheerio.load(iires.text);
208
209
                let result = {
210
                    name: escaper.unescape($('#Top1_divLoginName').text().match(/\s.+\(/)[0].replace(/\s|\(/g, '')),
211
                    id: escaper.unescape($('#Top1_divLoginName').text().match(/\(.+\)/)[0].replace(/\(|\)/g, '')),
212
                    sem: req.query.sem || getSem(),
213
                    exams: {},
214
                    'exams-count': 0,
215
                };
216
217
                $('#dataList tr').each(function (index) {
218
                    if (index === 0) {
219
                        return;
220
                    }
221
                    let element = $(this).find('td');
222
                    let title = escaper.unescape(element.eq(3).text());
223
224
                    let item = {
225
                        time: escaper.unescape(element.eq(4).text()),
226
                        location: escaper.unescape(element.eq(5).text()),
227
                        seat: escaper.unescape(element.eq(6).text())
228
                    };
229
230
                    result.exams[title] = item;
231
                    result['exams-count']++;
232
                });
233
234
                access.logout(headers, res, function() {
235
                    res.send(JSON.stringify(result));
236
                    if (fullLog) {
237
                        console.log(`${timeStamp()} Successfully logged out: `.green +
238
                            req.query.id.yellow +
239
                            ` (processed in ${new Date() - start} ms)`.green);
0 ignored issues
show
The variable start does not seem to be initialized in case fullLog on line 180 is false. Are you sure this can never be the case?
Loading history...
240
                    }
241
                });
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
242
            });
243
    });
244
});
245
246
app.listen(port, () => {
247
    console.log(`${timeStamp()} The API is now running on port ${port}. Full logging is ${fullLog ? 'enabled' : 'disabled'}`.green);
248
});