由于各种原因重新初始化服务器(使用全新框架改善界面,重构逻辑代码)
54
README.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
## 项目文件结构
|
||||||
|
|
||||||
|
root/
|
||||||
|
|- main/
|
||||||
|
| |- web.js 启动代码
|
||||||
|
|- routers/
|
||||||
|
| |- *.js 路由文件
|
||||||
|
| |- */ 各路由文件的子文件及使用的中间件
|
||||||
|
|- models/ 未来数据模型文件
|
||||||
|
|- views/ 视图文件(一般是不允许直接访问的文件,使用中间件访问)
|
||||||
|
|- static/ 静态文件(所有网页的静态资源存放处)
|
||||||
|
| |- css/ css文件
|
||||||
|
| |- js/ js文件
|
||||||
|
| |- pic/ 图片文件
|
||||||
|
| |- html/ html文件
|
||||||
|
| |- index.html 默认首页
|
||||||
|
|- .data/ 配置文件
|
||||||
|
|- .gitignore git忽略文件
|
||||||
|
|- package.json 项目依赖包
|
||||||
|
|- package-lock.json 项目依赖包(自动生成)
|
||||||
|
|- .env 配置文件(如需自己启用项目请根据default.env文件修改后启用项目)
|
||||||
|
|- server.js 主路由代码
|
||||||
|
|- utils.js 工具代码(包含路径,api返回格式)
|
||||||
|
|
||||||
|
- 默认首页简易解释
|
||||||
|
static/index.html 文件需要调用 static/js/base_script.js 动态加载在 static/html/base/*.html 的共享资源且需要动态引入.data内的json数据以渲染出一张张cards
|
||||||
|
|
||||||
|
## 前后端接口手册
|
||||||
|
- 请看utils.js的json内部格式
|
||||||
|
|
||||||
|
## 杂项
|
||||||
|
npm init
|
||||||
|
npm install [package names]
|
||||||
|
npm install
|
||||||
|
|
||||||
|
使用tmux:
|
||||||
|
安装tmux(如果尚未安装):
|
||||||
|
bash
|
||||||
|
sudo apt-get install tmux # 对于Debian/Ubuntu系统
|
||||||
|
sudo yum install tmux # 对于CentOS/RedHat系统
|
||||||
|
启动一个新的tmux会话:
|
||||||
|
bash
|
||||||
|
tmux
|
||||||
|
在tmux会话中启动你的Node.js应用:
|
||||||
|
bash
|
||||||
|
node app.js
|
||||||
|
按下Ctrl + B,然后按下D来“detach”(分离)tmux会话。
|
||||||
|
|
||||||
|
重新连接到tmux会话时,运行:
|
||||||
|
|
||||||
|
bash
|
||||||
|
tmux attach
|
||||||
|
|
||||||
|
https://zhuanlan.zhihu.com/p/667646001
|
5
default.env
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
LISTEN_PORT=3000
|
||||||
|
JWT_SECRET=YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG
|
||||||
|
DATABASE_NAME=sqlite3.db
|
||||||
|
USER_LOGIN_NAME=admin
|
||||||
|
USER_LOGIN_PASSWORD=MoAHzbzmZ9HZT5SGrhu9XyD7wUtxJRbW
|
87
main/web.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const app = require('../server.js');
|
||||||
|
const http = require('http');
|
||||||
|
require('dotenv').config();
|
||||||
|
env = process.env;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from config and store in Express.
|
||||||
|
*/
|
||||||
|
const port = normalizePort(env.LISTEN_PORT || '13000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
const server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
console.log('Web server listening on ' + bind);
|
||||||
|
}
|
20
package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "zzy_web",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "None",
|
||||||
|
"main": "./main/web.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "nodemon ./main/web.js",
|
||||||
|
"start": "node ./main/web.js"
|
||||||
|
},
|
||||||
|
"author": "zzy",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"nodemon": "^3.1.0",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
|
}
|
||||||
|
}
|
10
routes/api.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const utils = require('../utils.js');
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.use('/users', require('./api/users.js'));
|
||||||
|
router.use('/files', require('./api/files.js'));
|
||||||
|
router.use('/json', require('./api/json.js'));
|
||||||
|
|
||||||
|
module.exports = router;
|
55
routes/api/files.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const utils = require('../../utils.js');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', (req, res, next) => {
|
||||||
|
// 读取文件夹列表
|
||||||
|
const files = fs.readdirSync(path.join(FILE_PATH));
|
||||||
|
let data = [];
|
||||||
|
files.forEach((fileName) => {
|
||||||
|
const filePath = path.join(FILE_PATH, fileName);
|
||||||
|
const stats = fs.statSync(filePath);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
// 如果是文件夹,递归遍历
|
||||||
|
// data.push({
|
||||||
|
// fileName: fileName,
|
||||||
|
// filePath: fileName,
|
||||||
|
// isFolder: true,
|
||||||
|
// fileSize: stats.size,
|
||||||
|
// modifyTime: stats.mtime.toLocaleString()
|
||||||
|
// })
|
||||||
|
} else {
|
||||||
|
// 如果是文件,执行操作
|
||||||
|
data.push({
|
||||||
|
fileName: fileName,
|
||||||
|
filePath: fileName,
|
||||||
|
isFolder: false,
|
||||||
|
fileSize: stats.size,
|
||||||
|
modifyTime: stats.mtime.toLocaleString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fs.writeFileSync(path.join(FILE_DATA_PATH, 'files.json'), JSON.stringify(data));
|
||||||
|
res.json(data);
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/:filename', function(req, res, next) {
|
||||||
|
const filename = req.params.filename;
|
||||||
|
console.log(filename);
|
||||||
|
data = JSON.parse(fs.readFileSync(path.join(FILE_DATA_PATH, 'files.json')));
|
||||||
|
data.getItems = (fileName) => {
|
||||||
|
return data.find((item) => {
|
||||||
|
return item.fileName === fileName;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var item = data.getItems(filename);
|
||||||
|
console.log(item);
|
||||||
|
if (item.isFolder == false) {
|
||||||
|
res.sendFile(path.join(FILE_PATH, filename));
|
||||||
|
} else {
|
||||||
|
res.location('/index.html');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
9
routes/api/json.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const utils = require('../../utils.js');
|
||||||
|
const router = require('express').Router();
|
||||||
|
|
||||||
|
router.get('/:filename', function(req, res, next) {
|
||||||
|
const filename = req.params.filename;
|
||||||
|
res.sendFile(utils.path.join(utils.DATA_JSON_PATH, filename));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
36
routes/api/users.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const utils = require('../../utils.js');
|
||||||
|
const express = require('express');
|
||||||
|
const auth = require('../utils/auth_utils');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post('/login', function(req, res, next) {
|
||||||
|
const data = req.body;
|
||||||
|
if (data.username === (utils.env.USER_LOGIN_NAME || 'admin') &&
|
||||||
|
data.password === (utils.env.USER_LOGIN_PASSWORD || 'admin') ){
|
||||||
|
const token = auth.generateToken({
|
||||||
|
username: data.username,
|
||||||
|
id: -1
|
||||||
|
})
|
||||||
|
res.json({...utils.json.success,
|
||||||
|
data: token
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.json({...utils.json.user_is_valid})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//remote-data-processing-display
|
||||||
|
router.post('/_rdpd', function(req, res, next) {
|
||||||
|
express.response.send = function(data) {
|
||||||
|
res.json({...utils.json.success,
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/admin', function(req, res, next) {
|
||||||
|
res.send('admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
7
routes/db/db.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const utils = require('../../utils.js');
|
||||||
|
const sqlite3DB = require(utils.path.join(utils.RT_UTILS_PATH, 'db_utils'));
|
||||||
|
|
||||||
|
const db_path = utils.path.join(utils.DATA_DB_PATH, utils.env.DATABASE_NAME || 'sqlite3.db');
|
||||||
|
const db = new sqlite3DB(db_path);
|
||||||
|
|
||||||
|
module.exports = db;
|
19
routes/db/users.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const db = require('./db.js');
|
||||||
|
|
||||||
|
db.createTable('users', [
|
||||||
|
{name :'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'},
|
||||||
|
{name :'name', type: 'VARCHAR(32) NOT NULL UNIQUE'},
|
||||||
|
{name :'password', type: `VARCHAR(255) NOT NULL DEFAULT '123456'`},
|
||||||
|
{name :'update_time', type: 'DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP'}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// db.insertData('users', data);
|
||||||
|
// db.deleteData('users', `name='${data[0].name}'`);
|
||||||
|
// db.updateData('users', data[2], `name='${data[1].name}'`);
|
||||||
|
// db.selectData('users', ['name', 'email', 'password'], `name='${data[0].name}'`, (err, rows) => {
|
||||||
|
// console.log(rows);
|
||||||
|
// })
|
||||||
|
// db.selectData('users', ['name', 'email', 'password'], null, function(err, rows) {
|
||||||
|
// console.log(rows);
|
||||||
|
// });
|
||||||
|
// db.closeConnection();
|
11
routes/files.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const utils = require('../utils.js');
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
// res.sendFile(utils.path.join(utils.DATA_FILE_PATH, 'index.html'));
|
||||||
|
res.send('files');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
7
routes/index.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const utils = require('../utils.js');
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = router;
|
66
routes/users.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
const utils = require('../utils.js');
|
||||||
|
const express = require('express');
|
||||||
|
const db = require(utils.path.join(utils.RT_DB_PATH, 'users.js'));
|
||||||
|
const auth = require(utils.path.join(utils.RT_UTILS_PATH, 'auth_utils.js'));
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
/* GET users listing. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
token = req.cookies.token;
|
||||||
|
if (typeof token === 'string') {
|
||||||
|
return auth.verifyToken(token, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
res.redirect('/users/login');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.sendFile(utils.path.join(utils.VIEWS_PATH, 'users/users.html'));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
res.redirect('/users/login');
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /users/login
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
* @param {*} next
|
||||||
|
*/
|
||||||
|
router.get('/login', function(req, res, next) {
|
||||||
|
res.sendFile(utils.path.join(utils.VIEWS_PATH, 'users/login.html'));
|
||||||
|
})
|
||||||
|
|
||||||
|
router.use('/admin', function(req, res, next) {
|
||||||
|
res.send('admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
// WILL BE FILLING
|
||||||
|
router.get('/register', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/logout', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit/password', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit/username', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit/email', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit/phone', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
router.get('/profile/edit/website', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = router;
|
40
routes/utils/auth_utils.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const utils = require('../../utils.js');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const generateToken = (payload, options) => {
|
||||||
|
return jwt.sign(payload, utils.env.JWT_SECRET || 'YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG', {
|
||||||
|
expiresIn: 60,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyToken = (token, callback) => {
|
||||||
|
return jwt.verify(token, utils.env.JWT_SECRET || 'YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG', callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkToken(req, res, next) {
|
||||||
|
const token = req.get('token');
|
||||||
|
if (!token) {
|
||||||
|
return res.json({
|
||||||
|
code: '0001',
|
||||||
|
message: 'token is required',
|
||||||
|
data: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
verifyToken(token, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
return res.json({
|
||||||
|
code: '0002',
|
||||||
|
message: 'token is invalid',
|
||||||
|
data: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
req.tokenData = data;
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateToken,
|
||||||
|
verifyToken
|
||||||
|
};
|
138
routes/utils/db_utils.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
|
||||||
|
class sqlite3DB {
|
||||||
|
constructor(databaseName, errorFunc = null) {
|
||||||
|
if (!databaseName) {
|
||||||
|
throw new Error('Database name is required.');
|
||||||
|
}
|
||||||
|
this.errorFunc = errorFunc || (err => {
|
||||||
|
console.error(`sql: ${err.SQL}\nerror: ${err.message}`);
|
||||||
|
});
|
||||||
|
this.db = new sqlite3.Database(databaseName, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Failed to open database: ${err.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runSql(sql, data, callback) {
|
||||||
|
if (data == null) {
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
this.db.run(sql, data, (err) => {
|
||||||
|
if (err) {
|
||||||
|
err.SQL = sql;
|
||||||
|
this.errorFunc(err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runSerialize(sql, data, callback) {
|
||||||
|
this.db.serialize(() => {
|
||||||
|
this.runSql(sql, data, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareSql(sql, callback) {
|
||||||
|
return this.db.prepare(sql, (stmt, err) => {
|
||||||
|
if (err) {
|
||||||
|
err.SQL = sql;
|
||||||
|
this.errorFunc(err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return stmt;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable(tableName, columnDefinitions, callback) {
|
||||||
|
const columns = columnDefinitions.map(column => `${column.name} ${column.type}`).join(', ');
|
||||||
|
const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`;
|
||||||
|
this.db.serialize(() => {
|
||||||
|
this.runSql(sql, null, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
insertData(tableName, data, callback) {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
data = [data];
|
||||||
|
}
|
||||||
|
const columns = Object.keys(data[0]).join(', ');
|
||||||
|
const values = Object.keys(data[0]).map(() => '?').join(', ');
|
||||||
|
const sql = `INSERT INTO ${tableName} (${columns}) VALUES (${values})`;
|
||||||
|
this.db.serialize(() => {
|
||||||
|
const stmt = this.prepareSql(sql, callback);
|
||||||
|
data.forEach((item) => {
|
||||||
|
stmt.run(Object.values(item), (err) => { if (err) {
|
||||||
|
err.SQL = sql;
|
||||||
|
this.errorFunc(err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
});
|
||||||
|
stmt.finalize((err) => { if(err) {
|
||||||
|
err.SQL = sql;
|
||||||
|
this.errorFunc(err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selectData(tableName, columns, where, callback) {
|
||||||
|
const sql = `SELECT ${columns} FROM ${tableName}
|
||||||
|
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
|
||||||
|
return this.db.all(sql, [], (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
err.SQL = sql;
|
||||||
|
this.errorFunc(err);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback(undefined, rows);
|
||||||
|
} else {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(tableName, _data, where, callback) {
|
||||||
|
var data = null;
|
||||||
|
if (Array.isArray(_data)) {
|
||||||
|
data = _data[0];
|
||||||
|
} else {
|
||||||
|
data = _data;
|
||||||
|
}
|
||||||
|
const columns = Object.keys(data).map((values) => {
|
||||||
|
return `${values}=?`;
|
||||||
|
}).join(', ');
|
||||||
|
const sql = `UPDATE ${tableName} SET ${columns}
|
||||||
|
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
|
||||||
|
const values = Object.values(data);
|
||||||
|
this.runSql(sql, values, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteData(tableName, where, callback) {
|
||||||
|
const sql = `DELETE FROM ${tableName}
|
||||||
|
${where && typeof where !== 'function' ? ` WHERE ${where}` : ''}`;
|
||||||
|
this.runSql(sql, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeConnection() {
|
||||||
|
this.db.close((err) => { if (err) {
|
||||||
|
err.SQL = 'ERROR close connection'
|
||||||
|
this.errorFunc('ERROR db close', err);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = sqlite3DB;
|
36
server.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const path = require('path');
|
||||||
|
require('dotenv').config();
|
||||||
|
env = process.env;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(require('cookie-parser')());
|
||||||
|
app.use(express.static(path.join(__dirname, 'static')));
|
||||||
|
|
||||||
|
app.use('/', require('./routes/index.js'));
|
||||||
|
app.use('/users', require('./routes/users.js'));
|
||||||
|
app.use('/files', require('./routes/files.js'));
|
||||||
|
app.use('/api', require('./routes/api.js'));
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function(req, res, next) {
|
||||||
|
res.status(404);
|
||||||
|
res.end('<h1>404 Not Found</h1>');
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function(err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||||
|
|
||||||
|
console.log(err);
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.end('<h1>error</h1>');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
5
static/css/anchor/fonts_all.css
Normal file
9894
static/css/anchor/main.css
Normal file
1
static/css/anchor/vendor/aos.css
vendored
Normal file
4
static/css/anchor/vendor/bootstrap-toc.css
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Table of Contents v1.0.0 (http://afeld.github.io/bootstrap-toc/)
|
||||||
|
* Copyright 2015 Aidan Feldman
|
||||||
|
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */nav[data-toggle=toc] .nav>li>a{display:block;padding:4px 20px;font-size:15px;font-weight:500;color:#767676}nav[data-toggle=toc] .nav>li>a:focus,nav[data-toggle=toc] .nav>li>a:hover{padding-left:19px;color:#563d7c;text-decoration:none;background-color:transparent;border-left:1px solid #563d7c}nav[data-toggle=toc] .nav-link.active,nav[data-toggle=toc] .nav-link.active:focus,nav[data-toggle=toc] .nav-link.active:hover{padding-left:18px;font-weight:700;color:#563d7c;background-color:transparent;border-left:2px solid #563d7c}nav[data-toggle=toc] .nav-link+ul{display:none;padding-bottom:10px}nav[data-toggle=toc] .nav .nav>li>a{padding-top:1px;padding-bottom:1px;padding-left:30px;font-size:14px;font-weight:400}nav[data-toggle=toc] .nav .nav>li>a:focus,nav[data-toggle=toc] .nav .nav>li>a:hover{padding-left:29px}nav[data-toggle=toc] .nav .nav>li>.active,nav[data-toggle=toc] .nav .nav>li>.active:focus,nav[data-toggle=toc] .nav .nav>li>.active:hover{padding-left:28px;font-weight:500}nav[data-toggle=toc] .nav-link.active+ul{display:block}
|
273
static/css/anchor/vendor/prism.css
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
Name: Duotone Light
|
||||||
|
Author: Simurai, adapted from DuoTone themes for Atom (http://simurai.com/projects/2016/01/01/duotone-themes)
|
||||||
|
|
||||||
|
Conversion: Bram de Haan (http://atelierbram.github.io/Base2Tone-prism/output/prism/prism-base2tone-morning-light.css)
|
||||||
|
Generated with Base16 Builder (https://github.com/base16-builder/base16-builder)
|
||||||
|
*/
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.375;
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
background: #faf8f5;
|
||||||
|
color: #728fcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #333;
|
||||||
|
color:#ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #333;
|
||||||
|
color:#ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: .1em;
|
||||||
|
border-radius: .3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: #b6ad9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #b6ad9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.namespace {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.tag,
|
||||||
|
.token.operator,
|
||||||
|
.token.number {
|
||||||
|
color: #063289;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.function {
|
||||||
|
color: #b29762;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.tag-id,
|
||||||
|
.token.selector,
|
||||||
|
.token.atrule-id {
|
||||||
|
color: #2d2006;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.language-javascript,
|
||||||
|
.token.attr-name {
|
||||||
|
color: #896724;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.language-css,
|
||||||
|
code.language-scss,
|
||||||
|
.token.boolean,
|
||||||
|
.token.string,
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.language-css .token.string,
|
||||||
|
.language-scss .token.string,
|
||||||
|
.style .token.string,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.keyword,
|
||||||
|
.token.control,
|
||||||
|
.token.directive,
|
||||||
|
.token.unit,
|
||||||
|
.token.statement,
|
||||||
|
.token.regex,
|
||||||
|
.token.atrule {
|
||||||
|
color: #728fcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.placeholder,
|
||||||
|
.token.variable {
|
||||||
|
color: #93abdc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.deleted {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.inserted {
|
||||||
|
border-bottom: 1px dotted #2d2006;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important {
|
||||||
|
color: #896724;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code.highlight {
|
||||||
|
outline: .4em solid #896724;
|
||||||
|
outline-offset: .4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Numbers plugin
|
||||||
|
* http://prismjs.com/plugins/line-numbers/
|
||||||
|
*/
|
||||||
|
.line-numbers .line-numbers-rows {
|
||||||
|
border-right-color: #ece8de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers-rows > span:before {
|
||||||
|
color: #cdc4b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Highlight plugin
|
||||||
|
* http://prismjs.com/plugins/line-highlight/
|
||||||
|
*/
|
||||||
|
.line-highlight {
|
||||||
|
background: rgba(45, 32, 6, 0.2);
|
||||||
|
background: -webkit-linear-gradient(left, rgba(45, 32, 6, 0.2) 70%, rgba(45, 32, 6, 0));
|
||||||
|
background: linear-gradient(to right, rgba(45, 32, 6, 0.2) 70%, rgba(45, 32, 6, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* JUST SOME STYLES FOR PRE CODES */
|
||||||
|
code { font-size: 14px;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.5;}
|
||||||
|
|
||||||
|
.code-toolbar {position: relative;}
|
||||||
|
.code-toolbar .toolbar-item a {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
right: -10px;
|
||||||
|
color: #ffffff;
|
||||||
|
background:#333;
|
||||||
|
padding: 0 8px;
|
||||||
|
border-radius: 30px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.code-toolbar .toolbar-item a:hover, .code-toolbar .toolbar-item a:focus { background: #ff5684;color:#fff;}
|
||||||
|
|
||||||
|
#docsarea .alert-orange { color:rgba(255,255,255,0.83);}
|
||||||
|
#docsarea .alert-orange code {color:#b73b3b;}
|
||||||
|
#docsarea .alert-orange a {color:inherit; border-bottom:1px dashed;}
|
||||||
|
|
||||||
|
ul.ascii, ul.ascii ul { margin-left: 0; padding-left: 0; list-style: none; }
|
||||||
|
|
||||||
|
ul.ascii li { margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
/* level 1 */
|
||||||
|
ul.ascii > li::before { content: ""; }
|
||||||
|
|
||||||
|
/* level 2 */
|
||||||
|
ul.ascii > li > ul > li::before { content: "├──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child::before { content: "└──\00a0"; }
|
||||||
|
|
||||||
|
/* level 3 */
|
||||||
|
ul.ascii > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
|
||||||
|
/* level 4 */
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
|
||||||
|
/* level 5 */
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
|
||||||
|
/* level 6 */
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0│\00a0\00a0\00a0└──\00a0"; }
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0├──\00a0"}
|
||||||
|
ul.ascii > li > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child > ul > li:last-child::before { content: "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0└──\00a0"; }
|
0
static/css/main.css
Normal file
154
static/fetch-wrapper.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
class FetchWrapper {
|
||||||
|
constructor(baseURL, options = {}, contentTypeMappings = {}, onError = console.error) {
|
||||||
|
this.baseURL = baseURL;
|
||||||
|
this.contentTypeMappings = contentTypeMappings;
|
||||||
|
this.defaultContentType = 'application/json';
|
||||||
|
this.onError = onError;
|
||||||
|
this.defaultOptions = {
|
||||||
|
...{
|
||||||
|
method: 'GET',
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': this.defaultContentType
|
||||||
|
}),
|
||||||
|
mode: 'cors', // 指定是否允许跨域请求,默认为cors
|
||||||
|
cache: 'default', // 请求的缓存模式
|
||||||
|
credentials: 'same-origin', // 请求的凭据模式,默认同源请求时发送cookies
|
||||||
|
parseResponseHandler: {
|
||||||
|
'application/json': response => response.json(),
|
||||||
|
'text/*': response => response.text(),
|
||||||
|
'image/*': response => response.blob(),
|
||||||
|
'application/octet-stream': response => response.blob(),
|
||||||
|
'*/*': response => response.arrayBuffer(), // 默认处理程序
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async fetch(url, options = {}) {
|
||||||
|
// 合并默认选项与传入的options
|
||||||
|
const mergedOptions = { ...this.defaultOptions, ...options };
|
||||||
|
|
||||||
|
// 根据URL自动设置Content-Type(如果有匹配的映射)
|
||||||
|
const matchedMapping = Object.entries(this.contentTypeMappings).find(([pattern, type]) => url.endsWith(pattern));
|
||||||
|
if (matchedMapping && !mergedOptions.headers.has('Content-Type')) {
|
||||||
|
mergedOptions.headers['Content-Type'] = matchedMapping[1];
|
||||||
|
}
|
||||||
|
// 根据请求方法处理数据
|
||||||
|
if (['POST', 'PUT', 'PATCH'].includes(mergedOptions.method)) {
|
||||||
|
if (typeof mergedOptions.body === 'object') {
|
||||||
|
mergedOptions.body = JSON.stringify(mergedOptions.body);
|
||||||
|
// 如果Content-Type未设置或默认为application/json,则需要在这里设置
|
||||||
|
if (!mergedOptions.headers.has('Content-Type')) {
|
||||||
|
mergedOptions.headers.set('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
|
} else if (mergedOptions.body !== null && typeof mergedOptions.body !== 'string') {
|
||||||
|
throw new Error('非GET请求时, body必须是一个对象或字符串');
|
||||||
|
}
|
||||||
|
} else if (mergedOptions.method === 'GET' && mergedOptions.body) {
|
||||||
|
console.warn('GET请求不支持发送请求体, 已忽略提供的body参数');
|
||||||
|
delete mergedOptions.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = new URL(url, this.baseURL).href;
|
||||||
|
|
||||||
|
return fetch(url, mergedOptions)
|
||||||
|
.then(async response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
// console.warn(`HTTP warning! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers.get('content-type').split(';')[0].trim();
|
||||||
|
const parseResponseHandler = this.getParseResponseHandler(contentType);
|
||||||
|
|
||||||
|
const responseData = await parseResponseHandler(response);
|
||||||
|
return formatResponse(response, responseData);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
error.options = mergedOptions;
|
||||||
|
error.url = url;
|
||||||
|
error.timestamp = Date.now();
|
||||||
|
error.onFunction = 'fetch';
|
||||||
|
this.onError(error.message);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getParseResponseHandler(contentType) {
|
||||||
|
if (contentType.startsWith('text/')) {
|
||||||
|
return this.defaultOptions.parseResponseHandler['text/*'] || this.defaultOptions.parseResponseHandler['*/*'];
|
||||||
|
}
|
||||||
|
if (contentType.startsWith('image/')) {
|
||||||
|
return this.defaultOptions.parseResponseHandler['image/*'] || this.defaultOptions.parseResponseHandler['*/*'];
|
||||||
|
}
|
||||||
|
return this.defaultOptions.parseResponseHandler[contentType] || this.defaultOptions.parseResponseHandler['*/*'];
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000) {
|
||||||
|
let remainingAttempts = maxRetries;
|
||||||
|
const delays = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
delays.push(retryDelayBaseMs * Math.pow(2, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
const attemptFetch = async (retryIndex = 0) => {
|
||||||
|
try {
|
||||||
|
return await this.fetch(url, options);
|
||||||
|
} catch (error) {
|
||||||
|
if (remainingAttempts > 1) {
|
||||||
|
console.log(`请求失败,剩余重试次数:${remainingAttempts - 1}`);
|
||||||
|
setTimeout(() => attemptFetch(retryIndex + 1), delays[retryIndex]);
|
||||||
|
remainingAttempts--;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.onError(error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return attemptFetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可以为不同的HTTP方法提供便捷的方法,如:
|
||||||
|
async post(url, body, options = {}) {
|
||||||
|
return this.fetch(url, { ...options, method: 'POST', body });
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(url, options = {}) {
|
||||||
|
return this.fetch(url, { ...options, method: 'GET' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async put(url, body, options = {}) {
|
||||||
|
return this.fetch(url, { ...options, method: 'PUT', body });
|
||||||
|
}
|
||||||
|
|
||||||
|
async patch(url, body, options = {}) {
|
||||||
|
return this.fetch(url, { ...options, method: 'PATCH', body });
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(url, options = {}) {
|
||||||
|
return this.fetch(url, { ...options, method: 'DELETE' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatResponse(response, data) {
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
message: response.statusText,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否在Node.js环境中
|
||||||
|
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||||
|
// Node.js环境,使用CommonJS
|
||||||
|
module.exports = FetchWrapper;
|
||||||
|
} else if (typeof window !== 'undefined') {
|
||||||
|
// 浏览器环境,直接暴露到全局作用域(不推荐,但简单示例)
|
||||||
|
// window.FetchWrapper = FetchWrapper;
|
||||||
|
}
|
136
static/fetch-wrapper使用文档.md
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# FetchWrapper 类使用文档
|
||||||
|
|
||||||
|
**概述:**
|
||||||
|
|
||||||
|
`FetchWrapper` 是一个基于 `fetch API` 的封装类,用于简化网络请求操作,并提供默认选项、Content-Type 自动映射、错误处理和重试机制等功能。
|
||||||
|
|
||||||
|
## 构造函数
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
new FetchWrapper(baseURL, contentTypeMappings = {}, defaultOptions = {}, onError = console.error)
|
||||||
|
````
|
||||||
|
|
||||||
|
- **baseURL**:基础 URL 字符串,所有相对路径的请求都会基于此 URL 进行拼接。
|
||||||
|
- **contentTypeMappings**:对象,键为文件扩展名或通配符模式,值为对应的 Content-Type,当请求 URL 匹配时自动设置 Content-Type 头部。
|
||||||
|
- **defaultOptions**:对象,定义了发起请求时的默认选项,例如:
|
||||||
|
````javascript
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
// 其他默认配置项...
|
||||||
|
}
|
||||||
|
````
|
||||||
|
- **onError**:错误回调函数,当发生网络请求错误时触发,默认为 `console.error`。
|
||||||
|
|
||||||
|
## 主要方法
|
||||||
|
|
||||||
|
### formatResponse(response, data)
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
formatResponse(response, data)
|
||||||
|
````
|
||||||
|
|
||||||
|
- 格式化响应对象和数据,返回一个统一格式的对象。
|
||||||
|
|
||||||
|
### fetch(url, options = {})
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
async fetch(url, options = {})
|
||||||
|
````
|
||||||
|
|
||||||
|
- 发起 HTTP 请求,参数说明:
|
||||||
|
- **url**:请求 URL。
|
||||||
|
- **options**:覆盖默认选项的对象,可自定义请求方法、头部、请求体等信息。
|
||||||
|
|
||||||
|
### getParseResponseHandler(contentType)
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
getParseResponseHandler(contentType)
|
||||||
|
````
|
||||||
|
|
||||||
|
- 根据响应内容类型(Content-Type)获取相应的解析器函数。
|
||||||
|
|
||||||
|
### fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000)
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
async fetchWithRetry(url, options = {}, maxRetries = 3, retryDelayBaseMs = 1000)
|
||||||
|
````
|
||||||
|
|
||||||
|
- 发起带有重试机制的 HTTP 请求,在请求失败时会按照指数退避策略进行重试。
|
||||||
|
- **maxRetries**:最大重试次数,默认为3次。
|
||||||
|
- **retryDelayBaseMs**:首次重试延迟时间的基础单位,默认为1000毫秒。
|
||||||
|
|
||||||
|
### post(url, body, options = {})
|
||||||
|
### get(url, options = {})
|
||||||
|
### put(url, body, options = {})
|
||||||
|
### patch(url, body, options = {})
|
||||||
|
### delete(url, options = {})
|
||||||
|
|
||||||
|
这些是针对不同HTTP方法的便捷调用方法。
|
||||||
|
|
||||||
|
## 示例使用:
|
||||||
|
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
const wrapper = new FetchWrapper('https://api.example.com', {
|
||||||
|
'.json': 'application/json',
|
||||||
|
}, {
|
||||||
|
credentials: 'include',
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const response = await wrapper.get('/data.json');
|
||||||
|
console.log(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
wrapper.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//使用带重试机制的 POST 请求
|
||||||
|
async function postDataWithRetry() {
|
||||||
|
const body = { key: 'value' };
|
||||||
|
const options = { headers: { 'X-Custom-Header': 'value' } };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await wrapper.fetchWithRetry('/post-data', { body }, 5, 2000);
|
||||||
|
console.log(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
wrapper.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
|
以上JavaScript代码已用Markdown代码块包裹,可以直接复制并粘贴到您的项目中。通过创建一个 `FetchWrapper` 实例,您可以利用其便捷的方法来发起网络请求,并根据需要灵活定制请求选项以及实现重试功能。
|
||||||
|
|
||||||
|
当您在fetch-wrapper.js文件中使用export default class FetchWrapper {...}导出FetchWrapper类时,您可以在其他JavaScript模块中这样导入:
|
||||||
|
````javascript
|
||||||
|
// 导入 FetchWrapper 类
|
||||||
|
import FetchWrapper from './fetch-wrapper';
|
||||||
|
|
||||||
|
// 创建一个实例
|
||||||
|
const wrapper = new FetchWrapper('https://api.example.com', {}, { credentials: 'include' });
|
||||||
|
|
||||||
|
// 使用这个实例发起请求
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const response = await wrapper.get('/data.json');
|
||||||
|
console.log(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
wrapper.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
````
|
||||||
|
这里,./fetch-wrapper是相对路径,表示FetchWrapper类所在的模块位置。根据实际项目结构,您需要调整这个路径以便正确指向fetch-wrapper.js文件。
|
||||||
|
|
||||||
|
formatResponse(response, data) {
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
message: response.statusText,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
14
static/html/autor-link/author-linking-qq.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="/ico/qq.png">
|
||||||
|
<title>author-qq</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<img src="/pic/author-link/qq-link.png" alt="QQ: 24502666535">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
static/html/autor-link/author-linking-wechat.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" href="/ico/wechat.png">
|
||||||
|
<title>author-wechat</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<img src="/pic/author-link/wechat-link.png" alt="微信: ZZYXYZ2450266535">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
36
static/html/base/base_footer.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1440 126" style="enable-background:new 0 0 1440 126;" xml:space="preserve">
|
||||||
|
<path class="bg-light" d="M685.6,38.8C418.7-11.1,170.2,9.9,0,30v96h1440V30C1252.7,52.2,1010,99.4,685.6,38.8z"></path>
|
||||||
|
</svg>
|
||||||
|
<footer class="bg-light pb-5 pt-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col">
|
||||||
|
<div class="row">
|
||||||
|
<a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
|
||||||
|
法律声明 </a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
|
||||||
|
隐私政策</a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
|
||||||
|
使用条款</a> | <a class="legal-notice-link" href="/html/legal.html" target="_blank" rel="noopener noreferrer">
|
||||||
|
关于</a>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
联系作者:
|
||||||
|
</div>
|
||||||
|
<a href="./html/autor-link/author-linking-wechat.html" target="_blank" class="">
|
||||||
|
<img class="rounded-circle shadow" src="/pic/ico/wechat.png" alt="wechat-link" width="25">
|
||||||
|
</a>
|
||||||
|
<a href="./html/autor-link/author-linking-qq.html" target="_blank" class="">
|
||||||
|
<img class="rounded-circle shadow" src="/pic/ico/qq.png" alt="qq-link" width="25">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<a href="https://beian.miit.gov.cn" target="_blank" class="">
|
||||||
|
<img class="rounded-circle shadow" src="/pic/ico/beian.png" alt="beian-pic" width="25">
|
||||||
|
赣ICP备2023009103号
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="d-block mt-3 mb-3 text-muted">Copyright <i class="fas fa-copyright text-black"></i> 2024 zzy. All rights reserved.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
After Width: | Height: | Size: 2.0 KiB |
22
static/html/base/base_modal.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!-- The modal itself -->
|
||||||
|
<div class="modal fade" id="_zzy_base_modal_" tabindex="-1" role="dialog" aria-labelledby="base_modal_center_title" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header border-0">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="pb-4 pr-4 pl-4 text-center">
|
||||||
|
<h3 class="heading mt-4 text-black">服务条款</h3>
|
||||||
|
<p class="text-black">该网页仅用于学习,不用于其他用途,点击确认则默认同意。若不同意,请点击网站下方[法律声明]阅读详细内容。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer border-0">
|
||||||
|
<button type="button" class="btn btn-info btn-round confirm-action">Accept</button>
|
||||||
|
<button type="button" class="btn btn-info btn-round" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
35
static/html/base/base_modual.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="include" src="/html/base/base_modal.html"></div>
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<!-- <div id="linking-cards" class="row"></div> -->
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
24
static/html/base/base_nav.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<nav class="navbar navbar-dark bg-secondary navbar-expand-md">
|
||||||
|
<!-- Navbar content -->
|
||||||
|
<a class="navbar-brand" href="/">
|
||||||
|
<img src="/pic/ico/Home.png" width="30" height="30" class="d-inline-block align-top" alt="">
|
||||||
|
zzyxyz
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarText">
|
||||||
|
<div class="navbar-nav mr-auto">
|
||||||
|
<a class="nav-item nav-link active" href="/">Home <span class="sr-only">(current)</span></a>
|
||||||
|
<a class="nav-item nav-link disabled" href="#">Features</a>
|
||||||
|
<a class="nav-item nav-link disabled" href="#">Pricing</a>
|
||||||
|
<a class="nav-item nav-link disabled" href="#">Disabled</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-nav ml-auto">
|
||||||
|
<form class="form-inline">
|
||||||
|
<input class="form-control" type="search" placeholder="Search">
|
||||||
|
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
56
static/html/cs-study-link.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="include" src="/html/base/base_modal.html"></div>
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<div id="linking-cards" class="row"></div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$.getJSON("/api/json/cs-study-link-content.json", function(data) {
|
||||||
|
const default_item = data.default;
|
||||||
|
data.datas.forEach(i => {
|
||||||
|
item = {...default_item, ...i}
|
||||||
|
const card = `
|
||||||
|
<div class="card shadow-sm border-0 col-md-4">
|
||||||
|
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">${item.title}</h5>
|
||||||
|
<p class="card-text text-muted">${item.intro}</p>
|
||||||
|
<p class="card-text text-muted">${item.msg}</p>
|
||||||
|
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$("#linking-cards").append(card);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
38
static/html/legal.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="/ico/Home.png">
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>网站法律声明</h1>
|
||||||
|
<p>欢迎使用本网站!以下是本平台的法律声明,请仔细阅读。通过访问和使用本网站及其提供的各项服务,即表示您已阅读、理解并同意接受以下全部条款和条件。</p>
|
||||||
|
<h1>该网页仅用于学习,不用于其他用途</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<!--
|
||||||
|
---
|
||||||
|
title: 法律声明
|
||||||
|
---
|
||||||
|
|
||||||
|
# # 网站法律声明
|
||||||
|
|
||||||
|
欢迎使用本网站!以下是本平台的法律声明,请仔细阅读。通过访问和使用本网站及其提供的各项服务,即表示您已阅读、理解并同意接受以下全部条款和条件。
|
||||||
|
|
||||||
|
## 一、版权与知识产权声明
|
||||||
|
...
|
||||||
|
|
||||||
|
## 二、服务条款
|
||||||
|
...
|
||||||
|
|
||||||
|
## 三、隐私政策
|
||||||
|
...
|
||||||
|
|
||||||
|
## 四、免责声明
|
||||||
|
...
|
||||||
|
|
||||||
|
... -->
|
56
static/html/office-link.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="include" src="/html/base/base_modal.html"></div>
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<div id="linking-cards" class="row"></div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready( function() {
|
||||||
|
$.getJSON("/api/json/office-link.json", function(data) {
|
||||||
|
const default_item = data.default;
|
||||||
|
data.datas.forEach(i => {
|
||||||
|
item = {...default_item, ...i}
|
||||||
|
const card = `
|
||||||
|
<div class="card shadow-sm border-0 col-md-4">
|
||||||
|
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">${item.title}</h5>
|
||||||
|
<p class="card-text text-muted">${item.intro}</p>
|
||||||
|
<p class="card-text text-muted">${item.msg}</p>
|
||||||
|
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$("#linking-cards").append(card);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
61
static/html/self-study-link.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="include" src="/html/base/base_modal.html"></div>
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<div id="linking-cards" class="row"></div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready( function() {
|
||||||
|
$.getJSON("/api/json/self-study-link.json", function(data) {
|
||||||
|
const default_item = data.default;
|
||||||
|
data.datas.forEach(i => {
|
||||||
|
item = {...default_item, ...i}
|
||||||
|
item.ex_url = ''
|
||||||
|
item.other_urls.forEach(j => {
|
||||||
|
item.ex_url += `<a href="${j}" class="btn btn-info btn-round" target="${false ? '_blank' : ''}">${j}</a>`
|
||||||
|
})
|
||||||
|
const card = `
|
||||||
|
<div class="card shadow-sm border-0 col-md-4">
|
||||||
|
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">${item.title}</h5>
|
||||||
|
<p class="card-text text-muted">${item.intro}</p>
|
||||||
|
<p class="card-text text-muted">${item.msg}</p>
|
||||||
|
${item.ex_url}
|
||||||
|
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$("#linking-cards").append(card);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
75
static/index.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="./css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="./css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>zzyxyz</title>
|
||||||
|
<link rel="icon" href="./pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="include" src="./html/base/base_modal.html"></div>
|
||||||
|
<div class="include" src="./html/base/base_nav.html"></div>
|
||||||
|
<!-- wavy header -->
|
||||||
|
<div class="jumbotron jumbotron-lg jumbotron-fluid mb-0 pb-3 bg-secondary position-relative">
|
||||||
|
<div class="container-fluid text-white h-50">
|
||||||
|
<div class="d-lg-flex align-items-center justify-content-between text-center pl-lg-5">
|
||||||
|
<div class="col pt-4 pb-4">
|
||||||
|
<h1 class="display-3">Home</h1>
|
||||||
|
<h5 class="font-weight-light mb-4">This is Home Page</h5>
|
||||||
|
<a href="#" class="btn btn-lg btn-outline-white btn-round">Learn more</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg style="-webkit-transform:rotate(-180deg); -moz-transform:rotate(-180deg); -o-transform:rotate(-180deg); transform:rotate(-180deg); margin-top: -1px;" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewbox="0 0 1440 126" style="enable-background:new 0 0 1440 126;" xml:space="preserve">
|
||||||
|
<path class="bg-secondary" d="M685.6,38.8C418.7-11.1,170.2,9.9,0,30v96h1440V30C1252.7,52.2,1010,99.4,685.6,38.8z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<a href="/users/login" class="btn btn-outline-primary btn-round">Test Login</a>
|
||||||
|
<div id="linking-cards" class="row">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="./html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="./js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<script src="./js/base_script.js" type="text/javascript"></script>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$.getJSON("/api/json/index-content.json", function(data) {
|
||||||
|
const default_item = data.default;
|
||||||
|
data.datas.forEach(i => {
|
||||||
|
item = {...default_item, ...i}
|
||||||
|
const card = `
|
||||||
|
<div class="card shadow-sm border-0 col-md-4">
|
||||||
|
<!-- <img class="card-img-top" src="./assets/img/demo/2.jpg" alt="Card image cap"> -->
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">${item.title}</h5>
|
||||||
|
<p class="card-text text-muted">${item.intro}</p>
|
||||||
|
<p class="card-text text-muted">${item.msg}</p>
|
||||||
|
<a href="${item.url}" class="btn btn-info btn-round ${item.disable ? 'disabled' : ''}" target="${item.targetBlank ? '_blank' : ''}">Jump Link</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$("#linking-cards").append(card);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
static/js/anchor/functions.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
jQuery( document ).ready(function() {
|
||||||
|
|
||||||
|
$(window).scroll(function(){
|
||||||
|
$('.topnav').toggleClass('bg-white navbar-light shadow-sm scrollednav py-0', $(this).scrollTop() > 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#modal_newsletter').on('show.bs.modal', function () {
|
||||||
|
$('.downloadzip')[0].click();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
1
static/js/anchor/vendor/aos.js
vendored
Normal file
5
static/js/anchor/vendor/bootstrap-toc.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Table of Contents v1.0.0 (http://afeld.github.io/bootstrap-toc/)
|
||||||
|
* Copyright 2015 Aidan Feldman
|
||||||
|
* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
|
||||||
|
!function(e){"use strict";window.Toc={helpers:{findOrFilter:function(e,t){var n=e.find(t);return e.filter(t).add(n).filter(":not([data-toc-skip])")},generateUniqueIdBase:function(t){var n=e(t).text(),r=n.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g,"-");return r||t.tagName.toLowerCase()},generateUniqueId:function(e){for(var t=this.generateUniqueIdBase(e),n=0;;n++){var r=t;if(n>0&&(r+="-"+n),!document.getElementById(r))return r}},generateAnchor:function(e){if(e.id)return e.id;var t=this.generateUniqueId(e);return e.id=t,t},createNavList:function(){return e('<ul class="nav navbar-nav"></ul>')},createChildNavList:function(e){var t=this.createNavList();return e.append(t),t},generateNavEl:function(t,n){var r=e('<a class="nav-link"></a>');r.attr("href","#"+t),r.text(n);var a=e("<li></li>");return a.append(r),a},generateNavItem:function(t){var n=this.generateAnchor(t),r=e(t),a=r.data("toc-text")||r.text();return this.generateNavEl(n,a)},getTopLevel:function(e){for(var t=1;t<=6;t++){var n=this.findOrFilter(e,"h"+t);if(n.length>1)return t}return 1},getHeadings:function(e,t){var n="h"+t,r=t+1,a="h"+r;return this.findOrFilter(e,n+","+a)},getNavLevel:function(e){return parseInt(e.tagName.charAt(1),10)},populateNav:function(e,t,n){var r,a=e,i=this;n.each(function(n,o){var s=i.generateNavItem(o),u=i.getNavLevel(o);u===t?a=e:r&&a===e&&(a=i.createChildNavList(r)),a.append(s),r=s})},parseOps:function(t){var n;return n=t.jquery?{$nav:t}:t,n.$scope=n.$scope||e(document.body),n}},init:function(e){e=this.helpers.parseOps(e),e.$nav.attr("data-toggle","toc");var t=this.helpers.createChildNavList(e.$nav),n=this.helpers.getTopLevel(e.$scope),r=this.helpers.getHeadings(e.$scope,n);this.helpers.populateNav(t,n,r)}},e(function(){e('nav[data-toggle="toc"]').each(function(t,n){var r=e(n);Toc.init(r)})})}(jQuery);
|
7
static/js/anchor/vendor/bootstrap.min.js
vendored
Normal file
2
static/js/anchor/vendor/jquery.min.js
vendored
Normal file
5
static/js/anchor/vendor/popper.min.js
vendored
Normal file
41
static/js/anchor/vendor/prism.js
vendored
Normal file
1
static/js/anchor/vendor/share.js
vendored
Normal file
20
static/js/base_script.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
$(".include").each(function() {
|
||||||
|
if (!!$(this).attr("src")) {
|
||||||
|
var $includeObj = $(this);
|
||||||
|
$(this).load($(this).attr("src"), function(html) {
|
||||||
|
$includeObj.after(html).remove(); //加载的文件内容写入到当前标签后面并移除当前标签
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIX ME
|
||||||
|
setTimeout(function() {
|
||||||
|
const $_zzy_base_modal_ = $('#_zzy_base_modal_');
|
||||||
|
$_zzy_base_modal_.modal('show');
|
||||||
|
$_zzy_base_modal_.on('click', '.confirm-action', function() {
|
||||||
|
$_zzy_base_modal_.modal('hide');
|
||||||
|
console.log('用户已同意条款,页面开始加载...');
|
||||||
|
// 在这里执行其他初始化操作
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 1000);
|
BIN
static/pic/author-link/qq-link.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
static/pic/author-link/wechat-link.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
static/pic/ico/Home.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
static/pic/ico/beian.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
static/pic/ico/qq.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
static/pic/ico/wechat.png
Normal file
After Width: | Height: | Size: 13 KiB |
51
utils.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
utils = {};
|
||||||
|
utils.ROOT_PATH = path.resolve(__dirname);
|
||||||
|
utils.VIEWS_PATH = path.resolve(utils.ROOT_PATH, 'views');
|
||||||
|
utils.DATA_PATH = path.resolve(utils.ROOT_PATH, '.data');
|
||||||
|
utils.DATA_JSON_PATH = path.resolve(utils.DATA_PATH, 'json');
|
||||||
|
utils.DATA_DB_PATH = path.resolve(utils.DATA_PATH, 'db');
|
||||||
|
utils.DATA_CONFIG_PATH = path.resolve(utils.DATA_PATH, 'config');
|
||||||
|
utils.DATA_FILE_PATH = path.resolve(utils.DATA_PATH, 'file');
|
||||||
|
utils.ROUTES_PATH = path.resolve(utils.ROOT_PATH, 'routes');
|
||||||
|
utils.RT_DB_PATH = path.resolve(utils.ROUTES_PATH, 'db');
|
||||||
|
utils.RT_API_PATH = path.resolve(utils.ROUTES_PATH, 'api');
|
||||||
|
utils.RT_UTILS_PATH = path.resolve(utils.ROUTES_PATH, 'utils');
|
||||||
|
|
||||||
|
// 检查并创建路径
|
||||||
|
[utils.DATA_PATH, utils.DATA_JSON_PATH, utils.DATA_DB_PATH, utils.DATA_CONFIG_PATH, utils.DATA_FILE_PATH].forEach((p) => {
|
||||||
|
if (!fs.existsSync(p)) {
|
||||||
|
fs.mkdirSync(p, { recursive: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.json = {
|
||||||
|
success: {
|
||||||
|
code: '0000',
|
||||||
|
msg: 'success',
|
||||||
|
data: null
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
code: '0001',
|
||||||
|
msg: 'unknown error',
|
||||||
|
data: null
|
||||||
|
},
|
||||||
|
token_is_invalid: {
|
||||||
|
code: '0002',
|
||||||
|
msg: 'token invalid',
|
||||||
|
data: null
|
||||||
|
},
|
||||||
|
user_is_valid: {
|
||||||
|
code: '0100',
|
||||||
|
msg: 'login error',
|
||||||
|
data: null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.path = path;
|
||||||
|
utils.fs = fs;
|
||||||
|
utils.env = process.env;
|
||||||
|
module.exports = utils;
|
95
views/users/login.html
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- <div class="include" src="/html/base/base_modal.html"></div> -->
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<!-- <div id="linking-cards" class="row"></div> -->
|
||||||
|
<div class="container col-md-5">
|
||||||
|
<form id="login-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control input-round" id="username" placeholder="Enter Username" required="">
|
||||||
|
<small id="usernameHelp" class="form-text text-muted">We'll never share your username and password with anyone else.</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" class="form-control input-round" id="password" placeholder="Password" required="">
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="exampleCheck1">
|
||||||
|
<label class="form-check-label" for="exampleCheck1">Check me out</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-success btn-round">Login</button>
|
||||||
|
<button type="button" class="btn btn-success btn-round disabled">Register</button>
|
||||||
|
<p class="message"></p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="/fetch-wrapper.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById('login-form').addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var username = document.getElementById('username').value;
|
||||||
|
var password = document.getElementById('password').value;
|
||||||
|
|
||||||
|
// 这里仅做简单的输入非空验证,实际应用中需要后端配合进行账号密码验证
|
||||||
|
if (!username || !password) {
|
||||||
|
document.querySelector('.message').textContent = 'Both username and password are required.';
|
||||||
|
} else {
|
||||||
|
document.querySelector('.message').textContent = '';
|
||||||
|
// 假设这里发起一个异步登录请求
|
||||||
|
login(username, password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function login(username, password) {
|
||||||
|
try {
|
||||||
|
// 这里应该调用后端API进行登录操作,以下仅为模拟示例
|
||||||
|
const wrapper = new FetchWrapper(window.location.origin);
|
||||||
|
const response = await wrapper.post('/api/users/login', {
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.data.code === '0000') {
|
||||||
|
// 登录成功,跳转到主页
|
||||||
|
document.cookie = `token=${response.data.data};`;
|
||||||
|
window.location.href = '/users';
|
||||||
|
} else {
|
||||||
|
// 登录失败,给出错误提示
|
||||||
|
document.querySelector('.message').textContent =
|
||||||
|
'Login failed.登录失败...' + response.data.code +
|
||||||
|
'请检查用户名和密码是否正确, 或联系管理员添加新账号';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
0
views/users/register.html
Normal file
86
views/users/users.html
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport'/>
|
||||||
|
<!-- Fonts -->
|
||||||
|
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito:300,300i,400,600,700" rel="stylesheet"> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> -->
|
||||||
|
<!-- <link href="./css/anchor/fonts_all.css" rel="stylesheet"/> -->
|
||||||
|
<!-- CSS -->
|
||||||
|
<link href="/css/anchor/main.css" rel="stylesheet"/>
|
||||||
|
<link href="/css/anchor/vendor/aos.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<title>Users Page</title>
|
||||||
|
<link rel="icon" href="/pic/ico/Home.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- <div class="include" src="/html/base/base_modal.html"></div> -->
|
||||||
|
<div class="include" src="/html/base/base_nav.html"></div>
|
||||||
|
|
||||||
|
<main class="container pb-5 pt-5">
|
||||||
|
<!-- <div id="linking-cards" class="row"></div> -->
|
||||||
|
<h1>Users</h1>
|
||||||
|
<form id="login-form">
|
||||||
|
<!-- remote-data-processing-display -->
|
||||||
|
<h2>Remote Data Processing Display</h2>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="input" class="form-control input-round" id="input0" placeholder="input0" required="">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="input" class="form-control input-round" id="input1" placeholder="input1" required="">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="input" class="form-control input-round" id="input2" placeholder="input2" required="">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-success btn-round">submit</button>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="input" class="form-control input-round" id="output" placeholder="output" required="">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="include" src="/html/base/base_footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Javascript -->
|
||||||
|
<script src="/js/anchor/vendor/jquery.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/popper.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/vendor/bootstrap.min.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/anchor/functions.js" type="text/javascript"></script>
|
||||||
|
<script src="/js/base_script.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="/fetch-wrapper.js"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById('login-form').addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var input = [];
|
||||||
|
input[0] = document.getElementById('input0').value;
|
||||||
|
input[1] = document.getElementById('input1').value;
|
||||||
|
input[2] = document.getElementById('input2').value;
|
||||||
|
|
||||||
|
submit(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function submit(input) {
|
||||||
|
try {
|
||||||
|
// 这里应该调用后端API进行登录操作,以下仅为模拟示例
|
||||||
|
const wrapper = new FetchWrapper(window.location.origin);
|
||||||
|
const response = await wrapper.post('/api/users/_rdpd', {
|
||||||
|
input: input
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.data.code === '0000') {
|
||||||
|
document.querySelector('.message').textContent = `${response.data.data}`;
|
||||||
|
} else {
|
||||||
|
document.querySelector('.message').textContent =
|
||||||
|
'Login failed.登录失败...' + response.data.code;
|
||||||
|
window.location.href = '/users/login';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|