??從零開(kāi)始搭建站點(diǎn) - 前后端

某日,心血來(lái)潮,想將hexo搭建的站點(diǎn)移除,從零開(kāi)始搭建自己的站點(diǎn)。

涉及的內(nèi)容包含??:

  • 購(gòu)買服務(wù)器

  • 站點(diǎn)前后端分析

  • 服務(wù)器環(huán)境搭建

  • 發(fā)布前端項(xiàng)目

  • 發(fā)布后端項(xiàng)目

  • 關(guān)聯(lián)域名

站點(diǎn)提前體驗(yàn)地址:

戳地址:jimmyarea.com

購(gòu)買服務(wù)器

這里選擇了騰訊云的服務(wù)器,你也可以選擇阿里云之類的。

相關(guān)的云服務(wù)器的配置如下:

# 操作系統(tǒng)
CentOS 7.5 64位

# CPU
1核

# 內(nèi)存    
2GB

# 公網(wǎng)帶寬  
1Mbps

基礎(chǔ)配置(1核2GB)的機(jī)型適合有一定的訪問(wèn)量的網(wǎng)站;當(dāng)然,也可以選擇入門配置(1核1GB)的機(jī)型,它適用起步階段的個(gè)人網(wǎng)站。

PS:本次的搭建沒(méi)有特殊說(shuō)明均在騰訊云上完成?,并基于CentOS 7.5 64位

站點(diǎn)前后端分析

站點(diǎn)采用前后端分離思想,前后端獨(dú)立上線,方便開(kāi)發(fā)和維護(hù)等~

站點(diǎn)前端

目前的功能有

? 登陸

? 注冊(cè)

? 重置密碼

? 社交列表

? 個(gè)人簡(jiǎn)介

? 文章列表

? 留言功能

? 技能圖 COMING SOON

? 發(fā)表文章功能【管理端】COMING SOON

ETC

前端使用了業(yè)界成熟的UI框架 -- ANT DESING。

彈性布局,能兼容移動(dòng)端設(shè)備~

項(xiàng)目使用ANT DESIGN PRO V5進(jìn)行初始化,直接對(duì)請(qǐng)求的request進(jìn)行抽離改造,利于維護(hù)。

# src/utils/request.ts

// 默認(rèn)的框架請(qǐng)求
import { getStore } from '@/utils/storage';
import { extend } from 'umi-request';
import type { ResponseError } from 'umi-request';
import { notification } from 'antd';

// @see https://beta-pro.ant.design/docs/request-cn
const codeMessage = {
  200: '服務(wù)器成功返回請(qǐng)求的數(shù)據(jù)。',
  201: '新建或修改數(shù)據(jù)成功。',
  202: '一個(gè)請(qǐng)求已經(jīng)進(jìn)入后臺(tái)排隊(duì)(異步任務(wù))。',
  204: '刪除數(shù)據(jù)成功。',
  400: '發(fā)出的請(qǐng)求有錯(cuò)誤,服務(wù)器沒(méi)有進(jìn)行新建或修改數(shù)據(jù)的操作。',
  401: '用戶沒(méi)有權(quán)限(令牌、郵箱、密碼錯(cuò)誤)。',
  403: '用戶得到授權(quán),但是訪問(wèn)是被禁止的。',
  404: '發(fā)出的請(qǐng)求針對(duì)的是不存在的記錄,服務(wù)器沒(méi)有進(jìn)行操作。',
  405: '請(qǐng)求方法不被允許。',
  406: '請(qǐng)求的格式不可得。',
  410: '請(qǐng)求的資源被永久刪除,且不會(huì)再得到的。',
  422: '當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),發(fā)生一個(gè)驗(yàn)證錯(cuò)誤。',
  500: '服務(wù)器發(fā)生錯(cuò)誤,請(qǐng)檢查服務(wù)器。',
  502: '網(wǎng)關(guān)錯(cuò)誤。',
  503: '服務(wù)不可用,服務(wù)器暫時(shí)過(guò)載或維護(hù)。',
  504: '網(wǎng)關(guān)超時(shí)。',
};

/**
 * 異常處理程序
 */
const errorHandler = (error: ResponseError) => {
  const { response, data } = error;
  if (response && response.status) {
    const errorText = data.msg || codeMessage[response.status] || response.statusText;
    // const { status, url } = response;
    const { status } = response;

    notification.error({
      message: `請(qǐng)求錯(cuò)誤 ${status}`,
      description: errorText,
    });
  }

  if (!response) {
    notification.error({
      description: '您的網(wǎng)絡(luò)發(fā)生異常,無(wú)法連接服務(wù)器',
      message: '網(wǎng)絡(luò)異常',
    });
  }
  throw error;
};

/**
 * 配置request請(qǐng)求時(shí)的默認(rèn)參數(shù)
 */
const request = extend({
  errorHandler, // 默認(rèn)錯(cuò)誤處理
  credentials: 'include', // 默認(rèn)請(qǐng)求是否帶上cookie
  timeout: 5000,
  prefix: '',
});

// 請(qǐng)求攔截器
request.interceptors.request.use((url, options) => {
  const token = getStore('token') || '';
  const theOptions = options || {};
  if (token) {
    theOptions.headers = {
      // 處理header中的token
      'Content-Type': 'application/json',
      Accept: 'application/json',
      Authorization: `Bearer ${token}`,
    };
  }
  return {
    url,
    options: { ...theOptions },
  };
});

request.interceptors.response.use(async (response: any) => response);

export default request;

對(duì)授權(quán)登陸進(jìn)行移除等,形成一個(gè)純凈的,符合設(shè)計(jì)的jimmyarea站點(diǎn)的功能需求。

前端開(kāi)發(fā)完,運(yùn)行腳手架自帶的命令行yarn run build 或者 npm run build 生成一個(gè)dist文件。這個(gè)文件就是你要發(fā)布到服務(wù)器上的文件了~

當(dāng)然,完成前端的開(kāi)發(fā),如果你對(duì)性能有所要求,可以進(jìn)行相關(guān)的優(yōu)化。這里,我對(duì)moment.js進(jìn)行了優(yōu)化,剔除不必要的local,壓縮了相關(guān)的圖片等等...具體的你可以通過(guò)運(yùn)行yarn run analyze來(lái)觀察優(yōu)化

Nginx上開(kāi)啟了gzip 別忘了哦

站點(diǎn)后端

后端使用的是KOA2框架去開(kāi)發(fā),本地使用nodemon運(yùn)行服務(wù)調(diào)試,線上采用pm2來(lái)啟動(dòng)服務(wù)。

目前的接口有:

? 登陸

? 注冊(cè)

? 加密鹽

? 重置密碼

? 社交列表

? 文章列表

? 留言功能

? 郵件發(fā)送

? 發(fā)表文章功能【管理端】COMING SOON

ETC

我們從零開(kāi)始搭建一個(gè)NODE的服務(wù)端項(xiàng)目,目錄結(jié)構(gòu)如下:

 backend
 ├── config 
 |    ├── default.json 
 |    ├── production.json # 生產(chǎn)環(huán)境的配置
 |    └── test.json # 測(cè)試環(huán)境配置
 ├── src # 主要代碼存放地方
 |    ├── controllers 
 |    |    ├── user.js
 |    |    └── ...
 |    ├── middlewares 
 |    |    ├── auth.js
 |    |    └── ... 
 |    ├── models 
 |    |    └── mongo
 |    |        ├── schema
 |    |        |    ├── User.js
 |    |        |    └── ... 
 |    |        └── index.js
 |    ├── router
 |    |    └── index.js
 |    ├── service # 第三方服務(wù)
 |    |    └── index.js
 |    ├── utils
 |    |    ├── bcrypt.js
 |    |    └── ...
 |    ├── app.js
 |    └── index.js # 入口文件
 ├── .babelrc # 處理babel
 ├── apidoc.json # 生成接口文檔的配置 
 ├── package.json # 依賴
 ├── pm2.json # pm2 線上運(yùn)行的配置
 └── README.md 項(xiàng)目說(shuō)明

其中, package.json的內(nèi)容如下:

 {
  "version": "1.0.0",
  "author": "Jimmy",
  "scripts": {
    "start": "NODE_ENV=development node src/index.js",
    "dev": "NODE_ENV=development nodemon src/index.js",
    "nodepro": "yarn run build && NODE_ENV=production node app/index.js",
    "pm2pro": "yarn run build && NODE_ENV=production pm2 start pm2.json",
    "clean": "rm -rf app/",
    "compile": "babel src/ --out-dir app/ --retain-lines",
    "build": "yarn run clean && yarn run compile",
    "docs": "apidoc -i src/controllers -o apidoc/"
  },
  "dependencies": {
    "bcrypt": "^5.0.1",
    "bluebird": "^3.7.2",
    "config": "^3.3.6",
    "jsonwebtoken": "^8.5.1",
    "koa": "^2.13.1",
    "koa-bodyparser": "^4.3.0",
    "koa-convert": "^2.0.0",
    "koa-json": "^2.0.2",
    "koa-jwt": "^4.0.1",
    "koa-logger": "^3.2.1",
    "koa-router": "^10.0.0",
    "koa-session": "^6.2.0",
    "mongodb": "^3.6.7",
    "mongoose": "^5.12.7",
    "nodemailer": "^6.6.0",
    "uuid": "^8.3.2",
    "xss": "^1.0.8"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^8.2.3",
    "babel-plugin-add-module-exports": "^0.1.4",
    "babel-plugin-transform-runtime": "^6.8.0",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2015-loose": "^7.0.0",
    "babel-preset-stage-3": "^6.5.0",
    "babel-register": "^6.26.0",
    "nodemon": "^2.0.7"
  }
}

當(dāng)然,你也可以使用成熟的框架EGGJS

數(shù)據(jù)庫(kù)是MongoDB,對(duì)象模型工具Mongoose

下面是一個(gè)連接數(shù)據(jù)庫(kù)查詢文章列表的接口:

# src/models/mongo/schema/Article.js
 
const mongoose = require('mongoose')
const Schema = mongoose.Schema

// 定義文章字段
let ArticleSchema = new Schema({
  title: String, //  文章標(biāo)題
  url: String, // 文章url
  count: String, // 文章閱讀量
  thumb: String, // 文章點(diǎn)贊量
  meta: {
    createAt: {
      type: Date,
      default: Date.now()
    },
    updateAt: {
      type: Date,
      default: Date.now()
    }
  }
})

mongoose.model('Article', ArticleSchema)
# src/router/index.js
 
// 文章部分
router.get('/public/article', Article.getArticleData)
# src/controllers/article.js

let mongoose = require('mongoose')
let Article = mongoose.model('Article')

/**
 * @apiGroup Article
 * @api {get} /public/article 查看社交信息
 * @apiVersion 1.0.0
 * 
 *
 * @apiSuccessExample {json} 返回成功
 *    HTTP/1.1 200
 *    {
 *        "code": 0,
 *        "msg": "ok",
 *        "data": [{
 *            "key": "value"
 *        }]
 *    }
 * 
 * @apiSampleRequest off
 */
exports.getArticleData = async (ctx, next) => {
  let data = await Article.find({})
  ctx.body = {
    results: data,
    current: 1
  }
}

上面示例,線上的接口地址是https://jimmyarea.com/api/public/article ,感興趣的話可以點(diǎn)擊此鏈接感受下。

嗯~到這里,我們服務(wù)器也買了,本地上前端項(xiàng)目和后端項(xiàng)目都跑起來(lái)了,那么,我們可以考慮將我們的項(xiàng)目上線了。

服務(wù)器環(huán)境搭建

在項(xiàng)目上線前,我們得做好準(zhǔn)備,這涉及Nginx代理,數(shù)據(jù)庫(kù),后端node等環(huán)境。

安裝數(shù)據(jù)庫(kù)MongoDB

基于成本的考慮,我們將數(shù)據(jù)庫(kù)和應(yīng)用都放在同一臺(tái)云服務(wù)器上面。

如果是公司企業(yè)的話,建議購(gòu)買云數(shù)據(jù)庫(kù)等

演示的是:服務(wù)器操作系統(tǒng)為 CentOS 7.5 64位

mongodb的安裝教程: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/

當(dāng)然,你也可以在文章Linux Centos 7安裝MongoDB(簡(jiǎn)單!詳細(xì)!) 按照指引完成數(shù)據(jù)庫(kù)的安裝。

安裝完數(shù)據(jù)庫(kù)之后,建議開(kāi)啟權(quán)限驗(yàn)證及設(shè)置用戶名密碼,不然什么人都可以訪問(wèn)你的數(shù)據(jù)庫(kù),那很是尷尬??

比如:

db.createUser(
    {
    user: "simpleUser",
    pwd: “123456”,
    roles: [“readWrite”,”dbAdmin”,”userAdmin”]
    }
)

安裝Nginx

基于centosyum源操作

# 安裝nginx 卸載[yum -y remove nginx]
yum -y install nginx

# 查看版本
nginx -v

# 啟動(dòng)nginx
systemctl start nginx.service

# 訪問(wèn) 默認(rèn)端口是80
your_ip:80

# 配置
cd /etc/nginx/conf.d
... 有待補(bǔ)充

# 以下是nginx常用命令
# 啟動(dòng)nginx服務(wù)
systemctl start nginx.service
# 停止nginx服務(wù)
systemctl stop nginx.service
# 重啟nginx服務(wù)
systemctl restart nginx.service
# 重新讀取nginx配置(這個(gè)最常用, 不用停止nginx服務(wù)就能使修改的配置生效)
systemctl reload nginx.service
  • 相關(guān)啟動(dòng)命令也可如下
nginx -t                        #測(cè)試配置文件是否有語(yǔ)法錯(cuò)誤
nginx -s reopen                 #重啟Nginx
nginx -s reload                  #重新加載Nginx配置文件,然后以優(yōu)雅的方式重啟Nginx
nginx -s stop                   #強(qiáng)制停止Nginx服務(wù)
nginx -s quit                       #優(yōu)雅地停止Nginx服務(wù)(即處理完所有請(qǐng)求后再停止服務(wù))
nginx -c [配置文件路徑]       #為 Nginx 指定配置文件

配置Nginx

查看Nginx配置文件nginx.conf

vi /etc/nginx/nginx.conf

檢查是否有如下語(yǔ)句:

include /etc/nginx/conf.d/*.conf

如果存在,說(shuō)明nginx的配置文件均設(shè)置在conf.d文件夾下,我們進(jìn)入到conf.d文件夾

cd /etc/nginx/conf.d/ 

在 conf.d 目錄下新建一個(gè)站點(diǎn)的配置文件,例如:test.com.conf:

vi test.com.conf 

配置內(nèi)容這樣設(shè)置(按Insert鍵進(jìn)入編輯模式):

server {
         # 監(jiān)聽(tīng)80端口
         listen 80;
         # 服務(wù)器名稱(隨意)
         server_name www.test.com test.com; 

         # 路由匹配
         location / {
                 # 文件目錄,即你存放靜態(tài)資源的地方
                 root /usr/share/nginx/test.com;
                 # 設(shè)置首頁(yè)為根目錄下index.html文件
                 index index.html;         
         }
} 

寫好后,按ESC鍵退出編輯模式,輸入 :wq 保存并退出編輯

:wq 

檢查配置文件是否有誤

nginx -t

重啟Nginx服務(wù)

systemctl start nginx 

或者

nginx -s reload

接著我們將靜態(tài)資源存放在定義的 /usr/share/nginx/test.com 目錄下。

這里我們僅做測(cè)試,于是在該目錄下創(chuàng)建一個(gè)測(cè)試網(wǎng)頁(yè)index.html文件,你可以嘗試下,看瀏覽器頁(yè)面地址有沒(méi)有訪問(wèn)到這個(gè)index.html文件。

Nginx處理CORS

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 

發(fā)布前端項(xiàng)目

第一種方式

將前端項(xiàng)目打包,生成dist文件夾,之后放在/usr/share/nginx指定的目錄中即可。

第一種方式比較繁瑣,我們用第二種方式。

第二種方式

方式二:直接拉取代碼倉(cāng)庫(kù),打包即可

現(xiàn)在,我們用github為進(jìn)行項(xiàng)目托管,那么我們可以在自己都買的服務(wù)環(huán)境中將GitGithub進(jìn)行SSH連接。

首先在服務(wù)器上面安裝版本控制系統(tǒng)GIT。

yum install git

之后配置git與github關(guān)聯(lián)

git config --global user.name "your_name"
git config --global user.email "your_email"

然后用ssh生成公鑰

ssh-keygen -t rsa -C "your_email"

之后一直回車即可(如果你要更改文件命名,可按提示修改)。

最后,將公鑰添加到github中

  1. .ssh文件夾中找到id_rsa.pub這個(gè)文件,復(fù)制里面的所有內(nèi)容。

  2. 登陸github賬號(hào),點(diǎn)擊頭像旁的小三角展開(kāi),點(diǎn)擊Settings - SSH and GPG keys - New SSH key,在Title中取一個(gè)名字(任意),Key中粘貼你剛剛復(fù)制的內(nèi)容。然后點(diǎn)擊Add SSH key即可。

詳細(xì)內(nèi)容可以直接戳官方教程使用 SSH 連接到 GitHub

完成之后,我們可以在/usr/share/nginx下指定的目錄進(jìn)行操作:

# clone 
git clone your_github_repository_url

# 安裝依賴
yarn install || npm install

# 打包
yarn run build 

后期如果有新文件加入,重新拉取打包即可。

發(fā)布后端項(xiàng)目

這里是NODE項(xiàng)目,那么我們得先準(zhǔn)備自己相關(guān)的NODE環(huán)境。

安裝包文件nodejs

# 使用EPEL安裝
yum install epel-release

# 安裝node js
sudo yum install nodejs

# 查看安裝版本
node -v
# v6.13.3

# 版本過(guò)低,升級(jí)到最新穩(wěn)定版
# 安裝n,n是nodejs管理工具
npm install -g n

# 安裝nodejs最新版本
n latest
# 穩(wěn)定版 n node的穩(wěn)定版版本號(hào)

# 之后切換nodejs版本即可
n

上面是一種安裝方式,讀者可以嘗試其他的安裝方式,這里不展開(kāi)了

其他安裝

  • 安裝yarn包管理器,比npm性能優(yōu)越 npm install -g yarn

  • 安裝pm2的node進(jìn)程管理工具 npm install pm2 -gyarn global add pm2 pm2 讓 Nodejs 服務(wù)常駐

配置nginx反向代理

比如:

upstream api {
      server 127.0.0.1:6000;
      keepalive 2000;
}

server {
    location /api {
            proxy_pass http://api;
      }
}

發(fā)布

發(fā)布的操作也是通過(guò)git拉區(qū)在github上的后端項(xiàng)目,然后運(yùn)行本項(xiàng)目自定義的yarn run pm2pro啟動(dòng)項(xiàng)目即可。

你可以通過(guò)命令行pm2 list查看服務(wù)運(yùn)行的情況。

pm2 list
id name mode ? status cpu memory
0 jimmy-server cluster 880 online 0% 56.5mb

關(guān)聯(lián)域名

我們成功發(fā)布了前后端項(xiàng)目,配置好nginx代理之后,項(xiàng)目就能夠通過(guò) IP 地址進(jìn)行訪問(wèn)了,本項(xiàng)目的服務(wù)器的ip地址是111.230.192.27。

為了用戶友好體驗(yàn),我們是不是考慮買個(gè)域名了呢?

這里我購(gòu)買的域名是jimmyarea.com, 還順帶買了證書(shū) - 用于開(kāi)啟https

購(gòu)買域名

這里使用的是騰訊云提供的服務(wù)。

購(gòu)買完域名后,根據(jù)指引去實(shí)名進(jìn)行認(rèn)證。運(yùn)營(yíng)商有很詳細(xì)的操作指南,畢竟要用戶,這里不詳細(xì)說(shuō)明。

域名解析

這里采用的是騰訊云服務(wù)器和騰訊域名,你可以參考這個(gè) - 快速添加域名解析 來(lái)完成?

Nginx并配置SSL證書(shū)

  • https證書(shū)申請(qǐng)

  • 將證書(shū)上傳到服務(wù)器的指定目錄

# 用命令行上傳
scp -r local_dir username@servername:remote_dir

# 例子
scp -r test root@192.168.0.101:/var/www/ 把當(dāng)前目錄下的test目錄上傳到服務(wù)器的/var/www/ 目錄

具體可以參考官方- Nginx 服務(wù)器 SSL 證書(shū)安裝部署 進(jìn)行操作,這里不贅述。

  • 域名備案,以騰訊為例,域名申請(qǐng)n個(gè)自然日后可以提交申請(qǐng)備案了。域名不備案,通過(guò)域名訪問(wèn)不穩(wěn)定~囧 ??

親測(cè)域名備案前后需要兩個(gè)星期 審核-修改-審核

后話

至此,可以美滋滋地對(duì)項(xiàng)目進(jìn)行迭代開(kāi)發(fā)--上線。當(dāng)然,如果你想更加工程化,方便點(diǎn),你可以引入jenkins啥的 從零開(kāi)始搭建JENKINS+GITHUB持續(xù)集成環(huán)境【多圖】,docker啥的。

成品地址: jimmyarea.com,感興趣可以體驗(yàn)下。

倉(cāng)庫(kù)代碼地址不公開(kāi),畢竟里面涉及到數(shù)據(jù)庫(kù)賬號(hào)密碼等個(gè)人信息,我全部設(shè)置為privacy了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容