diff --git a/README.md b/README.md index 35f08f6..3b3a985 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,36 @@ ## 项目文件结构 - -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返回格式) +│root/ // 项目根目录 +├── server.js +├── utils.js // 各项通用接口(未来将会更改位置) +├── package.json +├── package-lock.json +├── .env +├── .gitignore +├── README.md +├── main/ // 主函数入口 +│ ├── web.js // 启动代码 +├── routes/ // 路由 +│ ├── index.js // 连接所有路由(其中api路由在controllers/api/index.js中) +│ ├── files.js // 文件相关路由 +│ └── users.js // 用户相关路由 +├── controllers/ // 控制器(主要中间件执行逻辑,用到services和utils) +│ ├── api/ // API控制器 +│ ├── files/ // 文件控制器 +│ └── users/ // 用户控制器 +├── services/ // 服务(主要服务逻辑,用到utils) +│ ├── auth.js // 身份验证服务(废案) +│ ├── files.js // 文件服务 +│ └── users.js // 用户服务 +├── utils/ // 工具库 +│ ├── db.js // 数据库定义 +│ ├── auth_utils.js // 身份验证服务 +│ ├── db_utils.js // 数据库接口 +│ └── db_api.js // better-sqlite官方文档截至于24-04-11 +└── static/ + ├── css/ // css + ├── js/ // js + ├── img/ // 图片 + └── index.html // 公开的静态首页 - 默认首页简易解释 static/index.html 文件需要调用 static/js/base_script.js 动态加载在 static/html/base/*.html 的共享资源且需要动态引入.data内的json数据以渲染出一张张cards diff --git a/VERSION.md b/VERSION.md new file mode 100644 index 0000000..3847ada --- /dev/null +++ b/VERSION.md @@ -0,0 +1,23 @@ +# web_zzyxyz Version History + +## [Version 1.0.0] - 2024-04-11 + +### History +0. 较于曾今版本重新开发,由于安全问题重新启用git。 +1. 修改了静态网页的代码,并使用booststrap框架和anchor模板。 +2. 对于曾经的express框架,对代码进行重构。 +3. 使用了dotenv模块,加载配置项。 +4. 修改部分文件架构尤其是各类隐藏文件夹。 + +## [Version 1.0.1-alpha] - 2023-04-13 + +### New Features +0. 大量修改文件结构,详情见README.md。 +1. 添加了登录,注册,管理员的功能。 +2. 使用better-sqlite3重构数据库接口。 +3. 重构代码结构使其路由更清晰,更易于使用cookie(未来将会重构cookie代码)。 +4. 添加VERSION.md文件,记录版本历史。 +5. 添加各种细节。 + +### Bugs Fixed +0. 修改default.env文件,使其初始化密码简易包括硬性写入代码中的密码(default.env文件就是代码内的各项参数的默认值)。 \ No newline at end of file diff --git a/controllers/api/index.js b/controllers/api/index.js new file mode 100644 index 0000000..bb9685b --- /dev/null +++ b/controllers/api/index.js @@ -0,0 +1,6 @@ +const router = require('express').Router(); + +router.use('/users', require('../users/api.js')); +router.use('/files', require('../files/api.js')); + +module.exports = router; \ No newline at end of file diff --git a/controllers/files/api.js b/controllers/files/api.js new file mode 100644 index 0000000..923fc23 --- /dev/null +++ b/controllers/files/api.js @@ -0,0 +1,6 @@ +const router = require('express').Router(); +const { getJsonFile } = require('./json'); + +router.get('/json/:filename', getJsonFile); + +module.exports = router; \ No newline at end of file diff --git a/routes/api/files.js b/controllers/files/files.js similarity index 100% rename from routes/api/files.js rename to controllers/files/files.js diff --git a/controllers/files/json.js b/controllers/files/json.js new file mode 100644 index 0000000..4a3e97c --- /dev/null +++ b/controllers/files/json.js @@ -0,0 +1,8 @@ +const { getJsonPath } = require('../../services/files'); + +function getJsonFile(req, res, next) { + const filename = req.params.filename; + res.sendFile(getJsonPath(filename)); +}; + +module.exports = { getJsonFile }; \ No newline at end of file diff --git a/controllers/users/api.js b/controllers/users/api.js new file mode 100644 index 0000000..205349c --- /dev/null +++ b/controllers/users/api.js @@ -0,0 +1,17 @@ +const router = require('express').Router(); +const {login, getJson, register } = require('./users'); + +router.post('/login', login); +router.post('/register', register); +router.get('/admin/:filename', getJson); + +//remote-data-processing-display +// router.post('/_rdpd', function(req, res, next) { +// express.response.send = function(data) { +// res.json({...utils.json.success, +// data: data +// }) +// } +// }); + +module.exports = router; \ No newline at end of file diff --git a/controllers/users/users.js b/controllers/users/users.js new file mode 100644 index 0000000..ee16b8c --- /dev/null +++ b/controllers/users/users.js @@ -0,0 +1,63 @@ +const {checkAdmin, checkUsers, registerUsers, getAllUsersToJson, VIEWS_USERS_PATH} = require('../../services/users'); +const path = require('path'); +const auth = require('../../utils/auth_utils'); +// +// const { Request, Response } = require('express'); + +function login(req, res, next) { + const data = req.body; + const isAdmin = req.query.admin !== undefined; // 检查查询参数 + if (isAdmin) { + res.json(checkAdmin(data.username, data.password)); + } else { + res.json(checkUsers(data.username, data.password)); + } +}; + +function getJson(req, res, next) { + const filename = req.params.filename; + if (filename === 'users_cards.json') { + return res.json(getAllUsersToJson(token)); + } +} + +function register(req, res, next) { + const data = req.body; + res.json(registerUsers(data.username, data.password)); +}; + +//------------------------------------// + +function getLogin(req, res, next) { + res.sendFile(path.join(VIEWS_USERS_PATH, 'login.html')); +} + +function getRoot(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(path.join(VIEWS_USERS_PATH, 'users.html')); + }) + } + res.redirect('/users/login'); +} + +function getAdmin(req, res, next) { + token = req.cookies.token; + if (typeof token === 'string') { + return auth.verifyToken(token, (err, data) => { + if (err) { + res.redirect('/users/login?admin'); + return; + } + res.sendFile(path.join(VIEWS_USERS_PATH, 'admin.html')); + }) + } + res.redirect('/users/login?admin'); +} + +module.exports = { getRoot, getAdmin, getLogin, login, register, getJson }; \ No newline at end of file diff --git a/default.env b/default.env index e59da06..6f92e8e 100644 --- a/default.env +++ b/default.env @@ -1,5 +1,5 @@ LISTEN_PORT=3000 -JWT_SECRET=YZ2Cgx82t4wEBJ7w8ibLNwYoQSoXuxLG +JWT_SECRET=123456 DATABASE_NAME=sqlite3.db -USER_LOGIN_NAME=admin -USER_LOGIN_PASSWORD=MoAHzbzmZ9HZT5SGrhu9XyD7wUtxJRbW \ No newline at end of file +ADMIN_LOGIN_NAME=admin +ADMIN_LOGIN_PASSWORD=admin \ No newline at end of file diff --git a/package.json b/package.json index 351b230..86b434f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "zzy_web", - "version": "1.0.0", + "name": "web_zzyxyz", + "version": "1.0.1", "description": "None", "main": "./main/web.js", "scripts": { @@ -10,11 +10,11 @@ "author": "zzy", "license": "ISC", "dependencies": { + "better-sqlite3": "^9.5.0", "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" + "nodemon": "^3.1.0" } } diff --git a/routes/api.js b/routes/api.js index 300dcf9..9d487bf 100644 --- a/routes/api.js +++ b/routes/api.js @@ -1,10 +1,7 @@ -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')); +router.use(require('../controllers/api/index')); module.exports = router; \ No newline at end of file diff --git a/routes/api/json.js b/routes/api/json.js deleted file mode 100644 index 5d912f3..0000000 --- a/routes/api/json.js +++ /dev/null @@ -1,9 +0,0 @@ -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; \ No newline at end of file diff --git a/routes/api/users.js b/routes/api/users.js deleted file mode 100644 index b75b74e..0000000 --- a/routes/api/users.js +++ /dev/null @@ -1,36 +0,0 @@ -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; \ No newline at end of file diff --git a/routes/db/db.js b/routes/db/db.js deleted file mode 100644 index 249b20c..0000000 --- a/routes/db/db.js +++ /dev/null @@ -1,7 +0,0 @@ -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; \ No newline at end of file diff --git a/routes/db/users.js b/routes/db/users.js deleted file mode 100644 index aac433f..0000000 --- a/routes/db/users.js +++ /dev/null @@ -1,19 +0,0 @@ -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(); \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 3f272e6..e82f124 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,7 +1,8 @@ -const utils = require('../utils.js'); const express = require('express'); const router = express.Router(); +router.use('/users', require('./users.js')); +router.use('/files', require('./files.js')); +router.use('/api', require('../controllers/api/index.js')); - -module.exports = router; +module.exports = router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index 9b06822..eafb9c8 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1,38 +1,11 @@ -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(); +const router = require('express').Router(); +const { getRoot, getAdmin, getLogin } = require('../controllers/users/users.js'); /* 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'); -}); +router.get('/', getRoot); +router.get('/admin', getAdmin); +router.get('/login', getLogin); -/** - * 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) { diff --git a/routes/utils/auth_utils.js b/routes/utils/auth_utils.js deleted file mode 100644 index dfc860d..0000000 --- a/routes/utils/auth_utils.js +++ /dev/null @@ -1,40 +0,0 @@ -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 -}; \ No newline at end of file diff --git a/routes/utils/db_utils.js b/routes/utils/db_utils.js deleted file mode 100644 index 11a4d71..0000000 --- a/routes/utils/db_utils.js +++ /dev/null @@ -1,138 +0,0 @@ -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; \ No newline at end of file diff --git a/server.js b/server.js index 0aa3aa5..bad27a4 100644 --- a/server.js +++ b/server.js @@ -11,9 +11,6 @@ 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) { diff --git a/services/files.js b/services/files.js new file mode 100644 index 0000000..f140190 --- /dev/null +++ b/services/files.js @@ -0,0 +1,15 @@ +const utils = require('../utils.js'); +const path = require('path'); +const fs = require('fs'); + +const DATA_JSON_PATH = path.resolve(utils.DATA_PATH, 'json'); + +if (!fs.existsSync(DATA_JSON_PATH)) { + fs.mkdirSync(DATA_JSON_PATH, { recursive: true }); +} + +function getJsonPath(filename) { + return path.resolve(DATA_JSON_PATH, filename); +} + +module.exports = { getJsonPath }; \ No newline at end of file diff --git a/services/users.js b/services/users.js new file mode 100644 index 0000000..e8a13eb --- /dev/null +++ b/services/users.js @@ -0,0 +1,102 @@ +const utils = require('../utils.js'); +const path = require('path'); +const auth = require('../utils/auth_utils'); +const db = require('../utils/db'); + +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'} +]); + +function getUser(username) { + let ret = db.selectData('users', null, `name='${username}'`); + let info = db.info; + return [ret, info]; +} + +function addUser(username, password) { + let ret = db.insertData('users', {name: username, password: password}); + if (ret == false) { + return [false, db.err.code]; + } + let info = db.info; + return [ret, info]; +} + +function getAllUsers() { + let ret = db.selectData('users'); + let info = db.info; + return [ret, info]; +} + +function checkAdmin(adminname, password) { + if (adminname === (utils.env.ADMIN_LOGIN_NAME || 'admin') && + password === (utils.env.ADMIN_LOGIN_PASSWORD || 'admin') ) { + const token = auth.generateToken({ + username: adminname, + id: -1 + }); + return {...utils.json.success, data: {token: token, href: '/users/admin'}}; + } else { + return {...utils.json.user_is_invalid}; + } +} + +function checkUsers(username, password) { + let [ret, info] = getUser(username); + if (!ret) { + return {...utils.json.user_is_invalid, data: info}; + } else if (!info || (Array.isArray(info) && info.length === 1)) { + info = info[0]; + if (info.password !== password) { + return {...utils.json.user_is_invalid, data: 'password error'}; + } else { + const token = auth.generateToken({ + username: username, + id: info.id + }) + return {...utils.json.success, data: {token: token, href: '/users'}}; + } + } else { + return {...utils.json.user_is_invalid, data: 'user name error'}; + } +} + + +function registerUsers(username, password) { + let [ret, info] = addUser(username, password); + if (!ret) { + if (info === 'SQLITE_CONSTRAINT_UNIQUE') { + info = 'user name is exist'; + } else { + info = 'unknown error'; + } + return {...utils.json.user_register_is_invalid, data: info}; + } else { + return {...utils.json.success}; + } +} + +function getAllUsersToJson(token) { + if (typeof token === 'string') { + return auth.verifyToken(token, (err) => { + if (err) { + return {...utils.json.token_is_invalid, data: info}; + } + let [ret, info] = getAllUsers(); + if (ret) { + return {...utils.json.success, data: info}; + } else { + return {...utils.json.error}; + } + }); + } else { + return {...utils.json.token_is_invalid, data: null}; + } +} + +const VIEWS_USERS_PATH = path.resolve(utils.VIEWS_PATH, 'users') + +module.exports = { checkAdmin, checkUsers, registerUsers, getAllUsersToJson, VIEWS_USERS_PATH }; \ No newline at end of file diff --git a/static/html/base/base_footer.html b/static/html/base/base_footer.html index a0b0402..1ecfb3f 100644 --- a/static/html/base/base_footer.html +++ b/static/html/base/base_footer.html @@ -15,10 +15,10 @@
联系作者:
- + wechat-link - + qq-link diff --git a/static/html/cs-study-link.html b/static/html/cs-study-link.html index 5e59602..3c037e8 100644 --- a/static/html/cs-study-link.html +++ b/static/html/cs-study-link.html @@ -33,7 +33,7 @@ + + + + + + + + \ No newline at end of file diff --git a/views/users/login.html b/views/users/login.html index 328a22e..e774457 100644 --- a/views/users/login.html +++ b/views/users/login.html @@ -35,14 +35,40 @@ - -

+ +
+ + +
- + @@ -51,45 +77,115 @@ + + \ No newline at end of file diff --git a/views/users/register.html b/views/users/register.html deleted file mode 100644 index e69de29..0000000