Srako's Blog

Recording Daily Life


  • Home

  • Archives

egg.js

Posted on 2020-10-28 | In Development

NodeJS初探 - egg.js

简单用egg.js搭建一个消息系统,可实现异步消息队列、延迟消息队列
以及替代corntab的定时任务

1、egg.js

  • 1.1、定时任务
1
2
3
4
5
6
7
8
9
10
cron-parser 支持可选的秒(linux crontab 不支持)
* * * * * *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ |
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── month (1 - 12)
│ │ │ └────────── day of month (1 - 31)
│ │ └─────────────── hour (0 - 23)
│ └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, optional)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
schedule: {
// cron: '0 0 */3 * * *', // corntab风格的定时任务

interval: '1m', // 1 分钟间隔
type: 'all', // all-指定所有的worker都需要执行 worker-随机某个worker执行
},
async task(ctx) {
const res = await ctx.curl('http://www.api.com/cache', {
dataType: 'json',
});
ctx.app.cache = res.data;
},
};
  • 1.2、自定义定时任务

在 agent.js 中继承 agent.ScheduleStrategy,然后通过 agent.schedule.use() 注册即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
const config = { redis: agent.redis };
Object.assign(config, agent.config.beeQueue, { isWorker: true });
// 创建异步队列对象
agent.redisQueue = new Queue('queue', config);

// bee-queue异步队列
class QueueStrategy extends agent.ScheduleStrategy {
start() {
agent.redisQueue.process(async job => this.sendOne(job.data));
}
}
// 输出自定义定时任务
agent.schedule.use('queue', QueueStrategy);

ScheduleStrategy 基类提供了:

  • schedule - 定时任务的属性,disable 是默认统一支持的,其他配置可以自行解析。
  • this.sendOne(…args) - 随机通知一个 worker 执行 task,args 会传递给 subscribe(…args) 或 task(ctx, …args)。
  • `this.sendAll(…args) - 通知所有的 worker 执行 task

2、bee-queue

采用redis作为驱动的消息队列中间件,支持异步队列、延迟队列

  • 2.1、创建队列实例

    创建队列需要一个beeQueue的实例,为了避免redis连接数爆炸,所以这里要用到单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// app.js
const config = {
// 任务名前缀
prefix: 'monitor',
// 心跳时间间隔 ms
stallInterval: 5000,
// 如果所有延迟的作业都在该时间之外,队列将再次检查经过该时间后是否没有错过任何作业 ms
nearTermWindow: 1200000,
// 任务允许延迟的时间 ms
delayedDebounce: 1000,
// 此队列是否要作业
isWorker: false,
// 接收作业事件
getEvents: false,
// 发送作业事件
sendEvents: false,
// 启用延迟作业
activateDelayedJobs: true,
// 自动删除成功的作业
removeOnSuccess: false,
// 自动删除失败的作业
removeOnFailure: false,
// 最大查询作业数量
redisScanCount: 100,
// redis实例
redis: app.redis
};
// 创建异步队列实例
this.app.redisQueue = new Queue('queue', config);
  • 2.2、队列入队
1
2
3
4
5
6
7
8
9
const job = this.app.redisQueue.createJob({name: 'test', data: {}});

// 延迟时间 (ms)
job.delayUntil(delay)

// 保存队列
job.save().then((job) => {
// job enqueued, job.id populated
});

3、实现异步队列

  • 3.1、定义一个控制器用于接收data创建异步队列
  • 3.2、在agent.js中自定义定时任务
  • 3.3、根据队列name创建schedule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// queue.js
'use strict';

/**
* bee-queue异步队列消费
* User: Srako
* Date: 2020/05/12 18:10
* @email srakor@163.com
* @link http://srako.github.io
*/

const moment = require('moment');

module.exports = app => {
return {
schedule: {
type: 'queue',
},

async task(ctx, args) {
if (!args.hasOwnProperty('name')) {
ctx.logger.error('异步队列名称不存在:', args.name);
return;
}
// 服务名称
const serviceName = ctx.helper.toCamel(args.name);

// 不存在处理服务
if (!ctx.service.hasOwnProperty(serviceName)) {
ctx.logger.error('异步队列不存在消费者:', args.name);
return;
}

try {
const result = await ctx.service[serviceName].consume(args.data);

if (result !== true) {
ctx.logger.error('异步队列消费失败:', args, ':', result);
return;
}
ctx.logger.info('异步队列消费成功:', args);
} catch (e) {
ctx.logger.error('异步队列消费失败:catch:', args, ':', e.res);

// 失败任务,两分钟后重试
const job = app.redisQueue.createJob({ name: args.name, data: args.data });
job.delayUntil(moment()
.add(2, 'm')
.format('x'))
.save()
.then(job => {
ctx.logger.info('异步队列重新创建成功:', job.data);
});
}
},
};
};
  • 3.4、根据name创建对应service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// queueService.js
'use strict';

/**
* 企业微信消息
* User: Srako
* Date: 2020/09/08 18:27
* @email srakor@163.com
* @link http://srako.github.io
*/

const Service = require('egg').Service;

class templateMessageService extends Service {

/**
* @param {object} data 对象
* @param {int} data.user_id 后台admin_id
* @param {string} data.scene 模版场景
* @return {Promise<*>} promise
*/
async consume(data) {
const { ctx, app } = this;

// 调用对应接口消费队列
const result = await ctx.curl(app.config.apiUrl + 'work/send_message', {
method: 'POST',
dataType: 'json',
data,
});
return result.status === 200 ? true : result;
}
}

module.exports = templateMessageService;

VPN

Posted on 2019-04-02 | In study

VPN - srako

  • 购买地址:https://vps.hostdare.com【优惠码:15UJZ1OUPK】
  • 服务器脚本地址:
    • https://shadowsocks.be
    • https://www.v2ray.com/ 或者 https://toutyrater.github.io/
  • VPN-WINDOWS客户端: https://github.com/shadowsocks/shadowsocks-windows/releases
  • VPN-MACOS客户端:https://github.com/shadowsocks/ShadowsocksX-NG/releases
  • VPN-ANDROID客户端:https://github.com/shadowsocks/shadowsocks-android/releases
  • VPN-IOS客户端:https://appledi.com/

文档说明

在国内使用国外搜索引擎并不是一件非常容易的事情,我们可以比较顺利地进行科学上网,打开新世界的大门。就像我们无法直接购买外国的正品一样,我们可以找代购帮我们买。同理VPN就是个代购的功能。本文就介绍在各个终端如何科学上网。

服务端安装

shadowsocks

  • 执行 shadowsocksR.sh

V2Ray

  • 执行 V2Ray.sh
  • 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// 入站配置
"inbounds": [{
"port": 10086, // 服务器监听端口,必须和上面的一样
"protocol": "vmess",
"settings": {
"clients": [{ "id": "b831381d-6324-4d53-ad4f-8cda48b30811" }]
}
}],
// 出站配置
"outbounds": [{
"protocol": "freedom",
"settings": {}
}]
}
  • settings配置详解

    • VMess InboundConfigurationObject

      1
      2
      3
      {
      "clients": [{ "id": "b831381d-6324-4d53-ad4f-8cda48b30811" }]
      }
    • HTTP InboundConfigurationObject

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      {
      "timeout": 0,
      "accounts": [
      {
      "user": "my-username",
      "pass": "my-password"
      }
      ],
      "allowTransparent": false,
      "userLevel": 0
      }
    • Shadowsocks InboundConfigurationObject

      加密方式列表

    • aes-256-cfb
    • aes-128-cfb
    • chacha20
    • chacha20-ietf
    • aes-256-gcm
    • aes-128-gcm
    • chacha20-poly1305 或 chacha20-ietf-poly1305
      1
      2
      3
      4
      5
      6
      7
      8
      {
      "email": "love@v2ray.com",
      "method": "aes-128-cfb",
      "password": "密码",
      "level": 0,
      "ota": true,
      "network": "tcp"
      }
  • 配置文件测试 v2ray -test -config ./config.json

  • 运行v2ray v2ray --config ./config.json

账号及密码

XX节点

ip:
端口:
加密方式:
密码:

软件安装

windows

windows下载地址

前言:需要安装 .NET Framework 4.6.2 和 Microsoft Visual C++ 2015 Redistributable (x86)(一般电脑已经安装,不需要再次安装)

  • 1、下载用于Windows设备的Shadowsocks-Windows软件
  • 2、打开应用程序
    • 2.1、启动应用,打开配置项
      img
    • 2.2、右键单击左下角的飞机图标进行配置
      img
  • 3、配置节点(主要有两种方法)

    • 3.1 扫描二维码

      首先网页上或者是聊天窗口打开节点的二维码图片
      然后右键单击右下角的软件,点击“服务器”-“扫描屏幕上的二维码”
      程序自动识别二维码并导入服务器节点信息
      最后启用系统代理即可使用

    • 3.2 手动编辑节点配置

      右键单击右下角的软件,点击“服务器”-“编辑服务器”

  • 4、若提示.NET framework过低
    则需要下载.NET framework软件点击下载,重新打开运行即可。
    完成,可打开游览器访问网站google.com进行测试

mac

MAC下载地址

  • 1、下载并安装ShadowsocksX-NG软件
  • 2、扫描二维码添加节点

    首先网页上打开节点的二维码图片,保证当前屏幕只有一个服务器二维码配置

  • 3、打开Shadowsocks

  • 4、确认是否打开

  • 5、模式解释

    PAC自动模式 - 需要代购的才选择代购(一般为国内买不到)
    全局模式 - 买所有东西都找代购

    Android

    安卓下载地址

    • 1、下载用于Android设备的Shadowsocks-ANDROID软件
    • 2、打开程序文件SHADOWSOCKS配置节点信息

打开节点设置

大部分的设备不支持Google Play服务 因此无法扫描二维码。所以第三部选择手动设置

修改配置为对应节点配置,路由选择GFW列表

路由:全局 - 买所有东西都找代购
绕过局域网地址 - 除了买邻居家的东西,其他找代购
绕过中国大陆地址 - 除了买国内的东西,其他找代购
绕过局域网及中国大陆地址 - 除了买邻居家和国内的东西,其他找代购
GFW列表 - 仅国内无法买到的东西找代购(一般使用这个)
仅代理中国大陆地址 - 仅买大陆境内的东西找代购

选择节点启动VPN

允许软件创建VPN链接

查看是否链接成功

iPhone

  • 目前无法使用
    <!– 美区APPLEID账号1:Gitwwww@gmail.com 密码:Git4wgmail

APPLEID账号2:4SSGIT@Gmail.com 密码:4ssORG168

IPhone在线安装 –>

常见问题

例如
问:vpn如果连接不上怎么办?
答:切换节点;

HTTP的请求过程

Posted on 2019-03-15 | In Development

从浏览器输入网址到网页展示的全过程

1、在浏览器输入要的网址

  • https://wwww.baidu.com

2、浏览器查找域名的IP地址

  • 浏览器缓存 - 浏览器会缓存DNS记录一段时间,不同的浏览器的缓存时间不一样(Chrome的过期时间是1分钟)。
  • 路由缓存 - 接着前面的查询请求发向路由器,它一般会有自己的DNS缓存。
  • ISP DNS缓存 - 接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的换粗记录。
  • 递归搜索 - 你的ISP的DNS服务器从跟域名服务器开始进行递归搜索, 从.com顶级域名服务器到百度的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以顶级服务器的匹配过程不是那么必要了。

3、浏览器与服务器建立链接

  • 在请求之前,需要浏览器与服务器建立连接(TCP或者UDP)

    • 与服务器建立连接时TCP属于安全的连接,需要三次握手。
      img
    • SYN:代表请求创建连接,所以在三次握手中前两次要SYN=1,表示这两次用于建立连接。

    • FIN:表示请求关闭连接,在四次分手时,我们发现FIN发了两遍。这是因为TCP的连接是双向的,所以一次FIN只能关闭一个方向。

    • ACK:代表确认接受,从上面可以发现,不管是三次握手还是四次分手,在回应的时候都会加上ACK=1,表示消息接收到了,并且在建立连接以后的发送数据时,都需加上ACK=1,来表示数据接收成功。

    • seq:序列号,什么意思呢?当发送一个数据时,数据是被拆成多个数据包来发送,序列号就是对每个数据包进行编号,这样接受方才能对数据包进行再次拼接。

  • 与服务器响应软件建立管道连接(socket)

4、浏览器给WEB服务器发送HTTP请求

  • 百度主页为动态页面,打开后在浏览器缓存中很快甚至马上就会国旗,毫无疑问他们不能从之读取。所以,浏览器将把以下请求发送给百度所在的服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    Cache-Control: no-cache
    Connection: keep-alive
    Cookie: BAIDUID=DB950B3D3996ECE570E2C2B148A24BF7:FG=1; BIDUPSID=DB950B3D3996ECE570E2C2B148A24BF7; PSTM=1552621892; delPer=0; BD_HOME=0; H_PS_PSSID=1426_21094_28558_28608_28585_28604_22159; BD_UPN=123253
    Host: www.baidu.com
    Pragma: no-cache
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
  • 解析请求含义

    • Accept 客户端希望接受的数据类型
    • Accept-Encoding 支持gzip, deflate, br类型
    • Accept-Language 允许客户端声明它可以理解的自然语言
    • Cache-Control - HTTP 1.1(Pragma - HTTP 1.0):

      • no-cache — 不要读取缓存中的文件,要求向WEB服务器重新请求
      • no-store — 请求和响应都禁止被缓存
      • max-age: — 表示当访问此网页后的max-age秒内再次访问不会去服务器请求,其功能与Expires类似,只是Expires是根据某个特定日期值做比较。一但缓存者自身的时间不准确.则结果可能就是错误的,而max-age,显然无此问题.。Max-age的优先级也是高于Expires的。
      • max-stale — 允许读取过期时间必须小于max-stale 值的缓存对象。
      • min-fresh — 接受其max-age生命期大于其当前时间 跟 min-fresh 值之和的缓存对象

      • only-if-cached — 告知缓存者,我希望内容来自缓存,我并不关心被缓存响应,是否是新鲜的.

      • no-transform — 告知代理,不要更改媒体类型,比如jpg,被你改成png.

    • Connection头要求服务器为了后边的请求不要关闭TCP连接。
    • Cookie 存储登陆用户名
    • User-Agent 浏览器标示

5、服务器处理请求(nginx+php-fpm)

6、服务器返回一个HTML响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html
Cxy_all: baidu+8ecc9869f7b15cf9e50e07e5d89e4a14
Date: Fri, 15 Mar 2019 06:33:47 GMT
Expires: Fri, 15 Mar 2019 06:32:51 GMT
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1457_21117_28558_28608_28584_28639_26350_28604_27542; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1

7、浏览器开始显示HTML

  • 在浏览器没有完整接受全部HTML文档时,它就已经开始显示这个页面了

8、加载网站文件

  • 解析HTML文档、构建DOM树、下载资源、构造CSSOM树、执行JS脚本(没有严格的先后执行顺序)
    • 构建DOM树:
      • Tokenizing:根据HTML规范将字符流解析为标记
      • Lexing:词法分析将标记转换为对象并定义属性和规则
      • DOM construction:根据HTML标记关系将对象组成DOM树
    • 解析过程中遇到图片、样式表、JS文件,启动下载
    • 构建CSSOM树:
      • Tokenizing:字符流转换为标记流
      • Node:根据标记创建节点
      • CSSOM:节点创建CSSOM树
    • 根据DOM树和CSSOM树构建渲染树:
      • 从DOM树的根节点遍历所有可见节点,不可见节点包括:1)script,meta这样本身不可见的标签。2)被css隐藏的节点,如display: none
      • 对每一个可见节点,找到恰当的CSSOM规则并应用
      • 发布可视节点的内容和计算样式
    • JS解析:
      • 浏览器创建Document对象并解析HTML,将解析到的元素和文本节点添加到文档中,此时document.readystate为loading
      • HTML解析器遇到没有async和defer的script时,将他们添加到文档中,然后执行行内或外部脚本。这些脚本会同步执行,并且在脚本下载和执行时解析器会暂停。这样就可以用document.write()把文本插入到输入流中。同步脚本经常简单定义函数和注册事件处理程序,他们可以遍历和操作script和他们之前的文档内容
      • 当解析器遇到设置了async属性的script时,开始下载脚本并继续解析文档。脚本会在它下载完成后尽快执行,但是解析器不会停下来等它下载。异步脚本禁止使用document.write(),它们可以访问自己script和之前的文档元素
      • 当文档完成解析,document.readState变成interactive
      • 所有defer脚本会按照在文档出现的顺序执行,延迟脚本能访问完整文档树,禁止使用document.write()
      • 浏览器在Document对象上触发DOMContentLoaded事件
      • 此时文档完全解析完成,浏览器可能还在等待如图片等内容加载,等这些内容完成载入并且所有异步脚本完成载入和执行,document.readState变为complete,window触发load事件
    • 显示页面(HTML解析过程中会逐步显示页面)

Homestead

Posted on 2019-03-12 | In Development

Homestead 安装

Laravel 官方推荐使用 homestead 搭建环境,那么这个 homestead 究竟是什么鬼?相信很多和我一样不是专门搞服务器的同学看过各种资料后还会有点迷糊。刚开始看了一些介绍后我以为 homestead 是一个整合了 Nginx + PHP + MySQL + Composer 还有其他 Laravel 需要环境的安装包加管理器的概念,类似 win 平台上的 wnmp 之类的东西。不过装一半就发现完全是另一回事。
实际上 homestead 是一台虚拟机的原型,类似我们买电脑后预装的系统,只是这个系统预装了一切 Laravel 需要的东西。而我们最先进行的步骤安装 VirtualBox 和 Vargrnt 实际上就是安装了虚拟机。这意味着实际上 homestead 安装完成后我们的 mac 上并没有也不需要安装 PHP、Nginx、MySQL 等等的东西,所有东西都安装在另一台机器上(虚拟机)。

1.vagrant、virtualbox安装

  • virtualbox下载地址

  • vagrant下载地址

  • 查看vagrant是否安装成功
    1
    vagrant -v

2.安装Homestead Vagrant Box

  • 下载地址

  • 切换到下载目录执行

1
2
3
4
5
6
7
8
9
vagrant box add laravel/homestead virtualbox.box --box-version 7.0.0
#是否安装成功
vagrant box list
```
## 3.安装Homestead
- 克隆存储库来安装Homestead

``` bash
git clone https://github.com/laravel/homestead.git ~/Homestead
  • 克隆Homestead存储库后,从Homestead目录运行命令以创建配置文件。
1
2
3
4
5
# Mac / Linux...
bash init.sh

# Windows...
init.bat
  • 示例文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
name: homestead-7
hostname: homestead
ip: 192.168.66.66
memory: 2048
cpus: 1
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa

folders:
# 配置共享文件夹
-
map: '~/Workspace/www/xiaoyinka/'
to: /www/xiaoyinka
-
map: '~/Workspace/www/wechat/'
to: /www/wechat
sites:
# 配置Nginx站点
# php: "5.6" 目前7.3,会报错
# type:apache, laravel (the default), thinkphp,proxy, silverstripe, statamic, symfony2, and symfony4.
-
map: xyk.cn
to: /www/xiaoyinka/public
type: thinkphp
php: "7.1"
-
map: we.cn
to: /www/wechat/public
type: thinkphp
php: "7.2"
databases:
- demo
# - eshuduoduo
# blackfire:
# - id: foo
# token: bar
# client-id: foo
# client-token: bar

# # ports:
# # - send: 50000
# # to: 5000
# # - send: 7777
# # to: 777
# # protocol: udp

# networks:
# - type: "public_network"
# ip: "192.168.10.80"
# bridge: "en0: Wi-Fi (AirPort)"
# - type: "public_network"
# ip: "192.168.10.81"
# bridge: "en0: Wi-Fi (AirPort)"
# - type: "public_network"
# ip: "192.168.10.82"
# bridge: "en0: Wi-Fi (AirPort)"
# momotang: 80 (guest) => 8000 (host) (adapter 1)
# momotang: 443 (guest) => 44300 (host) (adapter 1)
# momotang: 3306 (guest) => 33060 (host) (adapter 1)
# momotang: 5432 (guest) => 54320 (host) (adapter 1)
# momotang: 8025 (guest) => 8025 (host) (adapter 1)
# momotang: 27017 (guest) => 27017 (host) (adapter 1)
# momotang: 22 (guest) => 2222 (host) (adapter 1)

4.快捷配置

  • after.sh

    1
    2
    3
    #!/bin/sh
    sudo apt-get update
    sudo apt-get install php7.1-mcrypt php7.1-gmp php7.3-gmp php-redis
  • 数据库
    要从主机的数据库客户端连接到MySQL或PostgreSQL数据库,您应该连接到端口(MySQL)或(PostgreSQL)。两个数据库的用户名和密码是 。

    • ip:127.0.0.1
    • mysql端口:33060
    • PostgreSQL端口:54320
    • 用户名:homestead
    • 密码:secret

5.vagrant 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
#启动 - 需切换到配置所在目录
vagrant up

#ssh到vagrant
vagrant ssh

#刷新配置文件,切换到homestead目录
vagrant reload --provision

#增加快捷命令
function homestead() {
( cd ~/Workspace/Homestead && vagrant $* )
}

6.redis服务

  • 修改Homestead.yaml文件端口转发,并刷新配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        ports:
    - send: 63790
    to: 6379
    ````
    - 文件地址:/etc/redis/redis.conf
    requirepass yourpassword----设置任何你想要的密码;
    bind 127.0.0.1 修改为 bind 0.0.0.0;
    ``` bash
    #redis重启
    sudo service redis restart
  • redis连接配置

    1
    2
    3
    host: 127.0.0.1
    port: 63790
    auth: yourpassword

7.mongoDB服务

  • 要安装MongoDB Community Edition,请使用以下配置选项更新文件:Homestead.yaml。刷新配置文件

    1
    mongodb: true
  • 默认

    • 用户名:homestead
    • 密码: secret

8.其他

网址记录

  1. laravel/homestead Vagrant box
  2. Laravel 官网教程

Srako

4 posts
2 categories
© 2020 Srako
Powered by Hexo
|
Theme — NexT.Muse v5.1.4