Compare commits
No commits in common. "master" and "add-addresses" have entirely different histories.
master
...
add-addres
467 changed files with 10764 additions and 12969 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;"
|
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 {
|
||||
|
||||
}
|
||||
|
||||
?>
|
11
src/Model/Entity/Output.php
Normal file
11
src/Model/Entity/Output.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class Output 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