This article is not about bringing a project to life, but how to automate it.
A requirements management and release system is developed.
With this system, you can create requirements, create release plans, create branches, deploy to test, deploy to production, go live, merge code, and so on.
1. Functional design
The 9.9 yuan Alibaba Cloud server is really slow, but it’s still enough to finish the project off. Use three directories to simulate different environments.
directory | store |
---|---|
project | Store all items, such as the system of the front and back end code. |
pre-dir | Pre-release environments, of course, are for testing. |
pro-dir | Production environment, testing is ok, deployment is online. |
A picture is worth a thousand words.
Second, the system page
My task
When a new requirement is received, you can create a new requirement and create a development branch.
Release the queue
Once development is complete, you can go to the release queue and deploy to the pre-release environment for testing. The test can access the test code by specifying a Cookie. Finally, deploy online.
Project information
2. Technology stack
Front-end technology stack
Vue + elementUI, the specific code on Github, interested can see and click the star ha ~✨
Server side technology stack
Very common node.js (Koa2) + Mysql + Redis + Pm2. The specific code is available on Github, if you are interested, you can check it out and click “Star” ~✨
3. Configure Redis and Session
// utils/Store.js
const Redis = require("ioredis");
const { Store } = require("koa-session2");
class RedisStore extends Store {
constructor() {
super(a);this.redis = new Redis();
}
async get(sid, ctx) {
let data = await this.redis.get(`SESSION:${sid}`);
return JSON.parse(data);
}
async set(session, { sid = this.getID(24), maxAge = 1000 * 60 * 60 } = {}, ctx) {
try {
console.log(`SESSION:${sid}`);
// Use redis set EX to automatically drop expired sessions
await this.redis.set(`SESSION:${sid}`.JSON.stringify(session), 'EX', maxAge / 1000);
} catch (e) {}
return sid;
}
async destroy(sid, ctx) {
return await this.redis.del(`SESSION:${sid}`); }}module.exports = RedisStore;
Copy the code
// Import file
const session = require("koa-session2");
const Store = require("./utils/Store.js");
/ / the session configuration
app.use(session({
store: new Store(),
key: "SESSIONID",}));Copy the code
4. Configure Router
To make the Router look more elegant, also through middleware
// 1. Middleware configuration file
const routers = require('.. /routers');
module.exports = (app) = > {
app.use(routers());
}
// 2, index.js entry file
const middleware = require('./middleware');
middleware(app);
// 3. Registration documents of routers
const Router = require('koa-router');
const router = new Router();
const koaCompose = require('koa-compose');
// Interface entry
const {insertDemand} = require('.. /controllers/demand/insertDemand');
const {deleteDemand} = require('.. /controllers/demand/deleteDemandByDid');
const {updateDemand} = require('.. /controllers/demand/updateDemandByDid');
/ / add prefix
router.prefix('/api');
module.exports = (a)= > {
// New demand
router.get('/insertDemand', insertDemand);
// Delete the requirement
router.get('/deleteDemand', deleteDemand);
return koaCompose([router.routes(), router.allowedMethods()]);
}
Copy the code
5. Nginx configuration
The biggest headache is the nginx configuration, because I am not very familiar with it, it is always trial and error, stomping. But at last it worked!
Front-end projects provide services through Nignx, and Node services are forwarded through Nginx, primarily to validate various environments.
If you do not set cookies, the default access is to the online environment, setting cookies will go to the pre-release test environment, used for testing.
$proxy_node = $proxy_node = $proxy_node
map $cookie_TEST $proxy_node {
default "";
"1" "1";
"2" "2";
"3" "3";
}
Release management system front-end setup
server {
listen 80;
server_name test.xue.com;
if ($proxy_node = ' ') {set $dollar "/data/pro-dir/dandelion/dist/";
}
if ($proxy_node = "1") {
set $dollar "/data/pre-dir/dandelion/dist/";
}
location / {
root $dollar;
index index.html;
try_files $uri $uri/ /index.html; }}Release management system backend Settings
Reverse proxy to node services
server {
listen 80;
server_name m.xue.com;
if ($proxy_node = ' ') {set $dollar "/data/pro-dir/study-demo/";
}
if ($proxy_node = "2") {
set $dollar "/data/pre-dir/study-demo/";
}
location / {
root $dollar; index index.html; }}# Demo project front-end setup
server {
listen 80;
server_name api.xue.com;
location / {
if ($proxy_node = "") {
set $from 3001;
proxy_pass http://47.107.188.55:3001;
}
if ($proxy_node = "3") {
set $from3002; Proxy_pass http://47.107.188.55:3002; }}}Copy the code
Vi. Some middleware
Common HTTP Settings
Solve cross-domain, OPTIONS requests, carrying Cookie credentials and other issues.
module.exports = () => {
return async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin'.'http://test.xue.com');
ctx.set('Access-Control-Allow-Credentials'.true);
ctx.set('Access-Control-Allow-Headers'.'content-type');
ctx.set('Access-Control-Allow-Methods'.'OPTIONS, GET, HEAD, PUT, POST, DELETE, PATCH'); // The value of this response header is to set a relative time from the moment the non-simple request passes the validation on the server side. When the elapsed milliseconds are less than access-control-max-age, there is no need to precheck and the request can be sent directly once. ctx.set('Access-Control-Max-Age', 3600 * 24);
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else{ await next(); }}}Copy the code
The login
This system belongs to the mandatory login, login unified processing.
const Store = require(".. /.. /utils/Store");
const redis = new Store();
module.exports = () => {
returnAsync (CTX, next) => {// whitelistif (ctx.request.url === '/api/login') {
return await next();
}
const SESSIONID = ctx.cookies.get('SESSIONID');
if(! SESSIONID) {return ctx.body = {
mes: 'No SESSIONID~',
data: ' ',
err_code: 1,
success: false}; } const redisData = await redis.get(SESSIONID);if(! redisData) {return ctx.body = {
mes: 'SESSIONID has expired ~',
data: ' ',
err_code: 1,
success: false}; }if(redisData && redisdata.uid) {console.log(' logged in, user UID is${redisData.uid}`); await next(); }}}Copy the code
Operate shell scripts
For example, create a project branch
let path = ' '; // Project path
// Create a branch
const branch_name = `branch_The ${new Date().getTime()}`;
cp.execSync(`/data/dandelion-server/shell/createBranch.sh ${path} ${branch_name}`);
Copy the code
#! /bin/bash
cd The $1
git pull origin master
git checkout -b $2
git push --set-upstream origin $2
Copy the code
Connect to the database
Config.js Configuration file
let dbConf = null;
const DEV = {
database: 'dandelion'./ / database
user: 'root'./ / user
password: '123456'./ / password
port: '3306'./ / port
host: '127.0.0.1' // Service IP address
}
const PRO = {
database: 'dandelion'./ / database
user: 'root'./ / user
password: '123456'./ / password
port: '3306'./ / port
host: 'xx.xx.xx.xx' // Service IP address
}
dbConf = PRO; // This can be judged to distinguish the development environment
module.exports = dbConf;
Copy the code
Database connection file
const mysql = require('mysql');
const dbConf = require('. /.. /config/dbConf');
const pool = mysql.createPool({
host: dbConf.host,
user: dbConf.user,
password: dbConf.password,
database: dbConf.database,
})
let query = function( sql, values ) {
return new Promise(( resolve, reject ) => {
pool.getConnection(function(err, connection) {
if (err) {
reject( err )
} else {
connection.query(sql, values, ( err, rows) => {
if ( err ) {
reject( err )
} else {
resolve( rows )
}
connection.release()
})
}
})
})
}
module.exports = {
query,
}
Copy the code
~ can be called in the Model layer
const {query} = require('.. /common/mysql');
class UserModel {
constructor() {} /** * @description: create a branch based on pid and ID * @param {PID} Project ID * @param {did} Requirement ID * @param {branch_name} branch name * @return*/ async insertBranchInfo(sqlParams) {const SQL ='insert branch_info (pid, bid, branch_name, pub_time) values(? ,? ,? ,?) ';
console.log(sql)
let data = await query(sql, sqlParams, (err, result) => {
return result;
});
returndata; }}Copy the code
Nine, the domain name
Modify hosts locally without buying a domain name (you can use tools directly)
47.107.188.xx indicates the IP address of the server
47.107.188. Xx test.xue.com 47.107.188. Xx api.xue.com 47.107.188Copy the code
conclusion
It was my first time to build a complete project, from front end to back end.
Especially backend, as a front-end small white, from learning how to use the server, to the Linux/Vim/Shell/Nignx/Pm2 / Redis/Session/Mysql/Koa2. Instead of directly looking at other projects as before, I studied step by step. Although I barely scratched the surface, I felt that my knowledge system was much richer. I also learned a lot about continuous integration. Of course, the small project I did was relatively simple. If YOU like it, please give me a “like”.
Detailed use in the front-end project, back-end project, interested in can see and click the star ha ~✨