Completed
Push — master ( 2a41ae...0009c9 )
by Equim
01:00
created

app.js (7 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'),
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Date. This makes code hard to read, consider using a different name.
Loading history...
15
    access     = require('./lib/access.js');
16
17
program
18
    .option('-h, --help')
19
    .option('-v, --version')
20
    .option('-p, --port [n]', parseInt)
0 ignored issues
show
The variable parseInt seems to be never declared. If this is a global, consider adding a /** global: parseInt */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
21
    .option('-f, --fullLog')
22
    .parse(process.argv);
23
24
// 别问我为什么这里逻辑这么奇怪……测试的结果确实是这样的啊hhh
25
if (!program.help || !program.version) {
26
    console.log(('CSUEMS API v2.1.2').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);
1 ignored issue
show
Compatibility Debugging Code Best Practice introduced by
Use of process.exit() is discouraged as it will potentially stop the complete node.js application. Consider quitting gracefully instead by throwing an Error.
Loading history...
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
app.get(/\/[grades|g]$/, 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.status(404).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 View Code Duplication
    access.login(req.query.id, req.query.pwd, res, function (headers) {
76
        if (fullLog) {
77
            console.log(`${timeStamp()} Successfully logged in.`.green);
78
        }
79
80
        // 实际上xnxq01id为空的时候和GET这个URL的效果是一样的,都是查询所有学期
81
        superagent
82
            .post('http://csujwc.its.csu.edu.cn/jsxsd/kscj/yscjcx_list')
83
            .set(headers)
84
            .type('form')
85
            .send({
86
                xnxq01id: req.query.sem
87
            })
88
            .end(function (err, iires) {
89
                if (err) {
90
                    console.log(`${timeStamp()} Failed to get grades page\n${err.stack}`.red);
91
                    res.status(404).send({ error: '无法进入成绩页面' });
92
                    return next(err);
93
                }
94
                if (fullLog) {
95
                    console.log(`${timeStamp()} Successfully entered grades page.`.green);
96
                }
97
98
                let $ = cheerio.load(iires.text);
99
100
                let result = {
101
                    name: escaper.unescape($('#Top1_divLoginName').text().match(/\s.+\(/)[0].replace(/\s|\(/g, '')),
102
                    id: escaper.unescape($('#Top1_divLoginName').text().match(/\(.+\)/)[0].replace(/\(|\)/g, '')),
103
                    grades: {},
104
                    'subject-count': 0,
105
                    failed: {},
106
                    'failed-count': 0,
107
                };
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 result.grades) {
132
                        // 暂不考虑NaN
133
                        if (item.overall < result.grades[title].overall) {
134
                            return;
135
                        }
136
                        if (!element.eq(6).css('color')) {
137
                            delete result.failed[title];
138
                        }
139
                    } else if (element.eq(6).css('color')) {
140
                        result.failed[title] = item;
141
                    }
142
143
                    result.grades[title] = item;
144
                });
145
146
                result['subject-count'] = Object.keys(result.grades).length;
147
                result['failed-count'] = Object.keys(result.failed).length;
148
149
                access.logout(headers, res, function() {
150
                    // 返回JSON
151
                    res.send(JSON.stringify(result));
152
                    if (fullLog) {
153
                        console.log(`${timeStamp()} Successfully logged out: `.green +
154
                            req.query.id.yellow +
155
                            ` (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 71 is false. Are you sure this can never be the case?
Loading history...
156
                    }
157
                });
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...
158
            });
159
    });
160
});
161
162
// 查考试API,通过GET传入用户名和密码
163 View Code Duplication
app.get(/\/[exams|e]$/, 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.status(404).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) {
173
        if (fullLog) {
174
            console.log(`${timeStamp()} Successfully logged in.`.green);
175
        }
176
177
        superagent
178
            .post('http://csujwc.its.csu.edu.cn/jsxsd/xsks/xsksap_list')
179
            .set(headers)
180
            .type('form')
181
            .send({
182
                xqlbmc: '',
183
                xnxqid: req.query.sem || getSem(),
184
                xqlb: ''
185
            })
186
            .end(function (err, iires) {
187
                if (err) {
188
                    console.log(`${timeStamp()} Failed to reach exams page\n${err.stack}`.red);
189
                    res.status(404).send({ error: '获取成绩失败' });
190
                    return next(err);
191
                }
192
                if (fullLog) {
193
                    console.log(`${timeStamp()} Successfully entered exams page.`.green);
194
                }
195
196
                let $ = cheerio.load(iires.text);
197
198
                let result = {
199
                    name: escaper.unescape($('#Top1_divLoginName').text().match(/\s.+\(/)[0].replace(/\s|\(/g, '')),
200
                    id: escaper.unescape($('#Top1_divLoginName').text().match(/\(.+\)/)[0].replace(/\(|\)/g, '')),
201
                    sem: req.query.sem || getSem(),
202
                    exams: {},
203
                    'exams-count': 0,
204
                };
205
206
                $('#dataList tr').each(function (index) {
207
                    if (index === 0) {
208
                        return;
209
                    }
210
                    let element = $(this).find('td');
211
                    let title = escaper.unescape(element.eq(3).text());
212
213
                    let item = {
214
                        time: escaper.unescape(element.eq(4).text()),
215
                        location: escaper.unescape(element.eq(5).text()),
216
                        seat: escaper.unescape(element.eq(6).text())
217
                    };
218
219
                    result.exams[title] = item;
220
                    result['exams-count']++;
221
                });
222
223
                access.logout(headers, res, function() {
224
                    res.send(JSON.stringify(result));
225
                    if (fullLog) {
226
                        console.log(`${timeStamp()} Successfully logged out: `.green +
227
                            req.query.id.yellow +
228
                            ` (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 168 is false. Are you sure this can never be the case?
Loading history...
229
                    }
230
                });
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...
231
            });
232
    });
233
});
234
235
app.listen(port);
236
console.log(`${timeStamp()} The API is now running on port ${port}. Full logging is ${fullLog ? 'enabled' : 'disabled'}`.green);