v0.1.0
This commit is contained in:
29
backend/api/Readme.md
Normal file
29
backend/api/Readme.md
Normal file
@ -0,0 +1,29 @@
|
||||
# API
|
||||
|
||||
## Concepts
|
||||
+ [x] Coins
|
||||
+ [x] Wallets
|
||||
+ Name
|
||||
+ Public Address
|
||||
+ [x] Location
|
||||
+ Home PC
|
||||
+ Notebook
|
||||
+ Exchange
|
||||
+ [x] Transactions -
|
||||
Read from blockchain
|
||||
+ [x] Values
|
||||
+ [ ] In USD
|
||||
+ [ ] In CLF
|
||||
+ [ ] In BTC
|
||||
|
||||
## Actions
|
||||
|
||||
+ List
|
||||
+ Show/Read
|
||||
+ Add
|
||||
+ Edit
|
||||
+ Delete
|
||||
|
||||
+ [x] Coins
|
||||
+ [x] Wallets
|
||||
+ [x] Locations
|
46
backend/api/common/Controller/API.php
Normal file
46
backend/api/common/Controller/API.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use ProVM\Common\Define\Controller\JSON;
|
||||
|
||||
class API {
|
||||
use JSON;
|
||||
|
||||
public function __invoke(Request $request, Response $response): Response {
|
||||
$output = [
|
||||
'version' => '1.0.0',
|
||||
'routes' => [
|
||||
'/coins' => [
|
||||
'/' => 'List all coins',
|
||||
'/add' => 'Add coin'
|
||||
],
|
||||
'/coin/{coin_id}' => [
|
||||
'/' => 'Show coin information',
|
||||
'/edit' => 'Edit coin',
|
||||
'/delete' => 'Delete coin'
|
||||
],
|
||||
'/locations' => [
|
||||
'/' => 'List all locations',
|
||||
'/add' => 'Add location'
|
||||
],
|
||||
'/location/{location_id}' => [
|
||||
'/' => 'Show location information',
|
||||
'/edit' => 'Edit location',
|
||||
'/delete' => 'Delete location'
|
||||
],
|
||||
'/wallets' => [
|
||||
'/' => 'List all wallets',
|
||||
'/add' => 'Add wallet'
|
||||
],
|
||||
'/wallet/{wallet_id}' => [
|
||||
'/' => 'Show wallet information',
|
||||
'/edit' => 'Edit wallet',
|
||||
'/delete' => 'Delete wallet'
|
||||
]
|
||||
]
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
66
backend/api/common/Controller/Coins.php
Normal file
66
backend/api/common/Controller/Coins.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use ProVM\Common\Define\Controller\JSON;
|
||||
use ProVM\Common\Factory\Model as ModelFactory;
|
||||
use ProVM\Crypto\Coin;
|
||||
|
||||
class Coins {
|
||||
use JSON;
|
||||
|
||||
public function __invoke(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$coins = $factory->find(Coin::class)->array();
|
||||
usort($coins, function($a, $b) {
|
||||
return strcmp($a['code'], $b['code']);
|
||||
});
|
||||
$output = compact('coins');
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function show(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null]);
|
||||
}
|
||||
$output = ['coin' => $coin->toArray()];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function add(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$post = $request->getBody()->getContents();
|
||||
$post = json_decode($post);
|
||||
$coin = Coin::add($factory, $post);
|
||||
$status = false;
|
||||
if ($coin->isNew()) {
|
||||
$status = $coin->save();
|
||||
}
|
||||
$output = [
|
||||
'input' => $post,
|
||||
'coin' => $coin->toArray(),
|
||||
'created' => $status
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function edit(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null]);
|
||||
}
|
||||
$post = json_decode($request->getBody()->getContents());
|
||||
$edited = $coin->edit($post);
|
||||
$output = ['input' => $post, 'coin' => $coin->toArray(), 'edited' => $edited];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function delete(Request $request, Response $response, ModelFactory $factory, $coin_id): Response {
|
||||
$coin = $factory->find(Coin::class)->one($coin_id);
|
||||
if (!$coin) {
|
||||
return $this->withJson($response, ['coin' => null, 'deleted' => false]);
|
||||
}
|
||||
$output = [
|
||||
'coin' => $coin->toArray()
|
||||
];
|
||||
$status = $coin->delete();
|
||||
$output['deleted'] = $status;
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
70
backend/api/common/Controller/Locations.php
Normal file
70
backend/api/common/Controller/Locations.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use ProVM\Common\Define\Controller\JSON;
|
||||
use ProVM\Common\Factory\Model as ModelFactory;
|
||||
use ProVM\Crypto\Location;
|
||||
|
||||
class Locations {
|
||||
use JSON;
|
||||
|
||||
public function __invoke(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$locations = $factory->find(Location::class)->array();
|
||||
$output = compact('locations');
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function show(Request $request, Response $response, ModelFactory $factory, $location_id): Response {
|
||||
$location = $factory->find(Location::class)->one($location_id);
|
||||
if (!$location) {
|
||||
return $this->withJson($response, ['location' => null]);
|
||||
}
|
||||
$output = ['location' => $location->asArray()];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function add(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$post = json_decode($request->getBody()->getContents());
|
||||
$fields = [
|
||||
'name' => 'name',
|
||||
'description' => 'description'
|
||||
];
|
||||
$data = array_combine($fields, array_merge(array_intersect_key((array) $post, $fields), array_fill_keys(array_keys(array_diff_key($fields, (array) $post)), null)));
|
||||
$location = $factory->find(Location::class)->where([
|
||||
['name', $data['name']]
|
||||
])->one();
|
||||
$status = true;
|
||||
if (!$location) {
|
||||
$location = $factory->create(Location::class, $data);
|
||||
$status = $location->save();
|
||||
}
|
||||
$output = [
|
||||
'information_provided' => $post,
|
||||
'used_data' => $data,
|
||||
'location' => $location->asArray(),
|
||||
'saved' => $status
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function edit(Request $request, Response $response, ModelFactory $factory, $location_id): Response {
|
||||
$location = $factory->find(Location::class)->one($location_id);
|
||||
if (!$location) {
|
||||
return $this->withJson($response, ['location' => null]);
|
||||
}
|
||||
$post = json_decode($request->getBody()->getContents());
|
||||
$output = compact('location');
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function delete(Request $request, Response $response, ModelFactory $factory, $location_id): Response {
|
||||
$location = $factory->find(Location::class)->one($location_id);
|
||||
if (!$location) {
|
||||
return $this->withJson($response, ['location' => null, 'deleted' => false]);
|
||||
}
|
||||
$output = [
|
||||
'location' => $location->asArray()
|
||||
];
|
||||
$status = $location->delete();
|
||||
$output['deleted'] = $status;
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
27
backend/api/common/Controller/Update.php
Normal file
27
backend/api/common/Controller/Update.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use ProVM\Common\Define\Controller\JSON;
|
||||
use ProVM\Crypto\Common\Service\Update as Updater;
|
||||
|
||||
class Update {
|
||||
use JSON;
|
||||
|
||||
public function __invoke(Request $request, Response $response, Updater $updater) {
|
||||
$result = $updater->run();
|
||||
$output = [
|
||||
'result' => $result
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function register(Request $request, Response $response, Updater $updater, $coin_id, $type) {
|
||||
$result = $updater->register($coin_id, $type);
|
||||
$output = [
|
||||
'input' => ['coin_id' => $coin_id, 'type' => $type],
|
||||
'result' => $result
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
71
backend/api/common/Controller/Wallets.php
Normal file
71
backend/api/common/Controller/Wallets.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Controller;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use ProVM\Common\Define\Controller\JSON;
|
||||
use ProVM\Common\Factory\Model as ModelFactory;
|
||||
use ProVM\Crypto\Wallet;
|
||||
|
||||
class Wallets {
|
||||
use JSON;
|
||||
|
||||
public function __invoke(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$wallets = $factory->find(Wallet::class)->array();
|
||||
$output = compact('wallets');
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function show(Request $request, Response $response, ModelFactory $factory, $wallet_id): Response {
|
||||
$wallet = $factory->find(Wallet::class)->one($wallet_id);
|
||||
if (!$wallet) {
|
||||
return $this->withJson($response, ['wallet' => null]);
|
||||
}
|
||||
$output = ['wallet' => $wallet->asArray()];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function add(Request $request, Response $response, ModelFactory $factory): Response {
|
||||
$post = json_decode($request->getBody()->getContents());
|
||||
$fields = [
|
||||
'name' => 'name',
|
||||
'location' => 'location_id',
|
||||
'address' => 'public_address'
|
||||
];
|
||||
$data = array_combine($fields, array_merge(array_intersect_key((array) $post, $fields), array_fill_keys(array_keys(array_diff_key($fields, (array) $post)), null)));
|
||||
$wallet = $factory->find(Wallet::class)->where([
|
||||
['name', $data['name']]
|
||||
])->one();
|
||||
$status = true;
|
||||
if (!$wallet) {
|
||||
$wallet = $factory->create(Wallet::class, $data);
|
||||
$status = $wallet->save();
|
||||
}
|
||||
$output = [
|
||||
'information_provided' => $post,
|
||||
'used_data' => $data,
|
||||
'wallet' => $wallet->asArray(),
|
||||
'saved' => $status
|
||||
];
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function edit(Request $request, Response $response, ModelFactory $factory, $wallet_id): Response {
|
||||
$wallet = $factory->find(Wallet::class)->one($wallet_id);
|
||||
if (!$wallet) {
|
||||
return $this->withJson($response, ['wallet' => null]);
|
||||
}
|
||||
$post = json_decode($request->getBody()->getContents());
|
||||
$output = compact('wallet');
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
public function delete(Request $request, Response $response, ModelFactory $factory, $wallet_id): Response {
|
||||
$wallet = $factory->find(Wallet::class)->one($wallet_id);
|
||||
if (!$wallet) {
|
||||
return $this->withJson($response, ['wallet' => null, 'deleted' => false]);
|
||||
}
|
||||
$output = [
|
||||
'wallet' => $wallet->asArray()
|
||||
];
|
||||
$status = $wallet->delete();
|
||||
$output['deleted'] = $status;
|
||||
return $this->withJson($response, $output);
|
||||
}
|
||||
}
|
7
backend/api/common/Factory/Model.php
Normal file
7
backend/api/common/Factory/Model.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Factory;
|
||||
|
||||
use ProVM\Common\Factory\Model as BaseFactory;
|
||||
|
||||
class Model extends BaseFactory {
|
||||
}
|
38
backend/api/common/Middleware/CORS.php
Normal file
38
backend/api/common/Middleware/CORS.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Slim\Routing\RouteContext;
|
||||
|
||||
final class CORS implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Invoke middleware.
|
||||
*
|
||||
* @param ServerRequestInterface $request The request
|
||||
* @param RequestHandlerInterface $handler The handler
|
||||
*
|
||||
* @return ResponseInterface The response
|
||||
*/
|
||||
public function process(Request $request, Handler $handler): Response {
|
||||
$routeContext = RouteContext::fromRequest($request);
|
||||
$routingResults = $routeContext->getRoutingResults();
|
||||
$methods = $routingResults->getAllowedMethods();
|
||||
$requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');
|
||||
|
||||
$response = $handler->handle($request);
|
||||
|
||||
/*$response = $response
|
||||
->withHeader('Access-Control-Allow-Origin', '*')
|
||||
->withHeader('Access-Control-Allow-Methods', implode(', ', $methods))
|
||||
->withHeader('Access-Control-Allow-Headers', $requestHeaders ?: '*');*/
|
||||
|
||||
// Optional: Allow Ajax CORS requests with Authorization header
|
||||
$response = $response->withHeader('Access-Control-Allow-Credentials', 'true');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
49
backend/api/common/Service/Auth.php
Normal file
49
backend/api/common/Service/Auth.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Service;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ProVM\Common\Factory\Model as ModelFactory;
|
||||
use ProVM\Crypt\User;
|
||||
use ProVM\Crypt\Login;
|
||||
|
||||
class Auth {
|
||||
protected $login_time;
|
||||
public function __construct(int $login_time) {
|
||||
$this->login_time = $login_time;
|
||||
}
|
||||
protected $factory;
|
||||
public function setFactory(ModelFactory $factory) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
protected function createToken() {
|
||||
return password_hash(random_bytes(100), \PASSWORD_BCRYPT);
|
||||
}
|
||||
public function login(string $username, string $password) {
|
||||
$user = $this->factory->find(User::class)->where([['name', $username]])->one();
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
if (!password_verify($password, $user->password)) {
|
||||
return false;
|
||||
}
|
||||
$now = Carbon::now();
|
||||
$login = $this->factory->find(Login::class)->where([['user_id', $user->id], ['date_time', $now->copy()->subSeconds($this->login_time), '>=']])->one();
|
||||
if (!$login) {
|
||||
$token = $this->createToken();
|
||||
$data = [
|
||||
'user_id' => $user->id,
|
||||
'token' => $token,
|
||||
'date_time' => $now->format('Y-m-d H:i:s')
|
||||
];
|
||||
$login = $this->factory->create(Login::class, $data);
|
||||
} else {
|
||||
$login->date($now);
|
||||
}
|
||||
$login->save();
|
||||
return $token;
|
||||
}
|
||||
public function isLoggedIn($token) {
|
||||
$login = $this->factory->find(Login::class)->where([['token', $token]])->one();
|
||||
return $login->user();
|
||||
}
|
||||
}
|
60
backend/api/common/Service/Update.php
Normal file
60
backend/api/common/Service/Update.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto\Common\Service;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ProVM\Common\Factory\Model as Factory;
|
||||
use ProVM\Crypto\Coin;
|
||||
|
||||
class Update {
|
||||
protected $factory;
|
||||
protected $execs;
|
||||
public function __construct(Factory $factory, array $executables) {
|
||||
$this->factory = $factory;
|
||||
$this->execs = $executables;
|
||||
$this->load();
|
||||
}
|
||||
public function load() {
|
||||
$this->coins = [[], []];
|
||||
$results = \ORM::for_table('coin_registers')->find_many();
|
||||
foreach ($results as $result) {
|
||||
$this->coins[$result->type] []= $result->coin_id;
|
||||
}
|
||||
}
|
||||
protected $coins;
|
||||
public function register(int $coin_id, int $type = 0) {
|
||||
/*if (array_search($coin_id, $this->coins[$type]) !== false) {
|
||||
return;
|
||||
}*/
|
||||
$this->coins[$type] []= $coin_id;
|
||||
$this->getHistorical($coin_id, $type);
|
||||
$check = \ORM::for_table('coin_registers')->where('coin_id', $coin_id)->find_one();
|
||||
if (!$check) {
|
||||
\ORM::raw_execute("INSERT INTO coin_registers (coin_id, type) VALUES (?, ?)", [$coin_id, $type]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
protected function getHistorical(int $coin_id, int $type) {
|
||||
$coin = $this->factory->find(Coin::class)->one($coin_id);
|
||||
$f = Carbon::now();
|
||||
$exe = [$this->execs[$type]];
|
||||
switch ($type) {
|
||||
case 0:
|
||||
$exe []= '-i ' . $coin->identifier;
|
||||
$exe []= '-c usd,clp';
|
||||
$exe []= 'hist -hi';
|
||||
$exe []= '-f ' . $f->copy()->subYears(10)->timestamp;
|
||||
$exe []= '-t ' . $f->timestamp();
|
||||
break;
|
||||
case 1:
|
||||
$exe []= '-i ' . $coin->identifier;
|
||||
$exe []= 'hist -hi';
|
||||
$exe []= '-s ' . $f->copy()->subYears(10)->year;
|
||||
break;
|
||||
}
|
||||
!d(implode(' ', $exe));
|
||||
$output = shell_exec(implode(' ', $exe));
|
||||
!d($output);
|
||||
}
|
||||
public function run() {
|
||||
}
|
||||
}
|
44
backend/api/composer.json
Normal file
44
backend/api/composer.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "provm/crypto",
|
||||
"description": "Crypto currency API",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"slim/slim": "^4.7",
|
||||
"php-di/slim-bridge": "^3.1",
|
||||
"nyholm/psr7": "^1.4",
|
||||
"nyholm/psr7-server": "^1.0",
|
||||
"zeuxisoo/slim-whoops": "^0.7.3",
|
||||
"provm/models": "^1.0-rc",
|
||||
"spatie/crypto": "^2.0",
|
||||
"robmorgan/phinx": "^0.12.5",
|
||||
"nesbot/carbon": "^2.49"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"kint-php/kint": "^3.3"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Aldarien",
|
||||
"email": "aldarien85@gmail.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ProVM\\Crypto\\Common\\": "common",
|
||||
"ProVM\\Crypto\\": "src",
|
||||
"ProVM\\Common\\": "../../provm/common",
|
||||
"ProVM\\": "../../provm/src"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "http://git.provm.cl/ProVM/models.git"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"secure-http": false
|
||||
}
|
||||
}
|
73
backend/api/db/migrations/20210621171837_create_tables.php
Normal file
73
backend/api/db/migrations/20210621171837_create_tables.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreateTables extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* Write your reversible migrations using this method.
|
||||
*
|
||||
* More information on writing migrations is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
*
|
||||
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||
* with the Table class.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$this->table('coins')
|
||||
->addColumn('identifier', 'string')
|
||||
->addColumn('code', 'string', ['limit' => 5])
|
||||
->addColumn('name', 'string')
|
||||
->addColumn('prefix', 'string', ['default' => ''])
|
||||
->addColumn('suffix', 'string', ['default' => ''])
|
||||
->addColumn('decimals', 'integer', ['default' => 0])
|
||||
->addColumn('ref_url', 'text', ['default' => ''])
|
||||
->create();
|
||||
|
||||
$this->table('locations')
|
||||
->addColumn('name', 'string')
|
||||
->addColumn('description', 'string')
|
||||
->create();
|
||||
|
||||
$cascade = ['update' => 'CASCADE', 'delete' => 'CASCADE'];
|
||||
|
||||
$this->table('wallets')
|
||||
->addColumn('name', 'string')
|
||||
->addColumn('location_id', 'integer')
|
||||
->addForeignKey('location_id', 'locations', 'id', $cascade)
|
||||
->addColumn('public_address', 'string')
|
||||
->create();
|
||||
|
||||
$this->table('values')
|
||||
->addColumn('date_time', 'datetime')
|
||||
->addColumn('coin_id', 'integer')
|
||||
->addForeignKey('coin_id', 'coins', 'id', $cascade)
|
||||
->addColumn('value', 'double')
|
||||
->addColumn('unit_id', 'integer')
|
||||
->addForeignKey('unit_id', 'coins', 'id', $cascade)
|
||||
->create();
|
||||
|
||||
$this->table('transactions')
|
||||
->addColumn('date_time', 'datetime')
|
||||
->addColumn('coin_id', 'integer')
|
||||
->addForeignKey('coin_id', 'coins', 'id', $cascade)
|
||||
->addColumn('wallet_id', 'integer')
|
||||
->addForeignKey('wallet_id', 'wallets', 'id', $cascade)
|
||||
->addColumn('amount', 'double')
|
||||
->addColumn('value', 'double')
|
||||
->addColumn('unit_id', 'integer')
|
||||
->addForeignKey('unit_id', 'coins', 'id', $cascade)
|
||||
->create();
|
||||
|
||||
$this->table('coin_registers')
|
||||
->addColumn('type', 'integer')
|
||||
->addColumn('coin_id', 'integer')
|
||||
->addForeignKey('coin_id', 'coins', 'id')
|
||||
->addColumn('date_time', 'datetime')
|
||||
->create();
|
||||
}
|
||||
}
|
89
backend/api/db/migrations/schema.yml
Normal file
89
backend/api/db/migrations/schema.yml
Normal file
@ -0,0 +1,89 @@
|
||||
name: crypto
|
||||
tables:
|
||||
- name: coins
|
||||
columns:
|
||||
- name: id
|
||||
type: int
|
||||
unsigned: true
|
||||
auto_increment: true
|
||||
- name: code
|
||||
type: varchar(5)
|
||||
- name: name
|
||||
type: varchar(200)
|
||||
- name: ref_url
|
||||
type: text
|
||||
primary_key: id
|
||||
- name: locations
|
||||
columns:
|
||||
- name: id
|
||||
type: int
|
||||
unsigned: true
|
||||
auto_increment: true
|
||||
- name: name
|
||||
type: varchar(100)
|
||||
- name: description
|
||||
type: varchar(200)
|
||||
primary_key: id
|
||||
- name: transactions
|
||||
columns:
|
||||
- name: id
|
||||
type: int
|
||||
unsigned: true
|
||||
auto_increment: true
|
||||
- name: date_time
|
||||
type: datetime
|
||||
- name: coin_id
|
||||
type: int
|
||||
unsigned: true
|
||||
- name: wallet_id
|
||||
type: int
|
||||
unsigned: true
|
||||
- name: amount
|
||||
type: double
|
||||
- name: value
|
||||
type: double
|
||||
- name: unit_id
|
||||
type: int
|
||||
unsigned: true
|
||||
primary_key: id
|
||||
foreign_keys:
|
||||
- table: coins
|
||||
local_column: coin_id
|
||||
foreign_column: id
|
||||
- table: wallets
|
||||
local_column: wallet_id
|
||||
foreign_column: id
|
||||
- table: coins
|
||||
local_column: unit_id
|
||||
foreign_column: id
|
||||
- name: values
|
||||
columns:
|
||||
- name: id
|
||||
type: int
|
||||
unsigned: true
|
||||
auto_increment: true
|
||||
- name: date_time
|
||||
type: datetime
|
||||
- name: coin_id
|
||||
type: int
|
||||
unsigned: true
|
||||
- name: value
|
||||
type: double
|
||||
- name: unit_id
|
||||
type: int
|
||||
unsigned: true
|
||||
primary_key: id
|
||||
foreign_keys:
|
||||
- table: coins
|
||||
local_column: coin_id
|
||||
foreign_column: id
|
||||
- table: coins
|
||||
local_column: unit_id
|
||||
foreign_column: id
|
||||
- name: wallets
|
||||
columns:
|
||||
- name: id
|
||||
- name: name
|
||||
- name: location_id
|
||||
- name: public_address
|
||||
primary_key: id
|
41
backend/api/phinx.php
Normal file
41
backend/api/phinx.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
return
|
||||
[
|
||||
'paths' => [
|
||||
'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
|
||||
'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds'
|
||||
],
|
||||
'environments' => [
|
||||
'default_migration_table' => 'phinxlog',
|
||||
'default_environment' => 'development',
|
||||
'production' => [
|
||||
'adapter' => 'mysql',
|
||||
'host' => $_ENV['DB_HOST'],
|
||||
'name' => $_ENV['MYSQL_DATABASE'],
|
||||
'user' => $_ENV['MYSQL_USER'],
|
||||
'pass' => $_ENV['MYSQL_PASSWORD'],
|
||||
//'port' => '3306',
|
||||
'charset' => 'utf8',
|
||||
],
|
||||
'development' => [
|
||||
'adapter' => 'mysql',
|
||||
'host' => $_ENV['DB_HOST'],
|
||||
'name' => $_ENV['MYSQL_DATABASE'] . '_dev',
|
||||
'user' => $_ENV['MYSQL_USER'],
|
||||
'pass' => $_ENV['MYSQL_PASSWORD'],
|
||||
//'port' => '3306',
|
||||
'charset' => 'utf8',
|
||||
],
|
||||
'testing' => [
|
||||
'adapter' => 'mysql',
|
||||
'host' => $_ENV['DB_HOST'],
|
||||
'name' => $_ENV['MYSQL_DATABASE'] . '_test',
|
||||
'user' => $_ENV['MYSQL_USER'],
|
||||
'pass' => $_ENV['MYSQL_PASSWORD'],
|
||||
//'port' => '3306',
|
||||
'charset' => 'utf8',
|
||||
]
|
||||
],
|
||||
'version_order' => 'creation'
|
||||
];
|
0
backend/api/public/.htaccess
Normal file
0
backend/api/public/.htaccess
Normal file
9
backend/api/public/index.php
Normal file
9
backend/api/public/index.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
include_once implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__),
|
||||
'setup',
|
||||
'app.php'
|
||||
]);
|
||||
$app->run();
|
12
backend/api/resources/data/api.json
Normal file
12
backend/api/resources/data/api.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"routes": [
|
||||
{
|
||||
"alias": [
|
||||
"/",
|
||||
"/help"
|
||||
],
|
||||
"description": "API help"
|
||||
}
|
||||
]
|
||||
}
|
15
backend/api/resources/routes/api.php
Normal file
15
backend/api/resources/routes/api.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
use ProVM\Crypto\Common\Controller\API;
|
||||
|
||||
$folder = __DIR__ . DIRECTORY_SEPARATOR . 'api';
|
||||
if (file_exists($folder)) {
|
||||
$files = new DirectoryIterator($folder);
|
||||
foreach ($files as $file) {
|
||||
if ($file->getExtension() != 'php') {
|
||||
continue;
|
||||
}
|
||||
include_once $file->getRealPath();
|
||||
}
|
||||
}
|
||||
|
||||
$app->get('[/]', API::class);
|
13
backend/api/resources/routes/api/coins.php
Normal file
13
backend/api/resources/routes/api/coins.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
use ProVM\Crypto\Common\Controller\Coins;
|
||||
|
||||
$app->group('/coins', function($app) {
|
||||
$app->post('/add', [Coins::class, 'add']);
|
||||
$app->get('[/]', Coins::class);
|
||||
});
|
||||
|
||||
$app->group('/coin/{coin_id}', function($app) {
|
||||
$app->put('/edit', [Coins::class, 'edit']);
|
||||
$app->delete('/delete', [Coins::class, 'delete']);
|
||||
$app->get('[/]', [Coins::class, 'show']);
|
||||
});
|
13
backend/api/resources/routes/api/locations.php
Normal file
13
backend/api/resources/routes/api/locations.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
use ProVM\Crypto\Common\Controller\Locations;
|
||||
|
||||
$app->group('/locations', function($app) {
|
||||
$app->post('/add', [Locations::class, 'add']);
|
||||
$app->get('[/]', Locations::class);
|
||||
});
|
||||
|
||||
$app->group('/location/{location_id}', function($app) {
|
||||
$app->put('/edit', [Locations::class, 'edit']);
|
||||
$app->delete('/delete', [Locations::class, 'delete']);
|
||||
$app->get('[/]', [Locations::class, 'show']);
|
||||
});
|
7
backend/api/resources/routes/api/update.php
Normal file
7
backend/api/resources/routes/api/update.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
use ProVM\Crypto\Common\Controller\Update;
|
||||
|
||||
$app->group('/update', function($app) {
|
||||
$app->get('/register/{type:[0,1]}/{coin_id:[\d+]}', [Update::class, 'register']);
|
||||
$app->get('[/]', Update::class);
|
||||
});
|
13
backend/api/resources/routes/api/wallets.php
Normal file
13
backend/api/resources/routes/api/wallets.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
use ProVM\Crypto\Common\Controller\Wallets;
|
||||
|
||||
$app->group('/wallets', function($app) {
|
||||
$app->post('/add', [Wallets::class, 'add']);
|
||||
$app->get('[/]', Wallets::class);
|
||||
});
|
||||
|
||||
$app->group('/wallet/{wallet_id}', function($app) {
|
||||
$app->put('/edit', [Wallets::class, 'edit']);
|
||||
$app->delete('/delete', [Wallets::class, 'delete']);
|
||||
$app->get('[/]', [Wallets::class, 'show']);
|
||||
});
|
1
backend/api/setup/api/middleware.php
Normal file
1
backend/api/setup/api/middleware.php
Normal file
@ -0,0 +1 @@
|
||||
<?php
|
38
backend/api/setup/api/settings.php
Normal file
38
backend/api/setup/api/settings.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
use Psr\Container\ContainerInterface as Container;
|
||||
|
||||
return [
|
||||
'locations' => function() {
|
||||
$arr = ['base' => dirname(__DIR__, 2)];
|
||||
$arr['resources'] = implode(DIRECTORY_SEPARATOR, [
|
||||
$arr['base'],
|
||||
'resources'
|
||||
]);
|
||||
$arr['data'] = implode(DIRECTORY_SEPARATOR, [
|
||||
$arr['resources'],
|
||||
'data'
|
||||
]);
|
||||
$arr['routes'] = implode(DIRECTORY_SEPARATOR, [
|
||||
$arr['resources'],
|
||||
'routes'
|
||||
]);
|
||||
$arr['bin'] = implode(DIRECTORY_SEPARATOR, [
|
||||
dirname($arr['base']),
|
||||
'automation',
|
||||
'bin'
|
||||
]);
|
||||
return (object) $arr;
|
||||
},
|
||||
'coingecko' => function(Container $c) {
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
$c->get('locations')->bin,
|
||||
'coingecko'
|
||||
]);
|
||||
},
|
||||
'mindicador' => function(Container $c) {
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
$c->get('locations')->bin,
|
||||
'mindicador'
|
||||
]);
|
||||
}
|
||||
];
|
24
backend/api/setup/api/setups.php
Normal file
24
backend/api/setup/api/setups.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
use Psr\Container\ContainerInterface as Container;
|
||||
|
||||
return [
|
||||
ProVM\Crypto\Common\Service\API::class => function(Container $container) {
|
||||
$filename = implode(DIRECTORY_SEPARATOR, [
|
||||
$container->get('locations')->data,
|
||||
'api.json'
|
||||
]);
|
||||
return new ProVM\Crypto\Common\Service\API($filename);
|
||||
},
|
||||
ProVM\Common\Factory\Model::class => function(Container $container) {
|
||||
return new ProVM\Crypto\Common\Factory\Model();
|
||||
},
|
||||
ProVM\Crypto\Common\Service\Update::class => function(Container $container) {
|
||||
return new ProVM\Crypto\Common\Service\Update(
|
||||
$container->get(ProVM\Crypto\Common\Factory\Model::class),
|
||||
[
|
||||
$container->get('coingecko'),
|
||||
$container->get('mindicador')
|
||||
]
|
||||
);
|
||||
}
|
||||
];
|
55
backend/api/setup/app.php
Normal file
55
backend/api/setup/app.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
use DI\ContainerBuilder as Builder;
|
||||
use DI\Bridge\Slim\Bridge;
|
||||
|
||||
include_once 'composer.php';
|
||||
|
||||
$builder = new Builder();
|
||||
|
||||
$folders = [
|
||||
'env',
|
||||
'common',
|
||||
'api'
|
||||
];
|
||||
$files = [
|
||||
'settings',
|
||||
'setups'
|
||||
];
|
||||
|
||||
foreach ($files as $file) {
|
||||
foreach ($folders as $folder) {
|
||||
$filename = implode(DIRECTORY_SEPARATOR, [
|
||||
__DIR__,
|
||||
$folder,
|
||||
$file . '.php'
|
||||
]);
|
||||
if (!file_exists($filename)) {
|
||||
continue;
|
||||
}
|
||||
$builder->addDefinitions($filename);
|
||||
}
|
||||
}
|
||||
|
||||
$container = $builder->build();
|
||||
$app = Bridge::create($container);
|
||||
//$app->setBasePath($container->get('base_url'));
|
||||
$app->add(new ProVM\Crypto\Common\Middleware\CORS());
|
||||
$app->addRoutingMiddleware();
|
||||
|
||||
include_once 'databases.php';
|
||||
|
||||
foreach ($folders as $folder) {
|
||||
$filename = implode(DIRECTORY_SEPARATOR, [
|
||||
__DIR__,
|
||||
$folder,
|
||||
'middlewares.php'
|
||||
]);
|
||||
if (!file_exists($filename)) {
|
||||
continue;
|
||||
}
|
||||
include_once $filename;
|
||||
}
|
||||
|
||||
include_once 'router.php';
|
||||
|
||||
$app->add(new Zeuxisoo\Whoops\Slim\WhoopsMiddleware(['enable' => $container->get('debug') ?: true]));
|
6
backend/api/setup/composer.php
Normal file
6
backend/api/setup/composer.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
include_once implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__),
|
||||
'vendor',
|
||||
'autoload.php'
|
||||
]);
|
3
backend/api/setup/databases.php
Normal file
3
backend/api/setup/databases.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
$service = $app->getContainer()->get(ProVM\Common\Service\Database::class);
|
||||
$service->load();
|
25
backend/api/setup/env/settings.php
vendored
Normal file
25
backend/api/setup/env/settings.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
return [
|
||||
'base_url' => '',
|
||||
'debug' => $_ENV['DEBUG'],
|
||||
'databases' => function() {
|
||||
$settings = [
|
||||
'short_names' => true,
|
||||
'dbs' => []
|
||||
];
|
||||
$default = [
|
||||
'engine' => 'mysql',
|
||||
'host' => (object) [
|
||||
'name' => $_ENV['DB_HOST'],
|
||||
'port' => $_ENV['DB_PORT'] ?? null
|
||||
],
|
||||
'user' => (object) [
|
||||
'name' => $_ENV['MYSQL_USER'],
|
||||
'password' => $_ENV['MYSQL_PASSWORD']
|
||||
],
|
||||
'name' => $_ENV['MYSQL_DATABASE'] . ($_ENV['ENV'] ? '_' . $_ENV['ENV'] : '')
|
||||
];
|
||||
$settings['dbs']['default'] = (object) $default;
|
||||
return (object) $settings;
|
||||
}
|
||||
];
|
8
backend/api/setup/env/setups.php
vendored
Normal file
8
backend/api/setup/env/setups.php
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
use Psr\Container\ContainerInterface as Container;
|
||||
|
||||
return [
|
||||
ProVM\Common\Service\Database::class => function(Container $container) {
|
||||
return new ProVM\Common\Service\Database($container->get('databases'));
|
||||
}
|
||||
];
|
5
backend/api/setup/router.php
Normal file
5
backend/api/setup/router.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
include_once implode(DIRECTORY_SEPARATOR, [
|
||||
$app->getContainer()->get('locations')->routes,
|
||||
'api.php'
|
||||
]);
|
36
backend/api/src/Coin.php
Normal file
36
backend/api/src/Coin.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use ProVM\Common\Alias\Model;
|
||||
use ProVM\Common\Factory\Model as Factory;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $identifier
|
||||
* @property string $code
|
||||
* @property string $name
|
||||
* @property string $prefix
|
||||
* @property string $suffix
|
||||
* @property int $decimals
|
||||
* @property string $ref_url
|
||||
*/
|
||||
class Coin extends Model {
|
||||
protected static $_table = 'coins';
|
||||
protected static $fields = ['code', 'name', 'prefix', 'suffix', 'decimals', 'ref_url'];
|
||||
|
||||
public function format(float $value): string {
|
||||
$output = [];
|
||||
if ($this->prefix == '') {
|
||||
$output []= $this->prefix;
|
||||
}
|
||||
$output []= number_format($value, $this->decimals ?? 0, ',', '.');
|
||||
if ($this->suffix == '') {
|
||||
$output []= $this->suffix;
|
||||
}
|
||||
return implode(' ', $output);
|
||||
}
|
||||
|
||||
public static function find(Factory $factory, $input) {
|
||||
return $factory->find(Coin::class)->where([['code', $input->code]])->one();
|
||||
}
|
||||
}
|
14
backend/api/src/Location.php
Normal file
14
backend/api/src/Location.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use ProVM\Common\Alias\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
*/
|
||||
class Location extends Model {
|
||||
protected static $_table = 'locations';
|
||||
protected static $fields = ['name', 'description'];
|
||||
}
|
43
backend/api/src/Transaction.php
Normal file
43
backend/api/src/Transaction.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use ProVM\Common\Alias\Model;
|
||||
use ProVM\Common\Define\Model\DateTime as DT;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property \DateTime $date_time
|
||||
* @property Coin $coin_id
|
||||
* @property Wallet $wallet_id
|
||||
* @property float $amount
|
||||
* @property float $value
|
||||
* @property Coin $unit_id
|
||||
*/
|
||||
class Transaction extends Model {
|
||||
use DT;
|
||||
|
||||
protected static $_table = 'transactions';
|
||||
protected static $fields = ['date_time', 'coin_id', 'wallet_id', 'amount', 'value', 'unit_id'];
|
||||
|
||||
protected $coin;
|
||||
public function coin() {
|
||||
if ($this->coin === null) {
|
||||
$this->coin = $this->childOf(Coin::class, [Model::SELF_KEY => 'coin_id']);
|
||||
}
|
||||
return $this->coin;
|
||||
}
|
||||
protected $wallet;
|
||||
public function wallet() {
|
||||
if ($this->wallet === null) {
|
||||
$this->wallet = $this->childOf(Wallet::class, [Model::SELF_KEY => 'wallet_id']);
|
||||
}
|
||||
return $this->wallet;
|
||||
}
|
||||
protected $unit;
|
||||
public function unit() {
|
||||
if ($this->unit === null) {
|
||||
$this->unit = $this->childOf(Coin::class, [Model::SELF_KEY => 'unit_id']);
|
||||
}
|
||||
return $this->unit;
|
||||
}
|
||||
}
|
34
backend/api/src/Value.php
Normal file
34
backend/api/src/Value.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use ProVM\Common\Alias\Model;
|
||||
use ProVM\Common\Define\Model\DateTime as DT;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property \DateTime $date_time
|
||||
* @property Coin $coin_id
|
||||
* @property float $value
|
||||
* @property Coin $unit_id
|
||||
*/
|
||||
class Value extends Model {
|
||||
use DT;
|
||||
|
||||
public static $_table = 'values';
|
||||
protected static $fields = ['date_time', 'coin_id', 'value', 'unit_id'];
|
||||
|
||||
protected $coin;
|
||||
public function coin() {
|
||||
if ($this->coin === null) {
|
||||
$this->coin = $this->childOf(Coin::class, [Model::SELF_KEY => 'coin_id']);
|
||||
}
|
||||
return $this->coin;
|
||||
}
|
||||
protected $unit;
|
||||
public function unit() {
|
||||
if ($this->unit === null) {
|
||||
$this->unit = $this->childOf(Coin::class, [Model::SELF_KEY => 'unit_id']);
|
||||
}
|
||||
return $this->unit;
|
||||
}
|
||||
}
|
29
backend/api/src/Wallet.php
Normal file
29
backend/api/src/Wallet.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace ProVM\Crypto;
|
||||
|
||||
use ProVM\Common\Alias\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property Location $location_id
|
||||
* @property string $public_address
|
||||
*/
|
||||
class Wallet extends Model {
|
||||
protected static $_table = 'wallets';
|
||||
protected static $fields = ['name', 'location_id', 'public_address'];
|
||||
|
||||
protected $location;
|
||||
public function location() {
|
||||
if ($this->location === null) {
|
||||
$this->location = $this->childOf(Location::class, [Model::SELF_KEY => 'location_id']);
|
||||
}
|
||||
return $this->location;
|
||||
}
|
||||
public function setLocation(Location $location) {
|
||||
if ($location->name == $this->location->name) {
|
||||
return;
|
||||
}
|
||||
$this->location = $location;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user