Skip to content

Commit

Permalink
v0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
ML-Jason committed Dec 2, 2022
1 parent 86ce0e9 commit ea572c8
Show file tree
Hide file tree
Showing 12 changed files with 5,409 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.git
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# editorconfig.org
root = true

[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
apidoc
!.eslintrc.js
21 changes: 21 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
root: true,
env: {
node: true,
},
parserOptions: {
parser: 'babel-eslint',
},
extends: ['airbnb-base'],
settings: {},
plugins: [],
rules: {
// 很多時候會用底線開頭,尤其是MongoDB的_id衝突,關掉
'no-underscore-dangle': 0,
// max-len很煩,雖然取消,但還是盡量遵守比較好
'max-len': 0,
// 有時真的無法都用camelcase,關掉
camelcase: 0,
},
globals: {},
};
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# dependencies
node_modules

# logs
npm-debug.log

Thumbs.db
.DS_Store

log
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"file.eol":"\n",
"editor.tabSize": 2,
"eslint.workingDirectories": [
"./"
],
"javascript.validate.enable": false,
"typescript.validate.enable": false,
"typescript.format.enable": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint":true
}
}
138 changes: 137 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,137 @@
# mahudas-sequelize-mysql
# @mahudas/sequelize-mysql
Sequelize + mysql2 plugin for Mahudas.

## Dependencies
+ mahudas^0.0.5
+ mysql2^2.3.3
+ sequelize^6.26.0

## 使用
### Standalone
```console
npm i
npm run mahudas
```

### As a plugin
如同一般的plugin,透過npm安裝之後,在Application的plugin.env.js裡設定啟用。

```console
npm i @mahudas/sequelize-mysql -s
```
```js
// config/plugin.deafult.js
module.exports = {
mysql: {
enable: true,
package: '@mahudas/sequelize-mysql',
},
}
```

## 設定
```js
// config/config.default.js
module.exports = {
mysql: {
modelDir: 'app/model',
options: {
dialect: 'mysql',
database: '',
timezone: '+08:00',
host: 'mysql ip',
username: '',
password: '',
pool: {
max: 5,
min: 1,
acquire: 30000,
idle: 60000,
},
query: {
// raw設為false, 回傳的資料不會有多餘的sequelize資訊
raw: true,
// 設定type 為 QueryTypes.SELECT,使用sequlize.query所查詢的資料就不會帶有meta
type: 'SELECT',
},
// logging設為false,就不會把每個SQL都列印到console上
logging: false,
},
},
}
```
參數 | 說明
--- | ---
modelDir | model放置的目錄,系統會去掃描這個目錄之下的所有檔案與子目錄,若是檔案或目錄開頭是dash(-),則會被忽略不掃描
options | sequelize的參數,可直接參考sequelize文件

### 多資料庫連線
@mahudas/sequelize-mysql 也可以允許多個DB的連線,只要把config裡的mysql改成陣列就可以:
(需注意兩個資料庫的modelDir通常會是不同路徑)
```js
// config/config.default.js
module.exports = {
mysql: [
// 第一個DB
{
modelDir: 'app/model/first',
options: {...},
},
// 第二個DB
{
modelDir: 'app/model/second',
options: {...},
}
]
}
```

## model的寫法
接收conn與DataTypes兩個參數,回傳model。
```js
module.exports = (conn, DataTypes) => {
const Users = conn.define('users',
{
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
}
}, {
// Other model options go here
});
return Users;
}
```

## 取用sequelzie
@mahudas/sequelize-mysql 針對 application 與 context 進行了擴充,將 sequelize 放到了 app/ctx 裡,因此可以透過 app/ctx 取用:
```js
const { sequelize } = app;
//
const { sequelize } = ctx;

console.log(sequelize.DataTypes.STRING);
```

## 取用model
@mahudas/sequelize-mysql 針對 application 與 context 進行了擴充,可以透過app.mysql 或 ctx.mysql 進行操作。
model被載入後,會被儲存在mysql.models裡,命名則是與model的命名一致,例如有個model是:
```js
const Users = conn.define('users', ...);
```
則可以用以下方式取得model:
```js
const Users = app.mysql.models.users;
await Users.findOne({});
```

### 多個資料庫連線的情況
如果有多個資料庫連線,app.mysql就會變成一個陣列,對應到config裡的資料庫連線。
因此,如果要存取第二個資料庫的model:
```js
const Users = app.mysql[1].models.users;
await Users.findOne({});
```
50 changes: 50 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint no-param-reassign:0 */
const sequelize = require('sequelize');
const modelLoader = require('./lib/model_loader');

module.exports = (app) => {
app.on('configDidLoad', () => {
let dbConfigs = app.config.mysql;
// 如果dbConfigs不是陣列,一樣把它轉換成長度為1的陣列
if (!Array.isArray(dbConfigs)) dbConfigs = [dbConfigs];
app.mysql = [];
// 將sequelize放置到app/ctx裡,以供後續開發者使用
app.sequelize = sequelize;
app.context.sequelize = sequelize;

dbConfigs.forEach((itm, index) => {
const conn = new sequelize.Sequelize(itm.options);
if (dbConfigs.length === 1) {
app.mysql = conn;
} else {
app.mysql[index] = conn;
}

// 進行連線測試
conn.authenticate()
.then(() => {
console.log('\x1b[32m%s\x1b[0m', `[mysql] connection-${index} connected.`);
})
.catch((error) => {
console.error('\x1b[34m%s\x1b[0m', `[mysql] connection-${index} connect error`, error);
});

// 讀取該設定歸屬的model
if (itm.modelDir) modelLoader({ conn, modelPath: itm.modelDir });
});

// 擴充context,讓ctx也可以直接存取mysql
app.context.mysql = app.mysql;
});

app.on('beforeClose', async () => {
// 切斷所有DB連線
// 判斷app.mysql是否為單一連線,否的話就一一切斷
if (!Array.isArray(app.mysql)) {
await app.mysql.close();
} else {
const conns = app.mysql.map((v) => v.close());
await Promise.all(conns);
}
});
};
27 changes: 27 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = {
mysql: {
modelDir: 'app/model',
options: {
dialect: 'mysql',
database: '',
timezone: '+08:00',
host: '',
username: '',
password: '',
pool: {
max: 30,
min: 1,
acquire: 30000,
idle: 60000,
},
query: {
// raw設為false, 回傳的資料不會有多餘的sequelize資訊
raw: true,
// 設定type 為 QueryTypes.SELECT,使用sequlize.query所查詢的資料就不會帶有meta
type: 'SELECT',
},
// logging設為false,就不會把每個SQL都列印到console上
logging: false,
},
},
};
41 changes: 41 additions & 0 deletions lib/model_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint global-require:0, import/no-dynamic-require:0 */
const { DataTypes } = require('sequelize');
const fs = require('fs');
const path = require('path');

const recursiveSearch = ({ conn, dirPath }) => {
let modules = {};
if (!fs.existsSync(dirPath)) return modules;
const directX = fs.readdirSync(dirPath);
directX.forEach((pName) => {
const currentPath = path.join(dirPath, pName);
const targetFS = fs.statSync(currentPath);

// 如果命名是"-"開頭,就忽略
if (pName.indexOf('-') === 0) return;

if (targetFS.isDirectory()) {
const subModules = recursiveSearch(currentPath);
modules = { ...modules, ...subModules };
} else {
// 如果不是.js檔,就跳過
if (!/\.js$/.test(pName)) return;
const mName = pName.replace(/\.js$/, '')
.replace(/^./, pName[0].toUpperCase());
modules[mName] = require(currentPath)(conn, DataTypes);
}
});
return modules;
};

const load = ({ conn, modelPath }) => {
// 定義要掃描的目錄名稱,依序掃描並載入
let scanPath = path.resolve('app/model');
if (modelPath) {
scanPath = path.resolve(modelPath);
}
const modules = recursiveSearch({ conn, dirPath: scanPath });
return modules;
};

module.exports = load;
Loading

0 comments on commit ea572c8

Please sign in to comment.