add installation pages

This commit is contained in:
printempw 2016-07-28 15:10:08 +08:00
parent a0da888c1d
commit 881d73263a
14 changed files with 638 additions and 131 deletions

View File

@ -37,10 +37,10 @@
快速使用:
-----------
1. 下载发布的打包版源码,重命名 `.env.example``.env` 并配置你的数据库连接信息(如果是 windows 就重命名为 `.env.`,后面那个点会自动去掉的)
2. 导入根目录下的 `db.sql` 到你的数据库
2. 访问 `/setup/index.php` 进行安装
3. 如果你是用的是 Nginx请配置你的 `nginx.conf` 并加入重写规则
4. 访问你的站点,注册一个新账户
5. (在数据库的 `users` 表中将你的用户 permission 字段设置为 `1` 即可获取管理员权限)
4. 访问你的站点,注册一个新账户或者使用 `安装时所配置的账户` 登录
5. (在数据库的 `users` 表中将你的用户 permission 字段设置为 `1` 即可获取管理员权限 设置为 `2` 即为超级管理员
6. 在角色管理面板使用你的 Minecraft 角色名添加一个新角色
7. 在皮肤库上传你的皮肤 & 披风(可设为私有)并添加至衣柜
8. 应用皮肤 & 披风到你的角色

View File

@ -7,14 +7,20 @@ use \Pecee\SimpleRouter\SimpleRouter as Router;
class Boot
{
public static function loadDotEnv()
public static function loadDotEnv($dir)
{
if (Config::checkDotEnvExist()) {
$dotenv = new \Dotenv\Dotenv(BASE_DIR);
$dotenv = new \Dotenv\Dotenv($dir);
$dotenv->load();
}
}
public static function checkRuntimeEnv()
{
Config::checkPHPVersion();
Config::checkFolderExist();
}
public static function loadServices()
{
// Set Aliases for App\Services

View File

@ -18,6 +18,12 @@ class Config
return require BASE_DIR."/config/view.php";
}
public static function checkPHPVersion()
{
if (strnatcasecmp(phpversion(), '5.5.9') < 0)
throw new E('Blessing Skin Server v3 要求 PHP 版本不低于 5.5.9,当前版本为 '.phpversion(), -1, true);
}
/**
* Check database config
*
@ -46,6 +52,7 @@ class Config
return false;
}
}
return true;
}
@ -56,8 +63,10 @@ class Config
$view_config = self::getViewConfig();
if (!is_dir($view_config['cache_path']))
mkdir($view_config['cache_path']);
if (!is_dir($view_config['cache_path'])) {
if (!mkdir($view_config['cache_path']))
throw new E('缓存文件夹创建失败,请确认目录权限是否正确', -1);
}
return true;
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Services;
class Migration
{
public static function creatTables($prefix = "")
{
Schema::create($prefix.'users', function($table) {
$table->increments('uid');
$table->string('email', 100);
$table->string('nickname', 50);
$table->integer('score');
$table->integer('avatar');
$table->string('password', 255);
$table->string('ip', 32);
$table->integer('permission')->default('0');
$table->dateTime('last_sign_at');
$table->dateTime('register_at');
});
Schema::create($prefix.'closets', function($table) {
$table->increments('uid');
$table->longText('textures');
});
Schema::create($prefix.'players', function($table) {
$table->increments('pid');
$table->integer('uid');
$table->string('player_name', 50);
$table->string('preference', 10);
$table->integer('tid_steve');
$table->integer('tid_alex');
$table->integer('tid_cape');
$table->dateTime('last_modified');
});
Schema::create($prefix.'textures', function($table) {
$table->increments('tid');
$table->string('name', 50);
$table->string('type', 10);
$table->integer('likes');
$table->string('hash', 64);
$table->integer('size');
$table->integer('uploader');
$table->integer('public');
$table->dateTime('last_modified');
});
Schema::create($prefix.'options', function($table) {
$table->increments('id');
$table->string('option_name', 50);
$table->longText('option_value');
});
}
}

View File

@ -0,0 +1,319 @@
/*
* @Author: printempw
* @Date: 2016-07-28 13:15:18
* @Last Modified by: printempw
* @Last Modified time: 2016-07-28 14:09:59
*/
@import "style.scss";
html {
background: #f1f1f1;
margin: 0 20px;
font-weight: 400;
}
body {
background: #FFF none repeat scroll 0% 0%;
color: #444;
font-family: Ubuntu, 'Microsoft Yahei', 'Microsoft Jhenghei', sans-serif;
margin: 140px auto 25px;
padding: 20px 20px 10px;
max-width: 700px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13);
}
h1, h2 {
border-bottom: 1px solid #DEDEDE;
clear: both;
color: #666;
font-size: 24px;
}
#logo a, p, h1, h2 {
font-weight: 400;
}
#logo, h1, h2 {
padding: 0px 0px 7px;
}
p {
padding-bottom: 2px;
font-size: 14px;
line-height: 1.5;
}
#logo a {
font-family: Minecraft, sans-serif;
transition: all .2s ease-in-out;
font-size: 50px;
color: #666;
height: 84px;
line-height: 1.3em;
margin: -130px auto 25px;
padding: 0;
outline: 0;
text-decoration: none;
overflow: hidden;
display: block;
}
#logo a:hover {
color: #42a5f5;
}
/* Mobile phone */
@media (max-width: 48em) {
#logo a {
font-size: 40px;
}
}
@media (max-width: 35.5em) {
#logo a {
font-size: 30px;
}
}
#logo {
margin: 6px 0 14px;
border-bottom: none;
text-align: center;
}
.step {
margin: 20px 0 15px;
text-align: left;
padding: 0;
}
.step>input {
font-family: Ubuntu, 'Microsoft Yahei', 'Microsoft Jhenghei', sans-serif;
}
fieldset {
padding: 0;
margin: 0
}
label {
cursor: pointer
}
.step {
margin: 20px 0 15px
}
.step,th {
text-align: left;
padding: 0
}
.language-chooser.wp-core-ui .step .button.button-large {
height: 36px;
vertical-align: middle;
font-size: 14px
}
.form-table td,.form-table th {
padding: 10px 20px 10px 0;
vertical-align: top;
font-size: 14px
}
td > input {
font-family: Ubuntu, 'Microsoft Yahei', 'Microsoft Jhenghei', sans-serif;
}
textarea {
border: 1px solid #dfdfdf;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box
}
.form-table {
border-collapse: collapse;
margin-top: 1em;
width: 100%
}
.form-table td {
margin-bottom: 9px
}
.form-table th {
text-align: left;
width: 140px
}
.form-table code {
line-height: 18px;
font-size: 14px
}
.form-table p {
margin: 4px 0 0;
font-size: 11px
}
.form-table input {
line-height: 20px;
font-size: 15px;
padding: 3px 5px;
border: 1px solid #ddd;
-webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
box-shadow: inset 0 1px 2px rgba(0,0,0,.07)
}
.form-table input[type=email],.form-table input[type=password],.form-table input[type=text],.form-table input[type=url] {
width: 206px
}
.form-table.install-success td,.form-table.install-success th {
vertical-align: middle;
padding: 16px 20px 16px 0
}
.form-table.install-success td p {
margin: 0;
font-size: 14px
}
.form-table.install-success td code {
margin: 0;
font-size: 18px
}
.container .button,.container .button-primary,.container .button-secondary {
display:inline-block;
text-decoration:none;
font-size:13px;
line-height:26px;
height:28px;
margin:0;
padding:0 10px 1px;
cursor:pointer;
border-width:1px;
border-style:solid;
-webkit-appearance:none;
-webkit-border-radius:3px;
border-radius:3px;
white-space:nowrap;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box
}
.container button::-moz-focus-inner,.container input[type=reset]::-moz-focus-inner,.container input[type=button]::-moz-focus-inner,.container input[type=submit]::-moz-focus-inner {
border-width:0;
border-style:none;
padding:0
}
.container .button-group.button-large .button,.container .button.button-large {
height:30px;
line-height:28px;
padding:0 12px 2px
}
.container .button-group.button-small .button,.container .button.button-small {
height:24px;
line-height:22px;
padding:0 8px 1px;
font-size:11px
}
.container .button-group.button-hero .button,.container .button.button-hero {
font-size:14px;
height:46px;
line-height:44px;
padding:0 36px
}
.container .button:active,.container .button:focus {
outline:0
}
.container .button.hidden {
display:none
}
.container input[type=reset],.container input[type=reset]:active,.container input[type=reset]:focus,.container input[type=reset]:hover {
background:0 0;
border:none;
-webkit-box-shadow:none;
box-shadow:none;
padding:0 2px 1px;
width:auto
}
.container .button,.container .button-secondary {
color:#555;
border-color:#ccc;
background:#f7f7f7;
-webkit-box-shadow:0 1px 0 #ccc;
box-shadow:0 1px 0 #ccc;
vertical-align:top
}
.container p .button {
vertical-align:baseline
}
.container .button-secondary:focus,.container .button-secondary:hover,.container .button.focus,.container .button.hover,.container .button:focus,.container .button:hover {
background:#fafafa;
border-color:#999;
color:#23282d
}
.container .button-link:focus,.container .button-secondary:focus,.container .button.focus,.container .button:focus {
border-color:#5b9dd9;
-webkit-box-shadow:0 0 3px rgba(0,115,170,.8);
box-shadow:0 0 3px rgba(0,115,170,.8)
}
.container .button-secondary:active,.container .button.active,.container .button.active:hover,.container .button:active {
background:#eee;
border-color:#999;
-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);
box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);
-webkit-transform:translateY(1px);
-ms-transform:translateY(1px);
transform:translateY(1px)
}
.container .button.active:focus {
border-color:#5b9dd9;
-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 3px rgba(0,115,170,.8);
box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 3px rgba(0,115,170,.8)
}
.container .button-disabled,.container .button-secondary.disabled,.container .button-secondary:disabled,.container .button-secondary[disabled],.container .button.disabled,.container .button:disabled,.container .button[disabled] {
color:#a0a5aa!important;
border-color:#ddd!important;
background:#f7f7f7!important;
-webkit-box-shadow:none!important;
box-shadow:none!important;
text-shadow:0 1px 0 #fff!important;
cursor:default;
-webkit-transform:none!important;
-ms-transform:none!important;
transform:none!important
}
.container .button-link {
margin:0;
padding:0;
-webkit-box-shadow:none;
box-shadow:none;
border:0;
-webkit-border-radius:0;
border-radius:0;
background:0 0;
outline:0;
cursor:pointer
}
.container .button-primary {
background:#0085ba;
border-color:#0073aa #006799 #006799;
-webkit-box-shadow:0 1px 0 #006799;
box-shadow:0 1px 0 #006799;
color:#fff;
text-decoration:none;
text-shadow:0 -1px 1px #006799,1px 0 1px #006799,0 1px 1px #006799,-1px 0 1px #006799
}
.container .button-primary.focus,.container .button-primary.hover,.container .button-primary:focus,.container .button-primary:hover {
background:#008ec2;
border-color:#006799;
color:#fff
}
.container .button-primary.focus,.container .button-primary:focus {
-webkit-box-shadow:0 1px 0 #0073aa,0 0 2px 1px #33b3db;
box-shadow:0 1px 0 #0073aa,0 0 2px 1px #33b3db
}
.alert {
padding: 15px;
margin-top: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}

View File

@ -22,5 +22,6 @@ return [
'Updater' => 'App\Services\Updater',
'Config' => 'App\Services\Config',
'Schema' => 'App\Services\Schema',
'Boot' => 'App\Services\Boot'
'Boot' => 'App\Services\Boot',
'Migration' => 'App\Services\Migration'
];

120
db.sql
View File

@ -1,120 +0,0 @@
/*
* @Author: printempw
* @Date: 2016-07-24 13:37:40
* @Last Modified by: printempw
* @Last Modified time: 2016-07-24 14:49:24
*/
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
SET NAMES utf8mb4;
--
-- Database: `skin-v3`
--
-- --------------------------------------------------------
--
-- 表的结构 `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(100) NOT NULL,
`nickname` varchar(50) NOT NULL DEFAULT '',
`score` int(11) NOT NULL,
`avatar` int(11) NOT NULL,
`password` varchar(255) NOT NULL,
`ip` varchar(32) NOT NULL,
`permission` int(11) NOT NULL DEFAULT '0',
`last_sign_at` datetime NOT NULL,
`register_at` datetime NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- 表的结构 `closets`
--
CREATE TABLE IF NOT EXISTS `closets` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`textures` longtext NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- 表的结构 `players`
--
CREATE TABLE IF NOT EXISTS `players` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`player_name` varchar(50) NOT NULL,
`preference` varchar(10) NOT NULL,
`tid_steve` int(11) NOT NULL,
`tid_alex` int(11) NOT NULL,
`tid_cape` int(11) NOT NULL,
`last_modified` datetime NOT NULL,
PRIMARY KEY (`pid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- 表的结构 `textures`
--
CREATE TABLE IF NOT EXISTS `textures` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`type` varchar(10) NOT NULL,
`likes` int(11) NOT NULL,
`hash` varchar(64) NOT NULL,
`size` int(11) NOT NULL,
`uploader` int(11) NOT NULL,
`public` int(11) NOT NULL,
`upload_at` datetime NOT NULL,
PRIMARY KEY (`tid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- 表的结构 `options`
--
CREATE TABLE IF NOT EXISTS `options` (
`id` int(20) unsigned NOT NULL AUTO_INCREMENT,
`option_name` varchar(50) NOT NULL,
`option_value` longtext NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- 转存表中的数据 `options`
--
INSERT INTO `options` (`id`, `option_name`, `option_value`) VALUES
(1, 'site_url', ''),
(2, 'site_name', 'Blessing Skin'),
(3, 'site_description', '开源的 PHP Minecraft 皮肤站'),
(4, 'user_can_register', '1'),
(5, 'regs_per_ip', '3'),
(6, 'api_type', '0'),
(7, 'announcement', '欢迎使用 Blessing Skin Server 3.0'),
(8, 'color_scheme', 'skin-blue'),
(9, 'home_pic_url', './assets/images/bg.jpg'),
(10, 'current_version', '3.0-beta'),
(11, 'custom_css', ''),
(12, 'custom_js', ''),
(13, 'update_url', 'https://work.prinzeugen.net/update.json'),
(14, 'allow_chinese_playername', '1'),
(15, 'show_footer_copyright', '1'),
(16, 'comment_script', ''),
(17, 'user_initial_score', '1000'),
(18, 'sign_gap_time', '24');

View File

@ -12,8 +12,11 @@ require BASE_DIR.'/vendor/autoload.php';
// Load Aliases
App\Services\Boot::loadServices();
// Check Runtime Environment
Boot::checkRuntimeEnv();
// Load dotenv Configuration
Boot::loadDotEnv();
Boot::loadDotEnv(BASE_DIR);
// Register Error Handler
Boot::registerErrorHandler();
@ -30,8 +33,6 @@ if (!Config::checkTableExist($db_config)) {
Http::redirect('../setup/index.php');
}
Config::checkFolderExist();
// Start Session
Boot::startSession();

View File

@ -0,0 +1,10 @@
@extends('setup.master')
@section('content')
<h1>已安装过</h1>
<p>Blessing Skin Server 看起来已经安装妥当。如果想重新安装,请删除数据库中的旧数据表,或者换一个数据表前缀。</p>
<p class="step">
<a href="../" class="button button-large">返回首页</a>
</p>
@endsection

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="noindex,nofollow" />
<title>Blessing Skin Server 安装程序</title>
<link rel="shortcut icon" href="../assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="../assets/dist/css/install.css">
</head>
<body class="container">
<p id="logo"><a href="https://github.com/printempw/blessing-skin-server" tabindex="-1">Blessing Skin Server</a></p>
@yield('content')
</body>
</html>

View File

@ -0,0 +1,12 @@
@extends('setup.master')
@section('content')
<h1>欢迎</h1>
<p>欢迎使用 Blessing Skin Server V3</p>
<p>成功连接至 MySQL 服务器 {{ $server }},点击下一步以开始安装。</p>
<p class="step">
<a href="?step=2" class="button button-large">下一步</a>
</p>
@endsection

View File

@ -0,0 +1,55 @@
@extends('setup.master')
@section('content')
<h1>填写信息</h1>
<p>您需要填写一些基本信息。无需担心填错,这些信息以后可以再次修改。</p>
<form id="setup" method="post" action="index.php?step=3" novalidate="novalidate">
<table class="form-table">
<tr>
<th scope="row"><label for="email">管理员邮箱</label></th>
<td>
<input name="email" type="email" id="email" size="25" value="" />
<p>这是唯一的超级管理员账号,可 添加/取消 其他管理员。</p>
</td>
</tr>
<tr class="form-field form-required">
<th scope="row"><label for="password">密码</label></th>
<td>
<input type="password" name="password" id="password" class="regular-text" autocomplete="off" />
<p>
<span class="description important">
<b>重要:</b>您将需要此密码来登录管理皮肤站,请将其保存在安全的位置。
</span>
</p>
</td>
</tr>
<tr class="form-field form-required">
<th scope="row"><label for="confirm-pwd">重复密码</label></th>
<td>
<input type="password" name="confirm-pwd" id="confirm-pwd" autocomplete="off" />
</td>
</tr>
<tr>
<th scope="row"><label for="sitename">站点名称</label></th>
<td>
<input name="sitename" type="text" id="sitename" size="25" value="" />
<p>
<span class="description important">
将会显示在首页以及标题栏,最好用纯英文(字体原因)
</span>
</p>
</td>
</tr>
</table>
@if (isset($_SESSION['msg']))
<div class="alert alert-warning" role="alert">{{ htmlspecialchars($_SESSION['msg']) }}</div>
<?php unset($_SESSION['msg']); ?>
@endif
<p class="step">
<input type="submit" name="Submit" id="submit" class="button button-large" value="开始安装" />
</p>
</form>
@endsection

View File

@ -0,0 +1,21 @@
@extends('setup.master')
@section('content')
<h1>成功!</h1>
<p>Blessing Skin Server 安装完成。您是否还沉浸在愉悦的安装过程中?很遗憾,一切皆已完成! :)</p>
<table class="form-table install-success">
<tr>
<th>邮箱</th>
<td>{{ $email }}</td>
</tr>
<tr>
<th>密码</th>
<td><p><em>{{ $password }}</em></p></td>
</tr>
</table>
<p class="step">
<a href="../" class="button button-large">首页</a>
</p>
@endsection

117
setup/index.php Normal file
View File

@ -0,0 +1,117 @@
<?php
/**
* Installation of Blessing Skin Server
*/
// Define Base Directory
define('BASE_DIR', dirname(dirname(__FILE__)));
// Register Composer Auto Loader
require BASE_DIR.'/vendor/autoload.php';
// Load Aliases
App\Services\Boot::loadServices();
// Check Runtime Environment
Boot::checkRuntimeEnv();
// Load dotenv Configuration
Boot::loadDotEnv(BASE_DIR);
// Register Error Handler
Boot::registerErrorHandler();
$db_config = Config::getDbConfig();
// Boot Eloquent to make Schema available
if (Config::checkDbConfig($db_config)) {
Boot::bootEloquent($db_config);
}
Boot::startSession();
// If already installed
if (Config::checkTableExist($db_config)) {
View::show('setup.locked');
exit;
}
$step = isset($_GET['step']) ? $_GET['step'] : 1;
/*
* Stepped installation
*/
switch ($step) {
case 1:
$server = $db_config['username']."@".$db_config['host'];
echo View::make('setup.steps.1')->with('server', $server);
break;
case 2:
echo View::make('setup.steps.2');
break;
case 3:
// check post
if (isset($_POST['email']) && isset($_POST['password']) && isset($_POST['confirm-pwd']))
{
if ($_POST['password'] != $_POST['confirm-pwd'])
Http::redirect('index.php?step=2', '确认密码不一致');
$email = $_POST['email'];
$password = $_POST['password'];
$sitename = isset($_POST['sitename']) ? $_POST['sitename'] : "Blessing Skin Server";
if (Validate::checkValidEmail($email)) {
if (strlen($password) > 16 || strlen($password) < 8) {
Http::redirect('index.php?step=2', '无效的密码。密码长度应该大于 8 并小于 16。');
} else if (Utils::convertString($password) != $password) {
Http::redirect('index.php?step=2', '无效的密码。密码中包含了奇怪的字符。');
}
} else {
Http::redirect('index.php?step=2', '邮箱格式不正确。');
}
} else {
Http::redirect('index.php?step=2', '表单信息不完整。');
}
Migration::creatTables($db_config['prefix']);
$options = [
'site_url' => Http::getBaseUrl(),
'site_name' => $_POST['sitename'],
'site_description' => '开源的 PHP Minecraft 皮肤站',
'user_can_register' => '1',
'regs_per_ip' => '3',
'api_type' => '0',
'announcement' => '欢迎使用 Blessing Skin Server 3.0',
'color_scheme' => 'skin-blue',
'home_pic_url' => './assets/images/bg.jpg',
'current_version' => '3.0-beta',
'custom_css' => '',
'custom_js' => '',
'update_url' => 'https://work.prinzeugen.net/update.json',
'allow_chinese_playername' => '1',
'show_footer_copyright' => '1',
'comment_script' => '',
'user_initial_score' => '1000',
'sign_gap_time' => '24'
];
foreach ($options as $key => $value) {
Option::add($key, $value);
}
$user = new App\Models\User($_POST['email']);
$user->register($_POST['password'], Http::getRealIP());
if (!is_dir(BASE_DIR.'/textures/')) {
if (!mkdir(BASE_DIR.'/textures/'))
throw new E('textures 文件夹创建失败,请确认目录权限是否正确,或者手动放置一个。', -1);
}
echo View::make('setup.steps.3')->with('email', $_POST['email'])->with('password', $_POST['password']);
break;
}