Gowhich

Durban's Blog

开始Mac上安装powerline

首先我们需要下载安装powerline。在正式安装之前先啰嗦几句powerline的代码结构,github上的powerline项目下涵盖了用于适配各种APP(bash,vim等)的代码。因此,你完全可以在Mac任何一个地方下载该代码包,然后将不同的APP配置使用这个路径,以Plugin形式加载。为了方便读者选择性安装,本文对于不同的程序将分开给出安装路径和配置。

先确定本机环境有一套版本大于等于2.7的Python的环境。如果没有合适环境的话,可以通过homebrew安装,这里不多做赘述。

1
2
shell> python -v
Python 2.7.9

然后通过pip安装powerline:

1
shell> pip install powerline-status

安装完成后通过pip show powerline-status查看powerline所处的具体路径。注意:这个路径很重要,会用在之后的配置环节

1
2
3
4
5
shell> pip show powerline-status
Name: powerline-status
Version: 2.0
Location: /Library/Python/2.7/site-packages
Requires:

配置Bash使用powerline

配置方法很简单,只需要在Bash配置文件(例如:/etc/bashrc,/.bashrc,/.bash_profile)中增加一行调用安装路径下的bindings/bash/powerline.sh即可。这样每次调用生成新的Bash窗口时,都会自动执行powerline.sh文件中的内容。下面以~/.bash_profile为例:

1
2
3
4
shell> echo << EOF >> ~/.bash_profile 
. /Library/Python/2.7/site-packages/powerline/bindings/bash/powerline.sh
EOF
shell> . /Library/Python/2.7/site-packages/powerline/bindings/bash/powerline.sh

注意:根据python安装方式的不同,你的powerline所在路径也可能不同。如果你是通过python官网或者apple store通过安装工具安装的python,那么你的powerline安装路径就是/Library/Python/2.7/site-packages/powerline/。如果你是通过brew install python的话,那么你的powerline路径可能会有不同。请根据实际情况修改上面的命令。

Teriminal字体配置

执行完上面两步后,不出意外powerline就已经开始工作了。但是你会发现Bash提示符会是一些非常恶心的符号。

出现这样情况的原因是powerline为了美观自己造了一些符号,而这些符号不在Unicode字库内(如果你不知道Unicode字库是什么的话可以看下博主以前的相关介绍)。所以想要powerline正常显示的话,需要安装特殊处理过的字体。好在有一位热心人的帮助,他把大部分的程序猿常用的等宽字体都打上了powerline patch使得我们的这部配置将异常简单。首先我们从github上下载并安装字体:

1
2
3
shell> git clone https://github.com/powerline/fonts.git
shell> cd fonts
shell> ./install.sh

安装完成后我们就可以在iTerm2或者Terminal的字体选项里看到并选择多个xxx for powerline的字体了。*注意:对于ASCII fonts和non-ASCII fonts都需要选择for powerline的字体

VIM相关配置

这部分我们将介绍如何为VIM配置powerline。首先你需要确保你的vim编译时开启了python支持。如果通过python –version|grep +python没有结果的话,那么你需要通过brew install vim –with-python –with-ruby –with-perl重新编译安装vim,或者使用brew install macvim –env-std –override-system-vim安装macvim。

然后,你只需要在~/.vimrc中加上以下部分,VIM就能够正常加载powerline功能了:

注意:其中set rtp+=/Library/Python/2.7/site-packages/powerline/bindings/vim和上文一样需要按照自己的实际情况调整。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set rtp+=/Library/Python/2.7/site-packages/powerline/bindings/vim
" These lines setup the environment to show graphics and colors correctly.
set nocompatible
set t_Co=256

let g:minBufExplForceSyntaxEnable = 1
python from powerline.vim import setup as powerline_setup
python powerline_setup()
python del powerline_setup

if ! has('gui_running')
set ttimeoutlen=10
augroup FastEscape
autocmd!
au InsertEnter * set timeoutlen=0
au InsertLeave * set timeoutlen=1000
augroup END
endif

set laststatus=2 " Always display the statusline in all windows
set guifont=Inconsolata\ for\ Powerline:h14
set noshowmode " Hide the default mode text (e.g. -- INSERT -- below the statusline

如果有不明白的可以借鉴这篇文章:

https://coderwall.com/p/yiot4q/setup-vim-powerline-and-iterm2-on-mac-os-x

这两天Node的程序kill到手都软了,网上查了一个很好的方法,直接拿来用了。

功能:杀死进程名称中包含node的所有进程

1
ps -ef | grep node | awk '{print $2}' | xargs kill -9

记录下koajs的异常使用:

下面介绍一下 几个文件

test.js启动文件

router.js 路由文件

api/Admin.js 逻辑处理文件

koajs的异常处理逻辑代码如下[放在test.js 启动文件中]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 统一处理默认Error
*/
app.use(function *(next) {
try {
yield next;
} catch (err) {
this.status = err.status;
this.body = {
name: "GowhichApiServerError",
code: err.status || 600,
message: err.message || "Server internal error.",
success: false
}
}
});

600 :我自己定义的,为了区别系统的错误

接下来简单的看下路由:

1
2
router.post('/xxx/xxxxx', Admin.mailxxxxImport);
router.post('/xxxx/xxxx', Admin.mailxxxxBill);

这个router 我使用的是koa-router

好了, 到这里应该说一切都没问题了.下面就看我们如何出发这个错误了

那么在Admin.js里面我们写段代码:

1
2
3
4
5
6
7
8
9
10
11
module.exports.mailxxxxBill = function *(next){
var uid = _uid(this.request);
var taskid = this.request.body.taskid;

if (!uid || !taskid) {
return this.throw('任务参数不能为空');
}

//测试使用
return this.throw('任务参数不能为空');
}

没错就是这句代码:

1
this.throw('任务参数不能为空');

当触发的时候,就会调用test.js的错误处理逻辑,可以了,动起来吧.

npm (node package manager)是node模块管理工具,类似与Linux下的yum和apt。

常用npm命令(参考:https://npmjs.org/doc/)

安装模块

1
npm install

安装当前目录package.json文件中配置的dependencies模块

安装本地的模块文件

1
npm install <tarball file>

Example:

1
npm install ./package.tgz

安装指定URL的模块

1
npm install <tarball url>

Example:

1
npm install https://github.com/indexzero/forever/tarball/v0.5.6

安装本地文件系统中指定的目录包含的模块

1
npm install <folder>

安装并更新package.json中的版本配置

1
npm install <name> [–save|–save-dev|–save-optional]

其中:

添加–save 参数安装的模块的名字及其版本信息会出现在package.json的dependencies选项中

添加–save-dev 参数安装的模块的名字及其版本信息会出现在package.json的devDependencies选项中

添加–save-optional 参数安装的模块的名字及其版本信息会出现在package.json的optionalDependencies选项中

安装模块的config的tag配置中含有指定tag的版本

1
npm install <name>@<tag>

Example:

1
npm install sax@latest

安装模块的指定版本

1
npm install <name>@<version>

Example:

1
npm install xx@xx

安装模块指定版本号范围内的某一个版本

1
npm install <name>@<version range>

Example:

1
npm install async@”>=0.2.0 <0.2.9″

–force强制拉取远程资源,即使本地已经安装这个模块

Example:

1
npm install underscore –force

-g或–global全局安装模块,如果没有这个参数,会安装在当前目录的node_modules子目录下

Example:

1
npm install -g express

显示npm的bin目录

1
npm bin

设置npm配置

1
npm config set <key> <value> [–global]

使用–global参数,设置全局配置

Example:

设置代理

1
npm config set proxy=http://proxy.tencent.com:8080

设置npm的镜像地址

1
npm config set registry http://npm.oa.com

获取npm配置

1
npm config get <key>

Example:

获取npm当前镜像地址

1
npm config get registory

删除npm配置

1
npm config delete <key>

Example:

删除代理设置

1
npm config delete proxy

在编辑器中打开npm配置文件

1
npm config edit

交互式的创建package.json文件

1
npm init

创建模块的压缩包

1
npm pack [<pkg> [<pkg> … ]]

如果没有参数,则npm会打包当前模块目录

发布模块,发布后可通过名称来安装该模块

1
npm publish <tarball>
1
npm publish <folder>

其中:

:包含package.json文件的目录

:经过gzip压缩并归档的一个URL或文件路径,该压缩包包含单个目录,且该目录内有package.json文件

删除模块

1
2
3
4
npm rm <name>
npm r <name>
npm uninstall <name>
npm un <name>

注意:不会删除package.json文件dependencies选项中对应的依赖配置

查找模块

1
2
3
npm search [search terms ..]
npm s [search terms ..]
npm se [search terms ..]

查找匹配查找字符串的模块

更新模块

1
npm update [-g] [<name> [<name> … ]]

更新指定name列表中的模块。-g参数更新全局安装的模块。

如果没有指定name,且不是在某个模块内,会更新当前目录依赖的所有包都会被更新(包括全局和模块内);如果当前目录在某个模块目录内,会更新该模块依赖的模块,所以不指定name直接运行npm update时,最好在某个模块内运行,以免更新到其他不想更新的模块。

执行脚本

1
2
3
npm start [<name>]
npm stop [<name>]
npm test [<name>]

运行package的start脚本,该脚本写在package.json文件scripts的start字段中。

该字段的值可以是当前系统控制台可执行的脚本,也可以是当前系统可执行文件的路径。

如果不传name参数,则执行当前目录下package.json文件中定义的脚本。

详见https://npmjs.org/doc/misc/npm-scripts.html

package.json文件

模块的配置文件,详见https://npmjs.org/doc/files/package.json.html

Chrome的插件目录在:

Ubuntu:      ~/.config/google-chrome/Default/Extensions

Windows:   C:\Users\Administrator\AppData\Local\Google\Chrome\User Data/Default/Extensions/

Mac OS X:   ./Library/Application Support/Google/Chrome/Default/Extensions

一.修改字体大小

安装PackageResourceViewer

使用PackageResourceViewer打开Theme文件进行编辑

快捷键 ⌘(command)+⇧(shift)+P 打开 Command Palette 输入 PackageResourceViewer: Open Resource 回车,

打开包列表 选择 Theme - Default

再选择 Default.sublimt-theme [这里有可能是你自己目前安装正在使用的一个主题,如果不能用的话,可以自己更换一个试试]

搜索 sidebar_label,在 "class": "sidebar_label" 后边加一行:"font.size": 18,将字体大小设置为18,保存.

二.修改字体类型

进行第一步操作后,在sidebar_label后面加入一行:"font.face":"Courier New",这里的字体可以是你自己系统里面自带的.

wubi安装ubuntu14.04后,终端输入free -m可以查到如下信息:

1
2
3
4
             total       used       free     shared    buffers     cached  
Mem: 1944 1801 143 0 557 706
-/+ buffers/cache: 536 1407
Swap: 255 255 0

也即内存为2G,虚拟内存为256M,这太小了尤其是使用大型IDE编辑源码的时候。因此将其增加到1G大小。具体步骤是:

1,新建/swap文件夹。然后cd进去,终端输入:

1
sudo dd if=/dev/zero of=swapfile bs=1024 count=1000000

最后的count就是虚拟内存的大小,后面有6个0,前面是1,表示1G.稍等约1分钟看到如下信息:

1
2
3
记录了1000000+0 的读入
记录了1000000+0 的写出
1024000000字节(1.0 GB)已复制,16.0863 秒,63.7 MB/秒

2,这个时候在swap目录下就生成了swapfile文件。终端输入:du -h swapfile 可以查看生成的文件swapfile大小为977M.

1
2
durban@ubuntu:/swap$ du -h swapfile
977Mswapfile

约等于1G.接下来需要将swapfile转换成Swap文件,终端输入:

1
sudo mkswap -f  swapfile

(mkswap是命令,后面的swapfile是swap文件夹下新生成的文件名字)

1
2
3
4
durban@ubuntu:/swap$ sudo mkswap -f  swapfile
[sudo] password for durban:
正在设置交换空间版本 1,大小 = 999996 KiB
无标签, UUID=6c3c015d-9a42-4ced-b93c-a1635062e292

3,激活swap文件

终端输入:

1
sudo swapon swapfile

(swapon是命令,swapfile是文件名字)

然后再输入 free -m可以看到:

1
2
3
4
5
6
durban@ubuntu:/swap$ sudo swapon swapfile
durban@ubuntu:/swap$ free -m
total used free shared buffers cached
Mem: 3892 3693 199 92 209 850
-/+ buffers/cache: 2633 1258
Swap: 1232 247 984

Swap的大小1232 = 255(原来的) + 977 (新增加的)

如果要修改或者删除这个swapfile文件,需要先卸载这个swapfile。进入到swap目录,然后终端输入:

sudo sawpoff swapfile

这就卸载了。swapfile文件就可以删除了,否则会提示正在使用或忙 无法删除。

如果要一直保持这个新增的swap,通过切换到root

1
gedit /etc/fstab

在里面增加一句:

1
/swap/swapfile none swap defaults 0 0

关于这句话:

参考1:http://blog.csdn.net/mznewfacer/article/details/7334592 的命令是

1
/swap/swapfile none swap defaults 0 0

参考2:http://www.linuxidc.com/Linux/2010-09/28915.htm的命令是

1
/swap/swapfile /swap swap defaults 0 0

个人觉的第二个参数表示原来系统的swap文件夹,如果原系统没有swap文件夹,则用参考1里的命令。如果原系统本来就有/swap文件夹了,也即本来就有swap空间然后又新增加了一个,就用参考2的命令。 Ubuntu14.04上原本没有/swap文件夹,因此用参考1的命令。

另外,注意上面新建swap使用后,现有swap是两者之和。

这篇文章->http://www.blogjava.net/zygcs/archive/2011/09/02/357845.html

这里的方法大同小异,只不过是先

1
cd /host/ubuntu/disks/

可以看到有个swap.disk, 利用

1
du -h swap.disk

查看大小为255M,然后

1
sudo swapoff swap.disk

取消使用这个系统自带的交换空间,然后删除掉。

通过以下步骤我们可以修改系统自带的swap:

1).

1
sudo dd if=/dev/zero of=swap.disk bs=1M count=1k

(创建1G的swap, 这步比较慢)

2).

1
sudo mkswap -f swap.disk

3).

1
sudo swapon /host/ubuntu/disks/swap.disk

(这步更慢,大概1分钟不到)

这是在/host/ubuntu/disks/目录下创建的,名字为swap.disk. 本质是一样的。

再就是创建swap时大小是bs*count,如果bs=1M 则count =1k表示1G,如果bs=1024, count=1000000 表示1G,我觉的前者更精确。

=================================================

接下来是优化swap的使用参数,linux里有个参数swappiness。当值为0时最大限度的使用物理内存,物理内存使用完后再使用swap内存。为100时,最大限度的使用swap,并将内存中的数据也要搬到swap里处理,这是两个极端。默认的参数是60,根据这里将其改为10较优。步骤如下:

1,查看当前的swappiness

终端输入:

1
cat /proc/sys/vm/swappiness

2.修改swappiness值为10

1
$ sudo sysctl vm.swappiness=10

但是这只是临时性的修改,在你重启系统后会恢复默认的60,所以,还要做一步:

1
$ sudo gedit /etc/sysctl.conf

在这个文档的最后加上这样一行:

1
vm.swappiness=10

然后保存,重启。ok,你的设置就生效了。你会发现,现在乌斑兔儿跑得更快了!

jquery 实现 保持滚动条一直在底部

1
2
3
var e = $('#import-bill');  
e.scrollTop = e.scrollHeight;//让滚动条自动滚动顶部
$("#import-bill").scrollTop($("#import-bill")[0].scrollHeight);

Json字符串如果不是很规范的话,使用nodejs的JSON方法是无法 进行parse的,比如说下面这段代码:

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
{'code':'S_OK','result':[0,0,0,0,0],'sessionCount':85,'var':[
{
'mid':'3a010000d4eede56800000b5',
'fid':1,'mailSession':149,
'size':15074,
'sendDate':1433473712,
'receiveDate':1433473712,
'modifyDate':1434356744,
'taskDate':0,
'securityLevel':0,
'meetingFlag':0,
'priority':3,
'color':0,
'antivirusStatus':0,
'rcptFlag':1,
'attachmentNum':0,
'mailNum':1,
'keepDay':0,
'sendId':0,
'sendTotalNum':0,
'sendNewNum':0,
'mailFlag':5,'starType':0,'logoType':68,'denyForward':0, 'auditStatus':0, 'billType':0,'billFlag':0,'subscriptionFlag':0,'secureEncrypt':0,'secureSigned':0,
'flags':{'successed':1,'selfdestruct':1},
'label':[],
'from':'中国移动139邮箱<[email protected]>',
'to':'[email protected]',
'subject':'邮箱积分新用途?一起积分初体验',
'summary':'邮箱积分新用途知道自己的邮箱积分吗? 其实你已拥有诸多财富!想了解积分的用途吗?很多人早把积分'},
....... //这里省略了上下类似的代码
{
'mid':'1d0b257e9c2ec6b800000001',
'fid':1,
'mailSession':123,
'size':25633,
'sendDate':1424588244,
'receiveDate':1424588244,
'modifyDate':1433248644,
'taskDate':0,
'securityLevel':0,
'meetingFlag':0,
'priority':3,
'color':0,
'antivirusStatus':0,
'rcptFlag':1,
'attachmentNum':0,
'mailNum':1,
'keepDay':0,
'sendId':0,
'sendTotalNum':0,
'sendNewNum':0,
'mailFlag':5,'starType':0,'logoType':1,'denyForward':0, 'auditStatus':0, 'billType':0,'billFlag':0,'subscriptionFlag':0,'secureEncrypt':0,'secureSigned':0,
'flags':{'successed':1,'selfdestruct':1},
'label':[],
'from':'中国移动<xx@xx>',
'to':'xx@xx',
'subject':'尊敬的客户,您截至2月15日的积分账单已到,请查阅:巧用积分兑换好礼',
'summary':'用户号码:15000711265,尊敬的动感地带(M-Zone)客户:\r截止2月15日您的可兑换积分为:1074\r特别说明:\r1.'}
]
}

你可以将此n字符串放到文件内,多复制几个,然后通过JSON的方法试试.

首先肯定要进行读取文件

fs.readFile

然后将u读进来的Buffer字节码解析为字符串

buf.toString()

下面得到的就是字符串了,试试吧

JSON.stringify()

JSON.parse()

这两个方法随便搭配.最终还是无解,解析出来的仍然不是你要的对象.

…………………..

好吧,如果到了这里,我就只能说,别啥了,获得字符串之后,直接eval吧,哎,笨

1
json_obj = eval("(" + json_string + ")");

可以打印debug(json_obj),试试吧,应该可以不让你那么郁闷了.

代码逻辑

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import os
import sys
import argparse
import logging
import subprocess
from typing import List, Dict, Optional
import tempfile
import datetime

# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('nginx_config_manager')

class NginxConfigManager:
"""Nginx配置管理类"""

def __init__(self, config_dir: str = '/etc/nginx/conf.d',
nginx_bin: str = '/usr/sbin/nginx',
ssl_dir: str = '/etc/ssl'):
"""
初始化Nginx配置管理器

Args:
config_dir: Nginx配置文件目录
nginx_bin: Nginx可执行文件路径
ssl_dir: SSL证书存放目录
"""
self.config_dir = config_dir
self.nginx_bin = nginx_bin
self.ssl_dir = ssl_dir

# 确保配置目录存在
if not os.path.exists(config_dir):
logger.error(f"配置目录 {config_dir} 不存在")
sys.exit(1)

# 确保SSL目录存在
if not os.path.exists(ssl_dir):
os.makedirs(ssl_dir, exist_ok=True)
logger.info(f"创建SSL证书目录: {ssl_dir}")

def create_site_config(self, server_name: str, root_dir: str,
port: int = 80, ssl: bool = False,
additional_directives: Optional[List[str]] = None) -> str:
"""
创建网站配置文件内容

Args:
server_name: 服务器名称(域名)
root_dir: 网站根目录
port: 监听端口
ssl: 是否启用SSL
additional_directives: 额外的配置指令

Returns:
配置文件内容字符串
"""
# 基本配置模板
config = [
f"server {{",
f" listen {port}{' ssl' if ssl else ''};",
f" server_name {server_name};",
f" root {root_dir};"
]

# 添加SSL相关配置(如果启用)
if ssl:
config.extend([
f" ssl_certificate {self.ssl_dir}/certs/{server_name}.crt;",
f" ssl_certificate_key {self.ssl_dir}/private/{server_name}.key;",
" ssl_protocols TLSv1.2 TLSv1.3;",
" ssl_prefer_server_ciphers on;",
" ssl_session_cache shared:SSL:10m;",
" ssl_session_timeout 10m;"
])

# 添加默认位置块
config.extend([
" location / {",
" try_files $uri $uri/ =404;",
" }"
])

# 添加额外指令
if additional_directives:
config.extend(additional_directives)

config.append("}")

return "\n".join(config)

def write_config_file(self, server_name: str, config_content: str) -> str:
"""
写入配置文件

Args:
server_name: 服务器名称(用于生成文件名)
config_content: 配置文件内容

Returns:
配置文件路径
"""
# 生成配置文件名(基于服务器名称)
config_filename = f"{server_name.replace('.', '_')}.conf"
config_path = os.path.join(self.config_dir, config_filename)

try:
with open(config_path, 'w') as f:
f.write(config_content)
logger.info(f"配置文件已写入: {config_path}")
return config_path
except Exception as e:
logger.error(f"写入配置文件失败: {e}")
sys.exit(1)

def test_config(self) -> bool:
"""测试Nginx配置"""
try:
result = subprocess.run(
[self.nginx_bin, '-t'],
capture_output=True,
text=True
)

if result.returncode == 0:
logger.info("Nginx配置测试成功")
return True
else:
logger.error(f"Nginx配置测试失败: {result.stderr}")
return False
except Exception as e:
logger.error(f"执行配置测试时出错: {e}")
return False

def reload_nginx(self) -> bool:
"""重新加载Nginx服务"""
try:
# 先尝试优雅重启
result = subprocess.run(
[self.nginx_bin, '-s', 'reload'],
capture_output=True,
text=True
)

if result.returncode == 0:
logger.info("Nginx已重新加载")
return True
else:
logger.error(f"重新加载Nginx失败: {result.stderr}")
return False
except Exception as e:
logger.error(f"执行Nginx重载时出错: {e}")
return False

def generate_self_signed_ssl(self, server_name: str, days: int = 365) -> None:
"""
生成自签名SSL证书

Args:
server_name: 服务器名称(域名)
days: 证书有效期天数
"""
# 证书和私钥路径
cert_path = os.path.join(self.ssl_dir, "certs", f"{server_name}.crt")
key_path = os.path.join(self.ssl_dir, "private", f"{server_name}.key")

# 检查证书是否已存在
if os.path.exists(cert_path) and os.path.exists(key_path):
cert_mtime = datetime.datetime.fromtimestamp(os.path.getmtime(cert_path))
expires_at = cert_mtime + datetime.timedelta(days=days)
now = datetime.datetime.now()

if expires_at > now:
logger.info(f"SSL证书已存在且有效: {cert_path}")
return
else:
logger.info(f"SSL证书已过期,重新生成: {cert_path}")

logger.info(f"生成自签名SSL证书: {server_name}")

try:
# 创建临时配置文件
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
config = f"""
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req

[dn]
C = CN
ST = State
L = City
O = Organization
OU = Unit
CN = {server_name}

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = {server_name}
DNS.2 = www.{server_name}
"""
f.write(config)
config_path = f.name

# 生成私钥和证书
subprocess.run(
[
'openssl', 'req', '-x509', '-nodes',
'-days', str(days),
'-newkey', 'rsa:2048',
'-keyout', key_path,
'-out', cert_path,
'-config', config_path,
'-extensions', 'v3_req'
],
check=True,
capture_output=True
)

# 设置正确的权限
os.chmod(key_path, 0o600)
os.chmod(cert_path, 0o644)

logger.info(f"SSL证书生成成功: {cert_path}")
except subprocess.CalledProcessError as e:
logger.error(f"生成SSL证书失败: {e.stderr.decode()}")
sys.exit(1)
except Exception as e:
logger.error(f"生成SSL证书时出错: {e}")
sys.exit(1)
finally:
# 删除临时配置文件
if os.path.exists(config_path):
os.remove(config_path)

def main():
"""主函数"""
parser = argparse.ArgumentParser(description='Nginx配置管理工具')
parser.add_argument('--server-name', required=True, help='服务器名称(域名)')
parser.add_argument('--root-dir', required=True, help='网站根目录')
parser.add_argument('--port', type=int, default=80, help='监听端口(默认: 80)')
parser.add_argument('--ssl', action='store_true', help='启用SSL')
parser.add_argument('--dry-run', action='store_true', help='仅生成配置不应用')
parser.add_argument('--generate-ssl', action='store_true', help='生成自签名SSL证书')
parser.add_argument('--ssl-days', type=int, default=365, help='SSL证书有效期天数(默认: 365)')

args = parser.parse_args()

# 初始化配置管理器
manager = NginxConfigManager()

# 生成SSL证书(如果需要)
if args.generate_ssl or args.ssl:
manager.generate_self_signed_ssl(args.server_name, args.ssl_days)

# 创建配置内容
additional_directives = [
f" access_log /var/log/nginx/{args.server_name.replace('.', '_')}_access.log;",
f" error_log /var/log/nginx/{args.server_name.replace('.', '_')}_error.log;"
]

config_content = manager.create_site_config(
server_name=args.server_name,
root_dir=args.root_dir,
port=args.port,
ssl=args.ssl,
additional_directives=additional_directives
)

# 写入配置文件
config_path = manager.write_config_file(args.server_name, config_content)

# 显示配置内容
logger.info(f"生成的配置内容:\n{config_content}")

if args.dry_run:
logger.info("执行dry-run模式,配置未应用")
sys.exit(0)

# 测试配置
if not manager.test_config():
logger.error("配置测试失败,已回滚更改")
# 删除生成的配置文件
os.remove(config_path)
sys.exit(1)

# 重新加载Nginx
if not manager.reload_nginx():
logger.error("重新加载Nginx失败,配置可能未生效")
sys.exit(1)

logger.info(f"成功添加并应用Nginx配置: {args.server_name}")

if __name__ == "__main__":
main()


功能

配置生成:

可以为新网站生成基本的 Nginx 配置,支持 HTTP 和 HTTPS。

配置管理:

将配置文件写入到指定目录,文件名基于服务器名称自动生成。

安全检查:

在应用配置前会测试配置文件的有效性,防止错误配置导致 Nginx 无法启动。

自动重载:

配置测试通过后会自动重新加载 Nginx 服务。

详细日志:

提供详细的操作日志,方便排查问题。

SSL 证书生成:

  • 使用 OpenSSL 生成 2048 位 RSA 密钥和自签名证书
  • 支持设置证书有效期(默认为 365 天)
  • 自动配置 Subject Alternative Name (SAN)
  • 证书文件保存在/etc/nginx/ssl目录

证书管理:

  • 检查证书是否存在和有效期
  • 仅在证书不存在或已过期时重新生成
  • 自动设置正确的文件权限

命令行参数:

  • –generate-ssl:强制生成新的 SSL 证书
  • –ssl-days:设置证书有效期天数
  • –server-name:服务器名称(域名) example.com
  • –root-dir:网站根目录 /var/www/example.com
  • –port:监听端口(默认: 80)
  • –dry-run:仅生成配置不应用
  • –ssl:启用SSL

使用示例

添加 HTTPS 网站并自动生成 SSL 证书:

1
2
3
4
5
sudo python nginx_config_manager.py \
--server-name example.com \
--root-dir /var/www/example \
--port 443 \
--ssl

仅生成 SSL 证书不修改 Nginx 配置:

1
2
3
4
sudo python nginx_config_manager.py \
--server-name example.com \
--generate-ssl \
--ssl-days 730

添加 HTTPS 网站并使用现有证书:

1
2
3
4
5
sudo python nginx_config_manager.py \
--server-name example.com \
--root-dir /var/www/example \
--port 443 \
--ssl

注意事项

  • 运行此脚本需要有写入 Nginx 配置目录和执行 Nginx 命令的权限,建议使用 sudo 运行。
  • 如果启用了 SSL,需要手动配置正确的证书路径。
  • 脚本会自动为每个网站创建访问日志和错误日志。
  • 需要系统已安装 OpenSSL 命令行工具
  • 生成的是自签名证书,在浏览器中会显示不安全警告
  • 自签名证书适用于开发环境或内部网站
  • 生产环境建议使用 Let’s Encrypt 等受信任的 CA 颁发的证书
0%