separate v2 data migrations as a plugin

This commit is contained in:
printempw 2016-11-25 22:34:41 +08:00
parent 3811f7d704
commit 28ea2b2858
9 changed files with 0 additions and 583 deletions

View File

@ -1,169 +0,0 @@
<?php
namespace App\Services;
use Option;
class Updater
{
/**
* Current version
*
* @var string
*/
public $current_version = "";
/**
* Latest version
*
* @var string
*/
public $latest_version = "";
/**
* Latest update time in Y-m-d H:i:s format
*
* @var string
*/
public $update_time = "";
/**
* See /config/update.php
*
* @var array
*/
private $update_sources = null;
/**
* Current selected update source
*
* @var array
*/
private $current_source = null;
/**
* Details of updates
*
* @var array
*/
private $update_info = null;
public function __construct($current_version)
{
$this->current_version = $current_version;
$this->update_sources = config('update');
$source = Option::get('update_source');
if (!isset($this->update_sources[$source])) {
Option::set('update_source', config('options.update_source'));
}
$this->current_source = $this->update_sources[Option::get('update_source')];
}
/**
* Get update info from selected json source
*
* @return array Decoded json
*/
public function getUpdateInfo()
{
$ch = curl_init();
// add timestamp to control cdn cache
$url = $this->current_source['update_url']."?v=".substr(time(), 0, -3);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// quick fix for accessing https resources
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);
curl_close($ch);
$this->update_info = json_decode($result, true);
return $this->update_info;
}
/**
* Check for updates
*
* @return void
*/
public function checkUpdate()
{
$info = $this->getUpdateInfo();
$this->latest_version = $info['latest_version'];
$this->update_time = date('Y-m-d H:i:s', $info['update_time']);
}
/**
* Download update files
*
* @param bool $silent Don't print messages
* @return void
*/
public function downloadUpdate($silent = true)
{
$release_url = $this->update_info['releases'][$this->latest_version]['release_url'];
if (!$silent)
echo "<p>正在下载更新包:$release_url </p>";
// I don't know why curl can't get full file content here..
$file = file_get_contents($release_url);
if (!$silent)
echo "<p>下载完成。</p>";
$update_cache = storage_path('update_cache');
if (!is_dir($update_cache)) {
if (false === mkdir($update_cache)) {
exit('<p>创建下载缓存文件夹失败,请检查目录权限。</p>');
}
}
$zip_path = $update_cache."update_".time().".zip";
if (Storage::put($zip_path, $file) === false) {
Storage::removeDir($update_cache);
return false;
}
return $zip_path;
}
/**
* Check if a new version is available
*
* @return bool
*/
public function newVersionAvailable()
{
$this->checkUpdate();
return $this->compareVersion($this->latest_version, $this->current_version);
}
public function getUpdateSources()
{
return $this->update_sources;
}
/**
* Compare version string
*
* @param string $v1
* @param string $v2
* @return boolean
*/
private function compareVersion($v1, $v2)
{
if (version_compare($v1, $v2) > 0) {
// v1 > v2
return true;
} else {
// v1 < v2 || v1 = v2
return false;
}
}
}

View File

@ -23,7 +23,6 @@ $menu['admin'] = array(
['title' => 'general.customize', 'link' => 'admin/customize', 'icon' => 'fa-paint-brush'],
['title' => 'general.score-options', 'link' => 'admin/score', 'icon' => 'fa-credit-card'],
['title' => 'general.options', 'link' => 'admin/options', 'icon' => 'fa-cog'],
['title' => 'general.import-v2', 'link' => 'setup/migrations', 'icon' => 'fa-undo'],
['title' => 'general.check-update', 'link' => 'admin/update', 'icon' => 'fa-arrow-up']
);

View File

@ -1,27 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Update Sources
|--------------------------------------------------------------------------
|
| Urls to get update information
|
*/
return array(
'nyavm' => [
'name' => 'LizCat',
'update_url' => 'http://cdn.prinzeugen.net/update.json',
'description' => '感谢 <a href="https://www.nyavm.com/">NyaVM</a> 提供的 Anycast CDN国内外主机都可获得不错的速度。'
],
'little_service' => [
'name' => 'LittleService-COS',
'update_url' => 'http://cos.littleservice.cn/bs/update.json',
'description' => '由 <a href="http://littleqiu.net/">Little_Qiu</a> 及其 <a href="http://studio.littleservice.cn/">团队</a> 维护的非官方更新源,国内主机使用可能会获得一定的加速 Buff。不建议海外主机的用户使用。'
],
'local' => [
'name' => 'LocalHost',
'update_url' => 'http://127.0.0.1/test/update.json',
'description' => '本地调试用,请勿选择(炸了别怪我)'
]
);

View File

@ -1,67 +0,0 @@
@extends('admin.master')
@section('title', trans('general.download-update'))
@section('content')
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
{{ trans('general.download-update') }}
<small>Download Updates</small>
</h1>
</section>
<!-- Main content -->
<section class="content">
<div class="box box-solid">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('general.download-update') }}</h3>
</div><!-- /.box-header -->
<div class="box-body"> <?php
$updater = new Updater(config('app.version'));
if ($updater->newVersionAvailable()) {
$zip_path = $updater->downloadUpdate(false);
if ($zip_path === false) {
exit('<p>无法下载更新包。</p>');
}
$zip = new ZipArchive();
$extract_dir = storage_path("update_cache/{$updater->latest_version}");
$res = $zip->open($zip_path);
if ($res === true) {
echo "<p>正在解压更新包</p>";
$zip->extractTo($extract_dir);
} else {
exit('<p>更新包解压缩失败。错误代码:'.$res.'</p>');
}
$zip->close();
if (Storage::copyDir($extract_dir, base_path()) !== true) {
Storage::removeDir(storage_path('update_cache'));
exit('无法覆盖文件。');
} else {
echo "<p>正在覆盖文件</p>";
Storage::removeDir(storage_path('update_cache'));
echo "<p>正在清理</p>";
}
echo "<p>更新完成。</p>";
} else {
echo "<p>无可用更新。</p>";
} ?>
</div><!-- /.box-body -->
<div class="box-footer">
<a href="{{ url('setup/update') }}" class="btn btn-primary">下一步</a>
</div>
</div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
@endsection

View File

@ -1,95 +0,0 @@
@extends('setup.migrations.master')
@section('content')
<?php $step = isset($_GET['step']) ? $_GET['step'] : '1'; ?>
{{-- Step 1: --}}
@if ($step == '1')
<h1>同时导入用户数据以及用户材质</h1>
<p>将同时导入用户数据以及材质,逻辑比单独导入更加完善。</p>
<p>导入后材质的上传者将被设置为 v2 的原用户,上传时间将被设置为 v2 用户的最后修改时间。导入后的材质会被自动添加至原上传者的衣柜中,并应用至其所属角色。</p>
<p><b>注意:</b> 请先将 v2 users 表改名导入到当前 v3 的同一数据库中</p>
<hr />
<form id="setup" method="post" action="index.php?action=import-v2-both&step=2" novalidate="novalidate">
<table class="form-table">
<tr>
<th scope="row"><label for="v2_table_name">v2 的用户表名</label></th>
<td>
<input name="v2_table_name" type="v2_table_name" id="v2_table_name" size="25" value="" />
<p>就是你改名过的 v2 users 表现在的名字</p>
</td>
</tr>
<tr>
<th scope="row"><label for="texture_name_pattern">导入后的材质名称</label></th>
<td>
<input name="texture_name_pattern" type="text" id="texture_name_pattern" size="25" value="{username} - {model}" />
<p>
<span class="description important">
{username} 表示材质原本的上传者用户名,{model} 表示原来材质的模型
</span>
</p>
</td>
</tr>
<tr>
<th scope="row">私密材质</th>
<td>
<label for="import_as_private">
<input name="import_as_private" type="checkbox" id="import_as_private" size="25" /> 导入为私密材质
</label>
</td>
</tr>
</table>
@if (Session::has('msg'))
<div class="alert alert-warning" role="alert">{{ Session::pull('msg') }}</div>
@endif
<p class="step">
<input type="submit" name="submit" id="submit" class="button button-large" value="开始迁移" />
</p>
</form>
@endif
{{-- Step 2: --}}
@if ($step == '2')
<?php
if (check_post(['v2_table_name', 'texture_name_pattern'], true)) {
if ($_POST['v2_table_name'] == "") {
redirect_to('index.php?action=import-v2-both&step=1', 'v2 users 表名不能为空');
} else {
if (Utils::convertString($_POST['v2_table_name']) != $_POST['v2_table_name'])
redirect_to('index.php?action=import-v2-both&step=1', "表名 {$_POST['v2_table_name']} 中含有无效字符");
if (!Database::hasTable($_POST['v2_table_name'])) {
redirect_to('index.php?action=import-v2-both&step=1', "数据表 {$_POST['v2_table_name']} 不存在");
}
}
} else {
redirect_to('index.php?action=import-v2-both&step=1', '表单信息不完整');
}
?>
<h1>导入成功</h1>
<?php $result = migrate('import-v2-both'); ?>
<p>已导入 {{ $result['user']['imported'] }} 个用户,{{ $result['user']['duplicated'] }} 个用户因重复而未导入。</p>
<p>已导入 {{ $result['texture']['imported'] }} 个材质到皮肤库,{{ $result['texture']['duplicated'] }} 个材质因重复而未导入。</p>
<p class="step">
<a href="../../" class="button button-large">导入完成</a>
</p>
@endif
@endsection

View File

@ -1,105 +0,0 @@
@extends('setup.migrations.master')
@section('content')
<?php $step = isset($_GET['step']) ? $_GET['step'] : '1'; ?>
{{-- Step 1: --}}
@if ($step == '1')
<h1>导入皮肤库</h1>
<p>本功能用于导入 v2 用户皮肤至 v3 的皮肤库</p>
<p>注意:请先将 v2 users 表改名导入到当前 v3 的同一数据库中</p>
<form id="setup" method="post" action="index.php?action=import-v2-textures&step=2" novalidate="novalidate">
<table class="form-table">
<tr>
<th scope="row"><label for="v2_table_name">v2 的用户表名</label></th>
<td>
<input name="v2_table_name" type="v2_table_name" id="v2_table_name" size="25" value="" />
<p>就是你改名过的 v2 users 表现在的名字</p>
</td>
</tr>
<tr>
<th scope="row"><label for="uploader_uid">材质上传者 uid</label></th>
<td>
<input name="uploader_uid" type="text" id="uploader_uid" size="25" value="0" />
<p>
<span class="description important">
导入后的材质在皮肤库中显示的上传者,填写 0 会显示为「不存在的用户」
</span>
</p>
</td>
</tr>
<tr>
<th scope="row"><label for="texture_name_pattern">导入后的材质名称</label></th>
<td>
<input name="texture_name_pattern" type="text" id="texture_name_pattern" size="25" value="{username} - {model}" />
<p>
<span class="description important">
{username} 表示材质原本的上传者用户名,{model} 表示原来材质的模型
</span>
</p>
</td>
</tr>
<tr>
<th scope="row">私密材质</th>
<td>
<label for="import_as_private">
<input name="import_as_private" type="checkbox" id="import_as_private" size="25" /> 导入为私密材质
</label>
</td>
</tr>
</table>
@if (Session::has('msg'))
<div class="alert alert-warning" role="alert">{{ Session::pull('msg') }}</div>
@endif
<p class="step">
<input type="submit" name="submit" id="submit" class="button button-large" value="开始迁移" />
</p>
</form>
@endif
{{-- Step 2: --}}
@if ($step == '2')
<?php
if (check_post(['v2_table_name', 'uploader_uid', 'texture_name_pattern'], true)) {
if ($_POST['v2_table_name'] == "") {
redirect_to('index.php?action=import-v2-textures&step=1', 'v2 users 表名不能为空');
} else {
$_POST['uploader_uid'] = ($_POST['uploader_uid'] == "") ? 0 : (int)$_POST['uploader_uid'];
if (Utils::convertString($_POST['v2_table_name']) != $_POST['v2_table_name'])
redirect_to('index.php?action=import-v2-textures&step=1', "表名 {$_POST['v2_table_name']} 中含有无效字符");
if (!Database::hasTable($_POST['v2_table_name'])) {
redirect_to('index.php?action=import-v2-textures&step=1', "数据表 {$_POST['v2_table_name']} 不存在");
}
}
} else {
redirect_to('index.php?action=import-v2-textures&step=1', '表单信息不完整');
}
?>
<h1>导入成功</h1>
<?php $result = migrate('import-v2-textures'); ?>
<p>已导入 {{ $result['imported'] }} 个材质到皮肤库,{{ $result['duplicated'] }} 个材质因重复而未导入。</p>
<p>注意:请将 v2 textures 文件夹内容复制到 v3 textures 文件夹中</p>
<p class="step">
<a href="../../" class="button button-large">导入完成</a>
</p>
@endif
@endsection

View File

@ -1,72 +0,0 @@
@extends('setup.migrations.master')
@section('content')
<?php $step = isset($_GET['step']) ? $_GET['step'] : '1'; ?>
{{-- Step 1: --}}
@if ($step == '1')
<h1>导入用户数据</h1>
<p>本功能用于导入 v2 的用户账户数据至 v3请先将 v2 users 表改名导入到当前 v3 的同一数据库中</p>
<p>仅导入用户数据将会丢失用户的材质信息,如需保存原来的材质信息,请 <a href="index.php?action=import-v2-both">同时导入用户和材质</a></p>
<p><b>注意:</b> v3 当前设置的密码加密方式必须和之前 v2 的一致,否则导入后的用户将无法登录。</p>
<hr />
<form id="setup" method="post" action="index.php?action=import-v2-users&step=2" novalidate="novalidate">
<table class="form-table">
<tr>
<th scope="row"><label for="v2_table_name">v2 的用户表名</label></th>
<td>
<input name="v2_table_name" type="v2_table_name" id="v2_table_name" size="25" value="" />
<p>就是你改名过的 v2 users 表现在的名字</p>
</td>
</tr>
</table>
@if (Session::has('msg'))
<div class="alert alert-warning" role="alert">{{ Session::pull('msg') }}</div>
@endif
<p class="step">
<input type="submit" name="submit" id="submit" class="button button-large" value="开始迁移" />
</p>
</form>
@endif
{{-- Step 2: --}}
@if ($step == '2')
<?php
if (check_post(['v2_table_name'], true)) {
if ($_POST['v2_table_name'] == "") {
redirect_to('index.php?action=import-v2-users&step=1', 'v2 users 表名不能为空');
} else {
if (Utils::convertString($_POST['v2_table_name']) != $_POST['v2_table_name'])
redirect_to('index.php?action=import-v2-users&step=1', "表名 {$_POST['v2_table_name']} 中含有无效字符");
if (!Database::hasTable($_POST['v2_table_name'])) {
redirect_to('index.php?action=import-v2-users&step=1', "数据表 {$_POST['v2_table_name']} 不存在");
}
}
} else {
redirect_to('index.php?action=import-v2-users&step=1', '表单信息不完整');
}
?>
<h1>导入成功</h1>
<?php $result = migrate('import-v2-users'); ?>
<p>已导入 {{ $result['imported'] }} 个用户,{{ $result['duplicated'] }} 个用户因重复而未导入。</p>
<p class="step">
<a href="../../" class="button button-large">导入完成</a>
</p>
@endif
@endsection

View File

@ -1,18 +0,0 @@
@extends('setup.migrations.master')
@section('content')
<h1>欢迎</h1>
<p>欢迎使用 Blessing Skin Server 数据迁移工具,此工具用于迁移 v2 的数据至 v3。</p>
<p>目前支持导入 v2 的用户数据以及导入用户皮肤至 v3 的皮肤库中。</p>
<hr />
<p>选择一个操作以继续:</p>
<p class="step">
<a href="index.php?action=import-v2-textures" class="button button-large">导入 v2 皮肤库</a>
<a href="index.php?action=import-v2-users" class="button button-large">导入 v2 用户数据</a>
<a href="index.php?action=import-v2-both" class="button button-large">同时导入</a>
</p>
@endsection

View File

@ -1,29 +0,0 @@
<!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="../../resources/assets/images/favicon.ico">
<link rel="stylesheet" type="text/css" href="../../resources/assets/dist/css/install.css">
<style>
.container a.button {
margin-right: 15px;
}
hr {
border-bottom: 1px solid #bbbbbb;
border-top: 0;
}
</style>
</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>