Completed
Push — master ( baa285...accde5 )
by Equim
58s
created

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