1067 lines
42 KiB
PHP
Raw Normal View History

2024-08-31 01:03:37 +08:00
<?php
/**
* 初始化项目基础数据
* 需先创建数据库建表并配置好config/setting_user.php
*/
class installIndex extends Controller {
public $roleID;
public $admin = array();
public $userSetting;
public $installLock;
public $dbType;
public $engine;
public function __construct() {
parent::__construct();
$this->userSetting = BASIC_PATH . 'config/setting_user.php';
$this->installLock = USER_SYSTEM . 'install.lock';
$this->installFastLock = USER_SYSTEM . 'fastinstall.lock'; // 旧版
$this->authCheck();
}
// 请求权限检测
public function authCheck(){
if(MOD.'.'.ST == 'install.index'){
if(!ACT || !in_array(ACT, array('env', 'save', 'auto'))){
show_json(LNG('common.illegalRequest'), false);
}
$check = $this->initCheck();
if(ACT == 'auto') $this->cliInstall($check); // cli自动安装
if($check === 2) show_json(LNG('admin.install.errorRequest'), false);
}
}
// 安装配置初始化检测
private function initCheck(){
if(!defined('STATIC_PATH')){
define('STATIC_PATH',$GLOBALS['config']['settings']['staticPath']);
}
if(!defined('INSTALL_PATH')){
define('INSTALL_PATH', CONTROLLER_DIR.'install/');
}
// 1.setting_user.php、install.lock、数据库配置都存在已安装
// 2.setting_user.php、数据库配置存在install.lock不存在重置管理员密码
if(@file_exists($this->userSetting)) {
if($this->defDbConfig(true)) {
// if(@file_exists($this->installLock)) return true;
return @file_exists($this->installLock) ? 2 : 1;
} else {
del_file($this->userSetting);
}
}
if(!@file_exists($this->userSetting)) del_file($this->installLock);
// return false;
return 0;
}
// 安装检测
public function check(){
// 判断是否需要安装
if($this->initCheck() === 2) return;
if(ACTION == 'user.view.call'){exit;}
if(ACTION == 'user.view.options'){
$options = array(
"kod" => array(
'systemOS' => $this->config['systemOS'],
'phpVersion' => PHP_VERSION,
'appApi' => appHostGet(),
'APP_HOST' => APP_HOST,
'ENV_DEV' => !!STATIC_DEV,
'staticPath' => STATIC_PATH,
'version' => KOD_VERSION,
'build' => KOD_VERSION_BUILD,
'channel' => INSTALL_CHANNEL,
),
"user" => array('config' => array()),
"io" => array(),
"lang" => I18n::getType(),
);
if($this->in['full'] == '1'){
$options['_lang'] = array(
"list" => I18n::getAll(),
"lang" => I18n::getType(),
);
}
show_json($options);
}
$actions = array(
'install.index.env',
'install.index.save',
'user.view.lang',
);
if( in_array(ACTION, $actions) ){
ActionCall(ACTION);exit;
}
// 安装路径合法性检测; 不允许[, ;%][*][&=+%#?{}]; 允许(_!@$[]()-./); 需要转义([]@$)
$uri = str_replace(HOST,'',APP_HOST);$matchArr = array();
if($uri && !preg_match("/^[a-zA-Z0-9_!@$\[\]\(\)\-\.\/]*$/",$uri,$matchArr)){
show_tips("App path cann't has special chars<br/>(".LNG('admin.install.pathErrDesc').")<pre>".$uri.'</pre>');
}
$this->tpl = CONTROLLER_DIR . 'install/static/';
$value = array(
'installPath' => APP_HOST.'app/controller/install/static/',
'envList' => $this->envList(),
'installFast' => @file_exists($this->userSetting) ? 1 : 0, // 是否已安装setting_user.php存在
'installAuto' => '' // 管理员账号名
);
// 获取管理员账号
if ($value['installFast']) {
try{
$info = Model('User')->find('1');
if (!empty($info['name'])) $value['installAuto'] = $info['name'];
}catch(Exception $e){}
}
$this->values = $value;
$this->display('index.html');
exit;
}
private function envList(){
$opts = array(
'path_writable' => array('title' => LNG('admin.install.dirRight'), 'text' => LNG('admin.install.pathNeedWirte')),
'php_version' => array('title' => 'PHP '.LNG('common.version'), 'text' => LNG('admin.install.phpVersionTips')),
'allow_url_fopen' => array('text' => LNG('admin.install.mustOpen')),
'php_bit' => array('title' => 'PHP '.LNG('common.sysVersion'), 'text' => LNG('admin.install.phpBitTips').'<br/><span class="bit-desc">('.LNG('admin.install.phpBitDesc').')</span>'),
'iconv' => array(),
'mb_string' => array(),
'json' => array(),
'curl' => array(),
'xml' => array(),
'shell_exec' => array('title' => 'shell_exec、exec '.LNG('common.method')),
'gd' => array(),
'path_list' => array('title' => LNG('admin.install.serverDir'), 'text' => LNG('admin.install.suggestClose'))
);
foreach ($opts as $key => &$opt) {
$opt['title'] = _get($opt, 'title', strtoupper($key).' '.LNG('common.extend'));
$opt['text'] = _get($opt, 'text', LNG('admin.install.suggestOpen'));
}
return $opts;
}
/**
* cli一键安装
* php /usr/local/var/www/kod/kodbox/index.php "install/index/auto"
* --database mysql --database-name "kodbox" --database-user "root" --database-pass "root" --database-host "127.0.0.1:3306"
* --database sqlite // or
* --cache redis --redis-host "127.0.0.1" --redis-port "6379" --redis-auth "1234" // 不传递是默认文件缓存
* --user-name "admin" --user-pass "admin"
* --db-del 1/0 // 数据表已存在,是否删除/保留,默认保留
* --user-auto 1 // 随机密码admin/xxx
* --user-reset 1 // 重置管理员密码
* --language en // 默认为中文
* @return void
*/
public function cliInstall($check=0) {
if (!is_cli() || !isset($_SERVER['argv'])) return;
// 接管show_tips
Hook::bind('show_tips', array($this, 'cliShowTips'));
// 切换英文——会导致系统以英文初始化
if ($this->cliArgs('language') == 'en' && I18n::getType() != 'en') {
$array = include(LANGUAGE_PATH.'en/index.php');
if (!empty($array)) I18n::set($array);
}
// 已安装
if ($check === 2) $this->cliEcho(LNG('admin.install.errorRequest'));
// 获取一键安装参数
$action = _get($this->in, 'action', '');
if ($action && in_array($action, array('db', 'user'))) return; // 避免自动安装死循环——ACT=auto
$data = $this->cliInstallData();
//0.非重置账号检测db参数及配置——仅新版支持参数检查、重置密码
if (!isset($this->cliInstVer)) {
if (!$this->cliMore('reset')) {
if (!$check && empty($data['db'])) {
$this->cliEcho(LNG('common.invalidParam'));
}
// db配置已存在禁止再次执行
if ($check === 1) {
$msg = PHP_EOL.LNG('admin.install.dbWasSet').PHP_EOL;
$txt = empty($data['user']['pass']) ? '--user-name "'.LNG('user.account').'" --user-pass "'.LNG('common.password').'" ' : '';
$msg .= LNG('admin.install.ifResetAuto').$txt.'--user-reset 1';
$this->cliEcho($msg);
}
} else {
if (!$check) $this->cliEcho(LNG('admin.install.resetSysErr'));
$this->cliSaveUser($data); exit;
}
}
// 1.提交数据库设置——成功后自动提交管理员设置
$this->cliSaveDb($data);
}
// 1.自动提交数据库保存
public function cliSaveDb($data){
$self = $this;
$init = array(
'action' => 'db',
'del' => $this->cliMore('del') ? 1 : 0
);
$this->in = array_merge($init, $data['db']);
ActionCallResult("install.index.save",function(&$res) use($self,$data){
$msg = str_replace('<br/>',PHP_EOL,$res['data']);
if (!$res['code']) {
// 数据库已存在且有表视作自行导入的kod库暂不做对比检测
if (_get($res,'info') == 10001) {
$keep = !$self->cliMore('del'); // 默认保留del=0/null
if (!$keep) { // 不保留——执行不到这里:默认保留($keep不保留del=1会直接删除不返回info=10001
$msg = LNG('admin.install.ifDelDbAuto');
$msg = str_replace('[1]', '['._get($data,'db.dbName','').']', $msg);
} else {
// 添加配置文件——旧版存在,新版不存在
if (!isset($self->cliInstVer)) $self->cliUserSetting($data['db']);
}
} else {
$self->cliEcho($msg);
}
}
// $msg = stristr(I18n::getType(),'zh') ? '数据库配置完成!' : 'Database configuration completed!'; // LNG('admin.install.dbSetOk')
$self->cliEcho('1.Database configuration completed!',true,false);
// 2.执行成功,写入管理员账号
$self->cliSaveUser($data);
});
}
// 保存数据库配置文件
private function cliUserSetting($db) {
$type = _get($db, 'cacheType', 'file');
$cache = array('type' => $type);
if ($type != 'file') {
$cache['host'] = _get($db, $type.'Host', '');
$cache['port'] = _get($db, $type.'Port', '');
if ($type == 'redis' && !empty($db['redisAuth'])) {
$cache['auth'] = $db['redisAuth'];
}
}
$data = $this->dbConfig();
$this->setUserSetting($data, $cache);
}
// 2.自动提交管理员保存(系统初始化)
public function cliSaveUser($data) {
$name = _get($data, 'user.name', '');
$pass = _get($data, 'user.pass', '');
// 非重置账号没有传递账号密码时提醒在web访问设置
if (!$this->cliMore('reset') && (empty($name) || empty($pass))) {
del_file($this->installFastLock);
$this->cliEcho(LNG('admin.install.userOnWeb'), true);
}
$this->in = array(
'name' => $name,
'password' => $pass,
'action' => 'user'
);
$self = $this;
ActionCallResult("install.index.save",function(&$res) use($self,$name,$pass){
if (!$res['code']) {
$msg = str_replace('<br/>',PHP_EOL,$res['data']);
$self->cliEcho(LNG('admin.install.userSaveErr').$msg);
}
del_file($self->installFastLock);
if (!$self->cliMore('reset')) {
// $msg = stristr(I18n::getType(),'zh') ? '系统初始化完成!' : 'System initialization completed!'; // LNG('admin.install.userSetOk')
$self->cliEcho('2.System initialization completed!',true,false);
}
$msg = LNG('admin.install.autoPwdTips').$name.' '.$pass;
if (!$self->cliMore('auto')) $msg = ''; // 仅自动生成密码时,在回复中显示
$self->cliEcho($msg, true);
});
}
// 获取自动安装配置参数
private function cliInstallData(){
if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) return;
$this->cliInstallOld(); // 兼容旧版
$args = $this->cliArgs();
if (empty($args)) return;
$this->cliMore($args);
// 1.检查参数非重置密码时检查db、缓存重置时忽略数据库、缓存要求系统已完成初始化
$dbType = _get($args, 'database', '');
$ccType = _get($args, 'cache', 'file');
if (!$this->cliMore('reset')) {
if (!$dbType || !in_array($dbType, array('mysql', 'sqlite'))) {
$this->cliEcho(LNG('common.invalidParam').' database');
}
if (!in_array($ccType, array('file', 'redis', 'memcached'))) {
$this->cliEcho(LNG('common.invalidParam').' cache');
}
}
// 2.构建前端提交的参数样式
$db = $user = array();
$db['dbType'] = $dbType;
$db['cacheType'] = $ccType;
foreach ($args as $key => $value) {
$arr = explode('-', $key);
if (empty($arr[1])) continue;
$tmp = $arr[0];
// 管理员
if ($tmp == 'user') {
$user[$arr[1]] = $value;
continue;
}
// 数据库、缓存
if ($tmp == 'database') {
$tmp = 'db';
if ($arr[1] == 'pass') $arr[1] = 'pwd';
}
$db[$tmp.ucfirst($arr[1])] = $value;
}
// redis、memcached端口赋默认值
if ($ccType != 'file' && empty($db[$ccType.'Port'])) {
$db[$ccType.'Port'] = $ccType == 'redis' ? '6379' : '11211';
}
if ($dbType == 'mysql') $db['dbEngine'] = 'innodb';
// 3.账号密码
if (empty($user['name'])) $user['name'] = 'admin'; // 不传递默认为admin——重置时应该从数据库读取暂不处理
if ($this->cliMore('auto')) {
$user['pass'] = 'K0d#'.rand_string(6);
} else {
// 重置且非自动时,要求必须填写密码
if ($this->cliMore('reset') && empty($user['pass'])) {
$this->cliEcho(LNG('admin.install.resetPwdTips'));
}
}
// 4.重置用户,数据库、缓存置为空
if ($this->cliMore('reset')) $db = array();
return array('db' => $db, 'user' => $user);
}
private function cliArgs($key=false){
$argv = $_SERVER['argv'];
if (!is_array($argv)) return array();
array_shift($argv);
array_shift($argv); // 移除前2个参数./index.php、install/index/auto
$name = null;
$args = array();
foreach ($argv as $arg) {
if (substr($arg, 0, 1) == '-') {
$name = ltrim($arg, '-');
} else {
if ($name) {
$args[$name] = $arg;
$name = null;
}
}
}
return $key ? _get($args, $key) : $args;
}
// 获取cli调用附加参数
private function cliMore($key=false) {
if (!$key) unset($GLOBALS['AUTO_INSTALL_IN_MORE']);
if (is_string($key)) {
$val = _get($GLOBALS, 'AUTO_INSTALL_IN_MORE.'.$key, 0);
return $val == '1' ? true : false;
}
$args = $key;
$GLOBALS['AUTO_INSTALL_IN_MORE'] = array(
'del' => _get($args,'db-del',0), // 数据表删除/保留
'auto' => _get($args,'user-auto',0), // 管理员随机密码
'reset' => _get($args,'user-reset',0), // 管理员密码重置
);
}
// 命令行中输出结果——换行
private function cliEcho($msg, $code=false, $prfx=true) {
// $pre = !$this->cliMore('reset') ? 'admin.install.' : 'explorer.';
// echo ($prfx ? LNG($pre.($code ? 'success' : 'error')).' ' : '').$msg.PHP_EOL;
echo ($code ? 'Success! ' : 'Error! ').$msg.PHP_EOL;
if ($prfx) exit;
}
// 捕获show_tips错误直接输出信息——开启debug时无效pr输出
public function cliShowTips($message,$url, $time,$title) {
$msg = ''; // think_exception输出html
// if (stripos($message,'<div class="desc"') >= 0) {
if (stripos($message,'<div') >= 0) {
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($message, 'HTML-ENTITIES', 'UTF-8'));
// $xpath = new DOMXPath($dom);
// $elements = $xpath->query('//div[@class="desc"]');
// if (!is_null($elements)) $msg = $elements[0]->nodeValue;
foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
$msg .= $node->nodeValue.PHP_EOL;
}
$msg = rtrim($msg, PHP_EOL);
}
if (!$msg) $msg = $message;
$this->cliEcho($msg);
}
/**
* cli一键安装兼容旧版
* 根据fastinstall.lock判断默认包含数据库和配置文件以此构建argv参数以适配新版逻辑
* @return void
*/
public function cliInstallOld(){
$file = $this->installFastLock;
if (!@file_exists($file)) return; // KOD_VERSION >= 1.52
if (!@file_exists($this->userSetting)) $this->cliEcho('Invalid configuration file (config/setting_user.php).');
$this->cliInstVer = 'old';
// 构建argv参数
$data = array();
$db = $this->config['database'];
$cc = $this->config['cache'];
// 数据库
if (stripos($db['DB_TYPE'],'sqlite') === 0) {
$data = array('--database', 'sqlite');
} else {
$data = array(
'--database', 'mysql',
'--database-host',
$db['DB_HOST'].':'._get($db,'DB_PORT',3306),
'--database-user',
$db['DB_USER'],
'--database-pass',
$db['DB_PWD'],
'--database-name',
$db['DB_NAME'],
);
}
// 缓存
$ccType = _get($cc, 'cacheType', 'file');
if (in_array($ccType, array('redis', 'memcached'))) {
$data[] = '--cache';
$data[] = $ccType;
$data[] = '--'.$ccType.'-host';
$data[] = _get($cc, $ccType.'.host','');
$data[] = '--'.$ccType.'-port';
$data[] = _get($cc, $ccType.'.port','');
if ($ccType == 'redis' && !empty($cc['redis']['auth'])) {
$data[] = '--redis-auth';
$data[] = $cc['redis']['auth'];
}
}
// 管理员
$user = $this->getFastAcc($file);
if ($user) {
$data[] = '--user-name';
$data[] = $user['name'];
$data[] = '--user-pass';
$data[] = $user['pass'];
}
$_SERVER['argv'] = array_merge($_SERVER['argv'], $data);
}
// 获取自动安装账号密码
private function getFastAcc($file){
$content = trim(file_get_contents($file));
if(empty($content)) return false;
$data = array();
$content = array_filter(explode(PHP_EOL, $content));
foreach($content as $line) {
$tmp = explode("=", trim($line));
if(empty(trim($tmp[1]))) continue;
$data[strtolower(trim($tmp[0]))] = trim($tmp[1]);
}
if(isset($data['adm_name']) && isset($data['adm_pwd'])) {
return array(
'name' => $data['adm_name'],
'pass' => $data['adm_pwd']
);
}
return false;
}
/**
* 获取数据库默认配置信息
* @param boolean $return false:表单数据;true:db配置数据
* @return void
*/
public function defDbConfig($return=false){
$data = array();
// 1.获取文件数据
if(!$dbConfig = $this->config['database']) return $data;
if(!$dbConfig['DB_TYPE']) return $data;
if($return) return $dbConfig;
// 2.解析为form表单数据
$database = array();
unset($dbConfig['DB_SQL_LOG'],$dbConfig['DB_FIELDS_CACHE'],$dbConfig['DB_SQL_BUILD_CACHE']);
foreach($dbConfig as $key => $value) {
$keys = explode("_", strtolower($key));
$key = $keys[0] . ucfirst($keys[1]);
$database[$key] = $value;
}
// 2.1 pdo数据处理
if($database['dbType'] == 'pdo') {
$dsn = explode(":", $database['dbDsn']);
$database['pdoType'] = $dsn[0];
unset($database['dbDsn']);
foreach($database as $key => $value) {
if(in_array($key, array('dbType', 'pdoType'))) continue;
$database['pdo'.ucfirst($key)] = $value;
unset($database[$key]);
}
}
if($database['dbType'] == 'mysqli') $database['dbType'] = 'mysql';
if(in_array($database['dbType'], array('sqlite', 'sqlite3'))) {
if($database['dbType'] == 'sqlite3') $database['dbType'] = 'sqlite';
$database['dbName'] = '';
}
if(!$cache = $this->config['cache']) return $database; // 无cache
$cacheType = $cache['cacheType'];
$database['cacheType'] = $cacheType;
if($cacheType == 'file') return $database; // 文件cache
$database[$cacheType.'Host'] = $cache[$cacheType]['host'];
$database[$cacheType.'Port'] = $cache[$cacheType]['port'];
return $database;
}
// 环境检测
public function env(){
if(isset($this->in['db']) && $this->in['db'] == 1) {
$data = $this->defDbConfig();
show_json($data);
}
$env = array(
'path_writable' => array(),
'php_version' => phpversion(),
'allow_url_fopen' => ini_get('allow_url_fopen') == '1',
'php_bit' => phpBuild64() ? 64 : 32,
'iconv' => function_exists('iconv'),
'mb_string' => function_exists('mb_convert_encoding'),
'json' => function_exists('json_encode'),
'curl' => function_exists('curl_init'),
'xml' => function_exists('xml_parser_create'),
'shell_exec' => (function_exists('shell_exec') && function_exists('exec')),
'gd' => true,
'path_list' => check_list_dir(),
);
if( !function_exists('imagecreatefromjpeg')||
!function_exists('imagecreatefromgif')||
!function_exists('imagecreatefrompng')||
!function_exists('imagecolorallocate')){
$env['gd'] = false;
}
$pathWrt = true;
$pathList = array(BASIC_PATH, DATA_PATH, DATA_PATH.'system');
foreach ($pathList as $value) {
if(!path_writeable($value)) $pathWrt = false;
break;
}
$env['path_writable'] = $pathWrt ? $pathWrt : rtrim(BASIC_PATH, '/');
show_json($env);
}
/**
* 数据库、管理员账号提交
*
* @return void
*/
public function save(){
$action = Input::get('action', 'in', null, array('db', 'user'));
if ($action == 'db') {
$this->saveDb();
} else {
$this->saveUser();
}
}
/**
* 1. 数据库配置
*/
private function saveDb(){
// 1.1 获取db配置信息
$data = $this->dbConfig();
if (isset($dat['code']) && !$data['code']) return $data;
$dbName = $data['DB_NAME'];
$cacheType = Input::get('cacheType', 'in', null, array('file', 'redis', 'memcached'));
// 1.2 连接数据库
// 如果用include引入配置文件$config的值会是上次请求的所以直接用$data赋值
$GLOBALS['config']['database'] = $data;
// $GLOBALS['config']['cache']['sessionType'] = $cacheType;
// $GLOBALS['config']['cache']['cacheType'] = $cacheType;
think_config($GLOBALS['config']['databaseDefault']);
think_config($GLOBALS['config']['database']);
if($this->dbType == 'mysql'){
// mysql连接先不指定数据库配置错误时会报错
$GLOBALS['config']['database']['DB_NAME'] = '';
think_config($GLOBALS['config']['database']);
$db = Model()->db();
$dbexist = $db->execute("show databases like '{$dbName}'");
}
$GLOBALS['config']['database']['DB_NAME'] = $dbName; // 避免auto调用时后续取该值为空同一进程
// 1.3 检测缓存配置
// 判断所需缓存配置是否有效——redis、memcached
if(in_array($cacheType, array('redis', 'memcached'))){
if(!extension_loaded($cacheType)){
return show_json(sprintf(LNG('common.env.invalidExt'), "[php-{$cacheType}]"), false);
}
$host = Input::get("{$cacheType}Host", 'require');
$port = Input::get("{$cacheType}Port", 'require');
$type = ucfirst($cacheType);
$handle = new $type();
try{
if($cacheType == 'redis') {
$handle->connect($host, $port, 1);
$auth = Input::get('redisAuth');
if ($auth) $handle->auth($auth);
$conn = $handle->ping();
}else{
$conn = $handle->addServer($host, $port);
if($conn && !$handle->getStats()) $conn = false;
}
if(!$conn) return show_json(sprintf(LNG('admin.install.cacheError'),"[{$cacheType}]"), false);
}catch(Exception $e){
$msg = sprintf(LNG('admin.install.cacheConnectError'),"[{$cacheType}]");
$msg .= '<br/>'.$e->getMessage();
return show_json($msg, false);
}
}
// 1.4 创建数据库
if($this->dbType == 'mysql'){
if(!$dbexist){
// host=localhost时root空密码默认没有权限会创建失败
$db->execute("create database `{$dbName}`");
}
$db->execute("use `{$dbName}`");
$tables = $db->getTables($dbName);
if (!empty($tables)) {
if(!isset($this->in['del']) || $this->in['del'] != '1'){
return show_json(LNG('admin.install.ifDelDb'), false, 10001);
}
// 删除数据表
foreach($tables as $table) {
if ($table) {
$table = strtolower($table);
} else {continue;}
$db->execute("drop table if exists `{$table}`");
}
}
}else{
$db = Model()->db();
}
// 1.5 写入配置文件:数据库、缓存
$data['DB_NAME'] = $dbName;
$cache = array('type' => $cacheType);
if (isset($host) && isset($port)) {
$cache['host'] = $host;
$cache['port'] = $port;
if (!empty($auth)) $cache['auth'] = $auth;
}
$this->setUserSetting($data, $cache);
// 1.6 创建数据表
$res = $this->createTable($db);
if (isset($res['code']) && !$res['code']) return $res;
return show_json(LNG('explorer.success'));
}
private function sqliteFilter($content) {
$replaceFrom = "'DB_NAME' => '".USER_SYSTEM;
$replaceTo = "'DB_NAME' => USER_SYSTEM.'";
$replaceFrom2= "'DB_DSN' => 'sqlite:".USER_SYSTEM;
$replaceTo2 = "'DB_DSN' => 'sqlite:'.USER_SYSTEM.'";
$content = str_replace($replaceFrom,$replaceTo,$content);
$content = str_replace($replaceFrom2,$replaceTo2,$content);
return $content;
}
// 写入配置文件setting_user.php
private function setUserSetting($data, $cache){
if($data['DB_TYPE'] == 'pdo'){
$dbDsn = explode(':', $data['DB_DSN']);
if($dbDsn[0] == 'mysql'){
$data['DB_DSN'] .= ';dbname=' . $data['DB_NAME'];
}
}
$database = var_export($data, true);
$ccType = $cache['type'];
$text = array(
"<?php ",
"\$config['database'] = {$database};",
"\$config['cache']['sessionType'] = '{$ccType}';",
"\$config['cache']['cacheType'] = '{$ccType}';"
);
if(isset($cache['host'])){
$text[] = "\$config['cache']['{$ccType}']['host'] = '".$cache['host']."';";
$text[] = "\$config['cache']['{$ccType}']['port'] = '".$cache['port']."';";
if (isset($cache['auth'])) {
$text[] = "\$config['cache']['{$ccType}']['auth'] = '".$cache['auth']."';";
}
}
$file = $this->userSetting;
if(!@file_exists($file)) @touch($file);
$content = file_get_contents($file);
$pre = '';
if(stripos(trim($content),"<?php") !== false) {
$pre = PHP_EOL;
unset($text[0]);
}
$content = implode(PHP_EOL, $text);
if($this->dbType == 'sqlite') {
$content = $this->sqliteFilter($content);
}
if(!file_put_contents($file,$pre.$content, FILE_APPEND)) {
$msg = LNG('admin.install.dbSetError');
$tmp = explode('<br/>', LNG('common.env.pathPermissionError'));
$msg .= "<br/>" . $tmp[0];
return show_json($msg, false, 10000);
}
}
/**
* 创建数据表
* @param [type] $db
* @return void
*/
private function createTable($db){
$dbFile = INSTALL_PATH . "data/{$this->dbType}.sql";
if (!@file_exists($dbFile)) {
return show_json(LNG('admin.install.dbFileError'), false);
}
$content = file_get_contents($dbFile);
if($this->dbType == 'mysql') {
if(!empty($this->engine) && $this->engine == 'innodb') {
$content = str_ireplace('MyISAM', 'InnoDB', $content);
}
// fulltext索引兼容
$res = $db->query('select VERSION() as v');
$mysqlVersion = floatval(($res[0] && isset($res[0]['v'])) ? $res[0]['v'] : 0);
if($mysqlVersion && $mysqlVersion >= 5.7){
// 删除索引不存在时报错; 需手动处理;
//$content .= "\n".file_get_contents(INSTALL_PATH."data/fulltext.sql");
}else{
$content = str_ireplace('FULLTEXT ','', $content);
}
}
$sqlArr = sqlSplit($content);
foreach($sqlArr as $sql){
$db->execute($sql);
}
}
/**
* 获取数据库配置信息
*/
private function dbConfig(){
$this->dbType = $dbType = Input::get('dbType', 'in', null, array('sqlite', 'mysql', 'pdo'));
$dbList = $this->dbList();
$init = array('db_type' => $dbType);
if($dbType == 'mysql'){
$data = Input::getArray(array(
'dbHost' => array('aliasKey' => 'db_host', 'check' => 'require'),
'db_port' => array('default' => 3306),
'dbUser' => array('aliasKey' => 'db_user', 'check' => 'require'),
'dbPwd' => array('aliasKey' => 'db_pwd', 'check' => 'require', 'default' => ''),
'dbName' => array('aliasKey' => 'db_name', 'check' => 'require'),
));
$this->execPort($data);
$dbType = in_array('mysqli', $dbList) ? 'mysqli' : 'mysql';
$this->engine = Input::get('dbEngine', null, 'myisam');
}else if($dbType == 'pdo'){
$this->dbType = $pdoType = Input::get('pdoType', 'in', null, array('sqlite', 'mysql'));
if($pdoType == 'mysql'){
$data = Input::getArray(array(
'db_dsn' => array('default' => ''),
'pdoDbHost' => array('aliasKey' => 'db_host', 'check' => 'require'),
'db_port' => array('default' => 3306),
'pdoDbUser' => array('aliasKey' => 'db_user', 'check' => 'require'),
'pdoDbPwd' => array('aliasKey' => 'db_pwd', 'check' => 'require', 'default' => ''),
'pdoDbName' => array('aliasKey' => 'db_name', 'check' => 'require'),
));
$this->execPort($data);
$data['db_dsn'] = "mysql:host={$data['db_host']}";
$this->engine = Input::get('pdoDbEngine', null, 'myisam');
}else{
$data['db_dsn'] = "sqlite:" . $this->sqliteDbFile();
}
$dbType .= '_' . $pdoType;
}else{
$dbType = in_array('sqlite3', $dbList) ? 'sqlite3' : 'sqlite';
$data = array('db_name' => $this->sqliteDbFile());
}
if(!in_array($dbType, $dbList)){
if(stripos($dbType, 'sqlite') === 0 ||
(isset($data['db_dsn']) && stripos($data['db_dsn'], 'sqlite') === 0)) {
del_file($data['db_name']);
}
return show_json(sprintf(LNG('admin.install.dbTypeError'),$dbType), false);
}
if($init['db_type'] != 'pdo') $init['db_type'] = $dbType;
return array_change_key_case(array_merge($init, $data, array(
'DB_SQL_LOG' => true, // SQL执行错误日志记录
'DB_FIELDS_CACHE' => true, // 启用字段缓存
'DB_SQL_BUILD_CACHE' => false, // sql生成build缓存
)), CASE_UPPER);
}
private function execPort(&$data) {
$host = explode(':', $data['db_host']);
if(isset($host[1])) {
$data['db_host'] = $host[0];
$data['db_port'] = $host[1];
}
}
/**
* sqlite database文件
*/
private function sqliteDbFile(){
@chmod(DATA_PATH, 0755);
$dbFile = USER_SYSTEM . rand_string(12) . '.php';
@touch($dbFile);
return $dbFile;
}
/**
* 支持的数据库类型列表
* @return void
*/
private function dbList(){
$data = array('sqlite', 'sqlite3', 'mysql', 'mysqli', 'pdo_sqlite', 'pdo_mysql');
$list = array_map(function($type){
if (extension_loaded($type)){
return $type;
}
}, $data);
return array_filter($list);
}
/**
* 2. 账号设置
* @return void
*/
private function saveUser(){
$data = Input::getArray(array(
'name' => array('check' => 'require'),
'password' => array('check' => 'require')
));
$this->checkDbInit();
$userID = 1;
if(Model('User')->find($userID)) {
if(!Model('User')->userEdit($userID, $data)) {
show_json(LNG('user.bindUpdateError'), false);
}
@touch($this->installLock);
show_json(LNG('admin.install.updateSuccess'), true, $userID);
}
$this->admin = $data;
$this->sysInit();
}
// (检查)数据库初始化
private function checkDbInit(){
think_config($GLOBALS['config']['databaseDefault']);
// think_config($GLOBALS['config']['database']);
$data = $GLOBALS['config']['database'];
// 获取数据库类型,配置数据库信息(不指定数据库)
$type = $data['DB_TYPE'];
$dbName = $data['DB_NAME'];
switch ($data['DB_TYPE']) {
case 'pdo':
$dsn = explode(':', $data['DB_DSN']);
$type = $dsn[0];
$dsn = explode(';', $data['DB_DSN']);
$GLOBALS['config']['database']['DB_DSN'] = $dsn[0]; // 去掉数据库名
break;
case 'mysql':
case 'mysqli':
$type = 'mysql';
$GLOBALS['config']['database']['DB_NAME'] = '';
break;
case 'sqlite3':
$type = 'sqlite';
break;
}
think_config($GLOBALS['config']['database']);
// 判断数据库(表)是否存在
$db = Model()->db();
if ($type != 'sqlite') {
$exist = $db->execute("show databases like '{$dbName}'");
if (!$exist) show_json(LNG('ERROR_DB_NOT_EXIST'), false);
$db->execute("use `{$dbName}`");
}
// sqlite可直接调用该方法无论库文件是否存在
$tables = $db->getTables();
if (empty($tables) || !in_array('user', $tables)) {
$msg = $type == 'sqlite' ? 'dbError' : 'dbTableError';
show_json(LNG('admin.install.'.$msg), false);
}
// 重新配置数据库信息
$GLOBALS['config']['database'] = $data;
think_config($GLOBALS['config']['database']);
}
/**
* 初始化数据
*/
public function sysInit(){
define('USER_ID',1);
Cache::deleteAll();
$this->systemDefault();
$this->storageDefault();
$this->initLightApp();
$this->initPluginList();
$this->addGroup();
$this->addAuth();
$this->roleID = $this->getRoleID();
$this->addUser();
KodIO::initSystemPath();
@touch($this->installLock);
show_json(LNG('admin.install.createSuccess'), true);
}
/**
* 系统默认设置
*/
public function systemDefault(){
$default = $this->config['settingSystemDefault'];
$res = Model('SystemOption')->set($default);
if(!$res) show_json(LNG('admin.install.defSetError'), false);
}
/**
* 默认存储配置
*/
public function storageDefault(){
$driver = KodIO::defaultDriver();
if($driver) return $driver['id'];
$dataPath = './data/files/';
if(!is_dir($dataPath)) @mk_dir($dataPath);
$freeSize = @disk_free_space($dataPath);
$freeSize = $freeSize ? floor($freeSize / 1024 / 1024 / 1024) : 0;
$freeSize = $freeSize > 10 ? floor($freeSize / 10) * 10 : 10;
$data = array (
'name' => LNG('admin.storage.localStore'),
'sizeMax' => $freeSize,
'driver' => 'Local',
'default' => '1',
'system' => '1',
'config' => json_encode(array(
"basePath" => $dataPath
)),
);
$res = Model('Storage')->add($data);
if(!$res) show_json(LNG('admin.install.defStoreError'), false);
}
/**
* 轻应用列表初始化
*/
public function initLightApp(){
Action('explorer.lightApp')->initApp();
}
/**
* 初始化插件列表
*/
public function initPluginList(){
Model('Plugin')->viewList();
$list = Model('Plugin')->loadList();
foreach($list as $app => $item) {
Model('Plugin')->changeStatus($app, 1);
}
}
/**
* 添加根部门
*/
public function addGroup(){
$this->in = array(
"groupID" => 1,
"name" => $this->config['settingSystemDefault']['groupRootName'],
"sizeMax" => 0,
"parentID" => 0,
);
$res = ActionCallHook('admin.group.add');
if(!$res['code']){
show_json(LNG('admin.install.defGroupError'), false);
}
}
/**
* 文档权限
*/
public function addAuth(){
Model('Auth')->initData();
}
/**
* 系统内置角色
*/
public function getRoleID(){
$list = Model('SystemRole')->listData();
$roleList = array_to_keyvalue($list, 'administrator', 'id');
$administrator = 1;
if(!isset($roleList[$administrator])){
$roleID = $this->roleDefault();
if(!$roleID) show_json(LNG('admin.install.defRoleError'), false);
}else{
$roleID = $roleList[$administrator];
}
return $roleID;
}
/**
* 添加角色——Administrator、default
*/
private function roleDefault(){
$administrator = array (
'name' => LNG('admin.role.administrator'),
'display' => 1,
'system' => 1,
'administrator' => 1,
'auth' => 'explorer.add,explorer.upload,explorer.view,explorer.download,explorer.share,explorer.shareLink,explorer.remove,explorer.edit,explorer.move,explorer.serverDownload,explorer.search,explorer.unzip,explorer.zip,user.edit,user.fav,admin.index.dashboard,admin.index.setting,admin.index.loginLog,admin.index.log,admin.index.server,admin.role.list,admin.role.edit,admin.job.list,admin.job.edit,admin.member.list,admin.member.userEdit,admin.member.userAuth,admin.member.groupEdit,admin.auth.list,admin.auth.edit,admin.plugin.list,admin.plugin.edit,admin.storage.list,admin.storage.edit,admin.autoTask.list,admin.autoTask.edit',
'label' => 'label-green-deep',
'sort' => 2,
);
$groupOwner = array (
'name' => LNG('admin.role.group'),
'display' => 1,
'system' => 1,
'auth' => 'explorer.add,explorer.upload,explorer.view,explorer.download,explorer.share,explorer.shareLink,explorer.remove,explorer.edit,explorer.move,explorer.serverDownload,explorer.search,explorer.unzip,explorer.zip,user.edit,user.fav,admin.index.loginLog,admin.index.log,admin.member.list,admin.member.userEdit,admin.member.userAuth,admin.member.groupEdit,admin.auth.list',
'label' => 'label-blue-deep',
'sort' => 1,
);
$defaultUser = array (
'name' => LNG('admin.role.default'),
'display' => 1,
'system' => 1,
'auth' => 'explorer.add,explorer.upload,explorer.view,explorer.download,explorer.share,explorer.shareLink,explorer.remove,explorer.edit,explorer.move,explorer.serverDownload,explorer.search,explorer.unzip,explorer.zip,user.edit,user.fav',
'label' => 'label-blue-normal',
'sort' => 0,
);
Model('SystemRole')->add($defaultUser);
Model('SystemRole')->add($groupOwner);
$administrator = Model('SystemRole')->add($administrator);
return $administrator;
}
/**
* 新增用户-管理员
*/
public function addUser(){
$this->in = array(
"userID" => 1,
"name" => !empty($this->admin['name']) ? $this->admin['name'] : 'admin',
"nickName" => LNG('admin.role.administrator'),
"password" => !empty($this->admin['password']) ? $this->admin['password'] : 'admin',
"roleID" => $this->roleID,
"groupInfo" => json_encode(array('1'=>'1')),
"sizeMax" => 0,
);
$res = ActionCallHook('admin.member.add');
if(!$res) return;
if(!$res['code']){
show_json($res['data'], false);
}
}
}