Compare commits
10 Commits
2570955b4e
...
094ff95f71
| Author | SHA1 | Date | |
|---|---|---|---|
| 094ff95f71 | |||
| e280f941e6 | |||
| b27063691f | |||
| 83e251cab3 | |||
| 3a70309721 | |||
| 65fa46e5a9 | |||
| ca9e2f8b1d | |||
| 18fe4030a3 | |||
| 0223951285 | |||
| fc777da399 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ public/static/user
|
||||
!public/static/user/.kara
|
||||
config/config.php
|
||||
public/static/*.pem
|
||||
.vscode
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 0.5 (2026年??月??日)
|
||||
* OpenProviderライブラリ
|
||||
* PHP 8.5以上が必須になった
|
||||
|
||||
# ローリング・リリース (2025年01月01日)
|
||||
* 最初プライベートリリース
|
||||
@@ -7,7 +7,7 @@
|
||||
サンプル:[https://lbdemo.technicalsuwako.moe/](https://lbdemo.technicalsuwako.moe/)
|
||||
|
||||
## Little Beast とは?
|
||||
Little Beast は PHP 8.3 以上向けのフレームワークで、076.moe(ゲーム開発会社)と technicalsuwako.moe(社長のブログ)向けに作られました。\
|
||||
Little Beast は PHP 8.5 以上向けのフレームワークで、076.moe(ゲーム開発会社)と technicalsuwako.moe(社長のブログ)向けに作られました。\
|
||||
メイン考え方は「必要な物だけ拡張し、不要な物は削除する」です。\
|
||||
各コア機能はライブラリに分割されている為、必要な物だけを選び易い設計になっています。\
|
||||
全てのモジュールはゼロから書かれており、データベースは一切必要ありません。
|
||||
@@ -68,9 +68,9 @@ HTTP サーバーを設定して `/public` をルートとして `php-fpm` で
|
||||
|
||||
### OpenBSD サーバー
|
||||
```sh
|
||||
pkg_add php-8.4.14 php-gmp-8.4.14
|
||||
rcctl enable php84_fpm httpd relayd
|
||||
rcctl start php84_fpm httpd relayd
|
||||
pkg_add php-8.5.5 php-gmp-8.5.5
|
||||
rcctl enable php85_fpm httpd relayd
|
||||
rcctl start php85_fpm httpd relayd
|
||||
```
|
||||
|
||||
#### httpd
|
||||
|
||||
@@ -7,7 +7,7 @@ Simple, Pragmatic, Anti-bloat
|
||||
Demo installation: [https://lbdemo.technicalsuwako.moe/](https://lbdemo.technicalsuwako.moe/)
|
||||
|
||||
## What is Little Beast?
|
||||
Little Beast is a PHP 8.3 or above framework made for 076.moe (game developer company) and technicalsuwako.moe (CEO's blog).\
|
||||
Little Beast is a PHP 8.5 or above framework made for 076.moe (game developer company) and technicalsuwako.moe (CEO's blog).\
|
||||
The core mentality is to extend what you need, and remove what you don't need.\
|
||||
Each core feature is split into libraries, so it is easy to choose what you need.\
|
||||
All modules are written from scratch, nothing is to be require a database.
|
||||
@@ -68,9 +68,9 @@ That's all!
|
||||
|
||||
### OpenBSD server
|
||||
```sh
|
||||
pkg_add php-8.4.14 php-gmp-8.4.14
|
||||
rcctl enable php84_fpm httpd relayd
|
||||
rcctl start php84_fpm httpd relayd
|
||||
pkg_add php-8.5.5 php-gmp-8.5.5
|
||||
rcctl enable php85_fpm httpd relayd
|
||||
rcctl start php85_fpm httpd relayd
|
||||
```
|
||||
|
||||
#### httpd
|
||||
|
||||
@@ -71,6 +71,19 @@ a.op-button-edit:hover {
|
||||
color: #fcfcfc;
|
||||
}
|
||||
|
||||
div.op-grid {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
div.op-grid-item {
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 1px solid #fcfcfc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
tr.status-free { background-color: #34860e; color: #fff; }
|
||||
tr.status-reserved { background-color: #c59e1d; color: #fff; }
|
||||
tr.status-use { background-color: #850000; color: #fff; }
|
||||
|
||||
@@ -66,7 +66,7 @@ $routes = [
|
||||
];
|
||||
|
||||
if (ACTIVITYPUB_ENABLED) {
|
||||
$routes[] = Route::add('GET', '.well-known/webfinger', Home::class.'@apfinger');
|
||||
$routes[] = Route::add('GET', '.well-known/webfinger', Fediverse::class.'@apfinger');
|
||||
$routes[] = Route::add('GET', 'ap/following', Fediverse::class.'@apfollowing');
|
||||
$routes[] = Route::add('GET', 'ap/followers', Fediverse::class.'@apfollowers');
|
||||
$routes[] = Route::add('GET', 'ap/outbox', Fediverse::class.'@apoutbox');
|
||||
@@ -96,6 +96,9 @@ if (ATOM_ENABLED) {
|
||||
|
||||
if (OPENPROVIDER_ENABLED) {
|
||||
$routes[] = Route::add('GET', 'openprovider', Op::class.'@index');
|
||||
$routes[] = Route::add('GET', 'openprovider/listinvoices', Op::class.'@opListInvoices');
|
||||
$routes[] = Route::add('GET', 'openprovider/listpayments', Op::class.'@opListPayments');
|
||||
$routes[] = Route::add('GET', 'openprovider/listtransactions', Op::class.'@opListTransactions');
|
||||
$routes[] = Route::add('GET', 'openprovider/listcustomers', Op::class.'@opListCustomers');
|
||||
$routes[] = Route::add('GET', 'openprovider/createcustomer', Op::class.'@opCreateCustomer');
|
||||
$routes[] = Route::add('GET', 'openprovider/deletecustomer/{handle}', Op::class.'@opDeleteCustomer');
|
||||
|
||||
@@ -37,6 +37,8 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
namespace Site\Controller;
|
||||
|
||||
use Roles;
|
||||
|
||||
class BlogPost {
|
||||
/**
|
||||
* ブログ投稿を取得する
|
||||
@@ -46,9 +48,9 @@ class BlogPost {
|
||||
public function getPosts(string $section, ?\stdClass $user = null): array {
|
||||
$path = ROOT.$section;
|
||||
$posts = [];
|
||||
$isMember = $user !== NULL && $user->role !== \Roles::BANNED;
|
||||
$isPaywall = $user !== NULL && $user->role >= \Roles::SUBSCRIBER;
|
||||
$isStaff = $user !== NULL && $user->role & (\Roles::ADMIN | \Roles::STAFF);
|
||||
$isMember = $user !== NULL && $user->role !== Roles::BANNED;
|
||||
$isPaywall = $user !== NULL && $user->role >= Roles::SUBSCRIBER;
|
||||
$isStaff = $user !== NULL && $user->role & (Roles::ADMIN | Roles::STAFF);
|
||||
|
||||
if (!is_dir($path)) return $posts;
|
||||
$files = glob($path.'/*.md');
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Site\Controller;
|
||||
use Site\Controller\BlogPost;
|
||||
use Site\Controller\Mods;
|
||||
use Std\Lib\Activitypub;
|
||||
use LogType;
|
||||
|
||||
class Fediverse extends BlogPost {
|
||||
use Mods;
|
||||
@@ -98,7 +99,7 @@ class Fediverse extends BlogPost {
|
||||
exit;
|
||||
}
|
||||
|
||||
logger(\LogType::ActivityPub, "受付に入れた:".json_encode($activity));
|
||||
logger(LogType::ActivityPub, "受付に入れた:".json_encode($activity));
|
||||
|
||||
try {
|
||||
header('Content-Type: application/activity+json');
|
||||
|
||||
@@ -43,6 +43,7 @@ use Std\Lib\Activitypub;
|
||||
use Std\Lib\Auth;
|
||||
use Std\Lib\Markdown;
|
||||
use Std\Lib\Template;
|
||||
use Roles;
|
||||
|
||||
class Home extends BlogPost {
|
||||
use Mods;
|
||||
@@ -149,9 +150,9 @@ class Home extends BlogPost {
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$isMember = $user !== NULL && $user->role !== \Roles::BANNED;
|
||||
$isPaywall = $user !== NULL && $user->role >= \Roles::SUBSCRIBER;
|
||||
$isStaff = $user !== NULL && $user->role & (\Roles::ADMIN | \Roles::STAFF);
|
||||
$isMember = $user !== NULL && $user->role !== Roles::BANNED;
|
||||
$isPaywall = $user !== NULL && $user->role >= Roles::SUBSCRIBER;
|
||||
$isStaff = $user !== NULL && $user->role & (Roles::ADMIN | Roles::STAFF);
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Site\Controller;
|
||||
|
||||
trait Mods {
|
||||
public function getMenu(): array {
|
||||
return [
|
||||
$menu = [
|
||||
[
|
||||
'class' => 'menu-item',
|
||||
'href' => '/',
|
||||
@@ -82,13 +82,18 @@ trait Mods {
|
||||
'text' => 'スタッフ限定',
|
||||
'show' => true,
|
||||
],
|
||||
[
|
||||
];
|
||||
|
||||
if (OPENPROVIDER_ENABLED) {
|
||||
$menu[] = [
|
||||
'class' => 'menu-item',
|
||||
'href' => '/openprovider',
|
||||
'page' => 'openprovider',
|
||||
'text' => 'OpenProvider管理',
|
||||
'show' => true,
|
||||
],
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
}
|
||||
@@ -46,30 +46,100 @@ use Roles;
|
||||
class Op {
|
||||
use Mods;
|
||||
|
||||
private Template $tmpl;
|
||||
private Auth $auth;
|
||||
private Openprovider $op;
|
||||
private string $pagetit = 'OpenProvider管理 - ';
|
||||
private string $description = '';
|
||||
private ?\stdClass $user;
|
||||
|
||||
public function __construct() {
|
||||
$this->tmpl = new Template('/openprovider/');
|
||||
|
||||
$this->auth = new Auth();
|
||||
$this->user = $this->auth->getLoggedInUser();
|
||||
if ($this->user && $this->user->role & (Roles::ADMIN)) {
|
||||
$this->op = new Openprovider();
|
||||
$this->op->login();
|
||||
}
|
||||
|
||||
$this->tmpl->assign('user', $this->user);
|
||||
$this->tmpl->assign('curPage', 'openprovider');
|
||||
$this->tmpl->assign('custCss', false);
|
||||
$this->tmpl->assign('menu', $this->getMenu());
|
||||
$this->tmpl->assign('description', $this->description);
|
||||
$this->tmpl->addCss('openprovider');
|
||||
$this->tmpl->addCss('table');
|
||||
}
|
||||
|
||||
public function index(array $params): void {
|
||||
try {
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - ホーム';
|
||||
$description = '';
|
||||
$this->pagetit .= 'ホーム';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->render('index');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->getReseller(['with_statistics' => true]);
|
||||
$domain = $this->op->listDomains(['order_by.renewal_date' => 'asc', 'status' => 'ACT']);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->assign('domain', $domain->data);
|
||||
$this->tmpl->render('index');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function opListInvoices(array $params): void {
|
||||
try {
|
||||
$this->pagetit .= '請求書一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listInvoices();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listinvoices');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function opListPayments(array $params): void {
|
||||
try {
|
||||
$this->pagetit .= '請求書一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listPayments();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listpayments');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function opListTransactions(array $params): void {
|
||||
try {
|
||||
$this->pagetit .= '請求書一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listTransactions();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listtransactions');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -77,34 +147,17 @@ class Op {
|
||||
|
||||
public function opListCustomers(array $params): void {
|
||||
try {
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - 顧客様検索';
|
||||
$description = '';
|
||||
$this->pagetit .= '顧客様検索';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', true);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->listCustomers();
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('listcustomers');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listCustomers();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listcustomers');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -159,24 +212,12 @@ class Op {
|
||||
'subscriber_number' => $_POST['phone_subscriber_number'] ?? '',
|
||||
],
|
||||
];
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - 顧客様の新規作成";
|
||||
$description = '';
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', true);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
$this->pagetit .= '顧客様の新規作成';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
$err = [];
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
|
||||
if (!empty($_POST)) {
|
||||
if (NULL === $_POST['last_name'] || $_POST['last_name'] === ''
|
||||
@@ -199,23 +240,20 @@ class Op {
|
||||
if (($_POST['fax_area_code'] !== '' || $_POST['fax_subscriber_number'] !== '') && (!filter_var($_POST['fax_area_code'], FILTER_VALIDATE_INT) || !filter_var($_POST['fax_subscriber_number'], FILTER_VALIDATE_INT))) $err[] = 'FAXは数字をご入力下さい。';
|
||||
if (!empty($err)) goto render;
|
||||
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->createCustomer($payload, true);
|
||||
$data = $this->op->createCustomer($payload, true);
|
||||
header("Location: /openprovider/getcustomer/{$data->data['data']['handle']}");
|
||||
exit();
|
||||
}
|
||||
|
||||
render:
|
||||
$tmpl->assign('err', $err);
|
||||
$tmpl->assign('data', $payload);
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('createcustomer');
|
||||
$this->tmpl->assign('err', $err);
|
||||
$this->tmpl->assign('data', $payload);
|
||||
$this->tmpl->render('createcustomer');
|
||||
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -229,34 +267,17 @@ class Op {
|
||||
header('Location: /openprovider/listcustomers');
|
||||
exit();
|
||||
}
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - 顧客様「{$handle}」の表示";
|
||||
$description = '';
|
||||
$this->pagetit .= "顧客様「{$handle}」の表示";
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', true);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->getCustomer($handle, true);
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('getcustomer');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->getCustomer($handle, true);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('getcustomer');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -271,44 +292,29 @@ class Op {
|
||||
exit();
|
||||
}
|
||||
assert_not_null($handle);
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - 顧客様「{$handle}」の表示";
|
||||
$description = '';
|
||||
$this->pagetit .= "顧客様「{$handle}」の表示";
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', true);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
if (empty($_POST)) goto render_confirm;
|
||||
|
||||
if (isset($_POST['delete_reject'])) {
|
||||
header("Location: /openprovider/getcustomer/{$handle}");
|
||||
exit();
|
||||
} else if (isset($_POST['delete_confirm'])) {
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$op->deleteCustomer($handle);
|
||||
$this->op->deleteCustomer($handle);
|
||||
}
|
||||
|
||||
header('Location: /openprovider/listcustomers');
|
||||
exit();
|
||||
|
||||
render_confirm:
|
||||
$tmpl->assign('handle', $handle);
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('deletecustomer');
|
||||
$this->tmpl->assign('handle', $handle);
|
||||
$this->tmpl->render('deletecustomer');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -316,33 +322,17 @@ class Op {
|
||||
|
||||
public function opListTlds(array $params): void {
|
||||
try {
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - TLD一覧';
|
||||
$description = '';
|
||||
$this->pagetit .= 'TLD一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->listTlds();
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->render('listtlds');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listTlds();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listtlds');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -356,33 +346,17 @@ class Op {
|
||||
header('Location: /');
|
||||
exit();
|
||||
}
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - .{$tld}の表示";
|
||||
$description = '';
|
||||
$this->pagetit .= "{$tld}の表示";
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->getTld($tld);
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->render('gettld');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->getTld($tld);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('gettld');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -390,36 +364,17 @@ class Op {
|
||||
|
||||
public function opListDomains(array $params): void {
|
||||
try {
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - ドメイン確認';
|
||||
$description = '';
|
||||
$this->pagetit .= 'ドメイン確認';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->listDomains($domains, true);
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->assign('saved', $saved);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('checkdomain');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listDomains($domains, true);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('checkdomain');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -429,40 +384,22 @@ class Op {
|
||||
try {
|
||||
$domains = isset($_GET['domains']) ? explode("\n", $_GET['domains']) : [];
|
||||
$saved = '';
|
||||
$this->pagetit .= 'ドメイン確認';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - ドメイン確認';
|
||||
$description = '';
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
if (!empty($domains)) {
|
||||
$data = $op->checkDomainAvailable($domains, true);
|
||||
$tmpl->assign('data', $data->data);
|
||||
$data = $this->op->checkDomainAvailable($domains, true);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$saved = $_GET['domains'];
|
||||
}
|
||||
|
||||
$tmpl->assign('saved', $saved);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->addCss('openprovider');
|
||||
$tmpl->render('checkdomain');
|
||||
$this->tmpl->assign('saved', $saved);
|
||||
$this->tmpl->render('checkdomain');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -472,34 +409,17 @@ class Op {
|
||||
try {
|
||||
$domainname = $_GET['domain_name'] ?? '';
|
||||
$domainext = $_GET['domain_extension'] ?? '';
|
||||
$this->pagetit .= 'ドメイン値段一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = 'OpenProvider管理 - ドメイン値段一覧';
|
||||
$description = '';
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->getDomainPrices(['domain.name' => $domainname, 'domain.extension' => $domainext]);
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->render('getdomainprices');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->getDomainPrices(['domain.name' => $domainname, 'domain.extension' => $domainext]);
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('getdomainprices');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -507,34 +427,17 @@ class Op {
|
||||
|
||||
public function opListDns(array $params): void {
|
||||
try {
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - DNS一覧";
|
||||
$description = '';
|
||||
$this->pagetit .= 'DNS一覧';
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', true);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = $op->listDnsZones();
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
if ($user && $user->role & (Roles::ADMIN)) $tmpl->render('listdnszones');
|
||||
else goto noaccess;
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = $this->op->listDnsZones();
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('listdnszones');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
@@ -548,33 +451,17 @@ class Op {
|
||||
header('Location: /');
|
||||
exit();
|
||||
}
|
||||
$tmpl = new Template('/openprovider/');
|
||||
$pagetit = "OpenProvider管理 - {$domain}のDNS管理";
|
||||
$description = '';
|
||||
$this->pagetit .= "{$domain}のDNS管理";
|
||||
$this->tmpl->assign('pagetit', $this->pagetit);
|
||||
|
||||
// ユーザー
|
||||
$auth = new Auth();
|
||||
$user = $auth->getLoggedInUser();
|
||||
$tmpl->assign('user', $user);
|
||||
|
||||
$tmpl->assign('pagetit', $pagetit);
|
||||
$tmpl->assign('curPage', 'openprovider');
|
||||
$tmpl->assign('custCss', false);
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if (!$user || $user->role !== Roles::ADMIN) goto noaccess;
|
||||
$op = new Openprovider();
|
||||
$op->login();
|
||||
$data = ['rec' => $op->listZoneRecords($domain), 'zone' => $op->getDnsZone($domain)];
|
||||
$tmpl->assign('data', $data->data);
|
||||
|
||||
$tmpl->addCss('table');
|
||||
$tmpl->render('getdns');
|
||||
if (!$this->user || $this->user->role !== Roles::ADMIN) goto noaccess;
|
||||
$data = ['rec' => $this->op->listZoneRecords($domain), 'zone' => $this->op->getDnsZone($domain)];
|
||||
$this->tmpl->assign('data', $data->data);
|
||||
$this->tmpl->render('getdns');
|
||||
exit();
|
||||
|
||||
noaccess:
|
||||
$tmpl->render('nopermission');
|
||||
$this->tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Site\Controller;
|
||||
use Site\Controller\Mods;
|
||||
use Std\Lib\Auth;
|
||||
use Std\Lib\Template;
|
||||
use Roles;
|
||||
|
||||
class Page {
|
||||
use Mods;
|
||||
@@ -132,7 +133,7 @@ class Page {
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if ($user && $user->role !== \Roles::BANNED) $tmpl->render('memberonly');
|
||||
if ($user && $user->role !== Roles::BANNED) $tmpl->render('memberonly');
|
||||
else $tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
@@ -156,7 +157,7 @@ class Page {
|
||||
$tmpl->assign('menu', $this->getMenu());
|
||||
$tmpl->assign('description', $description);
|
||||
|
||||
if ($user && $user->role & (\Roles::ADMIN | \Roles::STAFF)) $tmpl->render('staffonly');
|
||||
if ($user && $user->role & (Roles::ADMIN | Roles::STAFF)) $tmpl->render('staffonly');
|
||||
else $tmpl->render('nopermission');
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
|
||||
@@ -38,6 +38,8 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
namespace Std\Lib;
|
||||
|
||||
use Std\Lib\Curl;
|
||||
use LogType;
|
||||
use Uri\Rfc3986\Uri;
|
||||
|
||||
/**
|
||||
* ActivityPubプロトコルの実装クラス
|
||||
@@ -297,14 +299,14 @@ class ActivityPub {
|
||||
*/
|
||||
public function getWebfinger(): string {
|
||||
if (!ACTIVITYPUB_ENABLED) return '';
|
||||
$domain = str_replace(['http://', 'https://'], '', $this->domain);
|
||||
$uri = new Uri($this->domain);
|
||||
$webfinger = [
|
||||
'subject' => "acct:{$this->actor}@{$domain}",
|
||||
'subject' => "acct:{$this->actor}@{$uri->getHost()}",
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
'type' => 'application/activity+json',
|
||||
'href' => "{$domain}/ap/actor",
|
||||
'href' => "{$this->domain}/ap/actor",
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -434,7 +436,7 @@ class ActivityPub {
|
||||
$privFile = FEDIINFO['privkey'];
|
||||
$priv = file_get_contents($privFile);
|
||||
if ($priv === false) {
|
||||
logger(\LogType::ActivityPub, "エラー:秘密鍵「{$privFile}」の読込に失敗");
|
||||
logger(LogType::ActivityPub, "エラー:秘密鍵「{$privFile}」の読込に失敗");
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
header('Content-Type: application/activity+json');
|
||||
echo json_encode(['error' => '秘密鍵の読込に失敗']);
|
||||
@@ -444,21 +446,21 @@ class ActivityPub {
|
||||
$body = json_encode($activity, JSON_UNESCAPED_SLASHES);
|
||||
$digest = base64_encode(hash('sha256', $body, true));
|
||||
$date = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$host = parse_url($inboxUrl, PHP_URL_HOST);
|
||||
$uri = new Uri($inboxUrl);
|
||||
|
||||
$headers = [
|
||||
'Host' => $host,
|
||||
'Host' => $uri->getHost().($uri->getPort() ? ':'.$uri->getPort() : ''),
|
||||
'Date' => $date,
|
||||
'Content-Type' => 'application/activity+json',
|
||||
'Digest' => "SHA-256=$digest",
|
||||
];
|
||||
|
||||
$stringToSign = "host: {$headers['Host']}\n"."date: {$headers['Date']}\n"."digest: {$headers['Digest']}";
|
||||
logger(\LogType::ActivityPub, "署名対象: {$stringToSign}");
|
||||
logger(LogType::ActivityPub, "署名対象: {$stringToSign}");
|
||||
|
||||
if (!openssl_sign($stringToSign, $signature, $priv, OPENSSL_ALGO_SHA256)) {
|
||||
$error = openssl_error_string();
|
||||
logger(\LogType::ActivityPub, "エラー:署名に失敗: {$error}");
|
||||
logger(LogType::ActivityPub, "エラー:署名に失敗: {$error}");
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
header('Content-Type: application/activity+json');
|
||||
echo json_encode(['error' => '署名に失敗']);
|
||||
@@ -470,7 +472,7 @@ class ActivityPub {
|
||||
$headers['Signature'] .= 'algorithm="rsa-sha256",';
|
||||
$headers['Signature'] .= 'headers="host date digest",';
|
||||
$headers['Signature'] .= 'signature="'.$sigValue.'"';
|
||||
logger(\LogType::ActivityPub, "署名: {$headers['Signature']}\n送信データ: {$body}");
|
||||
logger(LogType::ActivityPub, "署名: {$headers['Signature']}\n送信データ: {$body}");
|
||||
|
||||
$curl = new Curl($inboxUrl);
|
||||
$curl->setMethod('POST')
|
||||
@@ -486,9 +488,9 @@ class ActivityPub {
|
||||
$err = $curl->getError();
|
||||
|
||||
var_dump(print_r($res));
|
||||
logger(\LogType::ActivityPub, "アクティビティは「{$inboxUrl}」に送信しました: HTTP {$code}");
|
||||
logger(\LogType::ActivityPub, "エラー: {$err}");
|
||||
logger(\LogType::ActivityPub, "レスポンス: {$res}");
|
||||
logger(LogType::ActivityPub, "アクティビティは「{$inboxUrl}」に送信しました: HTTP {$code}");
|
||||
logger(LogType::ActivityPub, "エラー: {$err}");
|
||||
logger(LogType::ActivityPub, "レスポンス: {$res}");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,10 +573,10 @@ class ActivityPub {
|
||||
->setMaxRedirects(5)
|
||||
->setCaInfo('/etc/ssl/cert.pem');
|
||||
|
||||
logger(\LogType::ActivityPub, "アクターURLにリクエスト: {$actor}");
|
||||
logger(LogType::ActivityPub, "アクターURLにリクエスト: {$actor}");
|
||||
$success = $curl->execute();
|
||||
if (!$success) {
|
||||
logger(\LogType::ActivityPub, "アクターリクエストに失敗: ".$curl->getError());
|
||||
logger(LogType::ActivityPub, "アクターリクエストに失敗: ".$curl->getError());
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -583,7 +585,7 @@ class ActivityPub {
|
||||
$err = $curl->getError();
|
||||
|
||||
if ($code !== 200) {
|
||||
logger(\LogType::ActivityPub, "アクター取得に失敗: HTTP {$code}, エラー: {$err}");
|
||||
logger(LogType::ActivityPub, "アクター取得に失敗: HTTP {$code}, エラー: {$err}");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
namespace Std\Lib;
|
||||
|
||||
use Gender;
|
||||
use LogType;
|
||||
use Result;
|
||||
use Roles;
|
||||
|
||||
class Auth {
|
||||
private int $id;
|
||||
|
||||
@@ -72,22 +77,22 @@ class Auth {
|
||||
unset($user->password);
|
||||
unset($user->tokens);
|
||||
$myself = $this->getUserData();
|
||||
if (!$myself || ($myself->id != $user->id && $myself->role < \Roles::STAFF)) $user->email = '(秘密)';
|
||||
if (!$myself || ($myself->id != $user->id && $myself->role < Roles::STAFF)) $user->email = '(秘密)';
|
||||
$user->name = namecolor($user);
|
||||
$user->regDate = date('Y年m月d日', $user->regDate);
|
||||
$user->gender = $user->gender === \Gender::MALE ? '男' : ($user->gender === \Gender::FEMALE ? '女' : '不明');
|
||||
$user->gender = $user->gender === Gender::MALE ? '男' : ($user->gender === Gender::FEMALE ? '女' : '不明');
|
||||
|
||||
switch (true) {
|
||||
case ($user->role & \Roles::ADMIN):
|
||||
case ($user->role & Roles::ADMIN):
|
||||
$user->role = '管理者';
|
||||
break;
|
||||
case ($user->role & \Roles::STAFF):
|
||||
case ($user->role & Roles::STAFF):
|
||||
$user->role = '076スタジオの会社員';
|
||||
break;
|
||||
case ($user->role & (\Roles::FSUBSCRIBER | \Roles::PSUBSCRIBER | \Roles::NSUBSCRIBER | \Roles::SUBSCRIBER)):
|
||||
case ($user->role & (Roles::FSUBSCRIBER | Roles::PSUBSCRIBER | Roles::NSUBSCRIBER | Roles::SUBSCRIBER)):
|
||||
$user->role = '支援者♡';
|
||||
break;
|
||||
case ($user->role & \Roles::BANNED):
|
||||
case ($user->role & Roles::BANNED):
|
||||
$user->role = 'BANされた';
|
||||
break;
|
||||
default:
|
||||
@@ -103,14 +108,14 @@ class Auth {
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function setToken(string $username, #[SensitiveParameter] string $password): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
public function setToken(string $username, #[SensitiveParameter] string $password): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
$userData = $this->getUserData();
|
||||
if (!isset($userData->tokens) || !is_array($userData->tokens)) $userData->tokens = [];
|
||||
|
||||
$verify = $this->verifyLogin($username, $password);
|
||||
if (!$verify->isSuccess) {
|
||||
return \Result::error($verify->message);
|
||||
return Result::error($verify->message);
|
||||
}
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
@@ -119,7 +124,9 @@ class Auth {
|
||||
$ip = trim($ipList[0]);
|
||||
}
|
||||
|
||||
$userData = $this->purgeOldTokens($userData);
|
||||
$res = $this->purgeOldTokens($userData);
|
||||
if (!$res->isSuccess) return Result::Error($res->message);
|
||||
$userData = $res->data;
|
||||
$token = bin2hex(random_bytes(64));
|
||||
$expire = time() + $this->tokenDuration;
|
||||
$newToken = [
|
||||
@@ -137,8 +144,8 @@ class Auth {
|
||||
$json = json_encode($userData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if (file_put_contents($path, $json) === false) {
|
||||
logger(\LogType::Auth, '【setToken】JSONファイルにユーザーデータを保存出来なかった。パス:'.$path);
|
||||
return \Result::error('エラー:ユーザーデータの保存に失敗。');
|
||||
logger(LogType::Auth, '【setToken】JSONファイルにユーザーデータを保存出来なかった。パス:'.$path);
|
||||
return Result::error('エラー:ユーザーデータの保存に失敗。');
|
||||
}
|
||||
|
||||
if (!setcookie('kerozen', $token, [
|
||||
@@ -149,22 +156,24 @@ class Auth {
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
])) {
|
||||
logger(\LogType::Auth, '【setToken】クッキーを設定出来なかった。トークン:'.$token);
|
||||
return \Result::error('エラー:クッキーを設定に失敗。');
|
||||
logger(LogType::Auth, '【setToken】クッキーを設定出来なかった。トークン:'.$token);
|
||||
return Result::error('エラー:クッキーを設定に失敗。');
|
||||
}
|
||||
|
||||
return \Result::success('ログイン成功');
|
||||
return Result::success('ログイン成功');
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
public function getToken(): ?string {
|
||||
if (!AUTH_ENABLED) return '';
|
||||
$userData = $this->getUserData();
|
||||
if (!isset($userData->tokens) || !is_array($userData->tokens)) $userData->tokens = [];
|
||||
$userData = $this->purgeOldTokens($userData);
|
||||
$res = $this->purgeOldTokens($userData);
|
||||
if (!$res->isSuccess) return null;
|
||||
$userData = $res->data;
|
||||
}
|
||||
|
||||
public function logout(?string $token = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
public function logout(?string $token = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
$userData = $this->getUserData();
|
||||
|
||||
if (!$token) {
|
||||
@@ -182,27 +191,27 @@ class Auth {
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
|
||||
return \Result::success();
|
||||
return Result::success();
|
||||
}
|
||||
|
||||
public function isUserExist(?string $username = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $username) return \Result::Error('エラー:ユーザー名をご入力下さい。');
|
||||
public function isUserExist(?string $username = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $username) return Result::Error('エラー:ユーザー名をご入力下さい。');
|
||||
$userList = scandir($this->dataDir);
|
||||
|
||||
foreach ($userList as $list) {
|
||||
if ($list === '.kara' || $list === '.' || $list === '..') continue;
|
||||
$file = str_replace('.json', '', $list);
|
||||
$user = explode('.', $file)[1];
|
||||
if ($username === $user) return \Result::Success("ユーザー「{$username}」は既に存在します。");
|
||||
if ($username === $user) return Result::Success("ユーザー「{$username}」は既に存在します。");
|
||||
}
|
||||
|
||||
return \Result::Error('');
|
||||
return Result::Error('');
|
||||
}
|
||||
|
||||
public function mkUser(?string $username = null, #[SensitiveParameter] ?string $password = null, ?string $passwordVerify = null, ?string $email = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (!AUTH_REGISTER_ENABLED) return \Result::Error('ユーザー登録は無効です。');
|
||||
public function mkUser(?string $username = null, #[SensitiveParameter] ?string $password = null, ?string $passwordVerify = null, ?string $email = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (!AUTH_REGISTER_ENABLED) return Result::Error('ユーザー登録は無効です。');
|
||||
$resUsr = $this->verifyUsername($username);
|
||||
$resPwd = $this->verifyPassword($password, $passwordVerify);
|
||||
$resEml = $this->verifyEmail($email);
|
||||
@@ -213,12 +222,12 @@ class Auth {
|
||||
if (!$resPwd->isSuccess) $err .= $resPwd->message."<br />";
|
||||
if (!$resEml->isSuccess) $err .= $resEml->message."<br />";
|
||||
|
||||
if ($err != '') return \Result::Error($err);
|
||||
if ($err != '') return Result::Error($err);
|
||||
$err = '';
|
||||
|
||||
if (!mkdir($this->assDir.$username, 0755)) {
|
||||
logger(\LogType::Auth, '【mkUser】アセットディレクトリを作成出来なかった。chownをご確認下さい。パス:'.$this->assDir.$username);
|
||||
return \Result::Error('エラー:ユーザーのアイコンディレクトリの作成に失敗。');
|
||||
logger(LogType::Auth, '【mkUser】アセットディレクトリを作成出来なかった。chownをご確認下さい。パス:'.$this->assDir.$username);
|
||||
return Result::Error('エラー:ユーザーのアイコンディレクトリの作成に失敗。');
|
||||
}
|
||||
|
||||
$file = scandir($this->dataDir);
|
||||
@@ -246,29 +255,29 @@ class Auth {
|
||||
$user->regDate = time();
|
||||
$user->namecolor = '';
|
||||
$user->displayname = '';
|
||||
$user->gender = \Gender::UNKNOWN;
|
||||
$user->role = \Roles::MEMBER;
|
||||
$user->gender = Gender::UNKNOWN;
|
||||
$user->role = Roles::MEMBER;
|
||||
$user->tokens = [];
|
||||
|
||||
$path = "{$this->dataDir}{$lastId}.{$username}.json";
|
||||
$json = json_encode($user, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if (file_put_contents($path, $json) === false) {
|
||||
logger(\LogType::Auth, '【mkUser】JSONファイルを作成出来なかった。パス:'.$path);
|
||||
logger(LogType::Auth, '【mkUser】JSONファイルを作成出来なかった。パス:'.$path);
|
||||
rmdir($this->assDir.$username);
|
||||
return \Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
return Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
}
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
private function verifyLogin(string $username, #[SensitiveParameter] string $password): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
private function verifyLogin(string $username, #[SensitiveParameter] string $password): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
$userData = $this->getUserData();
|
||||
if ($username !== $userData->username || !password_verify($password, $userData->password)) {
|
||||
return \Result::Error('エラー:ユーザー名又はパスワードが一致していません。');
|
||||
return Result::Error('エラー:ユーザー名又はパスワードが一致していません。');
|
||||
}
|
||||
|
||||
if (password_needs_rehash($userData->password, $this->algo)) {
|
||||
@@ -277,31 +286,31 @@ class Auth {
|
||||
$json = json_encode($userData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if (file_put_contents($path, $json) === false) {
|
||||
logger(\LogType::Auth, '【verifyLogin】JSONファイルを変更出来なかった。chownをご確認下さい。パス:'.$path);
|
||||
return \Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
logger(LogType::Auth, '【verifyLogin】JSONファイルを変更出来なかった。chownをご確認下さい。パス:'.$path);
|
||||
return Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
}
|
||||
}
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
private function purgeOldTokens(\stdClass $userData): \stdClass|\Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
private function purgeOldTokens(\stdClass $userData): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
// 有効期限切れたトークンの削除
|
||||
$out = $userData;
|
||||
$out->tokens = array_filter($userData->tokens, function($t): bool {
|
||||
return ($t->expDate ?? 0) > time();
|
||||
return Result::Success('', ($t->expDate ?? 0) > time());
|
||||
});
|
||||
|
||||
$path = $this->dataDir.$userData->id.'.'.$userData->username.'.json';
|
||||
$json = json_encode($userData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if (file_put_contents($path, $json) === false) {
|
||||
logger(\LogType::Auth, '【purgeOldTokens】JSONファイルを変更出来なかった。chownをご確認下さい。パス:'.$path);
|
||||
return \Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
logger(LogType::Auth, '【purgeOldTokens】JSONファイルを変更出来なかった。chownをご確認下さい。パス:'.$path);
|
||||
return Result::Error('エラー:ユーザーデータの保存に失敗。');
|
||||
}
|
||||
|
||||
return $out;
|
||||
return Result::Success('', $out);
|
||||
}
|
||||
|
||||
private function getUserId(?string $username = null, ?string $token = null): int {
|
||||
@@ -396,8 +405,8 @@ class Auth {
|
||||
return json_decode($lines);
|
||||
}
|
||||
|
||||
private function isEmailExist(?string $email = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
private function isEmailExist(?string $email = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
$userList = scandir($this->dataDir);
|
||||
$matches = [];
|
||||
|
||||
@@ -410,67 +419,67 @@ class Auth {
|
||||
if (str_contains($content, '"email": "'.$email.'",')) {
|
||||
$matches[] = $email;
|
||||
}
|
||||
if (count($matches) > 0) return \Result::Error("ユーザー「{$email}」は既に存在します。");
|
||||
if (count($matches) > 0) return Result::Error("ユーザー「{$email}」は既に存在します。");
|
||||
}
|
||||
|
||||
return \Result::Success('エラー:メールアドレスが存在しません。');
|
||||
return Result::Success('エラー:メールアドレスが存在しません。');
|
||||
}
|
||||
|
||||
private function verifyEmail(?string $email = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $email || '' === $email) return \Result::Error('エラー:メールアドレスをご入力下さい。');
|
||||
if (strpos($email, '@') === false) return \Result::Error('エラー:メールアドレスは不正です。');
|
||||
private function verifyEmail(?string $email = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $email || '' === $email) return Result::Error('エラー:メールアドレスをご入力下さい。');
|
||||
if (strpos($email, '@') === false) return Result::Error('エラー:メールアドレスは不正です。');
|
||||
|
||||
$domain = explode('@', $email)[1];
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return \Result::Error('エラー:メールアドレスは不正です。');
|
||||
return Result::Error('エラー:メールアドレスは不正です。');
|
||||
}
|
||||
|
||||
if (!checkdnsrr($domain, 'MX')) {
|
||||
return \Result::Error('エラー:メールアドレスは不正です。');
|
||||
return Result::Error('エラー:メールアドレスは不正です。');
|
||||
}
|
||||
|
||||
$res = $this->isEmailExist($email);
|
||||
if (!$res->isSuccess) return \Result::Error($res->message);
|
||||
if (!$res->isSuccess) return Result::Error($res->message);
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
private function verifyUsername(?string $username): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $username || '' === $username) return \Result::Error('エラー:ユーザー名をご入力下さい。');
|
||||
if (strlen($username) < 6) return \Result::Error('エラー:ユーザー名は6文字以上をご入力下さい。');
|
||||
private function verifyUsername(?string $username): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $username || '' === $username) return Result::Error('エラー:ユーザー名をご入力下さい。');
|
||||
if (strlen($username) < 6) return Result::Error('エラー:ユーザー名は6文字以上をご入力下さい。');
|
||||
|
||||
$res = $this->isUserExist($username);
|
||||
if ($res->isSuccess) return \Result::Error($res->message);
|
||||
if ($res->isSuccess) return Result::Error($res->message);
|
||||
|
||||
$accepted = 'A-Za-z0-9';
|
||||
if (preg_match("/[^{$accepted}]/", $username)) {
|
||||
return \Result::Error('エラー:ユーザー名に不正な文字が含みます。');
|
||||
return Result::Error('エラー:ユーザー名に不正な文字が含みます。');
|
||||
}
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
private function verifyPassword(#[SensitiveParameter] ?string $password = null, #[SensitiveParameter] ?string $passwordVerify = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $password || '' === $password) return \Result::Error('エラー:パスワードをご入力下さい。');
|
||||
if ($password !== $passwordVerify) return \Result::Error('エラー:パスワードは一致していません。');
|
||||
private function verifyPassword(#[SensitiveParameter] ?string $password = null, #[SensitiveParameter] ?string $passwordVerify = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (null === $password || '' === $password) return Result::Error('エラー:パスワードをご入力下さい。');
|
||||
if ($password !== $passwordVerify) return Result::Error('エラー:パスワードは一致していません。');
|
||||
|
||||
$res = $this->checkPasswordStandards($password);
|
||||
if (!$res->isSuccess) {
|
||||
return \Result::Error($res->message);
|
||||
return Result::Error($res->message);
|
||||
}
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
private function checkPasswordStandards(#[SensitiveParameter] ?string $password = null): \Result {
|
||||
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
if (strlen($password) < $this->minPassLen) return \Result::Error("エラー:パスワードは{$this->minPassLen}以上をご入力下さい。");
|
||||
private function checkPasswordStandards(#[SensitiveParameter] ?string $password = null): Result {
|
||||
if (!AUTH_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (strlen($password) < $this->minPassLen) return Result::Error("エラー:パスワードは{$this->minPassLen}以上をご入力下さい。");
|
||||
|
||||
if (!\countmatch($password)) {
|
||||
return \Result::Error('エラー:パスワードに不正な文字が含みます。');
|
||||
return Result::Error('エラー:パスワードに不正な文字が含みます。');
|
||||
}
|
||||
|
||||
$countUpper = preg_match_all('/[A-Z]/', $password) < 2;
|
||||
@@ -479,9 +488,9 @@ class Auth {
|
||||
$countSymbol = \count_special_chars($password) < 2;
|
||||
|
||||
if ($countUpper || $countLower || $countDigit || $countSymbol) {
|
||||
return \Result::Error('エラー:パスワードは2つ以上の大文字、2つ以上の小文字、2つ以上の数字、及び2つ以上の特別文字を含む事が必須です。');
|
||||
return Result::Error('エラー:パスワードは2つ以上の大文字、2つ以上の小文字、2つ以上の数字、及び2つ以上の特別文字を含む事が必須です。');
|
||||
}
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ class Cache {
|
||||
protected int $dataDuration = 1800; // 30分
|
||||
|
||||
public function __construct(string $dataDir) {
|
||||
$this->dataDir = ROOT.'/data/cache/openprovider/'.$dataDir.'/';
|
||||
$this->dataDir = ROOT.'/data/cache/'.$dataDir.'/';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,9 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
namespace Std\Lib;
|
||||
|
||||
use Result;
|
||||
use Uri\Rfc3986\Uri;
|
||||
|
||||
/**
|
||||
* php_curlへの依存を排除するための独自のCURL実装
|
||||
*/
|
||||
@@ -317,11 +320,11 @@ class Curl {
|
||||
*
|
||||
* @return Result 成功または失敗
|
||||
*/
|
||||
public function execute(): \Result {
|
||||
if (!CURL_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
|
||||
public function execute(): Result {
|
||||
if (!CURL_ENABLED) return Result::Error('エラー:認証システムは無効です。');
|
||||
if (empty($this->url)) {
|
||||
$this->responseError = 'URLがありません';
|
||||
return \Result::Error($this->responseError);
|
||||
return Result::Error($this->responseError);
|
||||
}
|
||||
|
||||
// レスポンスデータのリセット
|
||||
@@ -352,19 +355,18 @@ class Curl {
|
||||
fwrite($this->stderr, "* 接続中: {$currentUrl}\n");
|
||||
}
|
||||
|
||||
$parsed = parse_url($currentUrl);
|
||||
if (!$parsed) {
|
||||
$uri = new Uri($currentUrl);
|
||||
if (!$uri) {
|
||||
$this->responseError = "無効なURL: {$currentUrl}";
|
||||
return \Result::Error($this->responseError);
|
||||
return Result::Error($this->responseError);
|
||||
}
|
||||
|
||||
$scheme = isset($parsed['scheme']) ? strtolower($parsed['scheme']) : 'http';
|
||||
$host = $parsed['host'];
|
||||
$port = isset($parsed['port'])
|
||||
? $parsed['port'] : ($scheme === 'https' ? 443 : 80);
|
||||
$path = isset($parsed['path']) ? $parsed['path'] : '/';
|
||||
if (isset($parsed['query'])) {
|
||||
$path .= '?'.$parsed['query'];
|
||||
$scheme = $uri->getScheme() ?? 'http';
|
||||
$port = $uri->getPort() ?? ($scheme === 'https' ? 443 : 80);
|
||||
$path = $uri->getPath();
|
||||
if ($path === '') $path = '/';
|
||||
if (NULL !== $uri->getQuery()) {
|
||||
$path .= '?'.$uri->getQuery();
|
||||
}
|
||||
|
||||
// 認証
|
||||
@@ -386,7 +388,7 @@ class Curl {
|
||||
if ($method === 'POST' || $method === 'PUT') {
|
||||
if (!empty($this->postRaw)) {
|
||||
$httpData = $this->postRaw;
|
||||
} elseif (!empty($this->postFields)) {
|
||||
} else if (!empty($this->postFields)) {
|
||||
$httpData = http_build_query($this->postFields);
|
||||
if (!isset($this->headers['Content-Type'])) {
|
||||
$this->headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
@@ -402,38 +404,27 @@ class Curl {
|
||||
}
|
||||
|
||||
$request = "{$method} {$path} HTTP/1.1\r\n";
|
||||
$request .= "Host: {$host}\r\n";
|
||||
$request .= "Host: {$uri->getHost()}\r\n";
|
||||
$request .= "User-Agent: {$this->userAgent}\r\n";
|
||||
$request .= "{$accept}\r\n";
|
||||
$request .= "Connection: close\r\n";
|
||||
|
||||
// 認証ヘッダー
|
||||
if (!empty($authHeader)) {
|
||||
$request .= $authHeader;
|
||||
}
|
||||
|
||||
// ヘッダーを追加
|
||||
foreach ($this->headers as $name => $value) {
|
||||
$request .= "{$name}: {$value}\r\n";
|
||||
}
|
||||
|
||||
// リファラーが設定されていれば追加
|
||||
if (!empty($this->referer) && !isset($this->headers['Referer'])) {
|
||||
$request .= "Referer: {$this->referer}\r\n";
|
||||
}
|
||||
|
||||
// クッキーヘッダーを追加
|
||||
if (!empty($this->cookies) && !isset($this->headers['Cookie'])) {
|
||||
$cookieStrings = [];
|
||||
foreach ($this->cookies as $name => $value) {
|
||||
$cookieStrings[] = $name.'='.urlencode($value);
|
||||
}
|
||||
$request .= 'Cookie: '.implode('; ', $cookieStrings)."\r\n";
|
||||
$ext = $this->buildHeaderString();
|
||||
if (!empty($ext)) {
|
||||
$request .= $ext;
|
||||
}
|
||||
|
||||
// ヘッダー終了
|
||||
$request .= "\r\n";
|
||||
|
||||
// POSTデータを追加
|
||||
if ($method === 'POST' || $method === 'PUT') {
|
||||
if ($httpData !== '') {
|
||||
$request .= $httpData;
|
||||
}
|
||||
|
||||
@@ -460,7 +451,7 @@ class Curl {
|
||||
$context = stream_context_create(['ssl' => $sslOptions]);
|
||||
|
||||
$socket = @stream_socket_client(
|
||||
"tls://{$host}:{$port}",
|
||||
"tls://{$uri->getHost()}:{$port}",
|
||||
$errno,
|
||||
$errstr,
|
||||
$this->timeout,
|
||||
@@ -468,7 +459,7 @@ class Curl {
|
||||
$context
|
||||
);
|
||||
} else {
|
||||
$socket = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
|
||||
$socket = @fsockopen($uri->getHost(), $port, $errno, $errstr, $this->timeout);
|
||||
}
|
||||
|
||||
if (!$socket) {
|
||||
@@ -476,7 +467,7 @@ class Curl {
|
||||
if ($this->verbose && $this->stderr) {
|
||||
fwrite($this->stderr, "* エラー: {$this->responseError}\n");
|
||||
}
|
||||
return \Result::Error($this->responseError);
|
||||
return Result::Error($this->responseError);
|
||||
}
|
||||
|
||||
// タイムアウトを設定
|
||||
@@ -497,7 +488,7 @@ class Curl {
|
||||
|
||||
if ($rawResponse === '') {
|
||||
$this->responseError = 'リスポンスエラー';
|
||||
return \Result::Error($this->responseError);
|
||||
return Result::Error($this->responseError);
|
||||
}
|
||||
|
||||
list($headerLines, $body) = $this->parseResponse($rawResponse);
|
||||
@@ -505,6 +496,7 @@ class Curl {
|
||||
// ヘッダーを解析
|
||||
$this->responseHeaders = [];
|
||||
$this->responseCode = 0;
|
||||
$redirectUrl = '';
|
||||
|
||||
foreach ($headerLines as $index => $header) {
|
||||
if ($index === 0) {
|
||||
@@ -518,6 +510,8 @@ class Curl {
|
||||
$name = trim($name);
|
||||
$value = trim($value);
|
||||
$this->responseHeaders[$name] = $value;
|
||||
|
||||
$redirectUrl = $this->checkReds($name, $value, $currentUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,27 +533,30 @@ class Curl {
|
||||
}
|
||||
|
||||
// リダイレクトが必要な場合
|
||||
if (!empty($redirectUrl) && $redirectCount < $this->maxRedirects) {
|
||||
if ($redirectUrl !== '' && $redirectCount < $this->maxRedirects) {
|
||||
$currentUrl = $redirectUrl;
|
||||
$redirectCount++;
|
||||
$this->info['redirect_count'] = $redirectCount;
|
||||
$this->info['redirect_url'] = $redirectUrl;
|
||||
|
||||
// 302や303リダイレクトはGETにメソッドを変更
|
||||
if ($this->responseCode == 302 || $this->responseCode == 303) {
|
||||
if (in_array($this->responseCode, [302, 303], true)) {
|
||||
$this->method = 'GET';
|
||||
$this->postRaw = '';
|
||||
$this->postFields = [];
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
// リクエスト完了後、元のメソッドに戻す
|
||||
$this->method = $originalMethod;
|
||||
$this->info['total_time'] = microtime(true) - $startTime;
|
||||
|
||||
return \Result::Success();
|
||||
return Result::Success();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,22 +624,20 @@ class Curl {
|
||||
* @return string リダイレクトURL、リダイレクトがない場合は空文字
|
||||
*/
|
||||
private function checkReds(string $name, string $value, string $currentUrl): string {
|
||||
$redirectUrl = '';
|
||||
if (!$this->followRedirects) return '';
|
||||
if (strtolower($name) !== 'location') return '';
|
||||
if ($this->responseCode < 300 || $this->responseCode >= 400) return '';
|
||||
$redirectUrl = $value;
|
||||
|
||||
if ($this->followRedirects && (strtolower($name) === 'location'
|
||||
&& $this->responseCode >= 300 && $this->responseCode < 400)) {
|
||||
$redirectUrl = $value;
|
||||
|
||||
if (strpos($redirectUrl, 'http') !== 0) {
|
||||
if ($redirectUrl[0] === '/') {
|
||||
$parsed = parse_url($currentUrl);
|
||||
$redirectUrl = $parsed['scheme'].'://'.$parsed['host']
|
||||
.(isset($parsed['port']) ? ':'.$parsed['port'] : '')
|
||||
.$redirectUrl;
|
||||
} else {
|
||||
$redirectUrl = dirname($currentUrl).'/'.$redirectUrl;
|
||||
}
|
||||
}
|
||||
if (strpos($redirectUrl, 'http') !== 0) {
|
||||
if ($redirectUrl[0] === '/') {
|
||||
$uri = new Uri($currentUrl);
|
||||
$redirectUrl = $uri->getScheme().'://'.$uri->getHost()
|
||||
.(NULL !== $uri->getPort() ? ':'.$uri->getPort() : '')
|
||||
.$redirectUrl;
|
||||
} else {
|
||||
$redirectUrl = dirname($currentUrl).'/'.$redirectUrl;
|
||||
}
|
||||
}
|
||||
|
||||
return $redirectUrl;
|
||||
@@ -654,16 +649,16 @@ class Curl {
|
||||
* @return string 構築されたヘッダー文字列
|
||||
*/
|
||||
private function buildHeaderString(): string {
|
||||
$headers = [];
|
||||
$request = '';
|
||||
|
||||
// ユーザー指定のヘッダーを追加
|
||||
foreach ($this->headers as $name => $value) {
|
||||
$headers[] = "{$name}: {$value}";
|
||||
$request .= "{$name}: {$value}\r\n";
|
||||
}
|
||||
|
||||
// リファラーが設定されていれば追加
|
||||
if (!empty($this->referer) && !isset($this->headers['Referer'])) {
|
||||
$headers[] = "Referer: {$this->referer}";
|
||||
$request .= "Referer: {$this->referer}\r\n";
|
||||
}
|
||||
|
||||
// 必要に応じてクッキーヘッダーを追加
|
||||
@@ -673,10 +668,10 @@ class Curl {
|
||||
$cookieStrings[] = $name.'='.urlencode($value);
|
||||
}
|
||||
|
||||
$headers[] = 'Cookie: '.implode('; ', $cookieStrings);
|
||||
$request .= 'Cookie: '.implode('; ', $cookieStrings)."\r\n";
|
||||
}
|
||||
|
||||
return implode("\r\n", $headers)."\r\n";
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,7 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
namespace Std\Lib\Image;
|
||||
|
||||
use Std\Lib\Image\ImageInterface;
|
||||
use LogType;
|
||||
|
||||
class Png implements ImageInterface {
|
||||
public \stdClass $IHDR; // 画像ヘッダー
|
||||
@@ -629,7 +630,7 @@ exif_crc:
|
||||
fclose($fp);
|
||||
$err = '不明なチャンク:'.$nextChunk.'、HEX:'.bin2hex($nextChunk);
|
||||
kys($err);
|
||||
logger(\LogType::Image, '【PNG】'.$err);
|
||||
logger(LogType::Image, '【PNG】'.$err);
|
||||
return $png;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
namespace Std\Lib;
|
||||
|
||||
use LogType;
|
||||
|
||||
class Mailer {
|
||||
private $socket;
|
||||
private string $host;
|
||||
@@ -70,7 +72,7 @@ class Mailer {
|
||||
$this->socket = fsockopen($this->host, $this->port, $errno, $err, 30);
|
||||
if (!$this->socket) {
|
||||
$msg = "接続に失敗: {$err} ({$errno})";
|
||||
logger(\LogType::Mailer, $msg);
|
||||
logger(LogType::Mailer, $msg);
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ class Mailer {
|
||||
if (!stream_socket_enable_crypto(
|
||||
$this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
|
||||
$msg = "TLSハンドシェイクに失敗";
|
||||
logger(\LogType::Mailer, $msg);
|
||||
logger(LogType::Mailer, $msg);
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ class Mailer {
|
||||
$response = $this->readResponse();
|
||||
if (substr($response, 0, 3) != '250') {
|
||||
$msg = "メール送信に失敗: {$response}";
|
||||
logger(\LogType::Mailer, $msg);
|
||||
logger(LogType::Mailer, $msg);
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
}
|
||||
@@ -235,7 +237,7 @@ class Mailer {
|
||||
$res = $this->readResponse();
|
||||
if (substr($res, 0, 3) != $retcode) {
|
||||
$msg = "「{$command}」に対する予期しないレスポンス: {$res}";
|
||||
logger(\LogType::Mailer, $msg);
|
||||
logger(LogType::Mailer, $msg);
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ class Openprovider {
|
||||
private string $ip = '';
|
||||
private int $resellerId = 0;
|
||||
private int $lastAuth = 0;
|
||||
// private string $BASEURL = 'https://api.openprovider.eu/v1beta';
|
||||
private string $BASEURL = DEBUG_MODE ? 'http://api.sandbox.openprovider.nl:8480/v1beta'
|
||||
: 'https://api.openprovider.eu/v1beta';
|
||||
|
||||
@@ -63,6 +64,10 @@ class Openprovider {
|
||||
$this->cache = new Cache($this->cacheDir);
|
||||
}
|
||||
|
||||
public function deleteCache(string $name): void {
|
||||
$this->cache->murder($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* トークンの受け取り。
|
||||
* このライブリリーを使ったら、一回「login()」を実行する事が必須となります。
|
||||
@@ -146,7 +151,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* ドメイン一覧。
|
||||
* @todo テスト
|
||||
*
|
||||
* @return Result 結果。
|
||||
*/
|
||||
@@ -156,11 +160,12 @@ class Openprovider {
|
||||
$cache = $this->cache->get($cacheName);
|
||||
if ($this->cache->resurrect($cache, $query)) return Result::Success('', $cache);
|
||||
|
||||
$curl = $this->setupCurl('/domains');
|
||||
$uri = "/domains?".http_build_query($query, '', '&', PHP_QUERY_RFC3986);
|
||||
$curl = $this->setupCurl($uri);
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res['data']['results'])) {
|
||||
if (isset($res->data['data']['results'])) {
|
||||
$this->cache->set($cacheName, $res->data);
|
||||
return Result::Success('', $res['data']['results']);
|
||||
return Result::Success('', $res->data['data']['results']);
|
||||
}
|
||||
|
||||
return Result::Error('ドメインの確認に失敗。');
|
||||
@@ -212,7 +217,8 @@ class Openprovider {
|
||||
'autorenew' => $autorenew,
|
||||
];
|
||||
|
||||
$curl = $this->setupCurl('/domains/', 'POST', $payload);
|
||||
$uri = '/domains/';
|
||||
$curl = $this->setupCurl($uri, 'POST', $payload);
|
||||
kys('TODO');
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res['data'])) return Result::Success('', $res);
|
||||
@@ -249,7 +255,8 @@ class Openprovider {
|
||||
'with_price' => $with_price,
|
||||
];
|
||||
|
||||
$curl = $this->setupCurl('/domains/check', 'POST', $payload);
|
||||
$uri = '/domains/check';
|
||||
$curl = $this->setupCurl($uri, 'POST', $payload);
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res->data['data']['results'])) return Result::Success('', $res->data['data']['results']);
|
||||
|
||||
@@ -266,7 +273,8 @@ class Openprovider {
|
||||
public function suggestDomainname(array $payload = []): Result {
|
||||
if (!OPENPROVIDER_ENABLED) return Result::error('エラー:OpenProviderは無効です。');
|
||||
|
||||
$curl = $this->setupCurl('/domains/suggest-name', 'POST', $payload);
|
||||
$uri = '/domains/suggest-name';
|
||||
$curl = $this->setupCurl($uri, 'POST', $payload);
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res->data['data']['results'])) {
|
||||
return Result::Success('ドメインを勧められる事に成功。', $res->data['data']['results']);
|
||||
@@ -285,7 +293,8 @@ class Openprovider {
|
||||
public function tradeDomainname(array $payload = []): Result {
|
||||
if (!OPENPROVIDER_ENABLED) return Result::error('エラー:OpenProviderは無効です。');
|
||||
|
||||
$curl = $this->setupCurl('/domains/trade', 'POST', $payload);
|
||||
$uri = '/domains/trade';
|
||||
$curl = $this->setupCurl($uri, 'POST', $payload);
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res->data['data']['results'])) {
|
||||
$this->cache->murder('listdomains');
|
||||
@@ -305,7 +314,8 @@ class Openprovider {
|
||||
public function transferDomainname(array $payload = []): Result {
|
||||
if (!OPENPROVIDER_ENABLED) return Result::error('エラー:OpenProviderは無効です。');
|
||||
|
||||
$curl = $this->setupCurl('/domains/transfer', 'POST', $payload);
|
||||
$uri = '/domains/transfer';
|
||||
$curl = $this->setupCurl($uri, 'POST', $payload);
|
||||
$res = $this->curlResult($curl);
|
||||
if (isset($res->data['data']['results'])) {
|
||||
$this->cache->murder('listdomains');
|
||||
@@ -693,7 +703,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* 請求書一覧
|
||||
* @todo テスト
|
||||
*
|
||||
* @param array $query 検索クエリー
|
||||
* @return Result
|
||||
@@ -722,7 +731,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* 支払一覧
|
||||
* @todo テスト
|
||||
*
|
||||
* @param array $query 検索クエリー
|
||||
* @return Result
|
||||
@@ -751,7 +759,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* トランザクション一覧
|
||||
* @todo テスト
|
||||
*
|
||||
* @param array $query 検索クエリー
|
||||
* @return Result
|
||||
@@ -2086,7 +2093,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* リセラーの受け取り
|
||||
* @todo テスト
|
||||
*
|
||||
* @param array $query 検索クエリー
|
||||
* @return Result
|
||||
@@ -2162,7 +2168,6 @@ class Openprovider {
|
||||
|
||||
/**
|
||||
* 統計の受け取り
|
||||
* @todo テスト
|
||||
*
|
||||
* @return Result
|
||||
*/
|
||||
|
||||
@@ -407,6 +407,7 @@ class Tester {
|
||||
*/
|
||||
public function assertArrayHasKey(mixed $key, array $array,
|
||||
?string $message = null): Tester {
|
||||
if (NULL === $key) throw new AssertionFailedException("配列鍵がありません");
|
||||
if (!is_array($array) && !($array instanceof \ArrayAccess)) {
|
||||
throw new AssertionFailedException(
|
||||
'第2引数は配列又はArrayAccessを実装している必要があります');
|
||||
|
||||
2
sync.sh
2
sync.sh
@@ -7,5 +7,5 @@ HTTPHOME=/var/www/htdocs/LittleBeast
|
||||
rsync ${RSYNCOPT} *.php ${SRV}:${HTTPHOME}
|
||||
rsync ${RSYNCOPT} blog ${SRV}:${HTTPHOME}
|
||||
#rsync ${RSYNCOPT} config ${SRV}:${HTTPHOME}
|
||||
rsync ${RSYNCOPT} src/Site/Controller ${SRV}:${HTTPHOME}/src/Site
|
||||
rsync ${RSYNCOPT} src ${SRV}:${HTTPHOME}
|
||||
rsync ${RSYNCOPT} view ${SRV}:${HTTPHOME}
|
||||
41
util.php
41
util.php
@@ -434,9 +434,40 @@ function getImageInfo(string $url): \Std\Lib\Image {
|
||||
return $img;
|
||||
}
|
||||
|
||||
// PHP 8.3と8.4の場合
|
||||
if (!function_exists('array_last')) {
|
||||
function array_last(array $array): mixed {
|
||||
return $array === [] ? null : $array[array_key_last($array)];
|
||||
}
|
||||
function align(int $val): int {
|
||||
if ($val <= 0) return 1;
|
||||
if ($val === 1) return 1;
|
||||
|
||||
$res = 0;
|
||||
$lower = 1;
|
||||
|
||||
while ($lower * 2 <= $val) $lower *= 2;
|
||||
$upper = $lower * 2;
|
||||
|
||||
if (($val - $lower) <= ($upper - $val)) return $lower;
|
||||
return $upper;
|
||||
}
|
||||
|
||||
function align_up(int $val): int {
|
||||
if ($val <= 0) return 1;
|
||||
if ($val === 1) return 1;
|
||||
|
||||
$res = 0;
|
||||
$lower = 1;
|
||||
|
||||
while ($lower * 2 <= $val) $lower *= 2;
|
||||
$upper = $lower * 2;
|
||||
|
||||
return $upper;
|
||||
}
|
||||
|
||||
function align_down(int $val): int {
|
||||
if ($val <= 0) return 1;
|
||||
if ($val === 1) return 1;
|
||||
|
||||
$res = 0;
|
||||
$lower = 1;
|
||||
|
||||
while ($lower * 2 <= $val) $lower *= 2;
|
||||
return $lower;
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
{@ endif @}
|
||||
{@ endif @}
|
||||
<meta name="description" content="{{ $description }}" />
|
||||
<meta name="keywords" content="{{ SITEINFO['tags'].(isset($meta->category) ? ',' : '') }}{@ if (isset($meta) && isset($meta->category)) @}{@ foreach ($meta->category as $k => $cat) @}{{ $cat.($k === array_key_last($meta->category) ? '' : ',') }}{@ endforeach @}{@ endif @}" />
|
||||
<meta name="keywords" content="{{ SITEINFO['tags'].(isset($meta->category) ? ','.implode(',', $meta->category) : '') }}" />
|
||||
|
||||
<meta property="og:title" content="{{ SITEINFO['title'] }}: {{ $pagetit }}" />
|
||||
<meta property="og:description" content="{{ $description }}" />
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
{@ include(common/header) @}
|
||||
<h1>{{ $description }}</h1>
|
||||
<h1>お帰り、{{ $data['data']['name']['full_name'] }}様</h1>
|
||||
<ul>
|
||||
<li>請求</li>
|
||||
<ul>
|
||||
<li><a href="/openprovider/listinvoices">請求書一覧</a></li>
|
||||
<li><a href="/openprovider/listpayments">支払一覧</a></li>
|
||||
<li><a href="/openprovider/listtransactions">トランザクション一覧</a></li>
|
||||
</ul>
|
||||
<li>顧客様</li>
|
||||
<ul>
|
||||
<li><a href="/openprovider/listcustomers">顧客様検索</a></li>
|
||||
@@ -17,4 +23,39 @@
|
||||
<li><a href="/openprovider/listdnszones">DNSゾーン一覧</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<div class="op-grid">
|
||||
<div class="op-grid-item">
|
||||
<div style="color: #00bb00; font-size: 24px;">利用可能な残高: € {{ $data['data']['balance'] }}</div>
|
||||
<div style="color: #777; font-size: 11px;">使用残高: € {{ $data['data']['reserved_balance'] }}</div>
|
||||
</div>
|
||||
<div class="op-grid-item">
|
||||
<a href="/openprovider/listdomains">ドメイン名</a>数:{{ $data['data']['statistics']['domain']['by_status']['ACT'] }}
|
||||
</div>
|
||||
<div class="op-grid-item">
|
||||
<a href="/openprovider/listsslorders">SSL証明書</a>数:{{ $data['data']['statistics']['ssl']['by_status']['ACT'] }}
|
||||
</div>
|
||||
<div class="op-grid-item">
|
||||
<a href="/openprovider/listlicenses">ライセンス</a>数:{{ $data['data']['statistics']['license']['total'] }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>間もなく更新が必要なドメイン名</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ドメイン名</th>
|
||||
<th>更新日</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{@ foreach ($domain as $d) @}
|
||||
<tr>
|
||||
{$ $dom = $d['domain']['name'].'.'.$d['domain']['extension'] $}
|
||||
<td><a href="/openprovider/getdomain/{{ $dom }}">{{ $dom }}</a></td>
|
||||
<td>{{ $d['renewal_date'] }}</td>
|
||||
</tr>
|
||||
{@ endforeach @}
|
||||
</tbody>
|
||||
</table>
|
||||
{@ include(common/footer) @}
|
||||
31
view/openprovider/listinvoices.maron
Normal file
31
view/openprovider/listinvoices.maron
Normal file
@@ -0,0 +1,31 @@
|
||||
{@ include(common/header) @}
|
||||
<h1>請求書一覧</h1>
|
||||
{@ if (isset($data['data']['results'])) @}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日時</th>
|
||||
<th>請求書番号</th>
|
||||
<th>単殻</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{@ foreach ($data['data']['results'] as $d) @}
|
||||
<tr>
|
||||
<td>{{ $d['creation_date'] }}</td>
|
||||
<td>{{ $d['invoice_number'] }}</td>
|
||||
<td>{{ $d['amount']['product']['price'].' '.$d['amount']['product']['currency'] }}</td>
|
||||
<td><a href="{{ $d['invoice_url'] }}">請求書をダウンロード</a></td>
|
||||
<td><a href="{{ $d['attachment_url'] }}">添付</a></td>
|
||||
</tr>
|
||||
{@ endforeach @}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>結果数:{{ $data['data']['total'] }}</p>
|
||||
{@ include(common/pagination) @}
|
||||
{@ else @}
|
||||
<p>何も見つけられませんでした。</p>
|
||||
{@ endif @}
|
||||
{@ include(common/footer) @}
|
||||
38
view/openprovider/listpayments.maron
Normal file
38
view/openprovider/listpayments.maron
Normal file
@@ -0,0 +1,38 @@
|
||||
{@ include(common/header) @}
|
||||
<h1>支払一覧</h1>
|
||||
{@ if (isset($data['data']['results'])) @}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日時</th>
|
||||
<th>支払ID</th>
|
||||
<th>種類</th>
|
||||
<th>単殻</th>
|
||||
<th>支払済み</th>
|
||||
<th>認証済み</th>
|
||||
<th>完了</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{@ foreach ($data['data']['results'] as $d) @}
|
||||
{$ $p = isset($d['payment_date']) $}
|
||||
{$ $c = isset($d['confirmation_date']) $}
|
||||
{$ $r = $d['is_processed'] == 1 $}
|
||||
<tr>
|
||||
<td>{{ $d['creation_date'] }}</td>
|
||||
<td>{{ $d['id'] }}</td>
|
||||
<td>{{ $d['type'] }}</td>
|
||||
<td>{{ $d['amount']['product']['price'].' '.$d['amount']['product']['currency'] }}</td>
|
||||
<td><span style="color: #{{ $p ? '14c014' : 'ee5040' }};">{{ $p ? '〇' : '✕' }}</span></td>
|
||||
<td><span style="color: #{{ $c ? '14c014' : 'ee5040' }};">{{ $c ? '〇' : '✕' }}</span></td>
|
||||
<td><span style="color: #{{ $r ? '14c014' : 'ee5040' }};">{{ $r ? '〇' : '✕' }}</span></td>
|
||||
</tr>
|
||||
{@ endforeach @}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>結果数:{{ $data['data']['total'] }}</p>
|
||||
{@ include(common/pagination) @}
|
||||
{@ else @}
|
||||
<p>何も見つけられませんでした。</p>
|
||||
{@ endif @}
|
||||
{@ include(common/footer) @}
|
||||
40
view/openprovider/listtransactions.maron
Normal file
40
view/openprovider/listtransactions.maron
Normal file
@@ -0,0 +1,40 @@
|
||||
{@ include(common/header) @}
|
||||
<h1>トランザクション一覧</h1>
|
||||
検索バー<br />
|
||||
{@ if (isset($data['data']['results'])) @}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日時</th>
|
||||
<th>状況</th>
|
||||
<th>ドメイン名・商品</th>
|
||||
<th>数</th>
|
||||
<th>値段</th>
|
||||
<th>デビット</th>
|
||||
<th>クレジット</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{@ foreach ($data['data']['results'] as $d) @}
|
||||
{$ $pp = $d['price']['product'] $}
|
||||
{$ $pr = $d['price']['reseller'] $}
|
||||
{$ $tp = $d['total']['product'] $}
|
||||
{$ $tr = $d['total']['reseller'] $}
|
||||
<tr>
|
||||
<td>{{ $d['creation_date'] }}</td>
|
||||
<td>{{ $d['action'] }}</td>
|
||||
<td>{{ $d['subject'] }}</td>
|
||||
<td>{{ $d['quantity'] }}</td>
|
||||
<td>{{ $pp['price'].' '.$pp['currency'] }}<br /><span style="color: #777777;">({{ $pr['price'].' '.$pr['currency'] }})</span></td>
|
||||
<td><span style="color: #ee5040;">{{ $tr['price'].' '.$tr['currency'] }}</span></td>
|
||||
<td><span style="color: #14c014;"></span></td>
|
||||
</tr>
|
||||
{@ endforeach @}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>結果数:{{ $data['data']['total'] }}</p>
|
||||
{@ include(common/pagination) @}
|
||||
{@ else @}
|
||||
<p>何も見つけられませんでした。</p>
|
||||
{@ endif @}
|
||||
{@ include(common/footer) @}
|
||||
Reference in New Issue
Block a user