separate v2 data migrations as a plugin
This commit is contained in:
parent
3811f7d704
commit
28ea2b2858
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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']
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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' => '本地调试用,请勿选择(炸了别怪我)'
|
||||
]
|
||||
);
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue
Block a user