Completed
Push — master ( 2a41ae...0009c9 )
by Equim
01:00
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
    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 [value]', 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.3').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, process.env.PORT || 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 || process.env.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) {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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);
156
                    }
157
                });
158
            });
159
    });
160
});
161
162
// 查考试API,通过GET传入用户名和密码
163 View Code Duplication
app.get(/\/[exams|e]$/, function (req, res, next) {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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);
229
                    }
230
                });
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)
237
};