Completed
Push — master ( a65c23...35c902 )
by Equim
01:15
created

app.js (2 issues)

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