What & How & Why

常见运维问题

一些个人网站环境配置及常见问题记录。


AMH

References:

AMH 查看默认密码

使用以下命令则可以查看:

cat /root/amh.log

MySQL无法自动启动

MYSQL 在重启服务器时不能启动,尝试以下方法:

删除 my.inf

cd /etc/
rm my.inf

PID file not found

#error msg
ERROR! MySQL server PID file could not be found!
这种情况多半是硬盘空间满了导致系统强制结束了 mysql 的进程。用下面的命令之后再重启一下 mysql 进系统看看。
rm -f /tmp/mysql-5.6.sock tmp/mysql-5.6.sock.lock

SSL 配置

SSL多虚拟主机

单IP的多个虚拟主机只需要一个 SSL证书即可。配置步骤:

  1. amh.sh/ssl.htm 上申请证书,申请的时候将所有虚拟主机的域名输入。
  2. 将得到的证书在 amhssl 中添加。
  3. 制定自动更新的策略。

Dokuwiki

Dokuwiki Nginx Rewrite 规则

将下列规则添加到 AMH 的 rewrite 模块中:

location / {
    index doku.php;
    try_files $uri $uri/ @dokuwiki;
}

#caching: warning: will cause picture 404 problem if your wiki is running in rewrite mode 1.
  location ~ ^/lib.*\.(gif|png|ico|jpg)$ {
    expires 30d;
}

#dir safety
location ^~ /conf/ { return 403; }
location ^~ /data/ { return 403; }

#rewrite
Location @dokuwiki {
        # rewrites "doku.php/" out of the URLs if you set the userewrite setting to .htaccess in dokuwiki config page
        rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
        rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
        rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
        rewrite ^/(.*) /doku.php?id=$1&$args last;
    }

Dokuwiki 重写后图片显示不正常(Nginx&AMH)

Dokuwiki URL Rewrite 与以下代码冲突:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp3|wma)$ {
    expires      30d;
}
而 AMH 的 nginx.conf 需要到所建立的环境下的 vhost 目录下找对应虚拟主机的文件(一般是以 域名.conf 格式的 conf 文件)。找到以后然后将里面的该段代码注释掉。如果用了 Https, 则同时需要注释掉 HTTPS 版本的 conf 文件。

Dokuwiki Nginx 安全规则

该段代码用于防止除 confdata 以外的重要文件本下载。
使用:添加以下代码到 rewrite 模块中:

# Block access to data folders
location ~ /(data|conf|bin|inc)/ {
    deny all;
}
# Block access to .htaccess files
location ~ /\.ht {
    deny all;
}

所有 MIMe 类型

Dokuwiki 使用

  • 插件的安装:直接到后台的插件管理器中寻找指定的插件下载安装即可。
常用插件
  • Add New Page:可以直接在没有创建页面的位置直接创建页面,非常方便。
  • Code Prettifier:非常好用的代码高亮。
  • Edittable :图形化界面的表格创建,比较方便,但有自适应问题。
  • Fastwiki :优化 Dokuwiki 的访问速度(预加载)。
  • Google Document Viewer :可以直接在 Dokuwiki 中查看 Google 文档。
  • MathJax :数学公式插件。
  • Pure SVG Insert:使 Dokuwiki 支持 SVG 的显示。
  • DokuWiki Upgrade:一键升级 Dokuwiki,必备插件。
  • Wrap:很好用的内容排版插件。

Dokuwiki 相关问题

Dokuwiki 上传附件显示 "Failed"

解决方法:进入服务器的 php.ini 配置文件,添加或修改以下设置:

always_populate_raw_post_data = -1

Igor 版本 svg 缩放不正确

官方 issue 中解释该设置是一个 guard,不应修改。推荐对指定插件进行修改。

/lib/styles/all.csssvg

width: 1.2em;
替换为
width: auto;

服务器通用前端优化

Leverage browser caching

如果使用 Nginx 对 Dokuwiki 进行 Leverage browser caching,会导致被缓存的图片无法在媒体管理器以及正文中显示。该问题出现在从 Apache 迁移到 Nginx 的过程中,怀疑是 Nginx 本身对 Dokuwiki 的支持不佳。

解决方法(过时):由于 AMH 的 Nginx.conf 自带 caching,需要手动去 /yourwebdir/vhost/yourweb.conf 将如下的缓存规则删除。

解决方法:使用新版本的重写规则,请参见前面。



Nginx 版本的通用优化代码如下:

location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 365d;
}

location ~*  \.(pdf)$ {
    expires 30d;
}

Typecho

一些 Typecho 的改造 / 使用 问题收集。

后台 Access Denied

在 AMH 中开启 pathinfo 插件的支持即可。

Typecho 改造指南

评论:添加额外样式给管理员

Typecho 可以针对当前用户是否是管理员来添加额外的样式。一个简单的应用例子如下:

首先我们需要官方帮助文档中的自定义函数。该函数用于判断当前评论用户是否为管理员,通过 AuthorIdownerid 是否相等来判断:

<?php function threadedComments($comments, $options) {
    $commentClass = '';
    if ($comments->authorId) {
        if ($comments->authorId == $comments->ownerId) {
            $commentClass .= ' comment-by-author';  //如果是文章作者的评论添加 .comment-by-author 样式
        } else {
            $commentClass .= ' comment-by-user';  //如果是评论作者的添加 .comment-by-user 样式
        }
    } 
    $commentLevelClass = $comments->_levels > 0 ? ' comment-child' : ' comment-parent';  //评论层数大于0为子级,否则是父级
?>
/* 自定义评论的代码结构 */
<?php } ?>

接下来会通过一段函数将增加的样式表打印出来,结果是 comment-by-author 或者 comment-by-user
<li id="li-<?php $comments->theId(); ?>" class="comment-body<?php 
if ($comments->levels > 0) {
    echo ' comment-child';
    $comments->levelsAlt(' comment-level-odd', ' comment-level-even');
} else {
    echo ' comment-parent';
}
$comments->alt(' comment-odd', ' comment-even');
echo $commentClass;
?>">

有了这两段函数以后,剩下的工作就很简单了。以我要替换管理员在评论中名字的背景为例。首先将打印出来的字段应用到 HTML 的调用中:
<span class="comment-author<?php echo $commentClass; ?>"><?php $comments->author(); ?></span>

上面的代码直接把 $commentClass,也就是我们要额外附加的样式表加入了 comment-author 这个样式里。如果是管理员,那么调用的样式表就是 .comment-author.comment-by-author,否则就是 .comment-author.comment-by-user

最后去 CSS 里添加样式表即可:

.comment-author.comment-by-author {background: #636363;padding: 5px;}

评论:重写发表评论时间

Typecho 默认的时间输出格式是 PHP 的格式。如果我们希望将其转换为“发表于多久多久之前” 的格式,可以通过以下的手段来实现:

首先我们需要往 function.php 中添加一个时间重写函数:

//时间转换输出
function timesince($older_date,$comment_date = false) {
$chunks = array(
array(86400 , 'Day'),
array(3600 , 'Hour'),
array(60 , 'Minute'),
array(1 , 'Second'),
);
$newer_date = time();
$since = abs($newer_date - $older_date);

for ($i = 0, $j = count($chunks); $i < $j; $i++){
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($since / $seconds)) != 0) break;
}
/*如果是中文则不需要 count 的判断,并且也不需要空格*/
if ($count == 1) {
$output = $count.' '.$name.' '.'ago';
}
else {$output = $count.' '.$name.'s'.' '.'ago'; }
return $output;
}
这个函数会获取以前的时间作为参数,并转换成相应的字段,我们只需要拿到 comments.php 中使用即可。使用方法如下:
<?php echo timesince($comments->created);?>

评论:回复评论自带@跳转

首先到 function.php 中添加如下两个函数:

//获取评论的锚点链接
function get_comment_at($coid)
{
    $db   = Typecho_Db::get();
    $prow = $db->fetchRow($db->select('parent')->from('table.comments')
                                 ->where('coid = ? AND status = ?', $coid, 'approved'));
    $parent = $prow['parent'];
    if ($parent != "0") {
        $arow = $db->fetchRow($db->select('author')->from('table.comments')
                                     ->where('coid = ? AND status = ?', $parent, 'approved'));
        $author = $arow['author'];
        $href   = '<a href="#comment-' . $parent . '">@' . $author . '</a>';
        echo $href;
    } else {
        echo '';
    }
}
//输出评论内容
function get_filtered_comment($coid){
    $db   = Typecho_Db::get();
    $rs=$db->fetchRow($db->select('text')->from('table.comments')
                                 ->where('coid = ? AND status = ?', $coid, 'approved'));
    $content=$rs['text'];
    echo $content;
}
?>

再到 comments.php 中使用如下代码调用即可:
<?php get_comment_at($comments->coid)?>

添加自定义html标签到文章内容

找到文件夹 /var 下的文件 HyperDown.php 第十七行,添加需要使用的标签即可。

Typecho 升级指南

Typecho 可以通过以下方式来进行升级:

  1. 下载最新的版本:Typecho On Github
  2. 删除服务器上三个目录的文件,如果修改了源代码可以使用 Github 的对比工具来进行合并。三个目录分别是:/admin, /var, 和根目录下的 index.php
  3. 上传最新的对应文件即可。

Typecho URL重写

Apache 重写规则

添加如下代码到 .htaccess 文件中即可。注意:RewriteBase 后是网站的目录。

<IfModule mod_rewrite.c >
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1]
</IfModule>

InstantClick

instantclick.js 是一种ajax无刷新和预加载页面的技术,对于普通博客程序,有着明显的加速作用。instantclick.jspjax 加上预加载页面,而且,使用方法也十分的简单。

安装InstantClick

下载 InstantClick

footer.php</body> 标签之前插入以下代码:

...
<script src="instantclick.min.js" data-no-instant></script>
<script data-no-instant>InstantClick.init();</script>
</body>
</html>

点击时候的预加载

默认情况下是在鼠标悬停到链接上方即开始加载,有时候这样会带来服务器的额外开销问题。将 init 函数的参数设置为 mousedown 则可以通过点击的一瞬间进行预加载:

InstantClick.init('mousedown');

修改预加载条的颜色

在样式表中加入如下代码即可:

#instantclick-bar { background: gray; }

与 PrismJS兼容

先对 Prismjs 中需要预加载的部分进行设置:

打开 Plugin.php 文件,找到 58 行:

<script>var pres = document.getElementsByTagName('pre');
改为:
<script data-no-instant>var pres = document.getElementsByTagName('pre');
找到 64 行:
<script src="' . $jsUrl . '"></script>
改为:
<script src="' . $jsUrl . '" data-no-instant></script>
找到 66 行:
<script defer="defer" src="' . Helper::options()->pluginUrl . '/Prismjs/line-number-wrap-fix.js' . '"></script>
改为:
<script defer="defer" src="' . Helper::options()->pluginUrl . '/Prismjs/line-number-wrap-fix.js' . '" data-no-instant></script>

再修改位于 footer.phpinstantclick 的白名单部分,添加属于 prismjs 的规则:
if (typeof Prism !== 'undefined') {<?php  if (Helper::options()->plugin('Prismjs')->showln): ?>
var pres = document.getElementsByTagName('pre');
    for (var i = 0; i < pres.length; i++){
        if (pres[i].getElementsByTagName('code').length > 0)
            pres[i].className  = 'line-numbers';} <?php endif; ?>
Prism.highlightAll(true,null);}
PHP 部分:
{<?php  if (Helper::options()->plugin('Prismjs')->showln): ?>''
对判断插件是否使用了行号。如果没启用插件,会导致网站 505 错误。

如果不使用行号,直接使用以下代码:
if (typeof Prism !== 'undefined') { Prism.highlightAll(true,null); }

Mathjax

mathjax 3 简单配置

MathJax = {
        showProcessingMessages: false,
        messageStyle: "none",    
    tex: {
        inlineMath: [ ["$","$"], ["\\\\(","\\\\)"] ],
        displayMath: [ ["$$","$$"], ["\\\\[","\\\\]"] ],
        processEscapes: true
    },
    svg: {
        fontCache: "global"
    }
}

igor 版本的缩放问题

添加以下代码到 /lib/styles/all.css

mjx-container[jax="SVG"] > svg {
    height: auto;
    width: auto;
}

Mathjax与InstantClick不兼容

footer.php 中找到 InstantClick.on('change', function(isInitialLoad) 部分,加入以下规则:

if (typeof MathJax !== 'undefined'){MathJax.Hub.Queue(["Typeset",MathJax.Hub]);}

Mathjax的直接添加

header.php 中的 <head> 之间加入如下代码:

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'], ["\\(","\\)"] ],
      displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
      processEscapes: true
    },
    
    "HTML-CSS": { availableFonts: ["TeX"] }
  });
</script>
<script type="text/javascript"
   src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>

近日 MathJax 自营 CDN 已经停止服务,需自行寻找相关 CDN。


Mathjax按需加载

首先在 post.php 中添加一个全局变量用于检测页面是否有数学公式:

<?php $GLOBALS['mathjax'] = strpos($this->content, '[') || strpos($this->content, '('); ?>
接着到 Mathjax.js 调用的地方设置条件(我的是footer.php):
<?php if($GLOBALS['mathjax']):?>
  <!--mathjax-->
  <script type="text/javascript" src="https://cdn.staticfile.org/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<?php endif;?>

Mathjax不显示加载信息

MathJax.Hub.Config 中添加如下代码:

showProcessingMessages: false, //关闭js加载过程信息
messageStyle: "none", //不显示信息

Disqus

Disqus loading on scroll

本实现需要 jquery 支持,并编辑 comments.php

新版本不再使用 jquery。



comments.php 中加入 如下 JS 代码:

<script>
var disqus_loaded = false;

function load_disqus()
{
  disqus_loaded = true;
  var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
  dsq.src = "https://codinghare.disqus.com/embed.js";
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
}

window.onscroll = function(e) {
    if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
        //hit bottom of page
        if (disqus_loaded==false){ load_disqus() };
    }
};
</script>

注意替换 embed.js 的地址为自己的 Disqus地址(直接替换 shortname 即可)。

html 调用:
<div id="disqus_thread"></div>

Flarum

解决 nginx 重写的问题

Flarum 自己带一个子目录 public 作为论坛的首页目录。很多网上的教程建议直接重写 vhost 中的 root 路径,但 AMH 对此似乎并不友好。另外一种办法是通过面板建立子域主机来重新定向域名的根目录到 public。举个例子,如果需要指定域名 domain.com 到该目录,需要修改的如下:

  1. 绑定 127.0.0.1父主机
  2. 绑定 domain.com 到子域主机
  3. 绑定子目录中填写 /public
  4. 在子主机中包含 .nginx.conf(位于父主机网站根目录下),可参考官方安装帮助。

Flarum 目录权限

$chown www:www -R /home/wwwroot/环境/domain/域名/web/

InnodB

如果遇到以下的错误:

flarum Something went wrong: SQLSTATE[42000]: Syntax error or access violation: 1286 Unknown storage engine 'InnoDB'

需要到 MAadmin - 参数设置 中打开 InnoDB 的开关。

https 403

如果出现 http 访问正常 而 https 访问 403 的情况,需要到 config.php 做如下修改:

//default
http://yourdomain.com 
//change to
https://yourdomain.com