Compare commits
No commits in common. "master" and "diff" have entirely different histories.
468 changed files with 10737 additions and 12957 deletions
|
@ -1,18 +1,18 @@
|
|||
; This file is for unifying the coding style for different editors and IDEs.
|
||||
; More information at http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
[*.bat]
|
||||
end_of_line = crlf
|
||||
|
||||
[*.{yml,yaml}]
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
|
|
65
.env.example
65
.env.example
|
@ -1,65 +0,0 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
49
.gitattributes
vendored
49
.gitattributes
vendored
|
@ -1,11 +1,42 @@
|
|||
* text=auto eol=lf
|
||||
# Define the line ending behavior of the different file extensions
|
||||
# Set default behaviour, in case users don't have core.autocrlf set.
|
||||
* text=auto
|
||||
* text eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
# Explicitly declare text files we want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.php text
|
||||
*.default text
|
||||
*.ctp text
|
||||
*.sql text
|
||||
*.md text
|
||||
*.po text
|
||||
*.js text
|
||||
*.css text
|
||||
*.ini text
|
||||
*.properties text
|
||||
*.txt text
|
||||
*.xml text
|
||||
*.svg text
|
||||
*.yml text
|
||||
.htaccess text
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.bat eol=crlf
|
||||
|
||||
# Declare files that will always have LF line endings on checkout.
|
||||
*.pem eol=lf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.mo binary
|
||||
*.pdf binary
|
||||
*.phar binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
*.otf binary
|
||||
*.eot binary
|
||||
|
|
16
.github/workflows/docker-image.yml
vendored
16
.github/workflows/docker-image.yml
vendored
|
@ -1,16 +0,0 @@
|
|||
name: Docker Image CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
pull_request:
|
||||
branches: ["**"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)
|
30
.gitignore
vendored
30
.gitignore
vendored
|
@ -1,23 +1,7 @@
|
|||
/.phpunit.cache
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpactor.json
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/auth.json
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.vscode
|
||||
/.zed
|
||||
/vendor/*
|
||||
/config/app.php
|
||||
/tmp/*
|
||||
/logs/*
|
||||
lbryexplorer.zip
|
||||
lbryexplorer.komodoproject
|
||||
.komodotools
|
||||
|
|
11
.htaccess
Normal file
11
.htaccess
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Uncomment the following to prevent the httpoxy vulnerability
|
||||
# See: https://httpoxy.org/
|
||||
#<IfModule mod_headers.c>
|
||||
# RequestHeader unset Proxy
|
||||
#</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ webroot/ [L]
|
||||
RewriteRule (.*) webroot/$1 [L]
|
||||
</IfModule>
|
30
.travis.yml
Normal file
30
.travis.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
sudo: false
|
||||
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
- php: 7.0
|
||||
env: PHPCS=1
|
||||
|
||||
before_script:
|
||||
- if [[ $PHPCS = 1 ]]; then composer require cakephp/cakephp-codesniffer:~2.1; fi
|
||||
- if [[ $PHPCS != 1 ]]; then composer install; fi
|
||||
- if [[ $PHPCS != 1 ]]; then composer require phpunit/phpunit:"^5.7|^6.0"; fi
|
||||
- if [[ $PHPCS != 1 ]]; then composer run-script post-install-cmd --no-interaction; fi
|
||||
|
||||
script:
|
||||
- if [[ $PHPCS != 1 ]]; then vendor/bin/phpunit; fi
|
||||
- if [[ $PHPCS = 1 ]]; then vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./config ./webroot; fi
|
||||
|
||||
notifications:
|
||||
email: false
|
64
Dockerfile
64
Dockerfile
|
@ -1,64 +0,0 @@
|
|||
FROM php:8.4.7-fpm-alpine
|
||||
|
||||
# Install PHPize packages
|
||||
RUN apk add --no-cache --virtual .phpize $PHPIZE_DEPS
|
||||
|
||||
# Install Source Packages
|
||||
ENV SRC_DEPS="gmp-dev icu-dev"
|
||||
RUN apk add --no-cache --virtual .source $SRC_DEPS
|
||||
|
||||
# Install Binary Packages
|
||||
ENV BIN_DEPS="gmp git icu nginx"
|
||||
RUN apk add --no-cache --virtual .binary $BIN_DEPS
|
||||
|
||||
# Install PHP Extensions
|
||||
RUN pecl install redis
|
||||
|
||||
RUN docker-php-ext-enable redis
|
||||
|
||||
RUN docker-php-ext-install bcmath
|
||||
RUN docker-php-ext-install gmp
|
||||
RUN docker-php-ext-install mysqli
|
||||
RUN docker-php-ext-install opcache
|
||||
RUN docker-php-ext-install pdo
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
|
||||
# Delete PHPize packages
|
||||
RUN apk del --no-network --no-cache --purge .phpize
|
||||
|
||||
# Delete Source packages
|
||||
RUN apk del --no-network --no-cache --purge .source
|
||||
|
||||
# Remove files
|
||||
RUN rm -rf /tmp/pear
|
||||
RUN rm -rf ~/.pearrc
|
||||
RUN rm -rf /var/cache/apk/*
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Copy files
|
||||
WORKDIR /var/www/html
|
||||
COPY . .
|
||||
RUN mv nginx.conf /etc/nginx/http.d/default.conf
|
||||
|
||||
# Install project using Composer
|
||||
RUN --mount=type=cache,target=/root/.composer composer install --no-interaction --optimize-autoloader --no-dev
|
||||
|
||||
# Change permissions
|
||||
RUN chown -R www-data:www-data storage/
|
||||
|
||||
# Setup Process Manager
|
||||
RUN echo "pm = ondemand" >> /usr/local/etc/php-fpm.d/zz-docker.conf
|
||||
RUN echo "pm.process_idle_timeout = 10s" >> /usr/local/etc/php-fpm.d/zz-docker.conf
|
||||
|
||||
# Setup PHP
|
||||
RUN mv php.ini /usr/local/etc/php/conf.d/
|
||||
|
||||
# Setup Opcache
|
||||
RUN mv opcache.ini /usr/local/etc/php/conf.d/
|
||||
|
||||
VOLUME /var/www/html/storage/framework/cache/data
|
||||
|
||||
# Cache project and Start PHP-FPM and NGINX
|
||||
CMD php artisan optimize; php artisan event:cache; php artisan view:cache; php-fpm -D; nginx -g "daemon off;"
|
10
LICENSE
Normal file
10
LICENSE
Normal file
|
@ -0,0 +1,10 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2018 LBRY Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
25
LICENSE.md
25
LICENSE.md
|
@ -1,25 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © 2024 The LBRY Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
11
README.md
11
README.md
|
@ -1,11 +0,0 @@
|
|||
# LBRY Block Explorer
|
||||
|
||||
[](https://codecov.io/gh/LBRYFoundation/block-explorer)
|
||||
[](https://github.com/LBRYFoundation/block-explorer/actions/workflows/docker-image.yml)
|
||||
|
||||
Explore blocks, transactions, addresses and claims on the blockchain of the LBRY network.
|
||||
|
||||

|
||||
|
||||
## License
|
||||
This project is MIT licensed. For the full license, see [LICENSE](LICENSE.md).
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Claim;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class ClaimsController extends Controller{
|
||||
|
||||
public function apibrowse(): array{
|
||||
$pageLimit = 48;
|
||||
$beforeId = intval(request()->query('before'));
|
||||
$afterId = intval(request()->query('after'));
|
||||
$sort = trim(request()->query('sort'));
|
||||
$nsfw = trim(request()->query('nsfw'));
|
||||
|
||||
switch ($sort) {
|
||||
case 'popular':
|
||||
// TODO: sort by upvote/downvote score
|
||||
break;
|
||||
case 'random':
|
||||
$order = 'RAND() ASC';
|
||||
break;
|
||||
case 'oldest':
|
||||
$order = 'created_at ASC';
|
||||
break;
|
||||
case 'newest':
|
||||
default:
|
||||
$order = 'created_at DESC';
|
||||
break;
|
||||
}
|
||||
|
||||
//$stmt = $conn->execute('SELECT COUNT(Id) AS Total FROM Claims WHERE ThumbnailUrl IS NOT NULL AND LENGTH(TRIM(ThumbnailUrl)) > 0');
|
||||
//$count = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
$numClaims = 23000000;
|
||||
|
||||
if ($beforeId < 0) {
|
||||
$beforeId = 0;
|
||||
}
|
||||
|
||||
$conditions = [
|
||||
['thumbnail_url','IS','NOT NULL'],
|
||||
['LENGTH(TRIM(thumbnail_url))','>',0],
|
||||
['is_filtered','<>',1],
|
||||
];
|
||||
if ($afterId > 0) {
|
||||
$conditions[] = ['id','>',$afterId];
|
||||
} else if ($beforeId) {
|
||||
$conditions[] = ['id','<',$beforeId];
|
||||
}
|
||||
|
||||
if ($nsfw !== 'true') {
|
||||
$conditions[] = ['is_nsfw','<>',1];
|
||||
}
|
||||
|
||||
//->contain(['Stream', 'Publisher' => ['fields' => ['Name']]])
|
||||
$claims = Claim::query()->distinct(['claim_id'])->where($conditions)->limit($pageLimit)->orderByRaw($order)->get();
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'claims' => $claims,
|
||||
'total' => (int) $numClaims,
|
||||
];
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed address
|
||||
* @property mixed first_seen
|
||||
* @property mixed created_at
|
||||
* @property mixed modified_at
|
||||
* @property mixed balance
|
||||
*/
|
||||
class Address extends Model{
|
||||
|
||||
protected $casts = [
|
||||
'first_seen' => 'datetime',
|
||||
];
|
||||
protected $table = 'address';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed bits
|
||||
* @property mixed chainwork
|
||||
* @property mixed confirmations
|
||||
* @property mixed difficulty
|
||||
* @property mixed hash
|
||||
* @property mixed height
|
||||
* @property mixed merkle_root
|
||||
* @property mixed name_claim_root
|
||||
* @property mixed nonce
|
||||
* @property mixed previous_block_hash
|
||||
* @property mixed next_block_hash
|
||||
* @property mixed block_size
|
||||
* @property mixed block_time
|
||||
* @property mixed version
|
||||
* @property mixed version_hex
|
||||
* @property mixed tx_count
|
||||
* @property mixed created_at
|
||||
* @property mixed modified_at
|
||||
*/
|
||||
class Block extends Model{
|
||||
|
||||
protected $table = 'block';
|
||||
public $timestamps = false;
|
||||
|
||||
public function jsonSerialize(): array{
|
||||
return [
|
||||
'height' => $this->height,
|
||||
'block_time' => $this->block_time,
|
||||
'tx_count' => $this->tx_count,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed transaction_hash_id
|
||||
* @property mixed vout
|
||||
* @property mixed name
|
||||
* @property mixed claim_id
|
||||
* @property mixed claim_type
|
||||
* @property mixed publisher_id
|
||||
* @property mixed publisher_sig
|
||||
* @property mixed certificate
|
||||
* @property mixed sd_hash
|
||||
* @property mixed transaction_time
|
||||
* @property mixed version
|
||||
* @property mixed value_as_hex
|
||||
* @property mixed value_as_json
|
||||
* @property mixed valid_at_height
|
||||
* @property mixed height
|
||||
* @property mixed effective_amount
|
||||
* @property mixed author
|
||||
* @property mixed description
|
||||
* @property mixed content_type
|
||||
* @property mixed is_nsfw
|
||||
* @property mixed language
|
||||
* @property mixed thumbnail_url
|
||||
* @property mixed title
|
||||
* @property mixed fee
|
||||
* @property mixed fee_currency
|
||||
* @property mixed fee_address
|
||||
* @property mixed is_filtered
|
||||
* @property mixed bid_state
|
||||
* @property mixed created_at
|
||||
* @property mixed modified_at
|
||||
* @property mixed claim_address
|
||||
* @property mixed is_cert_valid
|
||||
* @property mixed is_cert_processed
|
||||
* @property mixed license
|
||||
* @property mixed type
|
||||
* @property mixed release_time
|
||||
* @property mixed source_hash
|
||||
* @property mixed source_name
|
||||
* @property mixed source_size
|
||||
* @property mixed source_media_type
|
||||
* @property mixed source_url
|
||||
* @property mixed frame_width
|
||||
* @property mixed frame_height
|
||||
* @property mixed duration
|
||||
* @property mixed audio_duration
|
||||
* @property mixed email
|
||||
* @property mixed has_claim_list
|
||||
* @property mixed claim_reference
|
||||
* @property mixed list_type
|
||||
* @property mixed claim_id_list
|
||||
* @property mixed transaction_hash_update
|
||||
* @property mixed vout_update
|
||||
* @property mixed claim_count
|
||||
*/
|
||||
class Claim extends Model{
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
];
|
||||
protected $table = 'claim';
|
||||
public $timestamps = false;
|
||||
|
||||
// public function publisher(): BelongsTo{
|
||||
// return $this->belongsTo(Claim::class, 'publisher_id','claim_id');
|
||||
// }
|
||||
|
||||
public function getLbryLink(): string{
|
||||
$link = $this->name;
|
||||
if (isset($this->publisher)) {
|
||||
$link = $this->publisher . '/' . $link;
|
||||
}
|
||||
$link = 'lbry://' . $link;
|
||||
return $link;
|
||||
}
|
||||
|
||||
public function getExplorerLink(): string{
|
||||
$link = '/claims/' . $this->claim_id;
|
||||
return $link;
|
||||
}
|
||||
|
||||
public function getContentTag(): ?string{
|
||||
$ctTag = null;
|
||||
if (substr($this->content_type, 0, 5) === 'audio') {
|
||||
$ctTag = 'audio';
|
||||
} else if (substr($this->content_type, 0, 5) === 'video') {
|
||||
$ctTag = 'video';
|
||||
} else if (substr($this->content_type, 0, 5) === 'image') {
|
||||
$ctTag = 'image';
|
||||
}
|
||||
|
||||
if (!$ctTag && $this->claim_type == 2) {
|
||||
$ctTag = 'identity';
|
||||
}
|
||||
return $ctTag;
|
||||
}
|
||||
|
||||
public function getAutoThumbText(): string{
|
||||
$autoThumbText = '';
|
||||
if ($this->claim_type == 2) {
|
||||
$autoThumbText = strtoupper(substr($this->name, 1, min(strlen($this->name), 10)));
|
||||
} else {
|
||||
$str = (strlen(trim($this->title)) > 0) ? $this->title : $this->name;
|
||||
$autoThumbText = strtoupper(substr($str, 0, min(strlen($str), 5)));
|
||||
}
|
||||
return $autoThumbText;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed transaction_id
|
||||
* @property mixed transaction_hash
|
||||
* @property mixed input_address_id
|
||||
* @property mixed is_coinbase
|
||||
* @property mixed coinbase
|
||||
* @property mixed prevout_hash
|
||||
* @property mixed prevout_n
|
||||
* @property mixed sequence
|
||||
* @property mixed value
|
||||
* @property mixed script_sig_asm
|
||||
* @property mixed script_sig_hex
|
||||
* @property mixed created
|
||||
* @property mixed modified
|
||||
* @property mixed vin
|
||||
* @property mixed witness
|
||||
*/
|
||||
class Input extends Model{
|
||||
|
||||
protected $table = 'input';
|
||||
public $timestamps = false;
|
||||
|
||||
public function input_addresses(): BelongsToMany{
|
||||
return $this->belongsToMany(Address::class,'InputsAddresses','InputId','AddressId');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed transaction_id
|
||||
* @property mixed transaction_hash
|
||||
* @property mixed value
|
||||
* @property mixed vout
|
||||
* @property mixed type
|
||||
* @property mixed script_pub_key_asm
|
||||
* @property mixed script_pub_key_hex
|
||||
* @property mixed required_signatures
|
||||
* @property mixed address_list
|
||||
* @property mixed is_spent
|
||||
* @property mixed spent_by_input_id
|
||||
* @property mixed created_at
|
||||
* @property mixed modified_at
|
||||
* @property mixed claim_id
|
||||
*/
|
||||
class Output extends Model{
|
||||
|
||||
protected $table = 'output';
|
||||
public $timestamps = false;
|
||||
|
||||
public function output_addresses(): BelongsToMany{
|
||||
return $this->belongsToMany(Address::class,'OutputsAddresses','OutputId','AddressId');
|
||||
}
|
||||
|
||||
public function spend_input(): BelongsTo{
|
||||
return $this->belongsTo(Input::class,'SpentByInputId','Id');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @deprecated
|
||||
*/
|
||||
class TagAddressRequest extends Model{
|
||||
|
||||
protected $fillable = [];
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed id
|
||||
* @property mixed block_hash_id
|
||||
* @property mixed input_count
|
||||
* @property mixed output_count
|
||||
* @property mixed transaction_time
|
||||
* @property mixed transaction_size
|
||||
* @property mixed hash
|
||||
* @property mixed version
|
||||
* @property mixed lock_time
|
||||
* @property mixed created_at
|
||||
* @property mixed modified_at
|
||||
* @property mixed created_time
|
||||
* @property mixed value
|
||||
*/
|
||||
class Transaction extends Model{
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
];
|
||||
protected $table = 'transaction';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @mixin Model
|
||||
* @property mixed transaction_id
|
||||
* @property mixed address_id
|
||||
* @property mixed debit_amount
|
||||
* @property mixed credit_amount
|
||||
*/
|
||||
class TransactionAddress extends Model{
|
||||
|
||||
protected $table = 'transaction_address';
|
||||
public $timestamps = false;
|
||||
|
||||
}
|
18
artisan
18
artisan
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
$status = $app->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
46
bin/cake
Executable file
46
bin/cake
Executable file
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env sh
|
||||
################################################################################
|
||||
#
|
||||
# Cake is a shell script for invoking CakePHP shell commands
|
||||
#
|
||||
# CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
#
|
||||
# Licensed under The MIT License
|
||||
# For full copyright and license information, please see the LICENSE.txt
|
||||
# Redistributions of files must retain the above copyright notice.
|
||||
#
|
||||
# @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
# @link http://cakephp.org CakePHP(tm) Project
|
||||
# @since 1.2.0
|
||||
# @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# Canonicalize by following every symlink of the given name recursively
|
||||
canonicalize() {
|
||||
NAME="$1"
|
||||
if [ -f "$NAME" ]
|
||||
then
|
||||
DIR=$(dirname -- "$NAME")
|
||||
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
|
||||
fi
|
||||
while [ -h "$NAME" ]; do
|
||||
DIR=$(dirname -- "$NAME")
|
||||
SYM=$(readlink "$NAME")
|
||||
NAME=$(cd "$DIR" > /dev/null && cd $(dirname -- "$SYM") > /dev/null && pwd)/$(basename -- "$SYM")
|
||||
done
|
||||
echo "$NAME"
|
||||
}
|
||||
|
||||
CONSOLE=$(dirname -- "$(canonicalize "$0")")
|
||||
APP=$(dirname "$CONSOLE")
|
||||
|
||||
if [ $(basename $0) != 'cake' ]
|
||||
then
|
||||
exec php "$CONSOLE"/cake.php $(basename $0) "$@"
|
||||
else
|
||||
exec php "$CONSOLE"/cake.php "$@"
|
||||
fi
|
||||
|
||||
exit
|
27
bin/cake.bat
Normal file
27
bin/cake.bat
Normal file
|
@ -0,0 +1,27 @@
|
|||
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
::
|
||||
:: Cake is a Windows batch script for invoking CakePHP shell commands
|
||||
::
|
||||
:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
::
|
||||
:: Licensed under The MIT License
|
||||
:: Redistributions of files must retain the above copyright notice.
|
||||
::
|
||||
:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
:: @link http://cakephp.org CakePHP(tm) Project
|
||||
:: @since 2.0.0
|
||||
:: @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
::
|
||||
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
@echo off
|
||||
|
||||
SET app=%0
|
||||
SET lib=%~dp0
|
||||
|
||||
php "%lib%cake.php" %*
|
||||
|
||||
echo.
|
||||
|
||||
exit /B %ERRORLEVEL%
|
34
bin/cake.php
Normal file
34
bin/cake.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/php -q
|
||||
<?php
|
||||
/**
|
||||
* Command-line code generation utility to automate programmer chores.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
$minVersion = '5.6.0';
|
||||
if (file_exists('composer.json')) {
|
||||
$composer = json_decode(file_get_contents('composer.json'));
|
||||
if (isset($composer->require->php)) {
|
||||
$minVersion = preg_replace('/([^0-9\.])/', '', $composer->require->php);
|
||||
}
|
||||
}
|
||||
if (version_compare(phpversion(), $minVersion, '<')) {
|
||||
fwrite(STDERR, sprintf("Minimum PHP version: %s. You are using: %s.\n", $minVersion, phpversion()));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
include dirname(__DIR__) . '/config/bootstrap.php';
|
||||
|
||||
exit(Cake\Console\ShellDispatcher::run($argv));
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withExceptions()
|
||||
->withMiddleware(static function(Middleware $middleware){
|
||||
$middleware->removeFromGroup('api',$middleware->getMiddlewareGroups()['api']);
|
||||
$middleware->removeFromGroup('web',$middleware->getMiddlewareGroups()['web']);
|
||||
})
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
health: '/up',
|
||||
)
|
||||
->create();
|
2
bootstrap/cache/.gitignore
vendored
2
bootstrap/cache/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -1,81 +1,53 @@
|
|||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "laravel/laravel",
|
||||
"name": "cakephp/app",
|
||||
"description": "CakePHP skeleton app",
|
||||
"homepage": "http://cakephp.org",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"ext-bcmath": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-gmp": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"cakephp/utility": "^5.2",
|
||||
"endroid/qr-code": "^6.0",
|
||||
"laravel/framework": "^12.19",
|
||||
"laravel/tinker": "^2.10",
|
||||
"paragonie/ecc": "^2.4"
|
||||
"php": ">=5.6",
|
||||
"cakephp/cakephp": "3.4.*",
|
||||
"mobiledetect/mobiledetectlib": "2.*",
|
||||
"cakephp/migrations": "~1.0",
|
||||
"cakephp/plugin-installer": "~1.0",
|
||||
"mdanter/ecc": "^0.4.2",
|
||||
"nesbot/carbon": "~1.18",
|
||||
"endroid/qrcode": "^2.2.2",
|
||||
"predis/predis": "^1.1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.24",
|
||||
"laravel/pail": "^1.2",
|
||||
"laravel/pint": "^1.22",
|
||||
"laravel/sail": "^1.43",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.8",
|
||||
"phpunit/phpunit": "^11.5"
|
||||
"psy/psysh": "@stable",
|
||||
"cakephp/debug_kit": "~3.2",
|
||||
"cakephp/bake": "~1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
|
||||
"dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan compatibility.",
|
||||
"phpunit/phpunit": "Allows automated tests to be run without system-wide install.",
|
||||
"cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
"App\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
"App\\Test\\": "tests",
|
||||
"Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
"post-install-cmd": "App\\Console\\Installer::postInstall",
|
||||
"post-create-project-cmd": "App\\Console\\Installer::postInstall",
|
||||
"post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump",
|
||||
"check": [
|
||||
"@test",
|
||||
"@cs-check"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi",
|
||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
||||
"@php artisan migrate --graceful --ansi"
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||
],
|
||||
"test": [
|
||||
"@php artisan config:clear --ansi",
|
||||
"@php artisan test"
|
||||
]
|
||||
"cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
|
||||
"cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
|
||||
"test": "phpunit --colors=always"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
|
|
9726
composer.lock
generated
9726
composer.lock
generated
File diff suppressed because it is too large
Load diff
346
config/app.default.php
Normal file
346
config/app.default.php
Normal file
|
@ -0,0 +1,346 @@
|
|||
<?php
|
||||
return [
|
||||
/**
|
||||
* Debug Level:
|
||||
*
|
||||
* Production Mode:
|
||||
* false: No error messages, errors, or warnings shown.
|
||||
*
|
||||
* Development Mode:
|
||||
* true: Errors and warnings shown.
|
||||
*/
|
||||
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
|
||||
|
||||
/**
|
||||
* Configure basic information about the application.
|
||||
*
|
||||
* - namespace - The namespace to find app classes under.
|
||||
* - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time.
|
||||
* - encoding - The encoding used for HTML + database connections.
|
||||
* - base - The base directory the app resides in. If false this
|
||||
* will be auto detected.
|
||||
* - dir - Name of app directory.
|
||||
* - webroot - The webroot directory.
|
||||
* - wwwRoot - The file path to webroot.
|
||||
* - baseUrl - To configure CakePHP to *not* use mod_rewrite and to
|
||||
* use CakePHP pretty URLs, remove these .htaccess
|
||||
* files:
|
||||
* /.htaccess
|
||||
* /webroot/.htaccess
|
||||
* And uncomment the baseUrl key below.
|
||||
* - fullBaseUrl - A base URL to use for absolute links.
|
||||
* - imageBaseUrl - Web path to the public images directory under webroot.
|
||||
* - cssBaseUrl - Web path to the public css directory under webroot.
|
||||
* - jsBaseUrl - Web path to the public js directory under webroot.
|
||||
* - paths - Configure paths for non class based resources. Supports the
|
||||
* `plugins`, `templates`, `locales` subkeys, which allow the definition of
|
||||
* paths for plugins, view templates and locale files respectively.
|
||||
*/
|
||||
'App' => [
|
||||
'namespace' => 'App',
|
||||
'encoding' => env('APP_ENCODING', 'UTF-8'),
|
||||
'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
|
||||
'base' => false,
|
||||
'dir' => 'src',
|
||||
'webroot' => 'webroot',
|
||||
'wwwRoot' => WWW_ROOT,
|
||||
// 'baseUrl' => env('SCRIPT_NAME'),
|
||||
'fullBaseUrl' => false,
|
||||
'imageBaseUrl' => 'img/',
|
||||
'cssBaseUrl' => 'css/',
|
||||
'jsBaseUrl' => 'js/',
|
||||
'paths' => [
|
||||
'plugins' => [ROOT . DS . 'plugins' . DS],
|
||||
'templates' => [APP . 'Template' . DS],
|
||||
'locales' => [APP . 'Locale' . DS],
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Security and encryption configuration
|
||||
*
|
||||
* - salt - A random string used in security hashing methods.
|
||||
* The salt value is also used as the encryption key.
|
||||
* You should treat it as extremely sensitive data.
|
||||
*/
|
||||
'Security' => [
|
||||
'salt' => env('SECURITY_SALT', '__SALT__'),
|
||||
],
|
||||
|
||||
/**
|
||||
* Apply timestamps with the last modified time to static assets (js, css, images).
|
||||
* Will append a querystring parameter containing the time the file was modified.
|
||||
* This is useful for busting browser caches.
|
||||
*
|
||||
* Set to true to apply timestamps when debug is true. Set to 'force' to always
|
||||
* enable timestamping regardless of debug value.
|
||||
*/
|
||||
'Asset' => [
|
||||
// 'timestamp' => true,
|
||||
],
|
||||
|
||||
/**
|
||||
* Configure the cache adapters.
|
||||
*/
|
||||
'Cache' => [
|
||||
'default' => [
|
||||
'className' => 'File',
|
||||
'path' => CACHE,
|
||||
'url' => env('CACHE_DEFAULT_URL', null),
|
||||
],
|
||||
|
||||
/**
|
||||
* Configure the cache used for general framework caching.
|
||||
* Translation cache files are stored with this configuration.
|
||||
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
|
||||
* If you set 'className' => 'Null' core cache will be disabled.
|
||||
*/
|
||||
'_cake_core_' => [
|
||||
'className' => 'File',
|
||||
'prefix' => 'myapp_cake_core_',
|
||||
'path' => CACHE . 'persistent/',
|
||||
'serialize' => true,
|
||||
'duration' => '+1 years',
|
||||
'url' => env('CACHE_CAKECORE_URL', null),
|
||||
],
|
||||
|
||||
/**
|
||||
* Configure the cache for model and datasource caches. This cache
|
||||
* configuration is used to store schema descriptions, and table listings
|
||||
* in connections.
|
||||
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
|
||||
*/
|
||||
'_cake_model_' => [
|
||||
'className' => 'File',
|
||||
'prefix' => 'myapp_cake_model_',
|
||||
'path' => CACHE . 'models/',
|
||||
'serialize' => true,
|
||||
'duration' => '+1 years',
|
||||
'url' => env('CACHE_CAKEMODEL_URL', null),
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Configure the Error and Exception handlers used by your application.
|
||||
*
|
||||
* By default errors are displayed using Debugger, when debug is true and logged
|
||||
* by Cake\Log\Log when debug is false.
|
||||
*
|
||||
* In CLI environments exceptions will be printed to stderr with a backtrace.
|
||||
* In web environments an HTML page will be displayed for the exception.
|
||||
* With debug true, framework errors like Missing Controller will be displayed.
|
||||
* When debug is false, framework errors will be coerced into generic HTTP errors.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `errorLevel` - int - The level of errors you are interested in capturing.
|
||||
* - `trace` - boolean - Whether or not backtraces should be included in
|
||||
* logged errors/exceptions.
|
||||
* - `log` - boolean - Whether or not you want exceptions logged.
|
||||
* - `exceptionRenderer` - string - The class responsible for rendering
|
||||
* uncaught exceptions. If you choose a custom class you should place
|
||||
* the file for that class in src/Error. This class needs to implement a
|
||||
* render method.
|
||||
* - `skipLog` - array - List of exceptions to skip for logging. Exceptions that
|
||||
* extend one of the listed exceptions will also be skipped for logging.
|
||||
* E.g.:
|
||||
* `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']`
|
||||
* - `extraFatalErrorMemory` - int - The number of megabytes to increase
|
||||
* the memory limit by when a fatal error is encountered. This allows
|
||||
* breathing room to complete logging or error handling.
|
||||
*/
|
||||
'Error' => [
|
||||
'errorLevel' => E_ALL,
|
||||
'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
|
||||
'skipLog' => [],
|
||||
'log' => true,
|
||||
'trace' => true,
|
||||
],
|
||||
|
||||
/**
|
||||
* Email configuration.
|
||||
*
|
||||
* By defining transports separately from delivery profiles you can easily
|
||||
* re-use transport configuration across multiple profiles.
|
||||
*
|
||||
* You can specify multiple configurations for production, development and
|
||||
* testing.
|
||||
*
|
||||
* Each transport needs a `className`. Valid options are as follows:
|
||||
*
|
||||
* Mail - Send using PHP mail function
|
||||
* Smtp - Send using SMTP
|
||||
* Debug - Do not send the email, just return the result
|
||||
*
|
||||
* You can add custom transports (or override existing transports) by adding the
|
||||
* appropriate file to src/Mailer/Transport. Transports should be named
|
||||
* 'YourTransport.php', where 'Your' is the name of the transport.
|
||||
*/
|
||||
'EmailTransport' => [
|
||||
'default' => [
|
||||
'className' => 'Mail',
|
||||
// The following keys are used in SMTP transports
|
||||
'host' => 'localhost',
|
||||
'port' => 25,
|
||||
'timeout' => 30,
|
||||
'username' => 'user',
|
||||
'password' => 'secret',
|
||||
'client' => null,
|
||||
'tls' => null,
|
||||
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Email delivery profiles
|
||||
*
|
||||
* Delivery profiles allow you to predefine various properties about email
|
||||
* messages from your application and give the settings a name. This saves
|
||||
* duplication across your application and makes maintenance and development
|
||||
* easier. Each profile accepts a number of keys. See `Cake\Mailer\Email`
|
||||
* for more information.
|
||||
*/
|
||||
'Email' => [
|
||||
'default' => [
|
||||
'transport' => 'default',
|
||||
'from' => 'you@localhost',
|
||||
//'charset' => 'utf-8',
|
||||
//'headerCharset' => 'utf-8',
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Connection information used by the ORM to connect
|
||||
* to your application's datastores.
|
||||
* Do not use periods in database name - it may lead to error.
|
||||
* See https://github.com/cakephp/cakephp/issues/6471 for details.
|
||||
* Drivers include Mysql Postgres Sqlite Sqlserver
|
||||
* See vendor\cakephp\cakephp\src\Database\Driver for complete list
|
||||
*/
|
||||
'Datasources' => [
|
||||
'default' => [
|
||||
'className' => 'Cake\Database\Connection',
|
||||
'driver' => 'Cake\Database\Driver\Mysql',
|
||||
'persistent' => false,
|
||||
'host' => 'localhost',
|
||||
/**
|
||||
* CakePHP will use the default DB port based on the driver selected
|
||||
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
|
||||
* the following line and set the port accordingly
|
||||
*/
|
||||
//'port' => 'non_standard_port_number',
|
||||
'username' => 'my_app',
|
||||
'password' => 'secret',
|
||||
'database' => 'my_app',
|
||||
'encoding' => 'utf8',
|
||||
'timezone' => 'UTC',
|
||||
'flags' => [],
|
||||
'cacheMetadata' => true,
|
||||
'log' => false,
|
||||
|
||||
/**
|
||||
* Set identifier quoting to true if you are using reserved words or
|
||||
* special characters in your table or column names. Enabling this
|
||||
* setting will result in queries built using the Query Builder having
|
||||
* identifiers quoted when creating SQL. It should be noted that this
|
||||
* decreases performance because each query needs to be traversed and
|
||||
* manipulated before being executed.
|
||||
*/
|
||||
'quoteIdentifiers' => false,
|
||||
|
||||
/**
|
||||
* During development, if using MySQL < 5.6, uncommenting the
|
||||
* following line could boost the speed at which schema metadata is
|
||||
* fetched from the database. It can also be set directly with the
|
||||
* mysql configuration directive 'innodb_stats_on_metadata = 0'
|
||||
* which is the recommended value in production environments
|
||||
*/
|
||||
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
|
||||
|
||||
'url' => env('DATABASE_URL', null),
|
||||
],
|
||||
|
||||
/**
|
||||
* The test connection is used during the test suite.
|
||||
*/
|
||||
'test' => [
|
||||
'className' => 'Cake\Database\Connection',
|
||||
'driver' => 'Cake\Database\Driver\Mysql',
|
||||
'persistent' => false,
|
||||
'host' => 'localhost',
|
||||
//'port' => 'non_standard_port_number',
|
||||
'username' => 'my_app',
|
||||
'password' => 'secret',
|
||||
'database' => 'test_myapp',
|
||||
'encoding' => 'utf8',
|
||||
'timezone' => 'UTC',
|
||||
'cacheMetadata' => true,
|
||||
'quoteIdentifiers' => false,
|
||||
'log' => false,
|
||||
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
|
||||
'url' => env('DATABASE_TEST_URL', null),
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Configures logging options
|
||||
*/
|
||||
'Log' => [
|
||||
'debug' => [
|
||||
'className' => 'Cake\Log\Engine\FileLog',
|
||||
'path' => LOGS,
|
||||
'file' => 'debug',
|
||||
'levels' => ['notice', 'info', 'debug'],
|
||||
'url' => env('LOG_DEBUG_URL', null),
|
||||
],
|
||||
'error' => [
|
||||
'className' => 'Cake\Log\Engine\FileLog',
|
||||
'path' => LOGS,
|
||||
'file' => 'error',
|
||||
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
|
||||
'url' => env('LOG_ERROR_URL', null),
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Session configuration.
|
||||
*
|
||||
* Contains an array of settings to use for session configuration. The
|
||||
* `defaults` key is used to define a default preset to use for sessions, any
|
||||
* settings declared here will override the settings of the default config.
|
||||
*
|
||||
* ## Options
|
||||
*
|
||||
* - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'.
|
||||
* - `cookiePath` - The url path for which session cookie is set. Maps to the
|
||||
* `session.cookie_path` php.ini config. Defaults to base path of app.
|
||||
* - `timeout` - The time in minutes the session should be valid for.
|
||||
* Pass 0 to disable checking timeout.
|
||||
* Please note that php.ini's session.gc_maxlifetime must be equal to or greater
|
||||
* than the largest Session['timeout'] in all served websites for it to have the
|
||||
* desired effect.
|
||||
* - `defaults` - The default configuration set to use as a basis for your session.
|
||||
* There are four built-in options: php, cake, cache, database.
|
||||
* - `handler` - Can be used to enable a custom session handler. Expects an
|
||||
* array with at least the `engine` key, being the name of the Session engine
|
||||
* class to use for managing the session. CakePHP bundles the `CacheSession`
|
||||
* and `DatabaseSession` engines.
|
||||
* - `ini` - An associative array of additional ini values to set.
|
||||
*
|
||||
* The built-in `defaults` options are:
|
||||
*
|
||||
* - 'php' - Uses settings defined in your php.ini.
|
||||
* - 'cake' - Saves session files in CakePHP's /tmp directory.
|
||||
* - 'database' - Uses CakePHP's database sessions.
|
||||
* - 'cache' - Use the Cache class to save sessions.
|
||||
*
|
||||
* To define a custom session handler, save it at src/Network/Session/<name>.php.
|
||||
* Make sure the class implements PHP's `SessionHandlerInterface` and set
|
||||
* Session.handler to <name>
|
||||
*
|
||||
* To use database sessions, load the SQL file located at config/Schema/sessions.sql
|
||||
*/
|
||||
'Session' => [
|
||||
'defaults' => 'php',
|
||||
],
|
||||
];
|
125
config/app.php
125
config/app.php
|
@ -1,125 +0,0 @@
|
|||
<?php
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application, which will be used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| other UI elements where an application name needs to be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
222
config/bootstrap.php
Normal file
222
config/bootstrap.php
Normal file
|
@ -0,0 +1,222 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.10.8
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
// You can remove this if you are confident that your PHP version is sufficient.
|
||||
if (version_compare(PHP_VERSION, '5.6.0') < 0) {
|
||||
trigger_error('Your PHP version must be equal or higher than 5.6.0 to use CakePHP.', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* You can remove this if you are confident you have intl installed.
|
||||
*/
|
||||
if (!extension_loaded('intl')) {
|
||||
trigger_error('You must enable the intl extension to use CakePHP.', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* You can remove this if you are confident you have mbstring installed.
|
||||
*/
|
||||
if (!extension_loaded('mbstring')) {
|
||||
trigger_error('You must enable the mbstring extension to use CakePHP.', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure paths required to find CakePHP + general filepath
|
||||
* constants
|
||||
*/
|
||||
require __DIR__ . '/paths.php';
|
||||
|
||||
/*
|
||||
* Bootstrap CakePHP.
|
||||
*
|
||||
* Does the various bits of setup that CakePHP needs to do.
|
||||
* This includes:
|
||||
*
|
||||
* - Registering the CakePHP autoloader.
|
||||
* - Setting the default application paths.
|
||||
*/
|
||||
require CORE_PATH . 'config' . DS . 'bootstrap.php';
|
||||
|
||||
use Cake\Cache\Cache;
|
||||
use Cake\Console\ConsoleErrorHandler;
|
||||
use Cake\Core\App;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Configure\Engine\PhpConfig;
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Database\Type;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Error\ErrorHandler;
|
||||
use Cake\Log\Log;
|
||||
use Cake\Mailer\Email;
|
||||
use Cake\Network\Request;
|
||||
use Cake\Utility\Inflector;
|
||||
use Cake\Utility\Security;
|
||||
|
||||
/*
|
||||
* Read configuration file and inject configuration into various
|
||||
* CakePHP classes.
|
||||
*
|
||||
* By default there is only one configuration file. It is often a good
|
||||
* idea to create multiple configuration files, and separate the configuration
|
||||
* that changes from configuration that does not. This makes deployment simpler.
|
||||
*/
|
||||
try {
|
||||
Configure::config('default', new PhpConfig());
|
||||
Configure::load('app', 'default', false);
|
||||
} catch (\Exception $e) {
|
||||
exit($e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Load an environment local configuration file.
|
||||
* You can use a file like app_local.php to provide local overrides to your
|
||||
* shared configuration.
|
||||
*/
|
||||
//Configure::load('app_local', 'default');
|
||||
|
||||
/*
|
||||
* When debug = true the metadata cache should only last
|
||||
* for a short time.
|
||||
*/
|
||||
if (Configure::read('debug')) {
|
||||
Configure::write('Cache._cake_model_.duration', '+2 minutes');
|
||||
Configure::write('Cache._cake_core_.duration', '+2 minutes');
|
||||
}
|
||||
|
||||
/*
|
||||
* Set server timezone to UTC. You can change it to another timezone of your
|
||||
* choice but using UTC makes time calculations / conversions easier.
|
||||
*/
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
/*
|
||||
* Configure the mbstring extension to use the correct encoding.
|
||||
*/
|
||||
mb_internal_encoding(Configure::read('App.encoding'));
|
||||
|
||||
/*
|
||||
* Set the default locale. This controls how dates, number and currency is
|
||||
* formatted and sets the default language to use for translations.
|
||||
*/
|
||||
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
|
||||
|
||||
/*
|
||||
* Register application error and exception handlers.
|
||||
*/
|
||||
$isCli = PHP_SAPI === 'cli';
|
||||
if ($isCli) {
|
||||
(new ConsoleErrorHandler(Configure::read('Error')))->register();
|
||||
} else {
|
||||
(new ErrorHandler(Configure::read('Error')))->register();
|
||||
}
|
||||
|
||||
/*
|
||||
* Include the CLI bootstrap overrides.
|
||||
*/
|
||||
if ($isCli) {
|
||||
require __DIR__ . '/bootstrap_cli.php';
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the full base URL.
|
||||
* This URL is used as the base of all absolute links.
|
||||
*
|
||||
* If you define fullBaseUrl in your config file you can remove this.
|
||||
*/
|
||||
if (!Configure::read('App.fullBaseUrl')) {
|
||||
$s = null;
|
||||
if (env('HTTPS')) {
|
||||
$s = 's';
|
||||
}
|
||||
|
||||
$httpHost = env('HTTP_HOST');
|
||||
if (isset($httpHost)) {
|
||||
Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost);
|
||||
}
|
||||
unset($httpHost, $s);
|
||||
}
|
||||
|
||||
Cache::setConfig(Configure::consume('Cache'));
|
||||
ConnectionManager::setConfig(Configure::consume('Datasources'));
|
||||
Email::setConfigTransport(Configure::consume('EmailTransport'));
|
||||
Email::setConfig(Configure::consume('Email'));
|
||||
Log::setConfig(Configure::consume('Log'));
|
||||
Security::salt(Configure::consume('Security.salt'));
|
||||
|
||||
/*
|
||||
* The default crypto extension in 3.0 is OpenSSL.
|
||||
* If you are migrating from 2.x uncomment this code to
|
||||
* use a more compatible Mcrypt based implementation
|
||||
*/
|
||||
//Security::engine(new \Cake\Utility\Crypto\Mcrypt());
|
||||
|
||||
/*
|
||||
* Setup detectors for mobile and tablet.
|
||||
*/
|
||||
Request::addDetector('mobile', function ($request) {
|
||||
$detector = new \Detection\MobileDetect();
|
||||
|
||||
return $detector->isMobile();
|
||||
});
|
||||
Request::addDetector('tablet', function ($request) {
|
||||
$detector = new \Detection\MobileDetect();
|
||||
|
||||
return $detector->isTablet();
|
||||
});
|
||||
|
||||
/*
|
||||
* Enable immutable time objects in the ORM.
|
||||
*
|
||||
* You can enable default locale format parsing by adding calls
|
||||
* to `useLocaleParser()`. This enables the automatic conversion of
|
||||
* locale specific date formats. For details see
|
||||
* @link http://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html#parsing-localized-datetime-data
|
||||
*/
|
||||
Type::build('time')
|
||||
->useImmutable();
|
||||
Type::build('date')
|
||||
->useImmutable();
|
||||
Type::build('datetime')
|
||||
->useImmutable();
|
||||
Type::build('timestamp')
|
||||
->useImmutable();
|
||||
|
||||
/*
|
||||
* Custom Inflector rules, can be set to correctly pluralize or singularize
|
||||
* table, model, controller names or whatever other string is passed to the
|
||||
* inflection functions.
|
||||
*/
|
||||
//Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
|
||||
//Inflector::rules('irregular', ['red' => 'redlings']);
|
||||
//Inflector::rules('uninflected', ['dontinflectme']);
|
||||
//Inflector::rules('transliteration', ['/å/' => 'aa']);
|
||||
|
||||
/*
|
||||
* Plugins need to be loaded manually, you can either load them one by one or all of them in a single call
|
||||
* Uncomment one of the lines below, as you need. make sure you read the documentation on Plugin to use more
|
||||
* advanced ways of loading plugins
|
||||
*
|
||||
* Plugin::loadAll(); // Loads all plugins at once
|
||||
* Plugin::load('Migrations'); //Loads a single plugin named Migrations
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Only try to load DebugKit in development mode
|
||||
* Debug Kit should not be installed on a production system
|
||||
*/
|
||||
if (Configure::read('debug')) {
|
||||
Plugin::load('DebugKit', ['bootstrap' => true]);
|
||||
}
|
38
config/bootstrap_cli.php
Normal file
38
config/bootstrap_cli.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Exception\MissingPluginException;
|
||||
use Cake\Core\Plugin;
|
||||
|
||||
/**
|
||||
* Additional bootstrapping and configuration for CLI environments should
|
||||
* be put here.
|
||||
*/
|
||||
|
||||
// Set the fullBaseUrl to allow URLs to be generated in shell tasks.
|
||||
// This is useful when sending email from shells.
|
||||
//Configure::write('App.fullBaseUrl', php_uname('n'));
|
||||
|
||||
// Set logs to different files so they don't have permission conflicts.
|
||||
Configure::write('Log.debug.file', 'cli-debug');
|
||||
Configure::write('Log.error.file', 'cli-error');
|
||||
|
||||
try {
|
||||
Plugin::load('Bake');
|
||||
} catch (MissingPluginException $e) {
|
||||
// Do not halt if the plugin is missing
|
||||
}
|
||||
|
||||
Plugin::load('Migrations');
|
107
config/cache.php
107
config/cache.php
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
||||
|
||||
];
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => 'chainquery',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
'chainquery' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
return [
|
||||
'rpc_url' => env('LBRY_RPC_URL'),
|
||||
];
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'handler_with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
85
config/paths.php
Normal file
85
config/paths.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Use the DS to separate the directories in other defines
|
||||
*/
|
||||
if (!defined('DS')) {
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* These defines should only be edited if you have cake installed in
|
||||
* a directory layout other than the way it is distributed.
|
||||
* When using custom settings be sure to use the DS and do not add a trailing DS.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The full path to the directory which holds "src", WITHOUT a trailing DS.
|
||||
*/
|
||||
define('ROOT', dirname(__DIR__));
|
||||
|
||||
/**
|
||||
* The actual directory name for the application directory. Normally
|
||||
* named 'src'.
|
||||
*/
|
||||
define('APP_DIR', 'src');
|
||||
|
||||
/**
|
||||
* Path to the application's directory.
|
||||
*/
|
||||
define('APP', ROOT . DS . APP_DIR . DS);
|
||||
|
||||
/**
|
||||
* Path to the config directory.
|
||||
*/
|
||||
define('CONFIG', ROOT . DS . 'config' . DS);
|
||||
|
||||
/**
|
||||
* File path to the webroot directory.
|
||||
*/
|
||||
define('WWW_ROOT', ROOT . DS . 'webroot' . DS);
|
||||
|
||||
/**
|
||||
* Path to the tests directory.
|
||||
*/
|
||||
define('TESTS', ROOT . DS . 'tests' . DS);
|
||||
|
||||
/**
|
||||
* Path to the temporary files directory.
|
||||
*/
|
||||
define('TMP', ROOT . DS . 'tmp' . DS);
|
||||
|
||||
/**
|
||||
* Path to the logs directory.
|
||||
*/
|
||||
define('LOGS', ROOT . DS . 'logs' . DS);
|
||||
|
||||
/**
|
||||
* Path to the cache files directory. It can be shared between hosts in a multi-server setup.
|
||||
*/
|
||||
define('CACHE', TMP . 'cache' . DS);
|
||||
|
||||
/**
|
||||
* The absolute path to the "cake" directory, WITHOUT a trailing DS.
|
||||
*
|
||||
* CakePHP should always be installed with composer, so look there.
|
||||
*/
|
||||
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
|
||||
|
||||
/**
|
||||
* Path to the cake directory.
|
||||
*/
|
||||
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
|
||||
define('CAKE', CORE_PATH . 'src' . DS);
|
80
config/routes.php
Normal file
80
config/routes.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* Routes configuration
|
||||
*
|
||||
* In this file, you set up routes to your controllers and their actions.
|
||||
* Routes are very important mechanism that allows you to freely connect
|
||||
* different URLs to chosen controllers and their actions (functions).
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
use Cake\Routing\Router;
|
||||
use Cake\Routing\Route\DashedRoute;
|
||||
|
||||
/**
|
||||
* The default class to use for all routes
|
||||
*
|
||||
* The following route classes are supplied with CakePHP and are appropriate
|
||||
* to set as the default:
|
||||
*
|
||||
* - Route
|
||||
* - InflectedRoute
|
||||
* - DashedRoute
|
||||
*
|
||||
* If no call is made to `Router::defaultRouteClass()`, the class used is
|
||||
* `Route` (`Cake\Routing\Route\Route`)
|
||||
*
|
||||
* Note that `Route` does not do any inflections on URLs which will result in
|
||||
* inconsistently cased URLs when used with `:plugin`, `:controller` and
|
||||
* `:action` markers.
|
||||
*
|
||||
*/
|
||||
Router::defaultRouteClass(DashedRoute::class);
|
||||
|
||||
Router::scope('/', function (RouteBuilder $routes) {
|
||||
$routes->connect('/', ['controller' => 'Main', 'action' => 'index']);
|
||||
$routes->connect('/address/*', ['controller' => 'Main', 'action' => 'address']);
|
||||
$routes->connect('/blocks/*', ['controller' => 'Main', 'action' => 'blocks']);
|
||||
$routes->connect('/claims/*', ['controller' => 'Main', 'action' => 'claims']);
|
||||
$routes->connect('/find', ['controller' => 'Main', 'action' => 'find']);
|
||||
$routes->connect('/realtime', ['controller' => 'Main', 'action' => 'realtime']);
|
||||
$routes->connect('/stats', ['controller' => 'Main', 'action' => 'stats']);
|
||||
$routes->connect('/tx/*', ['controller' => 'Main', 'action' => 'tx']);
|
||||
$routes->connect('/qr/*', ['controller' => 'Main', 'action' => 'qr']);
|
||||
$routes->connect('/*', ['controller' => 'Main', 'action' => 'index']);
|
||||
|
||||
$routes->connect('/api/v1/address/:addr/tag', ['controller' => 'Main', 'action' => 'apiaddrtag'], ['addr' => '[A-Za-z0-9,]+', 'pass' => ['addr']]);
|
||||
$routes->connect('/api/v1/address/:addr/utxo', ['controller' => 'Main', 'action' => 'apiaddrutxo'], ['addr' => '[A-Za-z0-9,]+', 'pass' => ['addr']]);
|
||||
$routes->connect('/api/v1/address/:addr/balance', ['controller' => 'Main', 'action' => 'apiaddrbalance'], ['addr' => '[A-Za-z0-9]+', 'pass' => ['addr']]);
|
||||
$routes->connect('/api/v1/address/:addr/transactions', ['controller' => 'Main', 'action' => 'apiaddrtx'], ['addr' => '[A-Za-z0-9,]+', 'pass' => ['addr']]);
|
||||
|
||||
$routes->connect('/api/v1/charts/blocksize/:period', ['controller' => 'Main', 'action' => 'apiblocksize'], ['period' => '[012346789dhy]+', 'pass' => ['period']]);
|
||||
$routes->connect('/api/v1/realtime/blocks', ['controller' => 'Main', 'action' => 'apirealtimeblocks']);
|
||||
$routes->connect('/api/v1/realtime/tx', ['controller' => 'Main', 'action' => 'apirealtimetx']);
|
||||
$routes->connect('/api/v1/recentblocks', ['controller' => 'Main', 'action' => 'apirecentblocks']);
|
||||
$routes->connect('/api/v1/status', ['controller' => 'Main', 'action' => 'apistatus']);
|
||||
$routes->connect('/api/v1/supply', ['controller' => 'Main', 'action' => 'apiutxosupply']);
|
||||
//$routes->connect('/api/v1/recenttxs', ['controller' => 'Main', 'action' => 'apirecenttxs']);
|
||||
|
||||
$routes->connect('/api/v1/claims/browse', ['controller' => 'Claims', 'action' => 'apibrowse']);
|
||||
|
||||
//$routes->fallbacks(DashedRoute::class);
|
||||
});
|
||||
|
||||
/**
|
||||
* Load all plugin routes. See the Plugin documentation on
|
||||
* how to customize the loading of plugin routes.
|
||||
*/
|
||||
Plugin::routes();
|
18
config/schema/i18n.sql
Normal file
18
config/schema/i18n.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
#
|
||||
# Licensed under The MIT License
|
||||
# For full copyright and license information, please see the LICENSE.txt
|
||||
# Redistributions of files must retain the above copyright notice.
|
||||
# MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
|
||||
CREATE TABLE i18n (
|
||||
id int NOT NULL auto_increment,
|
||||
locale varchar(6) NOT NULL,
|
||||
model varchar(255) NOT NULL,
|
||||
foreign_key int(10) NOT NULL,
|
||||
field varchar(255) NOT NULL,
|
||||
content text,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
|
||||
INDEX I18N_FIELD(model, foreign_key, field)
|
||||
);
|
13
config/schema/sessions.sql
Normal file
13
config/schema/sessions.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
#
|
||||
# Licensed under The MIT License
|
||||
# For full copyright and license information, please see the LICENSE.txt
|
||||
# Redistributions of files must retain the above copyright notice.
|
||||
# MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id char(40) NOT NULL,
|
||||
data text,
|
||||
expires INT(11) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
4
cron/addrtx.sh
Executable file
4
cron/addrtx.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block addrtxamounts
|
||||
|
7
cron/blocks.sh
Executable file
7
cron/blocks.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block parsenewblocks
|
||||
rm tmp/lock/parsenewblocks
|
||||
bin/cake block parsetxs
|
||||
rm tmp/lock/parsetxs
|
||||
|
137
cron/blockstuff.php
Normal file
137
cron/blockstuff.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
define('TMP', '/tmp/');
|
||||
define('DS', '/');
|
||||
|
||||
class BlockSyncThread extends \Thread {
|
||||
private $_startHeight;
|
||||
private $_endHeight;
|
||||
private $_maxHeight;
|
||||
|
||||
public function __construct($startBlock, $endBlock, $maxHeight) {
|
||||
$this->_startHeight = $startBlock;
|
||||
$this->_endHeight = $endBlock;
|
||||
$this->_maxHeight = $maxHeight;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$conn = new \PDO("mysql:host=localhost;dbname=lbry", 'lbry-admin', '46D861aX#!yQ');
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$data_error = false;
|
||||
$conn->beginTransaction();
|
||||
|
||||
// TODO: Batch block synchronisation from memory to DB
|
||||
|
||||
for ($curr_height = $this->_startHeight; $curr_height <= $this->_endHeight; $curr_height++) {
|
||||
$idx_str = str_pad($curr_height, strlen($this->_maxHeight), '0', STR_PAD_LEFT);
|
||||
|
||||
// get the block hash
|
||||
$req = ['method' => 'getblockhash', 'params' => [$curr_height]];
|
||||
$response = BlockStuff::curl_json_post(BlockStuff::rpcurl, json_encode($req));
|
||||
$json = json_decode($response);
|
||||
$curr_block_hash = $json->result;
|
||||
|
||||
$req = ['method' => 'getblock', 'params' => [$curr_block_hash]];
|
||||
$response = BlockStuff::curl_json_post(BlockStuff::rpcurl, json_encode($req));
|
||||
$json = json_decode($response);
|
||||
$curr_block = $json->result;
|
||||
|
||||
$stmt = $conn->prepare('UPDATE Blocks SET Confirmations = ? WHERE Height = ?');
|
||||
try {
|
||||
$stmt->execute([$curr_block->confirmations, $curr_height]);
|
||||
echo "[$idx_str/$this->_maxHeight] Updated block height: $curr_height with confirmations $curr_block->confirmations.\n";
|
||||
} catch (Exception $e) {
|
||||
$data_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($data_error) {
|
||||
echo "Rolling back changes.\n";
|
||||
$conn->rollBack();
|
||||
return;
|
||||
}
|
||||
|
||||
echo "Committing data.\n";
|
||||
$conn->commit();
|
||||
}
|
||||
}
|
||||
|
||||
class BlockStuff {
|
||||
const rpcurl = 'http://lrpc:lrpc@127.0.0.1:9245';
|
||||
public static function blocksync() {
|
||||
self::lock('blocksync');
|
||||
|
||||
$conn = new \PDO("mysql:host=localhost;dbname=lbry", 'lbry-admin', '46D861aX#!yQ');
|
||||
|
||||
$stmt = $conn->prepare('SELECT Height FROM Blocks ORDER BY Height DESC LIMIT 1');
|
||||
$stmt->execute([]);
|
||||
$max_block = $stmt->fetch(PDO::FETCH_OBJ);
|
||||
if ($max_block) {
|
||||
$chunk_limit = 2;
|
||||
$curr_height = 0;
|
||||
$chunks = floor($max_block->Height / $chunk_limit);
|
||||
$threads = [];
|
||||
for ($i = 0; $i < $chunk_limit; $i++) {
|
||||
$start = $curr_height;
|
||||
$end = ($i == ($chunk_limit - 1)) ? $max_block->Height : $start + $chunks;
|
||||
$curr_height += $chunks + 1;
|
||||
$thread = new BlockSyncThread($start, $end, $max_block->Height);
|
||||
$threads[] = $thread;
|
||||
|
||||
$thread->start();
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($threads); $i++) {
|
||||
$threads[$i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
self::unlock('blocksync');
|
||||
}
|
||||
|
||||
public static function curl_json_post($url, $data, $headers = []) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
//Log::debug('Request execution completed.');
|
||||
if ($response === false) {
|
||||
$error = curl_error($ch);
|
||||
$errno = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
|
||||
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||
} else {
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
// Close any open file handle
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function lock($process_name) {
|
||||
if (!is_dir(TMP . 'lock')) {
|
||||
mkdir(TMP . 'lock');
|
||||
}
|
||||
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||
if (file_exists($lock_file)) {
|
||||
echo "$process_name is already running.\n";
|
||||
exit(0);
|
||||
}
|
||||
file_put_contents($lock_file, '1');
|
||||
}
|
||||
|
||||
public static function unlock($process_name) {
|
||||
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||
if (file_exists($lock_file)) {
|
||||
unlink($lock_file);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BlockStuff::blocksync();
|
4
cron/blocktx.sh
Executable file
4
cron/blocktx.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block parsetxs
|
||||
|
4
cron/claimindex.sh
Executable file
4
cron/claimindex.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block buildclaimindex
|
||||
|
3
cron/confirmations.sh
Executable file
3
cron/confirmations.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng/cron
|
||||
php -d extension=pthreads.so blockstuff.php
|
4
cron/fixzero.sh
Executable file
4
cron/fixzero.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block fixzerooutputs
|
||||
|
4
cron/forevermempool.sh
Executable file
4
cron/forevermempool.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block forevermempool &
|
||||
|
4
cron/livetx.sh
Executable file
4
cron/livetx.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block parsemempool
|
||||
|
4
cron/pricehistory.sh
Executable file
4
cron/pricehistory.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake aux pricehistory
|
||||
|
4
cron/spends.sh
Executable file
4
cron/spends.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake block updatespends
|
||||
|
4
cron/verifytags.sh
Executable file
4
cron/verifytags.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd /var/www/lbry.block.ng
|
||||
bin/cake aux verifytags
|
||||
|
1
database/.gitignore
vendored
1
database/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
*.sqlite*
|
Binary file not shown.
Before Width: | Height: | Size: 174 KiB |
16
index.php
Normal file
16
index.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.10.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
|
20
nginx.conf
20
nginx.conf
|
@ -1,20 +0,0 @@
|
|||
server{
|
||||
listen 80;
|
||||
|
||||
root /var/www/html/public;
|
||||
index index.php;
|
||||
|
||||
gzip on;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_buffers 16 1024k;
|
||||
fastcgi_buffer_size 1024k;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_pass localhost:9000;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
opcache.jit_buffer_size=256M
|
||||
opcache.max_accelerated_files=20000
|
||||
opcache.memory_consumption=256
|
||||
opcache.preload = '/var/www/html/preload.php'
|
||||
;opcache.preload = '/app/preload.php'
|
||||
opcache.preload_user = 'www-data'
|
||||
opcache.validate_timestamps=0
|
1
php.ini
1
php.ini
|
@ -1 +0,0 @@
|
|||
memory_limit = ${PHP_MEMORY_LIMIT}
|
33
phpunit.xml
33
phpunit.xml
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory>tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
40
phpunit.xml.dist
Normal file
40
phpunit.xml.dist
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
colors="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="./tests/bootstrap.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="memory_limit" value="-1"/>
|
||||
<ini name="apc.enable_cli" value="1"/>
|
||||
</php>
|
||||
|
||||
<!-- Add any additional test suites you want to run here -->
|
||||
<testsuites>
|
||||
<testsuite name="App Test Suite">
|
||||
<directory>./tests/TestCase</directory>
|
||||
</testsuite>
|
||||
<!-- Add plugin test suites here. -->
|
||||
</testsuites>
|
||||
|
||||
<!-- Setup a listener for fixtures -->
|
||||
<listeners>
|
||||
<listener
|
||||
class="\Cake\TestSuite\Fixture\FixtureInjector"
|
||||
file="./vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php">
|
||||
<arguments>
|
||||
<object class="\Cake\TestSuite\Fixture\FixtureManager" />
|
||||
</arguments>
|
||||
</listener>
|
||||
</listeners>
|
||||
|
||||
<!-- Ignore vendor tests in code coverage reports -->
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./src/</directory>
|
||||
<directory suffix=".php">./plugins/*/src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
echo '[Preloading] Started'.PHP_EOL;
|
||||
|
||||
/**@var \Composer\Autoload\ClassLoader $loader*/
|
||||
$loader = require_once 'vendor/autoload.php';
|
||||
|
||||
echo '[Preloading] Ended'.PHP_EOL;
|
|
@ -1,8 +0,0 @@
|
|||
.bids-chart-container { width: 1200px; margin: 0 auto 48px auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px 36px; position: relative; overflow: hidden }
|
||||
.bids-chart-container .load-progress { position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: #1e88e5; animation: indeterminate 4s linear infinite; }
|
||||
.bids-chart-container .chart { height: 414px }
|
||||
.bids-chart-container .btn-chart-export { position: absolute; right: 40px; bottom: 36px }
|
||||
@keyframes indeterminate {
|
||||
from { left: -70%; }
|
||||
to { left: 100% }
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
.mining-inflation-chart-container { width: 1200px; margin: 0 auto 48px auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px 36px; position: relative; overflow: hidden }
|
||||
.mining-inflation-chart-container .load-progress { position: absolute; top: 0; left: 0; width: 100%; height: 3px; background: #1e88e5; animation: indeterminate 4s linear infinite; }
|
||||
.mining-inflation-chart-container .chart { height: 414px }
|
||||
.mining-inflation-chart-container .btn-chart-export { position: absolute; right: 40px; bottom: 36px }
|
||||
@keyframes indeterminate {
|
||||
from { left: -70%; }
|
||||
to { left: 100% }
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->handleRequest(Request::capture());
|
|
@ -1,170 +0,0 @@
|
|||
function buildChartData(claimsData) {
|
||||
var chartData = [];
|
||||
var lastDate = 0;
|
||||
var nbClaimsDay = 0;
|
||||
var nbClaimsTotal = 0;
|
||||
var bidClaimsDay = 0;
|
||||
var bidClaimsTotal = 0;
|
||||
var nbChannelsDay = 0;
|
||||
var nbChannelsTotal = 0;
|
||||
for(var i = 0; i < claimsData.length; i++) {
|
||||
if(claimsData[i].transaction_time == 0) {
|
||||
continue;
|
||||
}
|
||||
var transactionDate = new Date(claimsData[i].transaction_time * 1000);
|
||||
transactionDate.setHours(0,0,0,0)
|
||||
if(lastDate == 0) {
|
||||
lastDate = transactionDate;
|
||||
}
|
||||
if(transactionDate.toString() != lastDate.toString()) {
|
||||
nbClaimsTotal += nbClaimsDay;
|
||||
bidClaimsTotal += bidClaimsDay;
|
||||
var dateData = {
|
||||
date: lastDate,
|
||||
NumberClaims: nbClaimsTotal,
|
||||
BidsClaims: bidClaimsTotal,
|
||||
};
|
||||
chartData.push(dateData);
|
||||
nbClaimsDay = 0;
|
||||
bidClaimsDay = 0;
|
||||
lastDate = transactionDate;
|
||||
}
|
||||
if(claimsData[i].claim_type == 1) {
|
||||
nbClaimsDay += 1;
|
||||
bidClaimsDay += claimsData[i].effective_amount/100000000;
|
||||
}
|
||||
}
|
||||
return chartData;
|
||||
}
|
||||
|
||||
function loadChartData() {
|
||||
var api_url = "https://chainquery.odysee.tv/api/sql?query=";
|
||||
var query = "SELECT c1.claim_type, c1.bid_state, c1.effective_amount, c1.transaction_time, o.transaction_time AS 'spent_time' FROM claim c1 LEFT JOIN (SELECT output.claim_id, tx.transaction_time FROM output INNER JOIN input ON input.prevout_hash = output.transaction_hash AND input.prevout_n = output.vout INNER JOIN transaction tx ON tx.id = input.transaction_id) o ON o.claim_id=c1.claim_id AND c1.bid_state='Spent' ORDER BY c1.transaction_time ASC";
|
||||
var url = api_url + query;
|
||||
var loadProgress = $('.bids-chart-container .load-progress');
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
chartLoadInProgress = true;
|
||||
loadProgress.css({ display: 'block' });
|
||||
},
|
||||
success: function(response) {
|
||||
if(response.success) {
|
||||
chartData = buildChartData(response.data);
|
||||
if(chart) {
|
||||
chart.dataProvider = chartData;
|
||||
chart.validateNow();
|
||||
chart.validateData();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Could not fetch block data.");
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
chartLoadInProgress = false;
|
||||
loadProgress.css({ display: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var chart;
|
||||
var chartData = [];
|
||||
var chartLoadInProgress = false;
|
||||
AmCharts.ready(function() {
|
||||
chart = AmCharts.makeChart('bids-chart', {
|
||||
type: 'serial',
|
||||
theme: 'light',
|
||||
mouseWheelZoomEnabled: true,
|
||||
height: '100%',
|
||||
categoryField: 'date',
|
||||
synchronizeGrid: true,
|
||||
dataProvider: chartData,
|
||||
responsive: {
|
||||
enabled: true,
|
||||
},
|
||||
valueAxes: [
|
||||
{
|
||||
id: 'v-number-claims',
|
||||
axisColor: '#1e88e5',
|
||||
axisThickness: 2,
|
||||
position: 'left',
|
||||
},
|
||||
{
|
||||
id: 'v-bids-claims',
|
||||
axisColor: '#0b7a06',
|
||||
axisThickness: 2,
|
||||
position: 'left',
|
||||
offset: 75,
|
||||
},
|
||||
],
|
||||
categoryAxis: {
|
||||
parseDates: true,
|
||||
autoGridCount: false,
|
||||
minorGridEnabled: true,
|
||||
minorGridAlpha: 0.04,
|
||||
axisColor: '#dadada',
|
||||
twoLineMode: true
|
||||
},
|
||||
graphs: [
|
||||
{
|
||||
id: 'g-number-claims',
|
||||
valueAxis: 'v-number-claims',
|
||||
title: 'Number of claims',
|
||||
valueField: 'NumberClaims',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#1e88e5',
|
||||
hideBulletsCount: 101,
|
||||
balloonText: '[[NumberClaims]]',
|
||||
},
|
||||
{
|
||||
id: 'g-bids-claims',
|
||||
valueAxis: 'v-bids-claims',
|
||||
title: 'Bids for claims (LBC)',
|
||||
valueField: 'BidsClaims',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#0b7a06',
|
||||
balloonText: '[[BidsClaims]] LBC',
|
||||
hideBulletsCount: 101
|
||||
},
|
||||
],
|
||||
chartCursor: {
|
||||
cursorAlpha: 0.1,
|
||||
fullWidth: true,
|
||||
valueLineBalloonEnabled: true,
|
||||
categoryBalloonColor: '#333333',
|
||||
cursorColor: '#1e88e5'
|
||||
},
|
||||
chartScrollbar: {
|
||||
scrollbarHeight: 36,
|
||||
color: '#888888',
|
||||
gridColor: '#bbbbbb'
|
||||
},
|
||||
legend: {
|
||||
marginLeft: 110,
|
||||
useGraphSettings: true,
|
||||
valueText: "",
|
||||
spacing: 64,
|
||||
|
||||
},
|
||||
export: {
|
||||
enabled: true,
|
||||
fileName: 'lbry-bids-chart',
|
||||
position: 'bottom-right',
|
||||
divId: 'chart-export'
|
||||
}
|
||||
});
|
||||
loadChartData();
|
||||
});
|
|
@ -1,228 +0,0 @@
|
|||
var chart;
|
||||
var chartData = [];
|
||||
var chartLoadInProgress = false;
|
||||
var minPeriod = 'hh';
|
||||
var validPeriods = ['24h', '72h', '168h', '30d', '90d', '1y'];
|
||||
var defaultPeriod = (validPeriods.indexOf(localStorage.getItem('chartPeriod')) > -1) ? localStorage.getItem('chartPeriod') : '24h';
|
||||
var periodGridCounts = {'24h': 24, '72h': 24, '168h': 14, '30d': 30, '90d': 45, '1y': 12 };
|
||||
AmCharts.ready(function() {
|
||||
chart = AmCharts.makeChart('block-size-chart', {
|
||||
type: 'serial',
|
||||
theme: 'light',
|
||||
mouseWheelZoomEnabled: true,
|
||||
categoryField: 'date',
|
||||
synchronizeGrid: true,
|
||||
dataProvider: chartData,
|
||||
valueAxes: [
|
||||
{
|
||||
id: 'v-block-size',
|
||||
axisColor: '#1e88e5',
|
||||
axisThickness: 2,
|
||||
labelFunction: function(value) {
|
||||
return (Math.round((value / 1000) * 100)/100).toFixed(2) + ' KB';
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'v-price',
|
||||
axisColor: '#00e676',
|
||||
offset: 75,
|
||||
gridAlpha: 0,
|
||||
axisThickness: 2,
|
||||
labelFunction: function(value) {
|
||||
return '$' + value.toFixed(2);
|
||||
}
|
||||
}
|
||||
],
|
||||
categoryAxis: {
|
||||
parseDates: true,
|
||||
minPeriod: minPeriod, // DD for daily
|
||||
autoGridCount: false,
|
||||
minorGridEnabled: true,
|
||||
minorGridAlpha: 0.04,
|
||||
axisColor: '#dadada',
|
||||
twoLineMode: true,
|
||||
dateFormats: [{
|
||||
period: 'fff',
|
||||
format: 'JJ:NN:SS'
|
||||
}, {
|
||||
period: 'ss',
|
||||
format: 'JJ:NN:SS'
|
||||
}, {
|
||||
period: 'mm',
|
||||
format: 'JJ:NN'
|
||||
}, {
|
||||
period: 'hh',
|
||||
format: 'JJ:NN'
|
||||
}, {
|
||||
period: 'DD',
|
||||
format: 'DD'
|
||||
}, {
|
||||
period: 'WW',
|
||||
format: 'DD MMM'
|
||||
}, {
|
||||
period: 'MM',
|
||||
format: 'MMM'
|
||||
}, {
|
||||
period: 'YYYY',
|
||||
format: 'YYYY'
|
||||
}]
|
||||
},
|
||||
graphs: [
|
||||
{
|
||||
id: 'g-block-size',
|
||||
valueAxis: 'v-block-size', // we have to indicate which value axis should be used
|
||||
title: 'Avg Block Size',
|
||||
valueField: 'AvgBlockSize',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#1e88e5',
|
||||
hideBulletsCount: 101,
|
||||
balloonText: '[[AvgBlockSize]] KB',
|
||||
switchable: false,
|
||||
balloonFunction: function(item, graph) {
|
||||
var result = graph.balloonText;
|
||||
return result.replace('[[AvgBlockSize]]', (Math.round((item.dataContext.AvgBlockSize / 1000) * 100)/100).toFixed(2));
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'g-price',
|
||||
valueAxis: 'v-price',
|
||||
title: 'Average Price',
|
||||
valueField: 'AvgUSD',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#00e676',
|
||||
balloonText: '$[[AvgUSD]]',
|
||||
balloonFunction: function(item, graph) {
|
||||
var result = graph.balloonText;
|
||||
if (!item.dataContext.AvgUSD) {
|
||||
return '';
|
||||
}
|
||||
return result.replace('[[AvgUSD]]', item.dataContext.AvgUSD.toFixed(2));
|
||||
},
|
||||
hideBulletsCount: 101,
|
||||
labelFunction: function(value) {
|
||||
return '$' + value;
|
||||
},
|
||||
}
|
||||
],
|
||||
chartCursor: {
|
||||
cursorAlpha: 0.1,
|
||||
fullWidth: true,
|
||||
valueLineBalloonEnabled: true,
|
||||
categoryBalloonColor: '#333333',
|
||||
cursorColor: '#1e88e5',
|
||||
categoryBalloonDateFormat: minPeriod === 'hh' ? 'D MMM HH:NN ' : 'D MMM'
|
||||
},
|
||||
chartScrollbar: {
|
||||
scrollbarHeight: 36,
|
||||
color: '#888888',
|
||||
gridColor: '#bbbbbb'
|
||||
},
|
||||
legend: {
|
||||
marginLeft: 110,
|
||||
useGraphSettings: true,
|
||||
valueAlign: 'right',
|
||||
valueWidth: 60,
|
||||
spacing: 64,
|
||||
valueFunction: function(item, formatted) {
|
||||
if (item.dataContext) {
|
||||
var g = item.graph;
|
||||
if (g.id === 'g-block-size' && item.dataContext.AvgBlockSize > 0) {
|
||||
return g.balloonText.replace('[[AvgBlockSize]]', (Math.round((item.dataContext.AvgBlockSize / 1000) * 100)/100).toFixed(2) );
|
||||
}
|
||||
if (g.id === 'g-price' && item.dataContext.AvgUSD) {
|
||||
return g.balloonText.replace('[[AvgUSD]]', item.dataContext.AvgUSD.toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
},
|
||||
export: {
|
||||
enabled: true,
|
||||
fileName: 'lbry-block-size-chart',
|
||||
position: 'bottom-right',
|
||||
divId: 'chart-export'
|
||||
}
|
||||
});
|
||||
|
||||
loadChartData(defaultPeriod);
|
||||
});
|
||||
|
||||
var loadChartData = function(dataPeriod) {
|
||||
var loadProgress = $('.block-size-chart-container .load-progress');
|
||||
// clear previous chart data
|
||||
$.ajax({
|
||||
url: '/api/v1/charts/blocksize/' + dataPeriod,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
chartLoadInProgress = true;
|
||||
loadProgress.css({ display: 'block' });
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
chartData = [];
|
||||
var data = response.data;
|
||||
for (var period in data) {
|
||||
if (data.hasOwnProperty(period)) {
|
||||
chartData.push({
|
||||
date: Date.parse(period),
|
||||
AvgBlockSize: data[period].AvgBlockSize,
|
||||
AvgUSD: data[period].AvgUSD
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// save selcted period to localStorage
|
||||
localStorage.setItem('chartPeriod', dataPeriod);
|
||||
|
||||
if (chart) {
|
||||
var isHourly = (dataPeriod.indexOf('h') > -1);
|
||||
var gridCount = periodGridCounts[dataPeriod];
|
||||
chart.categoryAxis.minPeriod = isHourly ? 'hh' : 'DD';
|
||||
chart.categoryAxis.dateFormats[4].format = isHourly ? 'DD MMM' : 'DD';
|
||||
chart.chartCursor.categoryBalloonDateFormat = isHourly ? 'D MMM HH:NN ' : 'D MMM YYYY';
|
||||
chart.categoryAxis.gridCount = gridCount;
|
||||
chart.chartScrollbar.gridCount = gridCount;
|
||||
chart.dataProvider = chartData;
|
||||
chart.validateNow();
|
||||
chart.validateData();
|
||||
}
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
chartLoadInProgress = false;
|
||||
loadProgress.css({ display: 'none' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.block-size-data-links a').on('click', function(evt) {
|
||||
evt.preventDefault();
|
||||
if (chartLoadInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
var link = $(this);
|
||||
if (link.hasClass('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
link.addClass('active').siblings().removeClass('active');
|
||||
var period = link.attr('data-period');
|
||||
loadChartData(period);
|
||||
});
|
||||
|
||||
$('a[data-period="' + defaultPeriod + '"]').addClass('active').siblings().removeClass('active');
|
||||
});
|
|
@ -1,264 +0,0 @@
|
|||
function getReward(blockHeight) {
|
||||
if (blockHeight == 0) {
|
||||
return 400000000;
|
||||
}
|
||||
else if (blockHeight <= 5100) {
|
||||
return 1;
|
||||
}
|
||||
else if (blockHeight <= 55000) {
|
||||
return 1 + Math.floor((blockHeight - 5001) / 100);
|
||||
}
|
||||
else {
|
||||
var level = Math.floor((blockHeight - 55001) / 32);
|
||||
var reduction = Math.floor((Math.floor(Math.sqrt((8 * level) + 1)) - 1) / 2);
|
||||
while(!(withinLevelBounds(reduction, level))) {
|
||||
if(Math.floor((reduction * reduction + reduction) / 2) > level) {
|
||||
reduction--;
|
||||
}
|
||||
else {
|
||||
reduction++;
|
||||
}
|
||||
}
|
||||
return Math.max(0, 500 - reduction);
|
||||
}
|
||||
}
|
||||
|
||||
function withinLevelBounds(reduction, level) {
|
||||
if(Math.floor((reduction * reduction + reduction) / 2) > level) {
|
||||
return false;
|
||||
}
|
||||
reduction += 1;
|
||||
if(Math.floor((reduction * reduction + reduction) / 2) <= level) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getAverageBlockTime(blocks) {
|
||||
var numBlocks = blocks.length;
|
||||
var windowSize = 100;
|
||||
var sum = 0;
|
||||
for(i = numBlocks - windowSize; i < numBlocks; i++) {
|
||||
sum += blocks[i].block_time - blocks[i-1].block_time;
|
||||
}
|
||||
return sum / windowSize;
|
||||
}
|
||||
|
||||
function buildChartData(blockData) {
|
||||
var chartData = [];
|
||||
var supply = 0;
|
||||
var reward = 0;
|
||||
var averageBlockTime = getAverageBlockTime(blockData);
|
||||
var blockTime = 0;
|
||||
var lastBlock = 4071017; // Last block with reward
|
||||
var skip = 100;
|
||||
var blocksPerYear = Math.floor((3600*24*365) / averageBlockTime);
|
||||
var historicalSupply = {};
|
||||
var lastYearSupply = 0;
|
||||
var lastYearBlock = 0;
|
||||
var inflationRate = 0;
|
||||
for(var i = 0; i < lastBlock; i++) {
|
||||
reward = getReward(i);
|
||||
supply += reward;
|
||||
historicalSupply[i + 1] = supply;
|
||||
if(i == 0) { // Reward for 1st block set to 0 for scale
|
||||
reward = 0;
|
||||
}
|
||||
if(i < blockData.length) {
|
||||
// Historical Data
|
||||
var b = blockData[i];
|
||||
blockTime = b.block_time;
|
||||
}
|
||||
else {
|
||||
// Future blocks
|
||||
skip = 1000;
|
||||
blockTime += averageBlockTime;
|
||||
}
|
||||
// Inflation Rate
|
||||
if(i + 1 - blocksPerYear <= 0) {
|
||||
lastYearBlock = 1;
|
||||
}
|
||||
else {
|
||||
lastYearBlock = i + 1 - blocksPerYear;
|
||||
}
|
||||
lastYearSupply = historicalSupply[lastYearBlock];
|
||||
inflationRate = ((supply - lastYearSupply) / lastYearSupply) * 100;
|
||||
if(i % skip == 0) { // Only push 1/<skip> of all blocks to optimize data loading
|
||||
chartData.push({
|
||||
date: new Date(blockTime * 1000),
|
||||
AvailableSupply: supply,
|
||||
RewardLBC: reward,
|
||||
InflationRate: inflationRate,
|
||||
BlockId: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
return chartData;
|
||||
}
|
||||
|
||||
function loadChartData() {
|
||||
var api_url = "https://chainquery.odysee.tv/api/sql?query=";
|
||||
var query = "SELECT height, block_time FROM block WHERE confirmations > 0 ORDER BY height";
|
||||
var url = api_url + query;
|
||||
var loadProgress = $('.mining-inflation-chart-container .load-progress');
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
chartLoadInProgress = true;
|
||||
loadProgress.css({ display: 'block' });
|
||||
},
|
||||
success: function(response) {
|
||||
if(response.success) {
|
||||
chartData = buildChartData(response.data);
|
||||
if(chart) {
|
||||
chart.dataProvider = chartData;
|
||||
chart.validateNow();
|
||||
chart.validateData();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Could not fetch block data.");
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
chartLoadInProgress = false;
|
||||
loadProgress.css({ display: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var chart;
|
||||
var chartData = [];
|
||||
var chartLoadInProgress = false;
|
||||
AmCharts.ready(function() {
|
||||
chart = AmCharts.makeChart('mining-inflation-chart', {
|
||||
type: 'serial',
|
||||
theme: 'light',
|
||||
mouseWheelZoomEnabled: true,
|
||||
height: '100%',
|
||||
categoryField: 'date',
|
||||
synchronizeGrid: true,
|
||||
dataProvider: chartData,
|
||||
responsive: {
|
||||
enabled: true,
|
||||
},
|
||||
valueAxes: [
|
||||
{
|
||||
id: 'v-supply',
|
||||
axisColor: '#1e88e5',
|
||||
axisThickness: 2,
|
||||
position: 'left',
|
||||
labelFunction: function(value) {
|
||||
return (Math.round((value / 1000000) * 1000000)/1000000).toFixed(2);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'v-reward',
|
||||
axisColor: '#0b7a06',
|
||||
axisThickness: 2,
|
||||
position: 'left',
|
||||
offset: 75,
|
||||
},
|
||||
{
|
||||
id: 'v-inflation-rate',
|
||||
axisColor: '#ff9900',
|
||||
axisThickness: 2,
|
||||
position: 'right',
|
||||
labelFunction: function(value) {
|
||||
return value.toFixed(2);
|
||||
}
|
||||
},
|
||||
],
|
||||
categoryAxis: {
|
||||
parseDates: true,
|
||||
autoGridCount: false,
|
||||
minorGridEnabled: true,
|
||||
minorGridAlpha: 0.04,
|
||||
axisColor: '#dadada',
|
||||
twoLineMode: true
|
||||
},
|
||||
graphs: [
|
||||
{
|
||||
id: 'g-supply',
|
||||
valueAxis: 'v-supply', // we have to indicate which value axis should be used
|
||||
title: 'Available supply (millions LBC)',
|
||||
valueField: 'AvailableSupply',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#1e88e5',
|
||||
hideBulletsCount: 101,
|
||||
balloonText: '[[AvailableSupply]]',
|
||||
balloonFunction: function(item, graph) {
|
||||
var result = graph.balloonText;
|
||||
return result.replace('[[AvailableSupply]]', (Math.round((item.dataContext.AvailableSupply / 1000000) * 1000000)/1000000).toFixed(2));
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'g-reward',
|
||||
valueAxis: 'v-reward',
|
||||
title: 'Block Reward (LBC)',
|
||||
valueField: 'RewardLBC',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#0b7a06',
|
||||
balloonText: '[[RewardLBC]] LBC<br>Block [[BlockId]]',
|
||||
hideBulletsCount: 101
|
||||
},
|
||||
{
|
||||
id: 'g-inflation-rate',
|
||||
valueAxis: 'v-inflation-rate',
|
||||
title: 'Annualized Inflation Rate',
|
||||
valueField: 'InflationRate',
|
||||
bullet: 'round',
|
||||
bulletBorderThickness: 1,
|
||||
bulletBorderAlpha: 1,
|
||||
bulletColor: '#ffffff',
|
||||
bulletSize: 5,
|
||||
useLineColorForBulletBorder: true,
|
||||
lineColor: '#ff9900',
|
||||
balloonText: '[[InflationRate]]%',
|
||||
hideBulletsCount: 101,
|
||||
balloonFunction: function(item, graph) {
|
||||
var result = graph.balloonText;
|
||||
return result.replace('[[InflationRate]]', item.dataContext.InflationRate.toFixed(2));
|
||||
}
|
||||
}
|
||||
],
|
||||
chartCursor: {
|
||||
cursorAlpha: 0.1,
|
||||
fullWidth: true,
|
||||
valueLineBalloonEnabled: true,
|
||||
categoryBalloonColor: '#333333',
|
||||
cursorColor: '#1e88e5'
|
||||
},
|
||||
chartScrollbar: {
|
||||
scrollbarHeight: 36,
|
||||
color: '#888888',
|
||||
gridColor: '#bbbbbb'
|
||||
},
|
||||
legend: {
|
||||
marginLeft: 110,
|
||||
useGraphSettings: true,
|
||||
valueText: "",
|
||||
spacing: 64,
|
||||
|
||||
},
|
||||
export: {
|
||||
enabled: true,
|
||||
fileName: 'lbry-supply-chart',
|
||||
position: 'bottom-right',
|
||||
divId: 'chart-export'
|
||||
}
|
||||
});
|
||||
loadChartData();
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
User-agent: *
|
||||
Disallow:
|
|
@ -1,84 +0,0 @@
|
|||
@php
|
||||
$autoThumbText = $claim->getAutoThumbText();
|
||||
$cost = '';
|
||||
if (isset($claim->price) && $claim->price > 0) {
|
||||
$cost = \App\Helpers\AmountHelper::formatCurrency($claim->price) . ' LBC';
|
||||
} else if (isset($claim->fee) && strtolower($claim->fee_currency) === 'lbc') {
|
||||
$cost = \App\Helpers\AmountHelper::formatCurrency($claim->fee) . ' LBC';
|
||||
}
|
||||
$a = ['purple', 'orange', 'blue', 'teal', 'green', 'yellow'];
|
||||
// content type
|
||||
$ctTag = $claim->getContentTag();
|
||||
@endphp
|
||||
<div data-id="{{ $claim->claim_id }}" class="{{ 'claim-grid-item' }}@if($idx % 3 == 0){{ ' last-item' }}@endif{{ '' }}@if($last_row){{ ' last-row' }}@endif">
|
||||
@if(strlen(trim($cost)) > 0)
|
||||
<div class="price-tag">{{ $cost }}</div>
|
||||
@endif
|
||||
|
||||
<div class="tags">
|
||||
@if($claim->bid_state == 'Controlling')
|
||||
<div class="bid-state">Controlling</div>
|
||||
@endif
|
||||
@if($ctTag)
|
||||
<div class="content-type">{{ strtoupper($ctTag) }}</div>
|
||||
@endif
|
||||
@if($claim->is_nsfw)
|
||||
<div class="nsfw">NSFW</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div data-autothumb="{{ $autoThumbText }}" class="thumbnail {{ $a[mt_rand(0, count($a) - 1)] }}">
|
||||
@if(!$claim->is_nsfw && strlen(trim($claim->thumbnail_url)) > 0)
|
||||
<img src="{{ 'https://thumbnails.odycdn.com/optimize/s:1280:720/quality:85/plain/'.$claim->thumbnail_url }}" alt="" />
|
||||
@else
|
||||
<div class="autothumb">{{ $autoThumbText }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($claim->isBlocked)
|
||||
<div class="blocked-info">
|
||||
In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more information, please refer to <a href="https://lbry.com/faq/dmca" target="_blank">DMCA takedown requests</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="metadata">
|
||||
<div class="title" title="{{ $claim->claim_type == 1 ? $claim->name : ((strlen(trim($claim->title)) > 0) ? $claim->title : '') }}">@if($claim->claim_type == 1){{ $claim->name }}@else{{ '' }}@if(strlen(trim($claim->title)) > 0){{ $claim->title }}@else<em>No Title</em>@endif{{ '' }}@endif</div>
|
||||
<div class="link" title="{{ $claim->getLbryLink() }}"><a href="{{ $claim->getLbryLink() }}" rel="nofollow">{{ $claim->getLbryLink() }}</a></div>
|
||||
|
||||
<div class="desc">@if(strlen(trim($claim->description)) > 0){{ $claim->description }}@else<em>No description available</em>@endif</div>
|
||||
|
||||
<div class="label half-width">Transaction</div>
|
||||
<div class="label half-width">Created</div>
|
||||
|
||||
<div class="value half-width"><a href="/tx/{{ $claim->transaction_hash_id }}#output-{{ $claim->vout }}" title="{{ $claim->transaction_hash_id }}">{{ $claim->transaction_hash_id }}</a></div>
|
||||
<div class="value half-width" title="{{ $claim->created_at->format('j M Y H:i:s') }} UTC">
|
||||
{{ \Carbon\Carbon::createFromTimestamp($claim->created_at->format('U'))->diffForHumans() }}
|
||||
</div>
|
||||
<div class="clear spacer"></div>
|
||||
|
||||
@if($claim->claim_type == 1)
|
||||
<div class="label half-width">Content Type</div>
|
||||
<div class="label half-width">Language</div>
|
||||
|
||||
<div class="value half-width" title="{{ $claim->content_type }}">{{ $claim->content_type }}</div>
|
||||
<div class="value half-width" title="{{ $claim->language == 'en' ? 'English' : $claim->language }}">{{ $claim->language == 'en' ? 'English' : $claim->language }}</div>
|
||||
|
||||
<div class="clear spacer"></div>
|
||||
|
||||
{{--<div class="label half-width">Author</div>--}}
|
||||
{{--<div class="label half-width">License</div>--}}
|
||||
|
||||
{{--<div class="value half-width" title="{{ strlen(trim($claim->author)) > 0 ? $claim->author : 'Unspecified' }}">@if(strlen(trim($claim->author)) > 0){{ $claim->author }}@else<em>Unspecified</em>@endif</div>--}}
|
||||
|
||||
{{--<div class="value half-width" title="{{ strlen(trim($claim->license)) > 0 ? $claim->license : '' }}">--}}
|
||||
{{-- @if(strlen(trim($claim->LicenseUrl)) > 0)--}}
|
||||
{{-- <a href="{{ $claim->LicenseUrl }}" rel="nofollow" target="_blank">--}}
|
||||
{{-- @if(strlen(trim($claim->License)) > 0){{ $claim->License }}@else<em>Unspecified</em>@endif--}}
|
||||
{{-- </a>--}}
|
||||
{{-- @else--}}
|
||||
{{-- @if(strlen(trim($claim->License)) > 0){{ $claim->License }}@else<em>Unspecified</em>@endif--}}
|
||||
{{-- @endif--}}
|
||||
{{--</div>--}}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
|
@ -1,55 +0,0 @@
|
|||
@if(isset($numPages) && $numPages > 1)
|
||||
<div class="pagination">
|
||||
<div class="prev">
|
||||
@if($currentPage > 1)
|
||||
<a href="?page={{ $currentPage - 1 }}">Previous</a>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
<div class="pages">
|
||||
@if($numRecords > 0)
|
||||
@php
|
||||
$start = $numPages > 1 ? 1 : 0;
|
||||
$end = $numPages > 1 ? min($numPages, 10) : 0;
|
||||
// use currentPage as the starting point
|
||||
if ($numPages > 10) {
|
||||
if ($currentPage > 5) {
|
||||
$start = $currentPage < 10 ? 1 : $currentPage - 5;
|
||||
$end = ($currentPage > ($numPages - 10) && $start > 5) ? $numPages : min($currentPage + 5, $numPages);
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
@if($start >= 5)
|
||||
<div class="page-number"><a href="?page=1">1</a></div>
|
||||
<div class="page-number">...</div>
|
||||
@endif
|
||||
|
||||
@if($start > 0)
|
||||
@for($i = $start; $i <= $end; $i++)
|
||||
<div class="page-number">
|
||||
@if($currentPage == $i)
|
||||
{{ $i }}
|
||||
@else
|
||||
<a href="?page={{ $i }}">{{ $i }}</a>
|
||||
@endif
|
||||
</div>
|
||||
@endfor
|
||||
@endif
|
||||
|
||||
@if($end < $numPages - 1)
|
||||
<div class="page-number">...</div>
|
||||
<div class="page-number">
|
||||
<a href="?page={{ $numPages }}">{{ $numPages }}</a>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
<div class="next">
|
||||
@if($currentPage < $numPages)
|
||||
<a href="?page={{ $currentPage + 1 }}">Next</a>
|
||||
@endif
|
||||
 
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
@endif
|
|
@ -1,68 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>
|
||||
LBRY Block Explorer • @yield('title')
|
||||
</title>
|
||||
{{--TODO: icon--}}
|
||||
|
||||
<script src="/js/{{ 'jquery' }}.js"></script>
|
||||
<script src="/js/{{ 'moment' }}.js"></script>
|
||||
|
||||
<script src="/js/{{ 'moment' }}.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/css/{{ 'main' }}.css" />
|
||||
|
||||
<script src="https://use.typekit.net/yst3vhs.js"></script>
|
||||
<script>try{Typekit.load({ async: true });}catch(e){}</script>
|
||||
|
||||
<!-- Analytics -->
|
||||
@if(env('GTM_ID'))
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="//www.googletagmanager.com/gtag/js?id={{ env('GTM_ID') }}"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '{{ env('GTM_ID') }}');
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<script type="text/javascript">
|
||||
// Handle Coinomi and LBRY app URLs
|
||||
var hashpart = window.location.hash;
|
||||
if (hashpart.length > 3) {
|
||||
hashpart = hashpart.substring(3);
|
||||
var txhash = null;
|
||||
if (hashpart.indexOf('?id=') > -1) {
|
||||
txhash = hashpart.substring(hashpart.indexOf('?id=') + 4);
|
||||
} else {
|
||||
var parts = hashpart.split('/');
|
||||
if (parts.length > 1) {
|
||||
txhash = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (txhash && $.trim(txhash.trim).length > 0) {
|
||||
window.location.href = '/tx/' + txhash;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@yield('meta')
|
||||
@yield('css')
|
||||
@yield('script')
|
||||
</head>
|
||||
<body>
|
||||
@yield('content')
|
||||
<footer>
|
||||
<div class="content">
|
||||
<a href="https://lbry.org">LBRY</a>
|
||||
|
||||
<div class="page-time">Page took {{ round((microtime(true) - LARAVEL_START) * 1000, 0) }}ms</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -1,180 +0,0 @@
|
|||
@extends('layout.default')
|
||||
{{ /**@var \App\Models\Address $address*/'' }}
|
||||
|
||||
@section('title','Address '.$address->address)
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
var buildTagRequest = function() {
|
||||
return {
|
||||
tag: $.trim($('input[name="tag_value"]').val()),
|
||||
url: $.trim($('input[name="tag_url"]').val()),
|
||||
vamount: parseFloat($('input[name="tag_verify_amount"]').val())
|
||||
};
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.tag-link').on('click', function(evt) {
|
||||
evt.preventDefault();
|
||||
var container = $('.tag-address-container');
|
||||
if (!container.is(':visible')) {
|
||||
container.slideDown(200);
|
||||
}
|
||||
});
|
||||
|
||||
$('.btn-tag').on('click', function(evt) {
|
||||
evt.preventDefault();
|
||||
var btn = $(this);
|
||||
var req = buildTagRequest();
|
||||
|
||||
var err = $('.tag-address-container .error-message');
|
||||
err.css({ color: '#ff0000' }).text('');
|
||||
if (req.tag.length === 0 || req.tag.length > 30) {
|
||||
return err.text('Oops! Please specify a valid tag. It should be no more than 30 characters long.');
|
||||
}
|
||||
|
||||
if (req.url.length > 200) {
|
||||
return err.text('Oops! The link should be no more than 200 characters long.');
|
||||
}
|
||||
|
||||
if (isNaN(req.vamount)) {
|
||||
return err.text('Oops! Invalid verification amount. Please refresh the page and try again.');
|
||||
}
|
||||
|
||||
var btnClose = $('.btn-close');
|
||||
$.ajax({
|
||||
url: '/api/v1/address/{{ $address->address }}/tag',
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: req,
|
||||
beforeSend: function() {
|
||||
btn.prop('disabled', true);
|
||||
btnClose.prop('disabled', true);
|
||||
btn.text('Loading...');
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
err.css({ color: '#00aa00'}).html('Your request for the tag, <strong>' + response.tag + '</strong> was successfully submitted. The tag will become active upon automatic transaction verification.');
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
var error = 'An error occurred with the request. If this problem persists, please send an email to hello@aureolin.co.';
|
||||
try {
|
||||
var json = JSON.parse(xhr.responseText);
|
||||
if (json.error) {
|
||||
error = json.message ? json.message : error;
|
||||
}
|
||||
} catch (e) {
|
||||
// return default error
|
||||
}
|
||||
err.css({ color: '#ff0000' }).text(error);
|
||||
},
|
||||
complete: function() {
|
||||
btn.text('Tag address');
|
||||
btn.prop('disabled', false);
|
||||
btnClose.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.btn-close').on('click', function() {
|
||||
$('input[name="tag_value"]').val('');
|
||||
$('input[name="tag_url"]').val('');
|
||||
$('.tag-address-container').slideUp(200);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
<div class="address-head">
|
||||
<h3>LBRY Address</h3>
|
||||
<h4>{{ $address->address }}</h4>
|
||||
@if(isset($address->Tag) && strlen(trim($address->Tag)) > 0)
|
||||
@if(strlen(trim($address->TagUrl)) > 0)
|
||||
<a href="{{ $address->TagUrl }}" target="_blank" rel="nofollow">{{ $address->Tag }}</a>
|
||||
@else
|
||||
{{ $address->Tag }}
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="address-subhead">
|
||||
<div class="address-qr">
|
||||
<img src="/qr/lbry%3A{{ $address->address }}" alt="lbry:{{ $address->address }}" />
|
||||
</div>
|
||||
|
||||
<div class="address-summary">
|
||||
<div class="box">
|
||||
<div class="title">Balance (LBC)</div>
|
||||
<div class="value">{{ \App\Helpers\AmountHelper::format($balanceAmount) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="title">Received (LBC)</div>
|
||||
<div class="value">{{ \App\Helpers\AmountHelper::format($totalReceived) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box last">
|
||||
<div class="title">Sent (LBC)</div>
|
||||
<div class="value">{{ \App\Helpers\AmountHelper::format($totalSent) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="recent-transactions">
|
||||
<h3>Transactions</h3>
|
||||
<div class="results-meta">
|
||||
@if($numRecords > 0)
|
||||
@php($begin = ($currentPage - 1) * $pageLimit + 1)
|
||||
Showing {{ number_format($begin, 0, '', ',') }} - {{ number_format(min($numRecords, ($begin + $pageLimit) - 1), 0, '', ',') }} of {{ number_format($numRecords, 0, '', ',') }} transaction{{ $numRecords == 1 ? '' : 's' }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<table class="table tx-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w125 left">Height</th>
|
||||
<th class="w250 left">Transaction Hash</th>
|
||||
<th class="left">Timestamp</th>
|
||||
<th class="w125 right">Confirmations</th>
|
||||
<th class="w80 right">Inputs</th>
|
||||
<th class="w80 right">Outputs</th>
|
||||
<th class="w225 right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@if(count($recentTxs) == 0)
|
||||
<tr>
|
||||
<td class="nodata" colspan="7">There are no recent transactions to display for this wallet.</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@foreach($recentTxs as $tx)
|
||||
<tr>
|
||||
<td class="w125">@if($tx->height === null)<em>Unconfirmed</em>@else<a href="/blocks/{{ $tx->height }}">{{ $tx->height }}</a>@endif</td>
|
||||
<td class="w250"><div><a href="/tx/{{ $tx->hash }}?address={{ $address->address }}#{{ $address->address }}">{{ $tx->hash }}</a></div></td>
|
||||
<td>{{ \DateTime::createFromFormat('U', $tx->transaction_time)->format('d M Y H:i:s') . ' UTC' }}</td>
|
||||
<td class="right">{{ number_format($tx->confirmations, 0, '', ',') }}</td>
|
||||
<td class="right">{{ $tx->input_count }}</td>
|
||||
<td class="right">{{ $tx->output_count }}</td>
|
||||
<td class="right{{ ' ' . ($tx->debit_amount > 0 && $tx->credit_amount > 0 ? 'diff' : ($tx->debit_amount > 0 ? 'debit' : 'credit')) }}">
|
||||
{{ number_format($tx->credit_amount - $tx->debit_amount, 8, '.', ',') }} LBC
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@include('element.pagination')
|
||||
|
||||
@endsection
|
|
@ -1,203 +0,0 @@
|
|||
@extends('layout.default')
|
||||
{{ /**@var \App\Models\Block $block*/'' }}
|
||||
|
||||
@section('title',isset($block)?('Block Height '.$block->height):'Blocks')
|
||||
|
||||
@section('script')
|
||||
@if(isset($block))
|
||||
<script type="text/javascript">
|
||||
var resizeCards = function() {
|
||||
var bSummary = $('.block-summary');
|
||||
var bTransactions = $('.block-transactions');
|
||||
if (bTransactions.outerHeight() < bSummary.outerHeight()) {
|
||||
bTransactions.outerHeight(bSummary.outerHeight());
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
resizeCards();
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
resizeCards();
|
||||
};
|
||||
</script>
|
||||
@else
|
||||
<script type="text/javascript" src="/amcharts/amcharts.js"></script>
|
||||
<script type="text/javascript" src="/amcharts/serial.js"></script>
|
||||
<script type="text/javascript" src="/amcharts/plugins/export/export.min.js"></script>
|
||||
<script type="text/javascript" src="/js/block-size-chart.js"></script>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@section('css')
|
||||
@if(!isset($block))
|
||||
<link rel="stylesheet" href="/amcharts/plugins/export/export.css" />
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
@if(isset($block))
|
||||
<div class="block-head">
|
||||
<h3>LBRY Block {{ $block->height }}</h3>
|
||||
<h4>{{ $block->hash }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="block-nav">
|
||||
@if($block->height > 0)
|
||||
<a class="btn btn-prev" href="/blocks/{{ ($block->height - 1) }}">« Previous Block</a>
|
||||
@endif
|
||||
|
||||
<a class="btn btn-next" href="/blocks/{{ $block->height + 1 }}">Next Block »</a>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="block-info">
|
||||
<div class="block-summary">
|
||||
<h3>Overview</h3>
|
||||
|
||||
<div class="label half-width">Block Size (bytes)</div>
|
||||
<div class="label half-width">Block Time</div>
|
||||
|
||||
<div class="value half-width">{{ number_format($block->block_size, 0, '', ',') }}</div>
|
||||
<div class="value half-width">{{ \DateTime::createFromFormat('U', $block->block_time)->format('j M Y H:i:s') . ' UTC' }}</div>
|
||||
|
||||
<div class="clear spacer"></div>
|
||||
|
||||
<div class="label half-width">Bits</div>
|
||||
<div class="label half-width">Confirmations</div>
|
||||
|
||||
<div class="value half-width">{{ $block->bits }}</div>
|
||||
<div class="value half-width">{{ $confirmations }}</div>
|
||||
|
||||
<div class="clear spacer"></div>
|
||||
|
||||
<div class="label half-width">Difficulty</div>
|
||||
<div class="label half-width">Nonce</div>
|
||||
|
||||
<div class="value half-width">{{ \App\Helpers\AmountHelper::format($block->difficulty,'') }}</div>
|
||||
<div class="value half-width">{{ $block->nonce }}</div>
|
||||
|
||||
<div class="clear spacer"></div>
|
||||
|
||||
<div class="label">Chainwork</div> <div class="value">{{ $block->chainwork }}</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="label">MerkleRoot</div> <div class="value">{{ $block->merkle_root }}</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="label">NameClaimRoot</div> <div class="value">{{ $block->name_claim_root }}</div>
|
||||
|
||||
<!--
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="label">Target</div> <div class="value">{{ $block->Target }}</div>
|
||||
-->
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="label">Version</div> <div class="value">{{ $block->version }}</div>
|
||||
</div>
|
||||
|
||||
<div class="block-transactions">
|
||||
<h3>{{ count($blockTxs) }} Transaction{{ (count($blockTxs) == 1 ? '' : 's') }}</h3>
|
||||
|
||||
<div class="transactions-list">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hash</th>
|
||||
<th class="w100 right">Inputs</th>
|
||||
<th class="w100 right">Outputs</th>
|
||||
<th class="w200 right">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(count($blockTxs) == 0)
|
||||
<tr>
|
||||
<td class="nodata" colspan="4">There are no transactions to display at this time.</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@foreach($blockTxs as $tx)
|
||||
<tr>
|
||||
<td class="w300"><div><a href="/tx/{{ $tx->hash }}">{{ $tx->hash }}</a></div></td>
|
||||
<td class="right">{{ $tx->input_count }}</td>
|
||||
<td class="right">{{ $tx->output_count }}</td>
|
||||
<td class="right"><div title="{{ $tx->value }} LBC">{{ \App\Helpers\AmountHelper::formatCurrency($tx->value) }} LBC</div></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
@else
|
||||
<div class="block-head">
|
||||
<h3>LBRY Blocks</h3>
|
||||
</div>
|
||||
|
||||
<div class="block-size-chart-container">
|
||||
<div class="load-progress inc"></div>
|
||||
<h3>Block Size Chart</h3>
|
||||
<div class="block-size-data-links">
|
||||
<a href="#" title="24 hours" data-period="24h">24h</a>
|
||||
<a href="#" title="72 hours" data-period="72h">72h</a>
|
||||
<a href="#" title="1 week" data-period="168h">1w</a>
|
||||
<a href="#" title="30 days" data-period="30d">30d</a>
|
||||
<a href="#" title="90 days" data-period="90d">90d</a>
|
||||
<a href="#" title="1 year" data-period="1y">1y</a>
|
||||
</div>
|
||||
<div id="block-size-chart" class="chart"></div>
|
||||
<div id="chart-export" class="btn-chart-export"></div>
|
||||
</div>
|
||||
|
||||
<div class="all-blocks">
|
||||
<h3>All Blocks</h3>
|
||||
<div class="results-meta">
|
||||
@if($numRecords > 0)
|
||||
@php($begin = ($currentPage - 1) * $pageLimit + 1)
|
||||
Showing {{ number_format($begin, 0, '', ',') }} - {{ number_format(min($numRecords, ($begin + $pageLimit) - 1), 0, '', ',') }} of {{ number_format($numRecords, 0, '', ',') }} block{{ $numRecords == 1 ? '' : 's' }}
|
||||
@endif
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w100">Height</th>
|
||||
<th class="w150 left pad-left">Difficulty</th>
|
||||
<th class="w100 right">Confirmations</th>
|
||||
<th class="w100 right">TX Count</th>
|
||||
<th class="w100 right">Block Size</th>
|
||||
<th class="w100 right pad-left">Nonce</th>
|
||||
<th class="w150 left pad-left">Block Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($blocks as $block)
|
||||
<tr>
|
||||
<td class="right"><a href="/blocks/{{ $block->height }}">{{ $block->height }}</a></td>
|
||||
<td class="pad-left">{{ number_format($block->difficulty, 8, '.', '') }}</td>
|
||||
<td class="right">{{ number_format((($currentBlock->height - $block->height) + 1), 0, '', ',') }}</td>
|
||||
<td class="right">{{ $block->tx_count }}</td>
|
||||
<td class="right">{{ round($block->block_size / 1024, 2) . 'KB' }}</td>
|
||||
<td class="right pad-left">{{ $block->nonce }}</td>
|
||||
<td class="pad-left">{{ \DateTime::createFromFormat('U', $block->block_time)->format('d M Y H:i:s') }} UTC</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@include('element.pagination')
|
||||
@endif
|
||||
|
||||
@endsection
|
|
@ -1,217 +0,0 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title',isset($claim)?'Claim • ' . $claim->name:'Claims Explorer')
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
var resizeCards = function() {
|
||||
var claimInfo = $('.claim-info');
|
||||
var claimMtdt = $('.claim-metadata');
|
||||
if (claimMtdt.outerHeight() < claimInfo.outerHeight()) {
|
||||
claimMtdt.outerHeight(claimInfo.outerHeight());
|
||||
} else if (claimInfo.outerHeight() < claimMtdt.outerHeight()) {
|
||||
claimInfo.outerHeight(claimMtdt.outerHeight());
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
resizeCards();
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
resizeCards();
|
||||
|
||||
$('.claim-grid-item img,.claim-info img').on('error', function() {
|
||||
var img = $(this);
|
||||
var parent = img.parent();
|
||||
var text = parent.attr('data-autothumb');
|
||||
img.remove();
|
||||
parent.append(
|
||||
$('<div></div>').attr({'class': 'autothumb' }).text(text)
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.claim-grid-item', function() {
|
||||
var id = $(this).attr('data-id');
|
||||
location.href = '/claims/' + id;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
@if(isset($claim))
|
||||
@php
|
||||
$a = ['purple', 'orange', 'blue', 'teal', 'green', 'yellow'];
|
||||
$autoThumbText = $claim->getAutoThumbText();
|
||||
$cost = 'Free';
|
||||
if (isset($claim->price) && $claim->price > 0) {
|
||||
$cost = \App\Helpers\AmountHelper::formatCurrency($claim->price) . ' LBC';
|
||||
} else if (isset($claim->fee) && strtolower($claim->fee_currency) === 'lbc') {
|
||||
$cost = \App\Helpers\AmountHelper::formatCurrency($claim->fee) . ' LBC';
|
||||
}
|
||||
|
||||
$desc = $claim->description;
|
||||
if (strlen(trim($desc)) == 0) {
|
||||
$desc = null;
|
||||
} else {
|
||||
$desc = preg_replace('#((https?|ftp|lbry)://([A-Za-z0-9\-\/]+|\S*?\.\S*?))([\s)\[\]{},;"\':<]|\.\s|$)#i','<a href="$1" target="_blank" rel="nofollow">$1</a>$4', $desc);
|
||||
$desc = preg_replace('/(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/is', '<a href="mailto:$0" rel="nofollow">$0</a>', $desc);
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="claims-head">
|
||||
<h3><a href="/claims">LBRY Claims</a> • {{ $claim->name }}</h3>
|
||||
<h4>{{ $claim->claim_id }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="claims-body">
|
||||
@if($claimIsBlocked)
|
||||
<div class="blocked-claim-info">
|
||||
<div class="content">
|
||||
In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more information, please refer to <a href="https://lbry.com/faq/dmca" target="_blank">DMCA takedown requests</a>.
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="claim-info">
|
||||
<div data-autothumb="{{ $autoThumbText }}" class="thumbnail {{ $a[mt_rand(0, count($a) - 1)] }}">
|
||||
@if(!$claim->is_nsfw && strlen(trim($claim->thumbnail_url)) > 0)
|
||||
<img src="{{ 'https://thumbnails.odycdn.com/optimize/s:1280:720/quality:85/plain/'.$claim->thumbnail_url }}" alt="" />
|
||||
@else
|
||||
<div class="autothumb">{{ $autoThumbText }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
@if($claim->claim_type == 1)
|
||||
<div class="label">Published By</div>
|
||||
<div class="value">
|
||||
@if(isset($claim->publisher))
|
||||
<a href="lbry://{{ $claim->publisher }}">{{ $claim->publisher }}</a>
|
||||
@else
|
||||
<em>Anonymous</em>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="label">Created On</div>
|
||||
<div class="value">{{ \DateTime::createFromFormat('U', $claim->transaction_time > 0 ? $claim->transaction_time : $claim->created_at->format('U'))->format('j M Y H:i:s') }} UTC</div>
|
||||
|
||||
<div class="label">Transaction ID</div>
|
||||
<div class="value"><a href="/tx/{{ $claim->transaction_hash_id }}#output-{{ $claim->vout }}">{{ $claim->transaction_hash_id }}</a></div>
|
||||
|
||||
@if($claim->claim_type == 1)
|
||||
<div class="label half-width">Cost</div>
|
||||
<div class="label half-width">Safe for Work</div>
|
||||
|
||||
<div class="value half-width">{{ $cost }}</div>
|
||||
<div class="value half-width">{{ $claim->is_nsfw ? 'No' : 'Yes' }}</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="claim-metadata">
|
||||
@if($claim->claim_type == 2)
|
||||
<div class="title">Identity Claim</div>
|
||||
<div class="desc">This is an identity claim.</div>
|
||||
@else
|
||||
<div class="title">{{ $claim->title }}</div>
|
||||
<div class="desc">@if($desc===null)<em>No description available.</em>@else{{ str_replace("\n", '<br />', $desc) }}@endif</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="label half-width">Author</div>
|
||||
<div class="label half-width">Content Type</div>
|
||||
|
||||
<div class="value half-width">@if(strlen(trim($claim->author)) > 0){{ $claim->author }}@else<em>Unspecified</em>@endif</div>
|
||||
<div class="value half-width">@if(strlen(trim($claim->content_type)) > 0){{ $claim->content_type }}@else<em>Unspecified</em>@endif</div>
|
||||
|
||||
{{--<div class="label half-width">License</div>--}}
|
||||
<div class="label">Language</div>
|
||||
|
||||
{{--<div class="value half-width" title="@if(strlen(trim($claim->license)) > 0){{ $claim->license }}@endif">--}}
|
||||
{{-- @if(strlen(trim($claim->License)) > 0)--}}
|
||||
{{-- {{ $claim->License }}--}}
|
||||
{{-- @else--}}
|
||||
{{-- <em>Unspecified</em>--}}
|
||||
{{-- @endif--}}
|
||||
{{--</div>--}}
|
||||
|
||||
<div class="value half-width">
|
||||
@if(strlen(trim($claim->language)) > 0)
|
||||
{{ $claim->language == 'en' ? 'English' : '' }}
|
||||
@else
|
||||
<em>Unspecified</em>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<a href="{{ $claim->getLbryLink() }}" class="open-lbry-link">Open in LBRY</a>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
@endif
|
||||
|
||||
@if(count($moreClaims) > 0)
|
||||
<div class="more-claims">
|
||||
<h4>{{ isset($claim->publisher) ? 'More from the publisher' : 'Published by this identity' }}</h4>
|
||||
|
||||
<div class="claims-grid">
|
||||
@php
|
||||
$idx = 1;
|
||||
$row = 1;
|
||||
$rowCount = ceil(count($moreClaims) / 3);
|
||||
@endphp
|
||||
@foreach($moreClaims AS $claim)
|
||||
@php
|
||||
$last_row = ($row == $rowCount);
|
||||
if ($idx % 3 == 0) {
|
||||
$row++;
|
||||
}
|
||||
@endphp
|
||||
@include('element.claimbox',['claim'=>$claim,'idx'=>$idx,'last_row'=>$last_row])
|
||||
@php
|
||||
$idx++
|
||||
@endphp
|
||||
@endforeach
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="claims-head">
|
||||
<h2>Claims Explorer</h2>
|
||||
</div>
|
||||
|
||||
<div class="claims-grid">
|
||||
@php
|
||||
$idx = 1;
|
||||
$row = 1;
|
||||
$rowCount = ceil(count($claims) / 3);
|
||||
$a = ['purple', 'orange', 'blue', 'teal', 'green', 'yellow'];
|
||||
@endphp
|
||||
@foreach($claims AS $claim)
|
||||
@php
|
||||
$last_row = ($row == $rowCount);
|
||||
if ($idx % 3 == 0) {
|
||||
$row++;
|
||||
}
|
||||
@endphp
|
||||
@include('element.claimbox',['claim'=>$claim,'idx'=>$idx,'last_row'=>$last_row])
|
||||
@php
|
||||
$idx++
|
||||
@endphp
|
||||
@endforeach
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
@include('element.pagination')
|
||||
|
||||
@endif
|
||||
|
||||
@endsection
|
|
@ -1,78 +0,0 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title','Search Results')
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
var resizeCards = function() {
|
||||
var claimInfo = $('.claim-info');
|
||||
var claimMtdt = $('.claim-metadata');
|
||||
if (claimMtdt.outerHeight() < claimInfo.outerHeight()) {
|
||||
claimMtdt.outerHeight(claimInfo.outerHeight());
|
||||
} else if (claimInfo.outerHeight() < claimMtdt.outerHeight()) {
|
||||
claimInfo.outerHeight(claimMtdt.outerHeight());
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
resizeCards();
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
resizeCards();
|
||||
|
||||
$('.claim-grid-item img,.claim-info img').on('error', function() {
|
||||
var img = $(this);
|
||||
var parent = img.parent();
|
||||
var text = parent.attr('data-autothumb');
|
||||
img.remove();
|
||||
parent.append(
|
||||
$('<div></div>').attr({'class': 'autothumb' }).text(text)
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.claim-grid-item', function() {
|
||||
var id = $(this).attr('data-id');
|
||||
location.href = '/claims/' + id;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
<div class="claims-head">
|
||||
<h3>Search results</h3>
|
||||
</div>
|
||||
|
||||
<div class="claims-grid">
|
||||
@if(isset($claims) && count($claims)>0)
|
||||
@php
|
||||
$idx = 1;
|
||||
$row = 1;
|
||||
$rowCount = ceil(count($claims) / 3);
|
||||
$a = ['purple', 'orange', 'blue', 'teal', 'green', 'yellow'];
|
||||
@endphp
|
||||
@foreach($claims AS $claim)
|
||||
@php
|
||||
$last_row = ($row == $rowCount);
|
||||
if ($idx % 3 == 0) {
|
||||
$row++;
|
||||
}
|
||||
@endphp
|
||||
@include('element.claimbox',['claim'=>$claim,'idx'=>$idx,'last_row'=>$last_row])
|
||||
@php
|
||||
$idx++;
|
||||
@endphp
|
||||
@endforeach
|
||||
@else
|
||||
<div class="no-results">No results were found.</div>
|
||||
@endif
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
@include('element.pagination')
|
||||
|
||||
@endsection
|
|
@ -1,235 +0,0 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title','Home')
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
var updateInterval = 120000;
|
||||
|
||||
var updateStatus = function() {
|
||||
$.ajax({
|
||||
url: '/api/v1/status',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
var status = response.status;
|
||||
var stats = $('.stats');
|
||||
stats.find('.box:eq(0) > .value').text(status.height);
|
||||
stats.find('.box:eq(1) > .value').text(status.difficulty);
|
||||
stats.find('.box:eq(2) > .value').text(status.hashrate);
|
||||
stats.find('.box:eq(3) > .value').text(status.price);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var updateRecentBlocks = function() {
|
||||
var tbody = $('.recent-blocks .table tbody');
|
||||
|
||||
$.ajax({
|
||||
url: '/api/v1/recentblocks',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
var blocks = response.blocks;
|
||||
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||
var block = blocks[i];
|
||||
var prevRow = tbody.find('tr[data-height="' + block.Height + '"]');
|
||||
if (prevRow.length > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var blockTime = moment(block.BlockTime * 1000);
|
||||
tbody.prepend(
|
||||
$('<tr></tr>').attr({'data-height': block.Height, 'data-time': block.BlockTime}).append(
|
||||
$('<td></td>').append(
|
||||
$('<a></a>').attr({'href': '/blocks/' + block.Height}).text(block.Height)
|
||||
)
|
||||
).append(
|
||||
$('<td></td>').text(blockTime.fromNow())
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text((block.BlockSize / 1024).toFixed(2) + 'KB')
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(block.TransactionCount)
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(block.Difficulty)
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'last-cell'}).text(blockTime.utc().format('D MMM YYYY HH:mm:ss') + ' UTC')
|
||||
)
|
||||
);
|
||||
|
||||
// Remove the last row
|
||||
tbody.find('tr:last').remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
tbody.find('tr').each(function() {
|
||||
var row = $(this);
|
||||
var blockTime = moment(row.attr('data-time') * 1000);
|
||||
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
setInterval(updateStatus, updateInterval);
|
||||
setInterval(updateRecentBlocks, updateInterval);
|
||||
|
||||
$(document).on('click', '.recent-claims .claim-box .tx-link', function(evt) {
|
||||
evt.stopImmediatePropagation();
|
||||
});
|
||||
|
||||
$(document).on('click', '.recent-claims .claim-box', function() {
|
||||
var id = $(this).attr('data-id');
|
||||
window.location.href = '/claims/' + id;
|
||||
|
||||
// center the popup
|
||||
/*var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
|
||||
var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;
|
||||
var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
|
||||
var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
|
||||
|
||||
var left = ((width / 2) - (1366 / 2)) + dualScreenLeft;
|
||||
var top = ((height / 2) - (768 / 2)) + dualScreenTop;
|
||||
|
||||
window.open('/claims/' + id, 'claim_details', 'width=1366,height=768,left=' + left + ',top=' + top);*/
|
||||
});
|
||||
|
||||
$('.claim-box img').on('error', function() {
|
||||
var img = $(this);
|
||||
var parent = img.parent();
|
||||
var text = parent.attr('data-autothumb');
|
||||
img.remove();
|
||||
parent.append(
|
||||
$('<div></div>').attr({'class': 'autothumb' }).text(text)
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="home-container">
|
||||
<div class="home-container-cell">
|
||||
<div class="main">
|
||||
<div class="title">LBRY Block Explorer</div><br>
|
||||
<form method="get" action="/find">
|
||||
<input class="search-input" name="q" type="text" placeholder="Enter a block height or hash, claim id or name, transaction hash or address" />
|
||||
<div class="ctls">
|
||||
{{--<div class="left-links"><a href="https://lbry.com/get">Download the LBRY App</a></div>--}}
|
||||
<button class="btn btn-search">Search</button>
|
||||
<div class="right-links">
|
||||
<a href="/realtime">Realtime</a> • <a href="/stats" class="last">Stats</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="box box-20">
|
||||
<div class="title">Block Height</div>
|
||||
<div class="value">{{ $recentBlocks[0]->height }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box box-30">
|
||||
<div class="title">Difficulty</div>
|
||||
<div class="value" title="{{ $recentBlocks[0]->difficulty }}">{{ number_format($recentBlocks[0]->difficulty, 2, '.', '') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box box-30">
|
||||
<div class="title">Network</div>
|
||||
<div class="value">{{ $hashRate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box box-20 last">
|
||||
<div class="title">Price</div>
|
||||
<div class="value">{{ $lbcUsdPrice }}</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="recent-blocks">
|
||||
<h3>Recent Blocks</h3>
|
||||
<a class="all-blocks-link" href="/blocks">LBRY Blocks</a>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="left w125">Height</th>
|
||||
<th class="left w125">Age</th>
|
||||
<th class="right w150">Block Size</th>
|
||||
<th class="right w150">Transactions</th>
|
||||
<th class="right w150">Difficulty</th>
|
||||
<th class="left w250 last-cell">Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($recentBlocks as $block)
|
||||
<tr data-height="{{ $block->height }}" data-time="{{ $block->block_time }}">
|
||||
<td><a href="/blocks/{{ $block->height }}">{{ $block->height }}</a></td>
|
||||
<td>{{ \Carbon\Carbon::createFromTimestamp($block->block_time)->diffForHumans() }}</td>
|
||||
<td class="right">{{ round($block->block_size / 1024, 2) . 'KB' }}</td>
|
||||
<td class="right">{{ $block->tx_count }}</td>
|
||||
<td class="right">{{ number_format($block->difficulty, 2, '.', '') }}</td>
|
||||
<td class="last-cell">{{ DateTime::createFromFormat('U', $block->block_time)->format('d M Y H:i:s') . ' UTC' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="recent-claims">
|
||||
<h3>Recent Claims</h3>
|
||||
<a class="claim-explorer-link" href="/claims">Claims Explorer</a>
|
||||
@php($idx = 0)
|
||||
@php($a = ['purple', 'orange', 'blue', 'teal', 'green', 'yellow'])
|
||||
@foreach($recentClaims as $claim)
|
||||
@php($idx++)
|
||||
@php($autoThumbText = $claim->getAutoThumbText())
|
||||
{{--content type--}}
|
||||
@php($ctTag = $claim->getContentTag())
|
||||
<div data-id="{{ $claim->claim_id }}" class="{{ 'claim-box' }}@if($idx == 5){{ ' last' }}@endif">
|
||||
<div class="tags">
|
||||
@if($ctTag)
|
||||
<div class="content-type">{{ strtoupper($ctTag) }}</div>
|
||||
@endif
|
||||
@if($claim->is_nsfw)
|
||||
<div class="nsfw">NSFW</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div data-autothumb="{{ $autoThumbText }}" class="thumbnail {{ $a[mt_rand(0, count($a) - 1)] }}">
|
||||
@if(!$claim->is_nsfw && strlen(trim($claim->thumbnail_url)) > 0)
|
||||
<img src="{{ strip_tags('https://thumbnails.odycdn.com/optimize/s:1280:720/quality:85/plain/'.$claim->thumbnail_url) }}" alt="" />
|
||||
@else
|
||||
<div class="autothumb">{{ $autoThumbText }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="metadata">
|
||||
<div class="title" title="{{ $claim->claim_type == 1 ? $claim->name : ((strlen(trim($claim->title)) > 0) ? $claim->title : '') }}">
|
||||
@if($claim->claim_type == 1){{ $claim->name }}@else{{ '' }}@if(strlen(trim($claim->title)) > 0){{ $claim->title }}@else<em>No Title</em>@endif{{ '' }}@endif
|
||||
</div>
|
||||
<div class="link" title="{{ $claim->getLbryLink() }}"><a href="{{ $claim->getLbryLink() }}">{{ $claim->getLbryLink() }}</a></div>
|
||||
|
||||
<div class="clear"></div>
|
||||
@if($claim->claim_type == 2 && strlen(trim($claim->description)) > 0)
|
||||
<div class="desc">{{ $claim->description }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<a class="tx-link" href="/tx/{{ $claim->transaction_hash_id }}#output-{{ $claim->vout }}" target="_blank">Transaction</a>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
|
@ -1,177 +0,0 @@
|
|||
@extends('layout.default')
|
||||
{{ /**@var \App\Models\Block $block*/'' }}
|
||||
|
||||
@section('title','Realtime Explorer')
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
var updateBlocksInterval = 120000;
|
||||
var updateTxInterval = 30000;
|
||||
|
||||
var updateRealtimeBlocks = function() {
|
||||
var tbody = $('.realtime-blocks .table tbody');
|
||||
|
||||
$.ajax({
|
||||
url: '/api/v1/realtime/blocks',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
var blocks = response.blocks;
|
||||
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||
var block = blocks[i];
|
||||
var prevRow = tbody.find('tr[data-height="' + block.Height + '"]');
|
||||
if (prevRow.length > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var blockTime = moment(block.BlockTime * 1000);
|
||||
tbody.prepend(
|
||||
$('<tr></tr>').attr({'data-height': block.Height, 'data-time': block.BlockTime}).append(
|
||||
$('<td></td>').append(
|
||||
$('<a></a>').attr({'href': ('/blocks/' + block.Height), 'target': '_blank'}).text(block.Height)
|
||||
)
|
||||
).append(
|
||||
$('<td></td>').text(blockTime.fromNow())
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(block.TransactionCount)
|
||||
)
|
||||
);
|
||||
|
||||
// Remove the last row
|
||||
tbody.find('tr:last').remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
tbody.find('tr').each(function() {
|
||||
var row = $(this);
|
||||
var blockTime = moment(row.attr('data-time') * 1000);
|
||||
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var updateRealtimeTransactions = function() {
|
||||
var tbody = $('.realtime-tx .table tbody');
|
||||
|
||||
$.ajax({
|
||||
url: '/api/v1/realtime/tx',
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
var txs = response.txs;
|
||||
for (var i = txs.length - 1; i >= 0; i--) {
|
||||
var tx = txs[i];
|
||||
var prevRow = tbody.find('tr[data-hash="' + tx.Hash + '"]');
|
||||
if (prevRow.length > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var txTime = moment(tx.TxTime * 1000);
|
||||
tbody.prepend(
|
||||
$('<tr></tr>').attr({'data-hash': tx.Hash, 'data-time': tx.TxTime}).append(
|
||||
$('<td></td>').attr({'class': 'w200'}).append(
|
||||
$('<div></div>').append(
|
||||
$('<a></a>').attr({'href': ('/tx/' + tx.Hash), 'target': '_blank'}).text(tx.Hash)
|
||||
)
|
||||
)
|
||||
).append(
|
||||
$('<td></td>').text(txTime.fromNow())
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(tx.InputCount)
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(tx.OutputCount)
|
||||
).append(
|
||||
$('<td></td>').attr({'class': 'right'}).text(Number(tx.Value).toFixed(8) + ' LBC')
|
||||
)
|
||||
);
|
||||
|
||||
// Remove the last row
|
||||
tbody.find('tr:last').remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
tbody.find('tr').each(function() {
|
||||
var row = $(this);
|
||||
var blockTime = moment(row.attr('data-time') * 1000);
|
||||
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
setInterval(updateRealtimeBlocks, updateBlocksInterval);
|
||||
setInterval(updateRealtimeTransactions, updateTxInterval);
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
<div class="realtime-head">
|
||||
<h3>Realtime Explorer</h3>
|
||||
</div>
|
||||
|
||||
<div class="realtime-main">
|
||||
<div class="realtime-blocks">
|
||||
<h3>Recent Blocks</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="left">Height</th>
|
||||
<th class="left">Age</th>
|
||||
<th class="right"># TXs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($blocks as $block)
|
||||
<tr data-height="{{ $block->height }}" data-time="{{ $block->block_time }}">
|
||||
<td><a href="/blocks/{{ $block->height }}" target="_blank">{{ $block->height }}</a></td>
|
||||
<td>{{ \Carbon\Carbon::createFromTimestamp($block->block_time)->diffForHumans() }}</td>
|
||||
<td class="right">{{ $block->tx_count }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="realtime-tx">
|
||||
<h3>Recent Transactions</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w200 left">Hash</th>
|
||||
<th class="left">Time</th>
|
||||
<th class="w100 right">Inputs</th>
|
||||
<th class="w100 right">Outputs</th>
|
||||
<th class="w200 right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($txs AS $tx)
|
||||
<tr data-hash="{{ $tx->hash }}" data-time="{{ $tx->transaction_time }}">
|
||||
<td class="w200"><div><a href="/tx/{{ $tx->hash }}" target="_blank">{{ $tx->hash }}</a></div></td>
|
||||
<td>{{ $tx->created_at->diffForHumans() }}</td>
|
||||
<td class="right">{{ $tx->input_count }}</td>
|
||||
<td class="right">{{ $tx->output_count }}</td>
|
||||
<td class="right">{{ number_format($tx->value, 8, '.', '') }} LBC</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -1,82 +0,0 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title','Stats & Rich List')
|
||||
|
||||
@section('script')
|
||||
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
|
||||
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
|
||||
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
|
||||
<script src="https://www.amcharts.com/lib/3/plugins/responsive/responsive.min.js" type="text/javascript"></script>
|
||||
<script type="text/javascript" src="/js/mining-inflation-chart.js"></script>
|
||||
@endsection
|
||||
|
||||
@section('css')
|
||||
<link rel="stylesheet" href="/css/mining-inflation-chart.css" />
|
||||
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" />
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@include('element.header')
|
||||
|
||||
<div class="stats-head">
|
||||
<h3>LBRY Stats</h3>
|
||||
</div>
|
||||
|
||||
<div class="stats-main">
|
||||
|
||||
<div class="mining-inflation-chart-container">
|
||||
<div class="load-progress inc"></div>
|
||||
<h3>Mining Inflation Chart</h3>
|
||||
<div id="mining-inflation-chart" class="chart"></div>
|
||||
<div id="chart-export" class="btn-chart-export"></div>
|
||||
</div>
|
||||
|
||||
<div class="richlist">
|
||||
<h3>LBRY Rich List (Top 500)</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w50 right">Rank</th>
|
||||
<th class="w300 left">Address</th>
|
||||
<th class="w150 right">Balance (LBC)</th>
|
||||
<th class="w150 right">Balance (USD)</th>
|
||||
<th class="w200 left med-pad-left">First Seen</th>
|
||||
<th class="w200 center">% Top 500</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@php($rank = 0)
|
||||
@foreach($richList AS $item)
|
||||
@php($rank++)
|
||||
<tr>
|
||||
<td class="right topvalign">{{ $rank }}</td>
|
||||
<td class="topvalign"><a href="/address/{{ $item->address }}" target="_blank">{{ $item->address }}</a>
|
||||
@if(in_array($item->address, $lbryAddresses))
|
||||
<span class="lbry-address">
|
||||
<img src="/img/lbry.png" height="18px" width="18px" title="Address owned by LBRY Inc."/>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if(isset($item->Tag) && strlen(trim($item->Tag)) > 0)
|
||||
<div class="tag">
|
||||
@if(strlen(trim($item->TagUrl)) > 0)
|
||||
<a href="{{ $item->TagUrl }}" target="_blank" rel="nofollow">{{ $tiem->Tag }}</a>
|
||||
@else
|
||||
{{ $item->Tag }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="right topvalign">{{ number_format($item->balance, 8, '.', ',') }}</td>
|
||||
<td class="right topvalign">${{ number_format(bcmul($item->balance, $rate, 8), 2, '.', ',') }}</td>
|
||||
<td class="med-pad-left topvalign">{{ $item->first_seen->format('d M Y H:i:s') . ' UTC' }}</td>
|
||||
<td class="w150 center top500-percent-cell"><div class="top500-percent" style="width: {{ $item->MinMaxPercent }}%"></div><div class="text">{{ number_format($item->Top500Percent, 5, '.', '') }}%</div></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
@endsection
|
|
@ -1,188 +0,0 @@
|
|||
@extends('layout.default')
|
||||
|
||||
@section('title','Transaction '.$tx->hash)
|
||||
|
||||
@section('script')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
if (location.hash && (location.hash.indexOf('input-') > -1 || location.hash.indexOf('output-') > -1)) {
|
||||
$(location.hash).addClass('highlighted');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('element.header')
|
||||
|
||||
<div class="tx-head">
|
||||
<h3>LBRY Transaction</h3>
|
||||
<h4>{{ $tx->hash }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="tx-time">
|
||||
<div class="created-time">
|
||||
<h3 title="Represents the time this transaction was created on the explorer">Time Created</h3>
|
||||
<div>{{ $tx->created_at->format('j M Y H:i:s') . ' UTC ' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="conf-time">
|
||||
<h3 title="The time the first confirmation of this transaction happened on the blockchain">Block Time</h3>
|
||||
<div>
|
||||
@if($tx->transaction_time == null || strlen(trim($tx->transaction_time)) == 0)
|
||||
<em>Not yet confirmed</em>
|
||||
@else
|
||||
{{ \DateTime::createFromFormat('U', $tx->transaction_time)->format('j M Y H:i:s') . ' UTC' }}
|
||||
@endif
|
||||
@if($tx->transaction_time > $tx->created_at->getTimestamp())
|
||||
@php
|
||||
$diffSeconds = $tx->transaction_time - $tx->created_at->getTimestamp();
|
||||
if ($diffSeconds <= 60) {
|
||||
echo sprintf(' (+%s second%s)', $diffSeconds, $diffSeconds == 1 ? '' : 's');
|
||||
} else {
|
||||
$diffMinutes = ceil($diffSeconds / 60);
|
||||
echo sprintf(' (+%s minute%s)', $diffMinutes, $diffMinutes == 1 ? '' : 's');
|
||||
}
|
||||
@endphp
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="tx-summary">
|
||||
<div class="box p25">
|
||||
<div class="title">Amount (LBC)</div>
|
||||
<div class="value">{{ \App\Helpers\AmountHelper::format($tx->value) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box p15">
|
||||
<div class="title">Block Height</div>
|
||||
@if(!isset($tx->block_hash_id) || strlen(trim($tx->block_hash_id)) === 0)
|
||||
<div class="value" title="Unconfirmed">Unconf.</div>
|
||||
@else
|
||||
<div class="value" title="{{ $tx->block_hash_id }}"><a href="/blocks/{{ $block->height }}">{{ $block->height }}</a></div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="box p15">
|
||||
<div class="title">Confirmations</div>
|
||||
<div class="value">{{ $confirmations }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box p15">
|
||||
<div class="title">Size (bytes)</div>
|
||||
<div class="value">{{ number_format($tx->transaction_size, 0, '', ',') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box p15">
|
||||
<div class="title">Inputs</div>
|
||||
<div class="value">{{ $tx->input_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="box p15 last">
|
||||
<div class="title">Outputs</div>
|
||||
<div class="value">{{ $tx->output_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div class="tx-details">
|
||||
<h3>Details</h3>
|
||||
<div class="tx-details-layout">
|
||||
<div class="inputs">
|
||||
<div class="subtitle">{{ $tx->input_count }} input{{ $tx->input_count === 1 ? '' : 's' }}</div>
|
||||
@php($setAddressIds = [])
|
||||
@foreach($inputs as $in)
|
||||
<div id="input-{{ $in->id }}" class="{{ 'input ' }}@if(isset($in->input_addresses) && count($in->input_addresses) > 0 && $in->input_addresses[0]->address == $sourceAddress){{ 'is-source' }}@endif">
|
||||
@if($in->is_coinbase)
|
||||
<div>Block Reward (New Coins)</div>
|
||||
@else
|
||||
@if(strlen(trim($in->value)) == 0)
|
||||
<div>Incomplete data</div>
|
||||
@else
|
||||
@php($addr = $in->input_addresses[0])
|
||||
|
||||
@if(!isset($setAddressIds[$addr->address]))
|
||||
@php($setAddressIds[$addr->address] = 1)
|
||||
<a id="{{ $addr->address }}"></a>
|
||||
@endif
|
||||
|
||||
<div><span class="value">{{ \App\Helpers\AmountHelper::format($in->value) }} LBC</span> from</div>
|
||||
<div class="address">
|
||||
<a href="/address/{{ $addr->address }}">{{ $addr->address }}</a>
|
||||
(<a class="output-link" href="/tx/{{ $in->prevout_hash }}#output-{{ $in->prevout_n }}">output</a>)
|
||||
@if(isset($addr->Tag) && strlen(trim($addr->Tag)) > 0)
|
||||
<div class="tag">
|
||||
@if(strlen(trim($addr->TagUrl)) > 0)
|
||||
<a href="{{ $addr->TagUrl }}" target="_blank" rel="nofollow">{{ $addr->Tag }}</a>
|
||||
@else
|
||||
{{ $addr->Tag }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="divider">
|
||||
<img src="{{ '/img/right-arrow.png' }}" alt="->" />
|
||||
</div>
|
||||
|
||||
<div class="outputs">
|
||||
<div class="subtitle">
|
||||
{{ $tx->output_count }} output{{ $tx->output_count === 1 ? '' : 's' }}
|
||||
@if($fee > 0)
|
||||
<span class="fee"><span class="label">Fee</span> <span class="value">{{ \App\Helpers\AmountHelper::format($fee) }} LBC</span></span>
|
||||
@endif
|
||||
</div>
|
||||
@foreach($outputs as $out)
|
||||
<div id="output-{{ $out->vout }}" class="{{ 'output ' }}@if(isset($out->output_addresses) && count($out->output_addresses) > 0 && $out->output_addresses[0]->address == $sourceAddress){{ 'is-source' }}@endif">
|
||||
<div class="labels">
|
||||
@if($out->Claim && ($out->IsClaim or $out->IsSupportClaim or $out->IsUpdateClaim))<a class="view-claim" href="{{ $out->Claim->getExplorerLink() }}">View</a>@endif
|
||||
@if($out->IsSupportClaim)<div class="support">SUPPORT</div>@endif
|
||||
@if($out->IsUpdateClaim)<div class="update">UPDATE</div>@endif
|
||||
@if($out->IsClaim)<div class="claim">CLAIM</div>@endif
|
||||
</div>
|
||||
@if(strlen(trim($out->value)) == 0)
|
||||
<div>Incomplete data</div>
|
||||
@else
|
||||
@php($addr = $out->output_addresses[0])
|
||||
|
||||
@if(!isset($setAddressIds[$addr->address]))
|
||||
@php($setAddressIds[$addr->address] = 1)
|
||||
<a id="{{ $addr->address }}"></a>
|
||||
@endif
|
||||
|
||||
<div><span class="value">{{ \App\Helpers\AmountHelper::format($out->value) }} LBC</span> to</div>
|
||||
<div class="address"><a href="/address/{{ $addr->address }}">{{ $addr->address }}</a>
|
||||
@if($out->is_spent)
|
||||
<a href="/tx/@if(isset($out->spend_input_id)){{ $out->spend_input_hash }}@endif#input-@if(isset($out->spend_input_id)){{ $out->spend_input_id }}@endif">spent</a>
|
||||
@else
|
||||
{{ '(unspent)' }}
|
||||
@endif
|
||||
|
||||
@if(isset($addr->Tag) && strlen(trim($addr->Tag)) > 0)
|
||||
<div class="tag">
|
||||
@if(strlen(trim($addr->TagUrl)) > 0)
|
||||
<a href="{{ $addr->TagUrl }}" target="_blank" rel="nofollow">{{ $addr->Tag }}</a>
|
||||
@else
|
||||
{{ $addr->Tag }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
use App\Http\Controllers\ClaimsController;
|
||||
use App\Http\Controllers\MainController;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('/v1')->group(static function(){
|
||||
Route::get('/address/{addr}/tag', [MainController::class,'apiaddrtag'])->where('addr','[A-Za-z0-9,]+');
|
||||
Route::get('/address/{addr}/utxo', [MainController::class,'apiaddrutxo'])->where('addr','[A-Za-z0-9,]+');
|
||||
Route::get('/address/{addr}/balance', [MainController::class,'apiaddrbalance'])->where('addr','[A-Za-z0-9,]+');
|
||||
Route::get('/address/{addr}/transactions', [MainController::class,'apiaddrtx'])->where('addr','[A-Za-z0-9,]+');
|
||||
|
||||
Route::get('/charts/blocksize/{period}', [MainController::class,'apiblocksize'])->where('period','[012346789dhy]+');
|
||||
|
||||
Route::get('/realtime/blocks', [MainController::class,'apirealtimeblocks']);
|
||||
Route::get('/realtime/tx', [MainController::class,'apirealtimetx']);
|
||||
Route::get('/recentblocks', [MainController::class,'apirecentblocks']);
|
||||
Route::get('/status', [MainController::class,'apistatus']);
|
||||
Route::get('/supply', [MainController::class,'apiutxosupply']);
|
||||
Route::get('/recenttxs', [MainController::class,'apirecenttxs']);
|
||||
|
||||
Route::get('/claims/browse', [ClaimsController::class,'apibrowse']);
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
use App\Http\Controllers\MainController;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('/')->group(static function(): void{
|
||||
Route::get('/', [MainController::class,'index']);
|
||||
Route::get('/address/{address?}', [MainController::class,'address'])->where('address','.*');
|
||||
Route::get('/blocks/{height?}', [MainController::class,'blocks'])->where('height','.*');
|
||||
Route::get('/claims/{claim?}', [MainController::class,'claims'])->where('claim','.*');
|
||||
Route::get('/find', [MainController::class,'find']);
|
||||
Route::get('/realtime', [MainController::class,'realtime']);
|
||||
Route::get('/stats', [MainController::class,'stats']);
|
||||
Route::get('/tx/{transaction?}', [MainController::class,'tx'])->where('transaction','.*');
|
||||
Route::get('/qr/{data?}', [MainController::class,'qr'])->where('data','.*');
|
||||
});
|
52
sql/dbg.sql
Normal file
52
sql/dbg.sql
Normal file
|
@ -0,0 +1,52 @@
|
|||
DELIMITER //
|
||||
|
||||
CREATE PROCEDURE DeleteBlock (
|
||||
IN BlockId BIGINT
|
||||
)
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
|
||||
DELETE FROM InputsAddresses WHERE InputId IN (
|
||||
SELECT Id FROM Inputs WHERE TransactionId IN (
|
||||
SELECT Id FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
DELETE FROM OutputsAddresses WHERE OutputId IN (
|
||||
SELECT Id FROM Outputs WHERE TransactionId IN (
|
||||
SELECT Id FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
DELETE FROM Inputs WHERE TransactionId IN (
|
||||
SELECT Id FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
)
|
||||
);
|
||||
|
||||
DELETE FROM Outputs WHERE TransactionId IN (
|
||||
SELECT Id FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
)
|
||||
);
|
||||
|
||||
DELETE FROM TransactionsAddresses WHERE TransactionId IN (
|
||||
SELECT Id FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
)
|
||||
);
|
||||
|
||||
DELETE FROM Transactions WHERE BlockHash IN (
|
||||
SELECT Hash FROM Blocks WHERE Id = BlockId
|
||||
);
|
||||
|
||||
DELETE FROM Blocks WHERE Id = BlockId;
|
||||
|
||||
COMMIT;
|
||||
END//
|
||||
|
||||
DELIMITER ;
|
18
sql/lbryexplorer.aux.sql
Normal file
18
sql/lbryexplorer.aux.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
-- Address Tagging TX tracking
|
||||
CREATE TABLE `TagAddressRequests`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`Address` VARCHAR(35) NOT NULL,
|
||||
`VerificationAmount` DECIMAL(18,8) NOT NULL,
|
||||
`Tag` VARCHAR(30) NOT NULL,
|
||||
`TagUrl` VARCHAR(200) NULL,
|
||||
`IsVerified` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_TagAddressRequest` (`Id`),
|
||||
UNIQUE KEY `Idx_TagAddressRequestId` (`Address`, `VerificationAmount`),
|
||||
INDEX `Idx_TagAddressRequestVerificationAmount` (`VerificationAmount`),
|
||||
INDEX `Idx_TagAddressRequestAddress` (`Address`),
|
||||
INDEX `Idx_TagAddressRequestCreated` (`Created`),
|
||||
INDEX `Idx_TagAddressRequestModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
235
sql/lbryexplorer.ddl.sql
Normal file
235
sql/lbryexplorer.ddl.sql
Normal file
|
@ -0,0 +1,235 @@
|
|||
l--DROP DATABASE IF EXISTS lbry;
|
||||
CREATE DATABASE lbry DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
USE lbry;
|
||||
|
||||
CREATE TABLE `Blocks`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
|
||||
`Bits` VARCHAR(20) NOT NULL,
|
||||
`Chainwork` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`Confirmations` INTEGER UNSIGNED NOT NULL,
|
||||
`Difficulty` DECIMAL(18,8) NOT NULL,
|
||||
`Hash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL ,
|
||||
`Height` BIGINT UNSIGNED NOT NULL,
|
||||
`MedianTime` BIGINT UNSIGNED NOT NULL,
|
||||
`MerkleRoot` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`NameClaimRoot` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`Nonce` BIGINT UNSIGNED NOT NULL,
|
||||
`PreviousBlockHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`NextBlockHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`BlockSize` BIGINT UNSIGNED NOT NULL,
|
||||
`Target` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`BlockTime` BIGINT UNSIGNED NOT NULL,
|
||||
`Version` BIGINT UNSIGNED NOT NULL,
|
||||
`VersionHex` VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`TransactionHashes` TEXT,
|
||||
`TransactionsProcessed` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
|
||||
PRIMARY KEY `PK_Block` (`Id`),
|
||||
UNIQUE KEY `Idx_BlockHash` (`Hash`),
|
||||
CONSTRAINT `Cnt_TransactionHashesValidJson` CHECK(`TransactionHashes` IS NULL OR JSON_VALID(`TransactionHashes`)),
|
||||
INDEX `Idx_BlockHeight` (`Height`),
|
||||
INDEX `Idx_BlockTime` (`BlockTime`),
|
||||
INDEX `Idx_MedianTime` (`MedianTime`),
|
||||
INDEX `Idx_PreviousBlockHash` (`PreviousBlockHash`),
|
||||
INDEX `Idx_BlockCreated` (`Created`),
|
||||
INDEX `Idx_BlockModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `Transactions`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`BlockHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`InputCount` INTEGER UNSIGNED NOT NULL,
|
||||
`OutputCount` INTEGER UNSIGNED NOT NULL,
|
||||
`Value` DECIMAL(18,8) NOT NULL,
|
||||
`Fee` DECIMAL(18,8) DEFAULT 0 NOT NULL,
|
||||
`TransactionTime` BIGINT UNSIGNED,
|
||||
`TransactionSize` BIGINT UNSIGNED NOT NULL,
|
||||
`Hash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`Version` INTEGER NOT NULL,
|
||||
`LockTime` INTEGER UNSIGNED NOT NULL,
|
||||
`Raw` TEXT,
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
`CreatedTime` INTEGER UNSIGNED DEFAULT UNIX_TIMESTAMP() NOT NULL,
|
||||
PRIMARY KEY `PK_Transaction` (`Id`),
|
||||
FOREIGN KEY `FK_TransactionBlockHash` (`BlockHash`) REFERENCES `Blocks` (`Hash`),
|
||||
UNIQUE KEY `Idx_TransactionHash` (`Hash`),
|
||||
INDEX `Idx_TransactionTime` (`TransactionTime`),
|
||||
INDEX `Idx_TransactionCreatedTime` (`CreatedTime`),
|
||||
INDEX `Idx_TransactionCreated` (`Created`),
|
||||
INDEX `Idx_TransactionModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `Addresses`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`Address` VARCHAR(40) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`FirstSeen` DATETIME,
|
||||
`TotalReceived` DECIMAL(18,8) DEFAULT 0 NOT NULL,
|
||||
`TotalSent` DECIMAL(18,8) DEFAULT 0 NOT NULL,
|
||||
`Balance` DECIMAL(18,8) AS (`TotalReceived` - `TotalSent`) PERSISTENT,
|
||||
`Tag` VARCHAR(30) NOT NULL,
|
||||
`TagUrl` VARCHAR(200),
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_Address` (`Id`),
|
||||
UNIQUE KEY `Idx_AddressAddress` (`Address`),
|
||||
UNIQUE KEY `Idx_AddressTag` (`Tag`),
|
||||
INDEX `Idx_AddressTotalReceived` (`TotalReceived`),
|
||||
INDEX `Idx_AddressTotalSent` (`TotalSent`),
|
||||
INDEX `Idx_AddressBalance` (`Balance`),
|
||||
INDEX `Idx_AddressCreated` (`Created`),
|
||||
INDEX `Idx_AddressModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `Inputs`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`TransactionId` BIGINT UNSIGNED NOT NULL,
|
||||
`TransactionHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`AddressId` BIGINT UNSIGNED,
|
||||
`IsCoinbase` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
`Coinbase` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`PrevoutHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`PrevoutN` INTEGER UNSIGNED,
|
||||
`PrevoutSpendUpdated` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
`Sequence` INTEGER UNSIGNED,
|
||||
`Value` DECIMAL(18,8),
|
||||
`ScriptSigAsm` TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`ScriptSigHex` TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_Input` (`Id`),
|
||||
FOREIGN KEY `FK_InputAddress` (`AddressId`) REFERENCES `Addresses` (`Id`),
|
||||
FOREIGN KEY `FK_InputTransaction` (`TransactionId`) REFERENCES `Transactions` (`Id`),
|
||||
INDEX `Idx_InputValue` (`Value`),
|
||||
INDEX `Idx_PrevoutHash` (`PrevoutHash`),
|
||||
INDEX `Idx_InputCreated` (`Created`),
|
||||
INDEX `Idx_InputModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `InputsAddresses`
|
||||
(
|
||||
`InputId` BIGINT UNSIGNED NOT NULL,
|
||||
`AddressId` BIGINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY `PK_InputAddress` (`InputId`, `AddressId`),
|
||||
FOREIGN KEY `Idx_InputsAddressesInput` (`InputId`) REFERENCES `Inputs` (`Id`),
|
||||
FOREIGN KEY `Idx_InputsAddressesAddress` (`AddressId`) REFERENCES `Addresses` (`Id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `Outputs`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`TransactionId` BIGINT UNSIGNED NOT NULL,
|
||||
`Value` DECIMAL(18,8),
|
||||
`Vout` INTEGER UNSIGNED,
|
||||
`Type` VARCHAR(20) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`ScriptPubKeyAsm` TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`ScriptPubKeyHex` TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`RequiredSignatures` INTEGER UNSIGNED,
|
||||
`Hash160` VARCHAR(50) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`Addresses` TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`IsSpent` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
`SpentByInputId` BIGINT UNSIGNED,
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_Output` (`Id`),
|
||||
FOREIGN KEY `FK_OutputTransaction` (`TransactionId`) REFERENCES `Transactions` (`Id`),
|
||||
FOREIGN KEY `FK_OutputSpentByInput` (`SpentByInputId`) REFERENCES `Inputs` (`Id`),
|
||||
CONSTRAINT `Cnt_AddressesValidJson` CHECK(`Addresses` IS NULL OR JSON_VALID(`Addresses`)),
|
||||
INDEX `Idx_OutputValue` (`Value`),
|
||||
INDEX `Idx_OuptutCreated` (`Created`),
|
||||
INDEX `Idx_OutputModified` (`Modified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `OutputsAddresses`
|
||||
(
|
||||
`OutputId` BIGINT UNSIGNED NOT NULL,
|
||||
`AddressId` BIGINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY `PK_OutputAddress` (`OutputId`, `AddressId`),
|
||||
FOREIGN KEY `Idx_OutputsAddressesOutput` (`OutputId`) REFERENCES `Outputs` (`Id`),
|
||||
FOREIGN KEY `Idx_OutputsAddressesAddress` (`AddressId`) REFERENCES `Addresses` (`Id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `TransactionsAddresses`
|
||||
(
|
||||
`TransactionId` BIGINT UNSIGNED NOT NULL,
|
||||
`AddressId` BIGINT UNSIGNED NOT NULL,
|
||||
`DebitAmount` DECIMAL(18,8) DEFAULT 0 NOT NULL COMMENT 'Sum of the inputs to this address for the tx',
|
||||
`CreditAmount` DECIMAL(18,8) DEFAULT 0 NOT NULL COMMENT 'Sum of the outputs to this address for the tx',
|
||||
`LatestTransactionTime` DATETIME DEFAULT UTC_TIMESTAMP() NOT NULL,
|
||||
PRIMARY KEY `PK_TransactionAddress` (`TransactionId`, `AddressId`),
|
||||
FOREIGN KEY `Idx_TransactionsAddressesTransaction` (`TransactionId`) REFERENCES `Transactions` (`Id`),
|
||||
FOREIGN KEY `Idx_TransactionsAddressesAddress` (`AddressId`) REFERENCES `Addresses` (`Id`),
|
||||
INDEX `Idx_TransactionsAddressesLatestTransactionTime` (`LatestTransactionTime`),
|
||||
INDEX `Idx_TransactionsAddressesDebit` (`DebitAmount`),
|
||||
INDEX `Idx_TransactionsAddressesCredit` (`CreditAmount`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `Claims`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`TransactionHash` VARCHAR(70) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`Vout` INTEGER UNSIGNED NOT NULL,
|
||||
`Name` VARCHAR(1024) NOT NULL,
|
||||
`ClaimId` CHAR(40) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
`ClaimType` TINYINT(1) NOT NULL, -- 1 - CertificateType, 2 - StreamType
|
||||
`PublisherId` CHAR(40) CHARACTER SET latin1 COLLATE latin1_general_ci COMMENT 'references a ClaimId with CertificateType',
|
||||
`PublisherSig` VARCHAR(200) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`Certificate` TEXT,
|
||||
`TransactionTime` INTEGER UNSIGNED,
|
||||
`Version` VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
|
||||
|
||||
-- Additional fields for easy indexing of stream types
|
||||
`Author` VARCHAR(512),
|
||||
`Description` MEDIUMTEXT,
|
||||
`ContentType` VARCHAR(162) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`IsNSFW` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
`Language` VARCHAR(20) CHARACTER SET latin1 COLLATE latin1_general_ci,
|
||||
`ThumbnailUrl` TEXT,
|
||||
`Title` TEXT,
|
||||
`Fee` DECIMAL(18,8) DEFAULT 0 NOT NULL,
|
||||
`FeeCurrency` CHAR(3),
|
||||
`IsFiltered` TINYINT(1) DEFAULT 0 NOT NULL,
|
||||
|
||||
`Created` DATETIME NOT NULL,
|
||||
`Modified` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_Claim` (`Id`),
|
||||
FOREIGN KEY `FK_ClaimTransaction` (`TransactionHash`) REFERENCES `Transactions` (`Hash`),
|
||||
FOREIGN KEY `FK_ClaimPublisher` (`PublisherId`) REFERENCES `Claims` (`ClaimId`),
|
||||
UNIQUE KEY `Idx_ClaimUnique` (`TransactionHash`, `Vout`, `ClaimId`),
|
||||
CONSTRAINT `Cnt_ClaimCertificate` CHECK(`Certificate` IS NULL OR JSON_VALID(`Certificate`)), -- certificate type
|
||||
INDEX `Idx_Claim` (`ClaimId`),
|
||||
INDEX `Idx_ClaimTransactionTime` (`TransactionTime`),
|
||||
INDEX `Idx_ClaimCreated` (`Created`),
|
||||
INDEX `Idx_ClaimModified` (`Modified`),
|
||||
|
||||
INDEX `Idx_ClaimAuthor` (`Author`(191)),
|
||||
INDEX `Idx_ClaimContentType` (`ContentType`),
|
||||
INDEX `Idx_ClaimLanguage` (`Language`),
|
||||
INDEX `Idx_ClaimTitle` (`Title`(191))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `ClaimStreams`
|
||||
(
|
||||
`Id` BIGINT UNSIGNED NOT NULL,
|
||||
`Stream` MEDIUMTEXT NOT NULL,
|
||||
PRIMARY KEY `PK_ClaimStream` (`Id`),
|
||||
FOREIGN KEY `PK_ClaimStreamClaim` (`Id`) REFERENCES `Claims` (`Id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
||||
|
||||
CREATE TABLE `PriceHistory`
|
||||
(
|
||||
`Id` SERIAL,
|
||||
`BTC` DECIMAL(18,8) DEFAULT 0 NOT NULL,
|
||||
`USD` DECIMAL(18,2) DEFAULT 0 NOT NULL,
|
||||
`Created` DATETIME NOT NULL,
|
||||
PRIMARY KEY `PK_PriceHistory` (`Id`),
|
||||
UNIQUE KEY `Idx_PriceHistoryCreated` (`Created`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4;
|
52
src/Application.php
Normal file
52
src/Application.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.3.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Error\Middleware\ErrorHandlerMiddleware;
|
||||
use Cake\Http\BaseApplication;
|
||||
use Cake\Routing\Middleware\AssetMiddleware;
|
||||
use Cake\Routing\Middleware\RoutingMiddleware;
|
||||
|
||||
/**
|
||||
* Application setup class.
|
||||
*
|
||||
* This defines the bootstrapping logic and middleware layers you
|
||||
* want to use in your application.
|
||||
*/
|
||||
class Application extends BaseApplication
|
||||
{
|
||||
/**
|
||||
* Setup the middleware your application will use.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middleware The middleware queue to setup.
|
||||
* @return \Cake\Http\MiddlewareQueue The updated middleware.
|
||||
*/
|
||||
public function middleware($middleware)
|
||||
{
|
||||
$middleware
|
||||
// Catch any exceptions in the lower layers,
|
||||
// and make an error page/response
|
||||
->add(ErrorHandlerMiddleware::class)
|
||||
|
||||
// Handle plugin/theme assets like CakePHP normally does.
|
||||
->add(AssetMiddleware::class)
|
||||
|
||||
// Apply routing
|
||||
->add(RoutingMiddleware::class);
|
||||
|
||||
return $middleware;
|
||||
}
|
||||
}
|
195
src/Console/Installer.php
Normal file
195
src/Console/Installer.php
Normal file
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Console;
|
||||
|
||||
use Cake\Utility\Security;
|
||||
use Composer\Script\Event;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Provides installation hooks for when this application is installed via
|
||||
* composer. Customize this class to suit your needs.
|
||||
*/
|
||||
class Installer
|
||||
{
|
||||
|
||||
/**
|
||||
* Does some routine installation tasks so people don't have to.
|
||||
*
|
||||
* @param \Composer\Script\Event $event The composer event object.
|
||||
* @throws \Exception Exception raised by validator.
|
||||
* @return void
|
||||
*/
|
||||
public static function postInstall(Event $event)
|
||||
{
|
||||
$io = $event->getIO();
|
||||
|
||||
$rootDir = dirname(dirname(__DIR__));
|
||||
|
||||
static::createAppConfig($rootDir, $io);
|
||||
static::createWritableDirectories($rootDir, $io);
|
||||
|
||||
// ask if the permissions should be changed
|
||||
if ($io->isInteractive()) {
|
||||
$validator = function ($arg) {
|
||||
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
|
||||
return $arg;
|
||||
}
|
||||
throw new Exception('This is not a valid answer. Please choose Y or n.');
|
||||
};
|
||||
$setFolderPermissions = $io->askAndValidate(
|
||||
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
|
||||
$validator,
|
||||
10,
|
||||
'Y'
|
||||
);
|
||||
|
||||
if (in_array($setFolderPermissions, ['Y', 'y'])) {
|
||||
static::setFolderPermissions($rootDir, $io);
|
||||
}
|
||||
} else {
|
||||
static::setFolderPermissions($rootDir, $io);
|
||||
}
|
||||
|
||||
static::setSecuritySalt($rootDir, $io);
|
||||
|
||||
if (class_exists('\Cake\Codeception\Console\Installer')) {
|
||||
\Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the config/app.php file if it does not exist.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function createAppConfig($dir, $io)
|
||||
{
|
||||
$appConfig = $dir . '/config/app.php';
|
||||
$defaultConfig = $dir . '/config/app.default.php';
|
||||
if (!file_exists($appConfig)) {
|
||||
copy($defaultConfig, $appConfig);
|
||||
$io->write('Created `config/app.php` file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `logs` and `tmp` directories.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function createWritableDirectories($dir, $io)
|
||||
{
|
||||
$paths = [
|
||||
'logs',
|
||||
'tmp',
|
||||
'tmp/cache',
|
||||
'tmp/cache/models',
|
||||
'tmp/cache/persistent',
|
||||
'tmp/cache/views',
|
||||
'tmp/sessions',
|
||||
'tmp/tests'
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$path = $dir . '/' . $path;
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
$io->write('Created `' . $path . '` directory');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set globally writable permissions on the "tmp" and "logs" directory.
|
||||
*
|
||||
* This is not the most secure default, but it gets people up and running quickly.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function setFolderPermissions($dir, $io)
|
||||
{
|
||||
// Change the permissions on a path and output the results.
|
||||
$changePerms = function ($path, $perms, $io) {
|
||||
// Get permission bits from stat(2) result.
|
||||
$currentPerms = fileperms($path) & 0777;
|
||||
if (($currentPerms & $perms) == $perms) {
|
||||
return;
|
||||
}
|
||||
|
||||
$res = chmod($path, $currentPerms | $perms);
|
||||
if ($res) {
|
||||
$io->write('Permissions set on ' . $path);
|
||||
} else {
|
||||
$io->write('Failed to set permissions on ' . $path);
|
||||
}
|
||||
};
|
||||
|
||||
$walker = function ($dir, $perms, $io) use (&$walker, $changePerms) {
|
||||
$files = array_diff(scandir($dir), ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
$path = $dir . '/' . $file;
|
||||
|
||||
if (!is_dir($path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changePerms($path, $perms, $io);
|
||||
$walker($path, $perms, $io);
|
||||
}
|
||||
};
|
||||
|
||||
$worldWritable = bindec('0000000111');
|
||||
$walker($dir . '/tmp', $worldWritable, $io);
|
||||
$changePerms($dir . '/tmp', $worldWritable, $io);
|
||||
$changePerms($dir . '/logs', $worldWritable, $io);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the security.salt value in the application's config file.
|
||||
*
|
||||
* @param string $dir The application's root directory.
|
||||
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||
* @return void
|
||||
*/
|
||||
public static function setSecuritySalt($dir, $io)
|
||||
{
|
||||
$config = $dir . '/config/app.php';
|
||||
$content = file_get_contents($config);
|
||||
|
||||
$newKey = hash('sha256', Security::randomBytes(64));
|
||||
$content = str_replace('__SALT__', $newKey, $content, $count);
|
||||
|
||||
if ($count == 0) {
|
||||
$io->write('No Security.salt placeholder to replace.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = file_put_contents($config, $content);
|
||||
if ($result) {
|
||||
$io->write('Updated Security.salt value in config/app.php');
|
||||
|
||||
return;
|
||||
}
|
||||
$io->write('Unable to update Security.salt value.');
|
||||
}
|
||||
}
|
80
src/Controller/AppController.php
Normal file
80
src/Controller/AppController.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.2.9
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Controller\Controller;
|
||||
use Cake\Event\Event;
|
||||
|
||||
/**
|
||||
* Application Controller
|
||||
*
|
||||
* Add your application-wide methods in the class below, your controllers
|
||||
* will inherit them.
|
||||
*
|
||||
* @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller
|
||||
*/
|
||||
class AppController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* Use this method to add common initialization code like loading components.
|
||||
*
|
||||
* e.g. `$this->loadComponent('Security');`
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->loadComponent('RequestHandler');
|
||||
$this->loadComponent('Flash');
|
||||
|
||||
/*
|
||||
* Enable the following components for recommended CakePHP security settings.
|
||||
* see http://book.cakephp.org/3.0/en/controllers/components/security.html
|
||||
*/
|
||||
//$this->loadComponent('Security');
|
||||
//$this->loadComponent('Csrf');
|
||||
}
|
||||
|
||||
/**
|
||||
* Before render callback.
|
||||
*
|
||||
* @param \Cake\Event\Event $event The beforeRender event.
|
||||
* @return \Cake\Network\Response|null|void
|
||||
*/
|
||||
public function beforeRender(Event $event)
|
||||
{
|
||||
if (!array_key_exists('_serialize', $this->viewVars) &&
|
||||
in_array($this->response->type(), ['application/json', 'application/xml'])
|
||||
) {
|
||||
$this->set('_serialize', true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function _jsonResponse($object = [], $statusCode = null)
|
||||
{
|
||||
$this->response->statusCode($statusCode);
|
||||
$this->response->type('json');
|
||||
$this->response->body(json_encode($object));
|
||||
}
|
||||
|
||||
protected function _jsonError($message, $statusCode = null) {
|
||||
return $this->_jsonResponse(['error' => true, 'message' => $message], $statusCode);
|
||||
}
|
||||
}
|
61
src/Controller/ClaimsController.php
Normal file
61
src/Controller/ClaimsController.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Log\Log;
|
||||
|
||||
class ClaimsController extends AppController {
|
||||
public function apibrowse() {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Claims');
|
||||
|
||||
$pageLimit = 48;
|
||||
$beforeId = intval($this->request->query('before'));
|
||||
$afterId = intval($this->request->query('after'));
|
||||
$sort = trim($this->request->query('sort'));
|
||||
$nsfw = trim($this->request->query('nsfw'));
|
||||
switch ($sort) {
|
||||
case 'popular':
|
||||
// TODO: sort by upvote/downvote score
|
||||
break;
|
||||
case 'random':
|
||||
$order = ['RAND()' => 'ASC'];
|
||||
break;
|
||||
case 'oldest':
|
||||
$order = ['Claims.Created' => 'ASC'];
|
||||
break;
|
||||
case 'newest':
|
||||
default:
|
||||
$order = ['Claims.Created' => 'DESC'];
|
||||
break;
|
||||
}
|
||||
|
||||
$conn = ConnectionManager::get('default');
|
||||
$stmt = $conn->execute('SELECT COUNT(Id) AS Total FROM Claims WHERE ThumbnailUrl IS NOT NULL AND LENGTH(TRIM(ThumbnailUrl)) > 0');
|
||||
$count = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
$numClaims = $count->Total;
|
||||
|
||||
if ($beforeId < 0) {
|
||||
$beforeId = 0;
|
||||
}
|
||||
|
||||
$conditions = ['Claims.ThumbnailUrl IS NOT' => null, 'LENGTH(TRIM(Claims.ThumbnailUrl)) >' => 0, 'Claims.IsFiltered <>' => 1];
|
||||
if ($afterId > 0) {
|
||||
$conditions['Claims.Id >'] = $afterId;
|
||||
} else if ($beforeId) {
|
||||
$conditions['Claims.Id <'] = $beforeId;
|
||||
}
|
||||
|
||||
if ($nsfw !== 'true') {
|
||||
$conditions['Claims.IsNSFW <>'] = 1;
|
||||
}
|
||||
|
||||
$claims = $this->Claims->find()->contain(['Stream', 'Publisher' => ['fields' => ['Name']]])->distinct(['Claims.ClaimId'])->where($conditions)->
|
||||
limit($pageLimit)->order($order)->toArray();
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'claims' => $claims, 'total' => (int) $numClaims]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
68
src/Controller/ErrorController.php
Normal file
68
src/Controller/ErrorController.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.3.4
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Event\Event;
|
||||
|
||||
/**
|
||||
* Error Handling Controller
|
||||
*
|
||||
* Controller used by ExceptionRenderer to render error responses.
|
||||
*/
|
||||
class ErrorController extends AppController
|
||||
{
|
||||
/**
|
||||
* Initialization hook method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->loadComponent('RequestHandler');
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeFilter callback.
|
||||
*
|
||||
* @param \Cake\Event\Event $event Event.
|
||||
* @return \Cake\Network\Response|null|void
|
||||
*/
|
||||
public function beforeFilter(Event $event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeRender callback.
|
||||
*
|
||||
* @param \Cake\Event\Event $event Event.
|
||||
* @return \Cake\Network\Response|null|void
|
||||
*/
|
||||
public function beforeRender(Event $event)
|
||||
{
|
||||
parent::beforeRender($event);
|
||||
|
||||
$this->viewBuilder()->setTemplatePath('Error');
|
||||
}
|
||||
|
||||
/**
|
||||
* afterFilter callback.
|
||||
*
|
||||
* @param \Cake\Event\Event $event Event.
|
||||
* @return \Cake\Network\Response|null|void
|
||||
*/
|
||||
public function afterFilter(Event $event)
|
||||
{
|
||||
}
|
||||
}
|
922
src/Controller/MainController.php
Normal file
922
src/Controller/MainController.php
Normal file
|
@ -0,0 +1,922 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Mdanter\Ecc\EccFactory;
|
||||
use Mdanter\Ecc\Crypto\Signature\Signer;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
|
||||
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Log\Log;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel;
|
||||
use Endroid\QrCode\LabelAlignment;
|
||||
use Endroid\QrCode\QrCode;
|
||||
|
||||
class MainController extends AppController {
|
||||
|
||||
const rpcurl = 'http://lrpc:lrpc@127.0.0.1:9245';
|
||||
|
||||
const lbcPriceKey = 'lbc.price';
|
||||
|
||||
const bittrexMarketUrl = 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LBC';
|
||||
|
||||
const blockchainTickerUrl = 'https://blockchain.info/ticker';
|
||||
|
||||
const tagReceiptAddress = 'bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp';
|
||||
|
||||
protected $redis;
|
||||
|
||||
public function initialize() {
|
||||
parent::initialize();
|
||||
$this->redis = new \Predis\Client('tcp://127.0.0.1:6379');
|
||||
try {
|
||||
$this->redis->info('mem');
|
||||
} catch (\Predis\Connection\ConnectionException $e) {
|
||||
$this->redis = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getLatestPrice() {
|
||||
$now = new \DateTime('now', new \DateTimeZone('UTC'));
|
||||
$priceInfo = new \stdClass();
|
||||
$priceInfo->time = $now->format('c');
|
||||
|
||||
$shouldRefreshPrice = false;
|
||||
if (!$this->redis) {
|
||||
$shouldRefreshPrice = true;
|
||||
} else {
|
||||
if (!$this->redis->exists(self::lbcPriceKey)) {
|
||||
$shouldRefreshPrice = true;
|
||||
} else {
|
||||
$priceInfo = json_decode($this->redis->get(self::lbcPriceKey));
|
||||
$lastPriceDt = new \DateTime($priceInfo->time);
|
||||
$diff = $now->diff($lastPriceDt);
|
||||
$diffMinutes = $diff->i;
|
||||
if ($diffMinutes >= 15 || $priceInfo->price == 0) { // 15 minutes (or if the price is 0)
|
||||
$shouldRefreshPrice = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($shouldRefreshPrice) {
|
||||
$btrxjson = json_decode(self::curl_get(self::bittrexMarketUrl));
|
||||
$blckjson = json_decode(self::curl_get(self::blockchainTickerUrl));
|
||||
|
||||
if ($btrxjson->success) {
|
||||
$onelbc = $btrxjson->result->Bid;
|
||||
$lbcPrice = 0;
|
||||
if (isset($blckjson->USD)) {
|
||||
$lbcPrice = $onelbc * $blckjson->USD->buy;
|
||||
if ($lbcPrice > 0) {
|
||||
$priceInfo->price = number_format($lbcPrice, 2, '.', '');
|
||||
$priceInfo->time = $now->format('c');
|
||||
if ($this->redis) {
|
||||
$this->redis->set(self::lbcPriceKey, json_encode($priceInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$lbcUsdPrice = (isset($priceInfo->price) && ($priceInfo->price > 0)) ? '$' . $priceInfo->price : 'N/A';
|
||||
return $lbcUsdPrice;
|
||||
}
|
||||
|
||||
public function index() {
|
||||
$this->loadModel('Blocks');
|
||||
$this->loadModel('Claims');
|
||||
|
||||
$lbcUsdPrice = $this->_getLatestPrice();
|
||||
$this->set('lbcUsdPrice', $lbcUsdPrice);
|
||||
|
||||
$blocks = $this->Blocks->find()->select(['Chainwork', 'Confirmations', 'Difficulty', 'Hash', 'Height', 'TransactionHashes', 'BlockTime', 'BlockSize'])->
|
||||
order(['Height' => 'desc'])->limit(6)->toArray();
|
||||
for ($i = 0; $i < count($blocks); $i++) {
|
||||
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||
}
|
||||
|
||||
// hash rate
|
||||
$hashRate = $this->_formatHashRate($this->_gethashrate());
|
||||
|
||||
// recent claims
|
||||
$claims = $this->Claims->find()->select(['TransactionHash', 'Name', 'Vout', 'ClaimId', 'ClaimType', 'Author', 'Title', 'Description', 'ContentType',
|
||||
'IsNSFW', 'Language', 'ThumbnailUrl', 'Created'])->
|
||||
distinct(['Claims.ClaimId'])->
|
||||
contain(['Publisher' => ['fields' => ['Name']]])->order(['Claims.Created' => 'DESC'])->limit(5)->toArray();
|
||||
|
||||
$this->set('recentBlocks', $blocks);
|
||||
$this->set('recentClaims', $claims);
|
||||
$this->set('hashRate', $hashRate);
|
||||
}
|
||||
|
||||
public function claims($id = null) {
|
||||
$this->loadModel('Claims');
|
||||
$this->loadModel('Transactions');
|
||||
|
||||
$canConvert = false;
|
||||
$priceInfo = json_decode($this->redis->get(self::lbcPriceKey));
|
||||
if (isset($priceInfo->price)) {
|
||||
$canConvert = true;
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
// paginate claims
|
||||
$offset = 0;
|
||||
$pageLimit = 96;
|
||||
$page = intval($this->request->query('page'));
|
||||
|
||||
$conn = ConnectionManager::get('default');
|
||||
$stmt = $conn->execute('SELECT COUNT(Id) AS Total FROM Claims');
|
||||
$count = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
$numClaims = $count->Total;
|
||||
|
||||
$numPages = ceil($numClaims / $pageLimit);
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
if ($page > $numPages) {
|
||||
$page = $numPages;
|
||||
}
|
||||
|
||||
$offset = ($page - 1) * $pageLimit;
|
||||
$claims = $this->Claims->find()->distinct(['Claims.ClaimId'])->contain(['Stream', 'Publisher' => ['fields' => ['Name']]])->order(['Claims.Created' => 'DESC'])->offset($offset)->limit($pageLimit)->toArray();
|
||||
for ($i = 0; $i < count($claims); $i++) {
|
||||
if ($canConvert && $claims[$i]->Fee > 0 && $claims[$i]->FeeCurrency == 'USD') {
|
||||
$claims[$i]->Price = $claims[$i]->Fee / $priceInfo->price;
|
||||
}
|
||||
|
||||
if (isset($claims[$i]->Stream)) {
|
||||
$json = json_decode($claims[$i]->Stream->Stream);
|
||||
if (isset($json->metadata->license)) {
|
||||
$claims[$i]->License = $json->metadata->license;
|
||||
}
|
||||
if (isset($json->metadata->licenseUrl)) {
|
||||
$claims[$i]->LicenseUrl = $json->metadata->licenseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->set('pageLimit', $pageLimit);
|
||||
$this->set('numPages', $numPages);
|
||||
$this->set('numRecords', $numClaims);
|
||||
$this->set('currentPage', $page);
|
||||
$this->set('claims', $claims);
|
||||
} else {
|
||||
$claim = $this->Claims->find()->contain(['Stream', 'Publisher' => ['fields' => ['ClaimId', 'Name']]])->where(['Claims.ClaimId' => $id])->order(['Claims.Created' => 'DESC'])->first();
|
||||
if (!$claim) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
if ($canConvert && $claim->Fee > 0 && $claim->FeeCurrency == 'USD') {
|
||||
$claim->Price = $claim->Fee / $priceInfo->price;
|
||||
}
|
||||
|
||||
if (isset($claim->Stream)) {
|
||||
$json = json_decode($claim->Stream->Stream);
|
||||
if (isset($json->metadata->license)) {
|
||||
$claim->License = $json->metadata->license;
|
||||
}
|
||||
if (isset($json->metadata->licenseUrl)) {
|
||||
$claim->LicenseUrl = $json->metadata->licenseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
$moreClaims = [];
|
||||
if (isset($claim->Publisher) || $claim->ClaimType == 1) {
|
||||
// find more claims for the publisher
|
||||
$moreClaims = $this->Claims->find()->contain(['Stream', 'Publisher' => ['fields' => ['Name']]])->
|
||||
where(['Claims.ClaimType' => 2, 'Claims.Id <>' => $claim->Id, 'Claims.PublisherId' => isset($claim->Publisher) ? $claim->Publisher->ClaimId : $claim->ClaimId])->
|
||||
limit(9)->order(['Claims.Fee' => 'DESC', 'RAND()' => 'DESC'])->toArray();
|
||||
for ($i = 0; $i < count($moreClaims); $i++) {
|
||||
if ($canConvert && $moreClaims[$i]->Fee > 0 && $moreClaims[$i]->FeeCurrency == 'USD') {
|
||||
$moreClaims[$i]->Price = $moreClaims[$i]->Fee / $priceInfo->price;
|
||||
}
|
||||
|
||||
if (isset($moreClaims[$i]->Stream)) {
|
||||
$json = json_decode($moreClaims[$i]->Stream->Stream);
|
||||
if (isset($json->metadata->license)) {
|
||||
$moreClaims[$i]->License = $json->metadata->license;
|
||||
}
|
||||
if (isset($json->metadata->licenseUrl)) {
|
||||
$moreClaims[$i]->LicenseUrl = $json->metadata->licenseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->set('claim', $claim);
|
||||
$this->set('moreClaims', $moreClaims);
|
||||
}
|
||||
}
|
||||
|
||||
public function realtime() {
|
||||
$this->loadModel('Blocks');
|
||||
$this->loadModel('Transactions');
|
||||
|
||||
// load 10 blocks and transactions
|
||||
$conn = ConnectionManager::get('default');
|
||||
$blocks = $this->Blocks->find()->select(['Height', 'BlockTime', 'TransactionHashes'])->order(['Height' => 'desc'])->limit(10)->toArray();
|
||||
for ($i = 0; $i < count($blocks); $i++) {
|
||||
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||
}
|
||||
|
||||
$stmt = $conn->execute('SELECT T.Hash, T.InputCount, T.OutputCount, T.Value, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||
'FROM Transactions T ORDER BY CreatedTime DESC LIMIT 10');
|
||||
$txs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
|
||||
$this->set('blocks', $blocks);
|
||||
$this->set('txs', $txs);
|
||||
}
|
||||
|
||||
public function apiblocksize($timePeriod = '24h') {
|
||||
$this->autoRender = false;
|
||||
|
||||
if (!$this->request->is('get')) {
|
||||
return $this->_jsonError('Invalid HTTP request method.', 400);
|
||||
}
|
||||
|
||||
$validPeriods = ['24h', '72h', '168h', '30d', '90d', '1y'];
|
||||
if (!in_array($timePeriod, $validPeriods)) {
|
||||
return $this->_jsonError('Invalid time period specified.', 400);
|
||||
}
|
||||
|
||||
$isHourly = (strpos($timePeriod, 'h') !== false);
|
||||
$now = new \DateTime('now', new \DateTimeZone('UTC'));
|
||||
$dateFormat = $isHourly ? 'Y-m-d H:00:00' : 'Y-m-d';
|
||||
$sqlDateFormat = $isHourly ? '%Y-%m-%d %H:00:00' : '%Y-%m-%d';
|
||||
$intervalPrefix = $isHourly ? 'PT' : 'P';
|
||||
$start = $now->sub(new \DateInterval($intervalPrefix . strtoupper($timePeriod)));
|
||||
|
||||
$resultSet = [];
|
||||
|
||||
$conn = ConnectionManager::get('default');
|
||||
|
||||
// get avg block sizes for the time period
|
||||
$stmt = $conn->execute("SELECT AVG(BlockSize) AS AvgBlockSize, DATE_FORMAT(FROM_UNIXTIME(BlockTime), '$sqlDateFormat') AS TimePeriod " .
|
||||
"FROM Blocks WHERE DATE_FORMAT(FROM_UNIXTIME(BlockTime), '$sqlDateFormat') >= ? GROUP BY TimePeriod ORDER BY TimePeriod ASC", [$start->format($dateFormat)]);
|
||||
$avgBlockSizes = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
foreach ($avgBlockSizes as $size) {
|
||||
if (!isset($resultSet[$size->TimePeriod])) {
|
||||
$resultSet[$size->TimePeriod] = [];
|
||||
}
|
||||
$resultSet[$size->TimePeriod]['AvgBlockSize'] = (float) $size->AvgBlockSize;
|
||||
}
|
||||
|
||||
// get avg prices
|
||||
$stmt = $conn->execute("SELECT AVG(USD) AS AvgUSD, DATE_FORMAT(Created, '$sqlDateFormat') AS TimePeriod " .
|
||||
"FROM PriceHistory WHERE DATE_FORMAT(Created, '$sqlDateFormat') >= ? GROUP BY TimePeriod ORDER BY TimePeriod ASC", [$start->format($dateFormat)]);
|
||||
$avgPrices = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
foreach ($avgPrices as $price) {
|
||||
if (!isset($resultSet[$price->TimePeriod])) {
|
||||
$resultSet[$price->TimePeriod] = [];
|
||||
}
|
||||
$resultSet[$price->TimePeriod]['AvgUSD'] = (float) $price->AvgUSD;
|
||||
}
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'data' => $resultSet]);
|
||||
}
|
||||
|
||||
public function apirealtimeblocks() {
|
||||
// load 10 blocks
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Blocks');
|
||||
$blocks = $this->Blocks->find()->select(['Height', 'BlockTime', 'TransactionHashes'])->order(['Height' => 'desc'])->limit(10)->toArray();
|
||||
for ($i = 0; $i < count($blocks); $i++) {
|
||||
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||
unset($blocks[$i]->TransactionHashes);
|
||||
}
|
||||
|
||||
$this->_jsonResponse(['success' => true, 'blocks' => $blocks]);
|
||||
}
|
||||
|
||||
public function apirealtimetx() {
|
||||
// load 10 transactions
|
||||
$this->autoRender = false;
|
||||
$conn = ConnectionManager::get('default');
|
||||
$stmt = $conn->execute('SELECT T.Hash, T.InputCount, T.OutputCount, T.Value, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||
'FROM Transactions T ORDER BY CreatedTime DESC LIMIT 10');
|
||||
$txs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
|
||||
$this->_jsonResponse(['success' => true, 'txs' => $txs]);
|
||||
}
|
||||
|
||||
protected function _formatHashRate($value) {
|
||||
if ($value === 'N/A') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/*if ($value > 1000000000000) {
|
||||
return number_format( $value / 1000000000000, 2, '.', '' ) . ' TH';
|
||||
}*/
|
||||
if ($value > 1000000000) {
|
||||
return number_format( $value / 1000000000, 2, '.', '' ) . ' GH/s';
|
||||
}
|
||||
if ($value > 1000000) {
|
||||
return number_format( $value / 1000000, 2, '.', '' ) . ' MH/s';
|
||||
}
|
||||
if ($value > 1000) {
|
||||
return number_format( $value / 1000, 2, '.', '' ) . ' KH/s';
|
||||
}
|
||||
|
||||
return number_format($value) . ' H/s';
|
||||
}
|
||||
|
||||
public function find() {
|
||||
$criteria = $this->request->query('q');
|
||||
if ($criteria === null || strlen(trim($criteria)) == 0) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
$this->loadModel('Blocks');
|
||||
$this->loadModel('Claims');
|
||||
$this->loadModel('Addresses');
|
||||
$this->loadModel('Transactions');
|
||||
|
||||
if (is_numeric($criteria)) {
|
||||
$height = (int) $criteria;
|
||||
$block = $this->Blocks->find()->select(['Id'])->where(['Height' => $height])->first();
|
||||
if ($block) {
|
||||
return $this->redirect('/blocks/' . $height);
|
||||
}
|
||||
} else if (strlen(trim($criteria)) === 34) {
|
||||
// Address
|
||||
$address = $this->Addresses->find()->select(['Id', 'Address'])->where(['Address' => $criteria])->first();
|
||||
if ($address) {
|
||||
return $this->redirect('/address/' . $address->Address);
|
||||
}
|
||||
} else if (strlen(trim($criteria)) === 40) {
|
||||
// Claim ID
|
||||
$claim = $this->Claims->find()->select(['ClaimId'])->where(['ClaimId' => $criteria])->first();
|
||||
if ($claim) {
|
||||
return $this->redirect('/claim/' . $claim->ClaimId);
|
||||
}
|
||||
} else if (strlen(trim($criteria)) === 64) { // block or tx hash
|
||||
// Try block hash first
|
||||
$block = $this->Blocks->find()->select(['Height'])->where(['Hash' => $criteria])->first();
|
||||
if ($block) {
|
||||
return $this->redirect('/blocks/' . $block->Height);
|
||||
} else {
|
||||
$tx = $this->Transactions->find()->select(['Hash'])->where(['Hash' => $criteria])->first();
|
||||
if ($tx) {
|
||||
return $this->redirect('/tx/' . $tx->Hash);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// finally, try exact claim name match
|
||||
$claim = $this->Claims->find()->select(['ClaimId'])->where(['Name' => $criteria])->first();
|
||||
if ($claim) {
|
||||
return $this->redirect('/claims/' . $claim->ClaimId);
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, redirect to index
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
public function blocks($height = null) {
|
||||
$this->loadModel('Blocks');
|
||||
|
||||
if ($height === null) {
|
||||
// paginate blocks
|
||||
$offset = 0;
|
||||
$pageLimit = 50;
|
||||
$page = intval($this->request->query('page'));
|
||||
|
||||
$conn = ConnectionManager::get('default');
|
||||
$stmt = $conn->execute('SELECT COUNT(Id) AS Total FROM Blocks');
|
||||
$count = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
$numBlocks = $count->Total;
|
||||
|
||||
$numPages = ceil($numBlocks / $pageLimit);
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
if ($page > $numPages) {
|
||||
$page = $numPages;
|
||||
}
|
||||
|
||||
$offset = ($page - 1) * $pageLimit;
|
||||
$blocks = $this->Blocks->find()->offset($offset)->limit($pageLimit)->order(['Height' => 'DESC'])->toArray();
|
||||
$this->set('blocks', $blocks);
|
||||
$this->set('pageLimit', $pageLimit);
|
||||
$this->set('numPages', $numPages);
|
||||
$this->set('numRecords', $numBlocks);
|
||||
$this->set('currentPage', $page);
|
||||
} else {
|
||||
$this->loadModel('Transactions');
|
||||
$height = intval($height);
|
||||
if ($height < 0) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
$block = $this->Blocks->find()->where(['Height' => $height])->first();
|
||||
if (!$block) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
try {
|
||||
// update the block confirmations
|
||||
$req = ['method' => 'getblock', 'params' => [$block->Hash]];
|
||||
$response = self::curl_json_post(self::rpcurl, json_encode($req));
|
||||
$json = json_decode($response);
|
||||
$rpc_block = $json->result;
|
||||
if (isset($rpc_block->confirmations)) {
|
||||
$block->Confirmations = $rpc_block->confirmations;
|
||||
$conn = ConnectionManager::get('default');
|
||||
$conn->execute('UPDATE Blocks SET Confirmations = ? WHERE Id = ?', [$rpc_block->confirmations, $block->Id]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// try again next time
|
||||
}
|
||||
|
||||
// Get the basic block transaction info
|
||||
$txs = $this->Transactions->find()->select(['InputCount', 'OutputCount', 'Hash', 'Value', 'Version'])->where(['BlockHash' => $block->Hash])->toArray();
|
||||
|
||||
$this->set('block', $block);
|
||||
$this->set('blockTxs', $txs);
|
||||
}
|
||||
}
|
||||
|
||||
public function tx($hash = null) {
|
||||
$this->loadModel('Blocks');
|
||||
$this->loadModel('Transactions');
|
||||
$this->loadModel('Inputs');
|
||||
$this->loadModel('Outputs');
|
||||
$sourceAddress = $this->request->query('address');
|
||||
|
||||
$tx = $this->Transactions->find()->select(
|
||||
['Id', 'BlockHash', 'InputCount', 'OutputCount', 'Hash', 'Value', 'TransactionTime', 'TransactionSize', 'Created', 'Version', 'LockTime', 'Raw'])->where(['Hash' => $hash])->first();
|
||||
if (!$tx) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
if ($tx->TransactionSize == 0) {
|
||||
$tx->TransactionSize = (strlen($tx->Raw) / 2);
|
||||
$conn = ConnectionManager::get('default');
|
||||
$conn->execute('UPDATE Transactions SET TransactionSize = ? WHERE Id = ?', [$tx->TransactionSize, $tx->Id]);
|
||||
}
|
||||
|
||||
$block = $this->Blocks->find()->select(['Confirmations', 'Height'])->where(['Hash' => $tx->BlockHash])->first();
|
||||
$inputs = $this->Inputs->find()->contain(['InputAddresses'])->where(['TransactionId' => $tx->Id])->order(['PrevoutN' => 'asc'])->toArray();
|
||||
$outputs = $this->Outputs->find()->contain(['OutputAddresses', 'SpendInput' => ['fields' => ['Id', 'TransactionHash', 'PrevoutN', 'PrevoutHash']]])->where(['Outputs.TransactionId' => $tx->Id])->order(['Vout' => 'asc'])->toArray();
|
||||
for ($i = 0; $i < count($outputs); $i++) {
|
||||
$outputs[$i]->IsClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'CLAIM') > -1);
|
||||
$outputs[$i]->IsSupportClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'SUPPORT_CLAIM') > -1);
|
||||
$outputs[$i]->IsUpdateClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'UPDATE_CLAIM') > -1);
|
||||
}
|
||||
|
||||
$totalIn = 0;
|
||||
$totalOut = 0;
|
||||
$fee = 0;
|
||||
foreach ($inputs as $in) {
|
||||
$totalIn = bcadd($totalIn, $in->Value, 8);
|
||||
}
|
||||
foreach ($outputs as $out) {
|
||||
$totalOut = bcadd($totalOut, $out->Value, 8);
|
||||
}
|
||||
$fee = bcsub($totalIn, $totalOut, 8);
|
||||
|
||||
$this->set('tx', $tx);
|
||||
$this->set('block', $block);
|
||||
$this->set('confirmations', $block ? number_format($block->Confirmations, 0, '', ',') : '0');
|
||||
$this->set('fee', $fee);
|
||||
$this->set('inputs', $inputs);
|
||||
$this->set('outputs', $outputs);
|
||||
$this->set('sourceAddress', $sourceAddress);
|
||||
}
|
||||
|
||||
public function stats() {
|
||||
$this->loadModel('Addresses');
|
||||
|
||||
// exclude bHW58d37s1hBjj3wPBkn5zpCX3F8ZW3uWf (genesis block)
|
||||
$richList = $this->Addresses->find()->where(['Address <>' => 'bHW58d37s1hBjj3wPBkn5zpCX3F8ZW3uWf'])->order(['Balance' => 'DESC'])->limit(500)->toArray();
|
||||
|
||||
$priceRate = 0;
|
||||
$priceInfo = json_decode($this->redis->get(self::lbcPriceKey));
|
||||
if (isset($priceInfo->price)) {
|
||||
$priceRate = $priceInfo->price;
|
||||
}
|
||||
|
||||
// calculate percentages
|
||||
$totalBalance = 0;
|
||||
$maxBalance = 0;
|
||||
$minBalance = 0;
|
||||
foreach ($richList as $item) {
|
||||
$totalBalance = bcadd($totalBalance, $item->Balance, 8);
|
||||
$minBalance = $minBalance == 0 ? $item->Balance : min($minBalance, $item->Balance);
|
||||
$maxBalance = max($maxBalance, $item->Balance);
|
||||
}
|
||||
for ($i = 0; $i < count($richList); $i++) {
|
||||
$item = $richList[$i];
|
||||
$percentage = bcdiv($item->Balance, $totalBalance, 8) * 100;
|
||||
$richList[$i]->Top500Percent = $percentage;
|
||||
$richList[$i]->MinMaxPercent = bcdiv($item->Balance, $maxBalance, 8) * 100;
|
||||
}
|
||||
|
||||
$this->set('richList', $richList);
|
||||
$this->set('rate', $priceRate);
|
||||
}
|
||||
|
||||
public function address($addr = null) {
|
||||
set_time_limit(0);
|
||||
|
||||
$this->loadModel('Addresses');
|
||||
$this->loadModel('Transactions');
|
||||
$this->loadModel('Inputs');
|
||||
$this->loadModel('Outputs');
|
||||
|
||||
if (!$addr) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
$pageLimit = 50;
|
||||
$numTransactions = 0;
|
||||
$page = intval($this->request->query('page'));
|
||||
|
||||
$canTag = false;
|
||||
$totalRecvAmount = 0;
|
||||
$totalSentAmount = 0;
|
||||
$balanceAmount = 0;
|
||||
$recentTxs = [];
|
||||
|
||||
$tagRequestAmount = 0;
|
||||
// Check for pending tag request
|
||||
$this->loadModel('TagAddressRequests');
|
||||
$pending = $this->TagAddressRequests->find()->where(['Address' => $addr, 'IsVerified <>' => 1])->first();
|
||||
if (!$pending) {
|
||||
$tagRequestAmount = '25.' . rand(11111111, 99999999);
|
||||
}
|
||||
|
||||
|
||||
$address = $this->Addresses->find()->where(['Address' => $addr])->first();
|
||||
if (!$address) {
|
||||
if (strlen($addr) === 34) {
|
||||
$address = new \stdClass();
|
||||
$address->Address = $addr;
|
||||
} else {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
} else {
|
||||
$conn = ConnectionManager::get('default');
|
||||
|
||||
$canTag = true;
|
||||
$addressId = $address->Id;
|
||||
|
||||
$stmt = $conn->execute('SELECT COUNT(TransactionId) AS Total FROM TransactionsAddresses WHERE AddressId = ?', [$addressId]);
|
||||
$count = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
$numTransactions = $count->Total;
|
||||
$all = $this->request->query('all');
|
||||
if ($all === 'true') {
|
||||
$offset = 0;
|
||||
$pageLimit = $numTransactions;
|
||||
$numPages = 1;
|
||||
$page = 1;
|
||||
} else {
|
||||
$numPages = ceil($numTransactions / $pageLimit);
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
if ($page > $numPages) {
|
||||
$page = $numPages;
|
||||
}
|
||||
|
||||
$offset = ($page - 1) * $pageLimit;
|
||||
}
|
||||
|
||||
$stmt = $conn->execute('SELECT A.TotalReceived, A.TotalSent, A.Balance FROM Addresses A WHERE A.Id = ?', [$address->Id]);
|
||||
$totals = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||
|
||||
$stmt = $conn->execute(sprintf('SELECT T.Id, T.Hash, T.InputCount, T.OutputCount, T.Value, ' .
|
||||
'TA.DebitAmount, TA.CreditAmount, ' .
|
||||
'B.Height, B.Confirmations, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||
'FROM Transactions T ' .
|
||||
'LEFT JOIN Blocks B ON T.BlockHash = B.Hash ' .
|
||||
'RIGHT JOIN (SELECT TransactionId, DebitAmount, CreditAmount FROM TransactionsAddresses ' .
|
||||
' WHERE AddressId = ? ORDER BY TransactionTime DESC LIMIT %d, %d) TA ON TA.TransactionId = T.Id', $offset, $pageLimit), [$addressId]);
|
||||
$recentTxs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
|
||||
$totalRecvAmount = $totals->TotalReceived == 0 ? '0' : $totals->TotalReceived + 0;
|
||||
$totalSentAmount = $totals->TotalSent == 0 ? '0' : $totals->TotalSent + 0;
|
||||
$balanceAmount = $totals->Balance == 0 ? '0' : $totals->Balance + 0;
|
||||
}
|
||||
|
||||
$this->set('offset', $offset);
|
||||
$this->set('canTag', $canTag);
|
||||
$this->set('pending', $pending);
|
||||
$this->set('tagRequestAmount', $tagRequestAmount);
|
||||
$this->set('address', $address);
|
||||
$this->set('totalReceived', $totalRecvAmount);
|
||||
$this->set('totalSent', $totalSentAmount);
|
||||
$this->set('balanceAmount', $balanceAmount);
|
||||
$this->set('recentTxs', $recentTxs);
|
||||
$this->set('numRecords', $numTransactions);
|
||||
$this->set('numPages', $numPages);
|
||||
$this->set('currentPage', $page);
|
||||
$this->set('pageLimit', $pageLimit);
|
||||
}
|
||||
|
||||
public function qr($data = null) {
|
||||
$this->autoRender = false;
|
||||
|
||||
if (!$data || strlen(trim($data)) == 0 || strlen(trim($data)) > 50) {
|
||||
return;
|
||||
}
|
||||
|
||||
$qrCode = new QrCode($data);
|
||||
$qrCode->setSize(300);
|
||||
|
||||
// Set advanced options
|
||||
$qrCode
|
||||
->setWriterByName('png')
|
||||
->setMargin(10)
|
||||
->setEncoding('UTF-8')
|
||||
->setErrorCorrectionLevel(ErrorCorrectionLevel::LOW)
|
||||
->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0])
|
||||
->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255])
|
||||
->setLogoWidth(150)
|
||||
->setValidateResult(false);
|
||||
|
||||
header('Content-Type: '.$qrCode->getContentType());
|
||||
echo $qrCode->writeString();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
public static function curl_get($url) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
Log::debug('Request execution completed.');
|
||||
|
||||
if ($response === false) {
|
||||
$error = curl_error($ch);
|
||||
$errno = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
|
||||
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||
} else {
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function _gethashrate() {
|
||||
$req = ['method' => 'getnetworkhashps', 'params' => []];
|
||||
try {
|
||||
$res = json_decode(self::curl_json_post(self::rpcurl, json_encode($req)));
|
||||
if (!isset($res->result)) {
|
||||
return 0;
|
||||
}
|
||||
return $res->result;
|
||||
} catch (\Exception $e) {
|
||||
return 'N/A';
|
||||
}
|
||||
}
|
||||
private function _gettxoutsetinfo() {
|
||||
$req = ['method' => 'gettxoutsetinfo', 'params' => []];
|
||||
try {
|
||||
$res = json_decode(self::curl_json_post(self::rpcurl, json_encode($req)));
|
||||
if (!isset($res->result)) {
|
||||
return 0;
|
||||
}
|
||||
return $res->result;
|
||||
} catch (\Exception $e) {
|
||||
return 'N/A';
|
||||
}
|
||||
}
|
||||
public function apistatus() {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Blocks');
|
||||
|
||||
// Get the max height block
|
||||
$height = 0;
|
||||
$difficulty = 0;
|
||||
$highestBlock = $this->Blocks->find()->select(['Height', 'Difficulty'])->order(['Height' => 'desc'])->first();
|
||||
$height = $highestBlock->Height;
|
||||
$difficulty = $highestBlock->Difficulty;
|
||||
$lbcUsdPrice = $this->_getLatestPrice();
|
||||
|
||||
// Calculate hash rate
|
||||
$hashRate = $this->_formatHashRate($this->_gethashrate());
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'status' => [
|
||||
'height' => $height,
|
||||
'difficulty' => number_format($difficulty, 2, '.', ''),
|
||||
'price' => $lbcUsdPrice,
|
||||
'hashrate' => $hashRate
|
||||
]]);
|
||||
}
|
||||
|
||||
public function apirecentblocks() {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Blocks');
|
||||
$blocks = $this->Blocks->find()->select(['Difficulty', 'Hash', 'Height', 'TransactionHashes', 'BlockTime', 'BlockSize'])->
|
||||
order(['Height' => 'desc'])->limit(6)->toArray();
|
||||
for ($i = 0; $i < count($blocks); $i++) {
|
||||
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||
$blocks[$i]->Difficulty = number_format($blocks[$i]->Difficulty, 2, '.', '');
|
||||
unset($blocks[$i]->TransactionHashes);
|
||||
}
|
||||
return $this->_jsonResponse(['success' => true, 'blocks' => $blocks]);
|
||||
}
|
||||
|
||||
public function apiaddrtag($base58address = null) {
|
||||
$this->autoRender = false;
|
||||
if (!isset($base58address) || strlen(trim($base58address)) !== 34) {
|
||||
return $this->_jsonError('Invalid base58 address specified.', 400);
|
||||
}
|
||||
if (!$this->request->is('post')) {
|
||||
return $this->_jsonError('Invalid HTTP request method.', 400);
|
||||
}
|
||||
|
||||
if (trim($base58address) == self::tagReceiptAddress) {
|
||||
return $this->_jsonError('You cannot submit a tag request for this address.', 400);
|
||||
}
|
||||
|
||||
$this->loadModel('Addresses');
|
||||
$this->loadModel('TagAddressRequests');
|
||||
$data = [
|
||||
'Address' => $base58address,
|
||||
'Tag' => trim($this->request->data('tag')),
|
||||
'TagUrl' => trim($this->request->data('url')),
|
||||
'VerificationAmount' => $this->request->data('vamount')
|
||||
];
|
||||
|
||||
// verify
|
||||
$entity = $this->TagAddressRequests->newEntity($data);
|
||||
if (strlen($entity->Tag) === 0 || strlen($entity->Tag) > 30) {
|
||||
return $this->_jsonError('Oops! Please specify a valid tag. It should be no more than 30 characters long.', 400);
|
||||
}
|
||||
|
||||
if (strlen($entity->TagUrl) > 0) {
|
||||
if (strlen($entity->TagUrl) > 200) {
|
||||
return $this->_jsonError('Oops! The link should be no more than 200 characters long.', 400);
|
||||
}
|
||||
if (!filter_var($entity->TagUrl, FILTER_VALIDATE_URL)) {
|
||||
return $this->_jsonError('Oops! The link should be a valid URL.', 400);
|
||||
}
|
||||
} else {
|
||||
unset($entity->TagUrl);
|
||||
}
|
||||
|
||||
if ($entity->VerificationAmount < 25.1 || $entity->VerificationAmount > 25.99999999) {
|
||||
return $this->_jsonError('Oops! The verification amount is invalid. Please refresh the page and try again.', 400);
|
||||
}
|
||||
|
||||
// check if the tag is taken
|
||||
$addrTag = $this->Addresses->find()->select(['Id'])->where(['LOWER(Tag)' => strtolower($entity->Tag)])->first();
|
||||
if ($addrTag) {
|
||||
return $this->_jsonError('Oops! The tag is already taken. Please specify a different tag.', 400);
|
||||
}
|
||||
|
||||
// check for existing verification
|
||||
$exist = $this->TagAddressRequests->find()->select(['Id'])->where(['Address' => $base58address, 'IsVerified' => 0])->first();
|
||||
if ($exist) {
|
||||
return $this->_jsonError('Oops! There is a pending tag verification for this address.', 400);
|
||||
}
|
||||
|
||||
// save the request
|
||||
if (!$this->TagAddressRequests->save($entity)) {
|
||||
return $this->_jsonError('Oops! The verification request could not be saved. If this problem persists, please send an email to hello@aureolin.co');
|
||||
}
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'tag' => $entity->Tag]);
|
||||
}
|
||||
|
||||
public function apiaddrbalance($base58address = null) {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Addresses');
|
||||
|
||||
if (!isset($base58address)) {
|
||||
return $this->_jsonError('Base58 address not specified.', 400);
|
||||
}
|
||||
|
||||
// TODO: Add unconfirmed_balance to response
|
||||
$result = $this->Addresses->find()->select(['Balance'])->where(['Address' => $base58address])->first();
|
||||
if (!$result) {
|
||||
// Return 0 for address that does not exist?
|
||||
$result = new \stdClass();
|
||||
$result->Balance = 0;
|
||||
}
|
||||
|
||||
return $this->_jsonResponse(['success' => true, ['balance' => ['confirmed' => $result->Balance, 'unconfirmed' => 0]]]);
|
||||
}
|
||||
|
||||
public function apiaddrutxo($base58address = null) {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Addresses');
|
||||
|
||||
if (!isset($base58address)) {
|
||||
return $this->_jsonError('Base58 address not specified.', 400);
|
||||
}
|
||||
|
||||
$arr = explode(',', $base58address);
|
||||
$addresses = $this->Addresses->find()->select(['Id'])->where(['Address IN' => $arr])->toArray();
|
||||
if (count($addresses) == 0) {
|
||||
return $this->_jsonError('No base58 address matching the specified parameter was found.', 404);
|
||||
}
|
||||
|
||||
$addressIds = [];
|
||||
$params = [];
|
||||
foreach ($addresses as $address) {
|
||||
$addressIds[] = $address->Id;
|
||||
$params[] = '?';
|
||||
}
|
||||
|
||||
// Get the unspent outputs for the address
|
||||
$conn = ConnectionManager::get('default');
|
||||
$stmt = $conn->execute(sprintf(
|
||||
'SELECT T.Hash AS TransactionHash, O.Vout, O.Value, O.Addresses, O.ScriptPubKeyAsm, O.ScriptPubKeyHex, O.Type, O.RequiredSignatures, B.Confirmations ' .
|
||||
'FROM Transactions T ' .
|
||||
'JOIN Outputs O ON O.TransactionId = T.Id ' .
|
||||
'JOIN Blocks B ON B.Hash = T.BlockHash ' .
|
||||
'WHERE O.Id IN (SELECT OutputId FROM OutputsAddresses WHERE AddressId IN (%s)) AND O.IsSpent <> 1 ORDER BY T.TransactionTime ASC', implode(',', $params)), $addressIds);
|
||||
$outputs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||
|
||||
$utxo = [];
|
||||
foreach ($outputs as $out) {
|
||||
$utxo[] = [
|
||||
'transaction_hash' => $out->TransactionHash,
|
||||
'output_index' => $out->Vout,
|
||||
'value' => (int) bcmul($out->Value, 100000000),
|
||||
'addresses' => json_decode($out->Addresses),
|
||||
'script' => $out->ScriptPubKeyAsm,
|
||||
'script_hex' => $out->ScriptPubKeyHex,
|
||||
'script_type' => $out->Type,
|
||||
'required_signatures' => (int) $out->RequiredSignatures,
|
||||
'spent' => false,
|
||||
'confirmations' => (int) $out->Confirmations
|
||||
];
|
||||
}
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'utxo' => $utxo]);
|
||||
}
|
||||
|
||||
public function apiutxosupply() {
|
||||
$this->autoRender = false;
|
||||
$this->loadModel('Addresses');
|
||||
|
||||
$circulating = 0;
|
||||
$reservedcommunity = 0;
|
||||
$reservedoperational = 0;
|
||||
$reservedinstitutional = 0;
|
||||
$reservedtotal = 0;
|
||||
$circulating = 0;
|
||||
|
||||
$txoutsetinfo = $this->_gettxoutsetinfo();
|
||||
$reservedcommunity = $this->Addresses->find()->select(['Balance'])->where(['Address =' => 'rFLUohPG4tP3gZHYoyhvADCtrDMiaYb7Qd'])->first();
|
||||
$reservedoperational = $this->Addresses->find()->select(['Balance'])->where(['Address =' => 'r9PGXsejVJb9ZfMf3QVdDEJCzxkd9JLxzL'])->first();
|
||||
$reservedinstitutional = $this->Addresses->find()->select(['Balance'])->where(['Address =' => 'r9srwX7DEN7Mex3a8oR1mKSqQmLBizoJvi'])->first();
|
||||
//aux is the address of hot wallets and where some of the LBRY operational fund are, but not sold on market.
|
||||
$reservedaux = $this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bRo4FEeqqxY7nWFANsZsuKEWByEgkvz8Qt'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bU2XUzckfpdEuQNemKvhPT1gexQ3GG3SC2'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bay3VA6YTQBL4WLobbG7CthmoGeUKXuXkD'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bLPbiXBp6Vr3NSnsHzDsLNzoy5o36re9Cz'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bMvUBo1h5WS46ThHtmfmXftz3z33VHL7wc'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bVUrbCK8hcZ5XWti7b9eNxKEBxzc1rr393'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bZja2VyhAC84a9hMwT8dwTU6rDRXowrjxH'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bMvUBo1h5WS46ThHtmfmXftz3z33VHL7wc'])->first() +
|
||||
$this->Addresses->find()->select(['Balance'])->where(['Address =' => 'bMgqQqYfwzWWYBk5o5dBMXtCndVAoeqy6h'])->first();
|
||||
|
||||
$reservedtotal = $reservedcommunity->Balance + $reservedoperational->Balance + $reservedinstitutional->Balance + $reservedaux->Balance;
|
||||
|
||||
|
||||
$circulating = $txoutsetinfo->total_amount - $reservedtotal;
|
||||
|
||||
return $this->_jsonResponse(['success' => true, 'utxosupply' => ['total' => $txoutsetinfo->total_amount, 'circulating' => $circulating]]);
|
||||
}
|
||||
|
||||
private static function curl_json_post($url, $data, $headers = []) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if ($response === false) {
|
||||
$error = curl_error($ch);
|
||||
$errno = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
|
||||
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||
} else {
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
// Close any open file handle
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
69
src/Controller/PagesController.php
Normal file
69
src/Controller/PagesController.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.2.9
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Network\Exception\ForbiddenException;
|
||||
use Cake\Network\Exception\NotFoundException;
|
||||
use Cake\View\Exception\MissingTemplateException;
|
||||
|
||||
/**
|
||||
* Static content controller
|
||||
*
|
||||
* This controller will render views from Template/Pages/
|
||||
*
|
||||
* @link http://book.cakephp.org/3.0/en/controllers/pages-controller.html
|
||||
*/
|
||||
class PagesController extends AppController
|
||||
{
|
||||
|
||||
/**
|
||||
* Displays a view
|
||||
*
|
||||
* @param string ...$path Path segments.
|
||||
* @return void|\Cake\Network\Response
|
||||
* @throws \Cake\Network\Exception\ForbiddenException When a directory traversal attempt.
|
||||
* @throws \Cake\Network\Exception\NotFoundException When the view file could not
|
||||
* be found or \Cake\View\Exception\MissingTemplateException in debug mode.
|
||||
*/
|
||||
public function display(...$path)
|
||||
{
|
||||
$count = count($path);
|
||||
if (!$count) {
|
||||
return $this->redirect('/');
|
||||
}
|
||||
if (in_array('..', $path, true) || in_array('.', $path, true)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
$page = $subpage = null;
|
||||
|
||||
if (!empty($path[0])) {
|
||||
$page = $path[0];
|
||||
}
|
||||
if (!empty($path[1])) {
|
||||
$subpage = $path[1];
|
||||
}
|
||||
$this->set(compact('page', 'subpage'));
|
||||
|
||||
try {
|
||||
$this->render(implode('/', $path));
|
||||
} catch (MissingTemplateException $e) {
|
||||
if (Configure::read('debug')) {
|
||||
throw $e;
|
||||
}
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
67
src/Model/Behavior/SimpleAuditBehavior.php
Normal file
67
src/Model/Behavior/SimpleAuditBehavior.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Behavior;
|
||||
|
||||
use Cake\ORM\Behavior;
|
||||
use Cake\ORM\Entity;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Routing\Router;
|
||||
|
||||
class SimpleAuditBehavior extends Behavior {
|
||||
private static $DefaultUser = '0';
|
||||
|
||||
private $_userField = 'Id';
|
||||
|
||||
protected $_defaultConfig = [
|
||||
'abortOnUserInvalid' => false,
|
||||
'implementedMethods' => ['audit' => 'audit'],
|
||||
'fieldMap' => [
|
||||
'CreatedOn' => 'Created',
|
||||
'ModifiedOn' => 'Modified',
|
||||
'CreatedBy' => 'CreatedBy',
|
||||
'ModifiedBy' => 'ModifiedBy'
|
||||
]
|
||||
];
|
||||
|
||||
public function audit(Entity $entity, $systemOperation = false) {
|
||||
$time = $this->_currentUtcTime()->format('Y-m-d H:i:s');
|
||||
$user = ($systemOperation) ? self::$DefaultUser : $this->_currentUser();
|
||||
|
||||
if (!$systemOperation
|
||||
&& $this->_config['abortOnUserInvalid']
|
||||
&& $user == self::$DefaultUser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$fieldMap = $this->_config['fieldMap'];
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$entity->set($fieldMap['CreatedOn'], $time);
|
||||
$entity->set($fieldMap['CreatedBy'], $user);
|
||||
}
|
||||
|
||||
$entity->set($fieldMap['ModifiedOn'], $time);
|
||||
$entity->set($fieldMap['ModifiedBy'], $user);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforeSave(Event $event, Entity $entity) {
|
||||
return $this->audit($entity);
|
||||
}
|
||||
|
||||
private function _currentUtcTime() {
|
||||
return new \DateTime('now', new \DateTimeZone('UTC'));
|
||||
}
|
||||
|
||||
private function _currentUser() {
|
||||
$request = Router::getRequest(true);
|
||||
if ($request) {
|
||||
$session = $request->session();
|
||||
$fieldValue = $session->read(sprintf('Auth.User.' . $this->_userField));
|
||||
return (intval($fieldValue) > 0) ? $fieldValue : self::$DefaultUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/Address.php
Normal file
11
src/Model/Entity/Address.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class Address extends Entity {
|
||||
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/Block.php
Normal file
11
src/Model/Entity/Block.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class Block extends Entity {
|
||||
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/Claim.php
Normal file
11
src/Model/Entity/Claim.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class Claim extends Entity {
|
||||
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/ClaimStream.php
Normal file
11
src/Model/Entity/ClaimStream.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class ClaimStream extends Entity {
|
||||
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/Input.php
Normal file
11
src/Model/Entity/Input.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class Input extends Entity {
|
||||
|
||||
}
|
||||
|
||||
?>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue