@ -0,0 +1,17 @@ | |||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. | |||
# For additional information regarding the format and rule options, please see: | |||
# https://github.com/browserslist/browserslist#queries | |||
# For the full list of supported browsers by the Angular framework, please see: | |||
# https://angular.io/guide/browser-support | |||
# You can see what browsers were selected by your queries by running: | |||
# npx browserslist | |||
last 1 Chrome version | |||
last 1 Firefox version | |||
last 2 Edge major versions | |||
last 2 Safari major versions | |||
last 2 iOS major versions | |||
Firefox ESR | |||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. |
@ -0,0 +1,16 @@ | |||
# Editor configuration, see https://editorconfig.org | |||
root = true | |||
[*] | |||
charset = utf-8 | |||
indent_style = space | |||
indent_size = 2 | |||
insert_final_newline = true | |||
trim_trailing_whitespace = true | |||
[*.ts] | |||
quote_type = single | |||
[*.md] | |||
max_line_length = off | |||
trim_trailing_whitespace = false |
@ -0,0 +1,45 @@ | |||
# See http://help.github.com/ignore-files/ for more about ignoring files. | |||
# compiled output | |||
/dist | |||
/tmp | |||
/out-tsc | |||
# Only exists if Bazel was run | |||
/bazel-out | |||
# dependencies | |||
/node_modules | |||
# profiling files | |||
chrome-profiler-events*.json | |||
# IDEs and editors | |||
/.idea | |||
.project | |||
.classpath | |||
.c9/ | |||
*.launch | |||
.settings/ | |||
*.sublime-workspace | |||
# IDE - VSCode | |||
.vscode/* | |||
!.vscode/settings.json | |||
!.vscode/tasks.json | |||
!.vscode/launch.json | |||
!.vscode/extensions.json | |||
.history/* | |||
# misc | |||
/.sass-cache | |||
/connect.lock | |||
/coverage | |||
/libpeerconnection.log | |||
npm-debug.log | |||
yarn-error.log | |||
testem.log | |||
/typings | |||
# System Files | |||
.DS_Store | |||
Thumbs.db |
@ -0,0 +1,27 @@ | |||
# Panel Boilerplate | |||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.1.4. | |||
## Development server | |||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. | |||
## Code scaffolding | |||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. | |||
## Build | |||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. | |||
## Running unit tests | |||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). | |||
## Running end-to-end tests | |||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. | |||
## Further help | |||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. |
@ -0,0 +1,111 @@ | |||
{ | |||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | |||
"version": 1, | |||
"newProjectRoot": "projects", | |||
"projects": { | |||
"panel-boilerplate": { | |||
"projectType": "application", | |||
"schematics": { | |||
"@schematics/angular:component": { | |||
"style": "scss" | |||
}, | |||
"@schematics/angular:application": { | |||
"strict": true | |||
} | |||
}, | |||
"root": "", | |||
"sourceRoot": "src", | |||
"prefix": "app", | |||
"architect": { | |||
"build": { | |||
"builder": "@angular-devkit/build-angular:browser", | |||
"options": { | |||
"outputPath": "dist/panel-boilerplate", | |||
"index": "src/index.html", | |||
"main": "src/main.ts", | |||
"polyfills": "src/polyfills.ts", | |||
"tsConfig": "tsconfig.app.json", | |||
"inlineStyleLanguage": "scss", | |||
"assets": [ | |||
"src/favicon.ico", | |||
"src/assets" | |||
], | |||
"styles": [ | |||
"src/styles.scss" | |||
], | |||
"scripts": [] | |||
}, | |||
"configurations": { | |||
"production": { | |||
"budgets": [ | |||
{ | |||
"type": "initial", | |||
"maximumWarning": "500kb", | |||
"maximumError": "1mb" | |||
}, | |||
{ | |||
"type": "anyComponentStyle", | |||
"maximumWarning": "2kb", | |||
"maximumError": "4kb" | |||
} | |||
], | |||
"fileReplacements": [ | |||
{ | |||
"replace": "src/environments/environment.ts", | |||
"with": "src/environments/environment.prod.ts" | |||
} | |||
], | |||
"outputHashing": "all" | |||
}, | |||
"development": { | |||
"buildOptimizer": false, | |||
"optimization": false, | |||
"vendorChunk": true, | |||
"extractLicenses": false, | |||
"sourceMap": true, | |||
"namedChunks": true | |||
} | |||
}, | |||
"defaultConfiguration": "production" | |||
}, | |||
"serve": { | |||
"builder": "@angular-devkit/build-angular:dev-server", | |||
"configurations": { | |||
"production": { | |||
"browserTarget": "panel-boilerplate:build:production" | |||
}, | |||
"development": { | |||
"browserTarget": "panel-boilerplate:build:development" | |||
} | |||
}, | |||
"defaultConfiguration": "development" | |||
}, | |||
"extract-i18n": { | |||
"builder": "@angular-devkit/build-angular:extract-i18n", | |||
"options": { | |||
"browserTarget": "panel-boilerplate:build" | |||
} | |||
}, | |||
"test": { | |||
"builder": "@angular-devkit/build-angular:karma", | |||
"options": { | |||
"main": "src/test.ts", | |||
"polyfills": "src/polyfills.ts", | |||
"tsConfig": "tsconfig.spec.json", | |||
"karmaConfig": "karma.conf.js", | |||
"inlineStyleLanguage": "scss", | |||
"assets": [ | |||
"src/favicon.ico", | |||
"src/assets" | |||
], | |||
"styles": [ | |||
"src/styles.scss" | |||
], | |||
"scripts": [] | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
"defaultProject": "panel-boilerplate" | |||
} |
@ -0,0 +1,44 @@ | |||
// Karma configuration file, see link for more information | |||
// https://karma-runner.github.io/1.0/config/configuration-file.html | |||
module.exports = function (config) { | |||
config.set({ | |||
basePath: '', | |||
frameworks: ['jasmine', '@angular-devkit/build-angular'], | |||
plugins: [ | |||
require('karma-jasmine'), | |||
require('karma-chrome-launcher'), | |||
require('karma-jasmine-html-reporter'), | |||
require('karma-coverage'), | |||
require('@angular-devkit/build-angular/plugins/karma') | |||
], | |||
client: { | |||
jasmine: { | |||
// you can add configuration options for Jasmine here | |||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html | |||
// for example, you can disable the random execution with `random: false` | |||
// or set a specific seed with `seed: 4321` | |||
}, | |||
clearContext: false // leave Jasmine Spec Runner output visible in browser | |||
}, | |||
jasmineHtmlReporter: { | |||
suppressAll: true // removes the duplicated traces | |||
}, | |||
coverageReporter: { | |||
dir: require('path').join(__dirname, './coverage/panel-boilerplate'), | |||
subdir: '.', | |||
reporters: [ | |||
{ type: 'html' }, | |||
{ type: 'text-summary' } | |||
] | |||
}, | |||
reporters: ['progress', 'kjhtml'], | |||
port: 9876, | |||
colors: true, | |||
logLevel: config.LOG_INFO, | |||
autoWatch: true, | |||
browsers: ['Chrome'], | |||
singleRun: false, | |||
restartOnFileChange: true | |||
}); | |||
}; |
@ -0,0 +1,64 @@ | |||
{ | |||
"name": "panel-boilerplate", | |||
"description": "This is a panel from boilerplate", | |||
"keywords": [ | |||
"Panel", | |||
"Admin Panel", | |||
"Angular Panel", | |||
"Boilerplate" | |||
], | |||
"license": "MIT", | |||
"homepage": "https://iamroot.ir/repo/panel-boilerplate", | |||
"readme": "https://github.com/iamnonroot/panel-boilerplate#readme", | |||
"author": { | |||
"name": "!Root", | |||
"url": "https://iamroot.ir", | |||
"email": "iamnonroot@gmail.com" | |||
}, | |||
"bugs": { | |||
"email": "iamnonroot@gmail.com", | |||
"url": "https://github.com/iamnonroot/panel-boilerplate/issues" | |||
}, | |||
"repository": { | |||
"type": "git", | |||
"url": "https://github.com/iamnonroot/panel-boilerplate" | |||
}, | |||
"version": "0.0.0", | |||
"scripts": { | |||
"ng": "ng", | |||
"start": "ng serve", | |||
"build": "ng build", | |||
"watch": "ng build --watch --configuration development", | |||
"test": "ng test" | |||
}, | |||
"private": true, | |||
"dependencies": { | |||
"@angular/animations": "~12.1.0", | |||
"@angular/cdk": "^12.2.0", | |||
"@angular/common": "~12.1.0", | |||
"@angular/compiler": "~12.1.0", | |||
"@angular/core": "~12.1.0", | |||
"@angular/forms": "~12.1.0", | |||
"@angular/material": "^12.2.0", | |||
"@angular/platform-browser": "~12.1.0", | |||
"@angular/platform-browser-dynamic": "~12.1.0", | |||
"@angular/router": "~12.1.0", | |||
"rxjs": "~6.6.0", | |||
"tslib": "^2.2.0", | |||
"zone.js": "~0.11.4" | |||
}, | |||
"devDependencies": { | |||
"@angular-devkit/build-angular": "~12.1.4", | |||
"@angular/cli": "~12.1.4", | |||
"@angular/compiler-cli": "~12.1.0", | |||
"@types/jasmine": "~3.8.0", | |||
"@types/node": "^12.11.1", | |||
"jasmine-core": "~3.8.0", | |||
"karma": "~6.3.0", | |||
"karma-chrome-launcher": "~3.1.0", | |||
"karma-coverage": "~2.0.3", | |||
"karma-jasmine": "~4.0.0", | |||
"karma-jasmine-html-reporter": "~1.7.0", | |||
"typescript": "~4.3.2" | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
export * from './auth/api.auth.interface' | |||
export * from './wallet/api.wallet.interface' | |||
export * from './transactions/api.transactions.interface' | |||
export * from './kyc/api.kyc.interface' | |||
export * from './system/api.system.interface' | |||
export * from './fund/api.fund.interface' |
@ -0,0 +1,16 @@ | |||
import { TestBed } from '@angular/core/testing'; | |||
import { ApiService } from './api.service'; | |||
describe('ApiService', () => { | |||
let service: ApiService; | |||
beforeEach(() => { | |||
TestBed.configureTestingModule({}); | |||
service = TestBed.inject(ApiService); | |||
}); | |||
it('should be created', () => { | |||
expect(service).toBeTruthy(); | |||
}); | |||
}); |
@ -0,0 +1,19 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { CAuth, Auth } from './auth/api.auth'; | |||
import { CFund, Fund } from './fund/api.fund'; | |||
import { CKYC, KYC } from './kyc/api.kyc'; | |||
import { CSystem, System } from './system/api.system'; | |||
import { CTransaction, Transaction } from './transactions/api.transactions'; | |||
import { CWallet, Wallet } from './wallet/api.wallet'; | |||
@Injectable({ | |||
providedIn: 'root' | |||
}) | |||
export class ApiService { | |||
public Auth: CAuth = Auth | |||
public Wallet: CWallet = Wallet | |||
public Transaction: CTransaction = Transaction | |||
public KYC: CKYC = KYC | |||
public System: CSystem = System | |||
public Fund: CFund = Fund | |||
} |
@ -0,0 +1,76 @@ | |||
export interface IRegisterData { | |||
ref_code: string | |||
email: string | |||
phone: string | |||
password: string | |||
} | |||
export interface ILoginData { | |||
email: string | |||
password: string | |||
} | |||
// ============== response ============== // | |||
export interface IErrorResponse { | |||
statusCode: number; | |||
message: string; | |||
} | |||
export interface ITokensResponse { | |||
access: { | |||
token: string; | |||
expires: Date; | |||
} | |||
refresh: { | |||
token: string; | |||
expires: Date; | |||
} | |||
} | |||
export interface IAuthLoginResponse { | |||
expiresIn: number; | |||
token: string; | |||
} | |||
export interface IAuthRegisterResponse { | |||
id: number; | |||
name: null; | |||
name_status: null | number; | |||
last_name: null; | |||
last_name_status: null | number; | |||
phone_status: null | number; | |||
homeNumber: null; | |||
homeNumber_status: null | number; | |||
birthDate: null; | |||
birthDate_status: null | number; | |||
nationalId: null; | |||
nationalId_status: null | number; | |||
city: null; | |||
city_status: null | number; | |||
address: null; | |||
address_status: null | number; | |||
introducer: null; | |||
nationalId_image: null; | |||
nationalId_image_status: null | number; | |||
selfi_image: null; | |||
selfi_image_status: null | number; | |||
status: null | 'pending' | 'active' | 'suspend' | 'failed' | ''; | |||
token_email: null; | |||
token_sms: null; | |||
ref_code: string; | |||
email_status: null | number; | |||
role: null; | |||
_id: string; | |||
email: string; | |||
password: string; | |||
phone: string; | |||
__v: number; | |||
} | |||
export interface IAuthProfileResponse extends IAuthRegisterResponse { } | |||
export interface IAuthResetPassword { | |||
status: boolean | |||
} |
@ -0,0 +1,109 @@ | |||
import { IRegisterData, IAuthLoginResponse, ILoginData, IAuthRegisterResponse, IAuthProfileResponse, IAuthResetPassword } from './api.auth.interface'; | |||
import { environment } from '../../environments/environment'; | |||
export class CAuth { | |||
public get AccessToken(): string | null { | |||
return window.localStorage.getItem('melon:access-token') | |||
} | |||
public set AccessToken(value: string | null) { | |||
value == null ? | |||
window.localStorage.removeItem('melon:access-token') : | |||
window.localStorage.setItem('melon:access-token', value!) | |||
} | |||
public async Register(data: IRegisterData): Promise<IAuthRegisterResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json' | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Login(data: ILoginData): Promise<IAuthLoginResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/auth`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json' | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Me(): Promise<IAuthProfileResponse | null> { | |||
try { | |||
if (!this.AccessToken) return Promise.resolve(null) | |||
let res = await fetch(`${environment.endpoint}/auth/profile`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${this.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async RequestResetPassword(email: string): Promise<IAuthResetPassword> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/password/send/email`, { | |||
method: 'POST', | |||
body: JSON.stringify({ email }), | |||
headers: { | |||
'content-type': 'application/json' | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async ResetPassword(email: string, password: string, token: string): Promise<IAuthResetPassword> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/password/check/email`, { | |||
method: 'POST', | |||
body: JSON.stringify({ email, password, token }), | |||
headers: { | |||
'content-type': 'application/json' | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async ChangePassword(password: string): Promise<IAuthResetPassword> { | |||
try { | |||
if (!this.AccessToken) return Promise.resolve({ 'status': false }) | |||
let res = await fetch(`${environment.endpoint}/users/password/update`, { | |||
method: 'POST', | |||
body: JSON.stringify({ password }), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${this.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
} | |||
export const Auth: CAuth = new CAuth() |
@ -0,0 +1,67 @@ | |||
export interface Fund { | |||
id: number; | |||
init_date: Date; | |||
portfolio: Portfolio[]; | |||
chart: Chart[]; | |||
_id: string; | |||
title: string; | |||
description: string; | |||
minimum_investment: number; | |||
lock_time: number; | |||
ftype: string; | |||
kyc: boolean | undefined | null | |||
createdAt: Date; | |||
updatedAt: Date; | |||
} | |||
export interface Chart { | |||
date: number; | |||
percent: number; | |||
} | |||
export interface Portfolio { | |||
title: string; | |||
color: string; | |||
percent: number; | |||
} | |||
export interface IFundResponse { | |||
funds: Fund | |||
} | |||
export type IFundsResponse = Fund[] | |||
export interface IFundType { | |||
type: string | |||
name: string | |||
description: string | |||
} | |||
export interface IInvestData { | |||
value: number | |||
fund_id: string | |||
} | |||
export interface IDeinvestData { | |||
id: string | |||
} | |||
export interface IInvestResponse { | |||
id: string | |||
} | |||
export interface IInvest { | |||
id: number; | |||
status: number; | |||
created_at: Date; | |||
updated_at: Date; | |||
_id: string; | |||
user_id: number; | |||
type: string; | |||
currency: string; | |||
value: number; | |||
fund_id: string; | |||
locked: boolean; | |||
} | |||
export type IInvests = IInvest[] |
@ -0,0 +1,102 @@ | |||
import { IDeinvestData, IFundResponse, IFundsResponse, IFundType, IInvestData, IInvestResponse, IInvests } from "./api.fund.interface"; | |||
import { environment } from '../../environments/environment'; | |||
import { Auth } from "../auth/api.auth"; | |||
export class CFund { | |||
public async All(): Promise<IFundsResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/fund/all`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async One(id: string): Promise<IFundResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/fund/${id}`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Type(): Promise<IFundType[]> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/fund/types`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Invest(data: IInvestData): Promise<IInvestResponse> { | |||
try { | |||
(data as any).type = 'stake'; | |||
(data as any).currency = 'USDT'; | |||
let res = await fetch(`${environment.endpoint}/stakes`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Deinvest(data: IDeinvestData): Promise<IInvestResponse> { | |||
try { | |||
(data as any).type = 'unstake'; | |||
(data as any).currency = 'USDT'; | |||
let res = await fetch(`${environment.endpoint}/stakes`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Table(): Promise<IInvests> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/stakes`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
} | |||
export const Fund: CFund = new CFund() |
@ -0,0 +1,19 @@ | |||
export interface IUploadResponse { | |||
status: number | |||
} | |||
export type UploadID = 'nationalId' | 'selfi' | |||
export interface ISubmitData { | |||
name: string, | |||
lastName: string, | |||
homeNumber: string, | |||
BirthDate: string, | |||
nationalId: string, | |||
city: string, | |||
address: string | |||
} | |||
export interface ISubmitResponse { | |||
status: boolean | |||
} |
@ -0,0 +1,116 @@ | |||
import { ISubmitData, ISubmitResponse, IUploadResponse, UploadID } from "./api.kyc.interface"; | |||
import { environment } from '../../environments/environment'; | |||
import { Auth } from "../auth/api.auth"; | |||
export class CKYC { | |||
public async Upload(id: UploadID, file: File): Promise<IUploadResponse> { | |||
try { | |||
let form = new FormData() | |||
form.append(id, file) | |||
let res = await fetch(`${environment.endpoint}/users/kyc/${id}`, { | |||
method: 'POST', | |||
body: form, | |||
headers: { | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Submit(data: ISubmitData): Promise<ISubmitResponse> { | |||
try { | |||
(data as any).token = 'is_angular' | |||
let res = await fetch(`${environment.endpoint}/users/kyc/check/final`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async SendEmailToken(): Promise<any> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/kyc/send/email`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async SendPhoneToken(): Promise<any> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/kyc/send/sms`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async VerifyEmailToken(token: string): Promise<ISubmitResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/kyc/check/email`, { | |||
method: 'POST', | |||
body: JSON.stringify({ token }), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async VerifyPhoneToken(token: string): Promise<ISubmitResponse> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/users/kyc/check/sms`, { | |||
method: 'POST', | |||
body: JSON.stringify({ token }), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public ImageOf(file: string): string { | |||
return `${environment.endpoint}/uploads/kyc/${file}` | |||
} | |||
} | |||
export const KYC: CKYC = new CKYC() |
@ -0,0 +1,6 @@ | |||
export interface INetwork { | |||
type: 'ERC20' | 'TRC20' | 'KCC' | |||
address: string | |||
fee: number | |||
description: string | |||
} |
@ -0,0 +1,20 @@ | |||
import { INetwork } from "./api.system.interface"; | |||
import { environment } from '../../environments/environment'; | |||
export class CSystem { | |||
public async Networks(): Promise<INetwork[]> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/wallet/system/list`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
} | |||
export const System: CSystem = new CSystem() |
@ -0,0 +1,33 @@ | |||
export interface Transaction { | |||
id: number; | |||
fee: number; | |||
status: number; | |||
tx_id: string; | |||
created_at: Date; | |||
updated_at: Date; | |||
_id: string; | |||
user_id: number; | |||
type: 'deposit' | 'withdraw'; | |||
currency: string; | |||
value: number; | |||
network: string; | |||
wallet: string; | |||
} | |||
export type Transactions = Transaction[] | |||
export interface ITransactionResponse { | |||
id: string | |||
} | |||
export interface IDepositData { | |||
network: string | |||
value: number | |||
tx_id: string | |||
} | |||
export interface IWithdrawData { | |||
network: string | |||
value: number | |||
wallet: string | |||
} |
@ -0,0 +1,58 @@ | |||
import { IDepositData, ITransactionResponse, IWithdrawData, Transactions } from "./api.transactions.interface" | |||
import { environment } from '../../environments/environment' | |||
import { Auth } from "../auth/api.auth" | |||
export class CTransaction { | |||
public async All(): Promise<Transactions> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/transactions`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Deposit(data: IDepositData): Promise<ITransactionResponse> { | |||
try { | |||
(data as any)['type'] = 'deposit'; | |||
(data as any)['currency'] = 'USDT'; | |||
let res = await fetch(`${environment.endpoint}/transactions`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Widthdraw(data: IWithdrawData): Promise<ITransactionResponse> { | |||
try { | |||
(data as any)['type'] = 'withdraw'; | |||
(data as any)['currency'] = 'USDT'; | |||
let res = await fetch(`${environment.endpoint}/transactions`, { | |||
method: 'POST', | |||
body: JSON.stringify(data), | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
} | |||
export const Transaction: CTransaction = new CTransaction() |
@ -0,0 +1,14 @@ | |||
export interface Wallet { | |||
id: number; | |||
status: number; | |||
last_locked_at: Date; | |||
_id: string; | |||
user_id: number; | |||
balance: number; | |||
locked: number; | |||
createdAt: Date; | |||
updatedAt: Date; | |||
__v: number; | |||
} | |||
export type Wallets = Wallet[] |
@ -0,0 +1,37 @@ | |||
import { Wallets } from "./api.wallet.interface"; | |||
import { environment } from '../../environments/environment'; | |||
import { Auth } from "../auth/api.auth"; | |||
export class CWallet { | |||
public async All(): Promise<Wallets> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/wallet`, { | |||
method: 'GET', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
public async Create(): Promise<Wallets> { | |||
try { | |||
let res = await fetch(`${environment.endpoint}/wallet`, { | |||
method: 'POST', | |||
headers: { | |||
'content-type': 'application/json', | |||
'Authorization': `Bearer ${Auth.AccessToken}` | |||
} | |||
}) | |||
return await res.json() | |||
} catch (error) { | |||
return Promise.reject(error) | |||
} | |||
} | |||
} | |||
export const Wallet: CWallet = new CWallet() |
@ -0,0 +1,19 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { RouterModule, Routes } from '@angular/router'; | |||
const routes: Routes = [ | |||
{ | |||
path: 'auth', | |||
loadChildren: async () => (await import('../routes/auth/auth.module')).AuthModule | |||
}, | |||
{ | |||
path: 'panel', | |||
loadChildren: async () => (await import('../routes/view/view.module')).ViewModule | |||
} | |||
]; | |||
@NgModule({ | |||
imports: [RouterModule.forRoot(routes)], | |||
exports: [RouterModule] | |||
}) | |||
export class AppRoutingModule { } |
@ -0,0 +1 @@ | |||
<router-outlet></router-outlet> |
@ -0,0 +1,35 @@ | |||
import { TestBed } from '@angular/core/testing'; | |||
import { RouterTestingModule } from '@angular/router/testing'; | |||
import { AppComponent } from './app.component'; | |||
describe('AppComponent', () => { | |||
beforeEach(async () => { | |||
await TestBed.configureTestingModule({ | |||
imports: [ | |||
RouterTestingModule | |||
], | |||
declarations: [ | |||
AppComponent | |||
], | |||
}).compileComponents(); | |||
}); | |||
it('should create the app', () => { | |||
const fixture = TestBed.createComponent(AppComponent); | |||
const app = fixture.componentInstance; | |||
expect(app).toBeTruthy(); | |||
}); | |||
it(`should have as title 'panel-boilerplate'`, () => { | |||
const fixture = TestBed.createComponent(AppComponent); | |||
const app = fixture.componentInstance; | |||
expect(app.title).toEqual('panel-boilerplate'); | |||
}); | |||
it('should render title', () => { | |||
const fixture = TestBed.createComponent(AppComponent); | |||
fixture.detectChanges(); | |||
const compiled = fixture.nativeElement as HTMLElement; | |||
expect(compiled.querySelector('.content span')?.textContent).toContain('panel-boilerplate app is running!'); | |||
}); | |||
}); |
@ -0,0 +1,10 @@ | |||
import { Component } from '@angular/core'; | |||
@Component({ | |||
selector: 'app-root', | |||
templateUrl: './app.component.html', | |||
styleUrls: ['./app.component.scss'] | |||
}) | |||
export class AppComponent { | |||
title = 'panel-boilerplate'; | |||
} |
@ -0,0 +1,20 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { BrowserModule } from '@angular/platform-browser'; | |||
import { AppRoutingModule } from './app-routing.module'; | |||
import { AppComponent } from './app.component'; | |||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | |||
@NgModule({ | |||
declarations: [ | |||
AppComponent | |||
], | |||
imports: [ | |||
BrowserModule, | |||
AppRoutingModule, | |||
BrowserAnimationsModule | |||
], | |||
providers: [], | |||
bootstrap: [AppComponent] | |||
}) | |||
export class AppModule { } |
@ -0,0 +1,15 @@ | |||
<div class="image-container flex column ai-c jc-c" [ngClass]="{'disabled': disabled, 'error': error}"> | |||
<div class="click-area" (click)="click()"></div> | |||
<ng-container *ngIf="deleteable && src && src.length != 0"> | |||
<button mat-icon-button color="warn" (click)="delete()"> | |||
<mat-icon>delete</mat-icon> | |||
</button> | |||
</ng-container> | |||
<ng-container *ngIf="src == null || src.length == 0"> | |||
<mat-icon class="photo">insert_photo</mat-icon> | |||
<span>افزودن تصویر</span> | |||
</ng-container> | |||
<ng-container *ngIf="src != null && src.length != 0"> | |||
<div class="image" [ngStyle]="{'background-image': 'url('+src+')'}"></div> | |||
</ng-container> | |||
</div> |
@ -0,0 +1,92 @@ | |||
@mixin positionFrom($length) { | |||
top: $length; | |||
left: $length; | |||
right: $length; | |||
bottom: $length; | |||
} | |||
div.image-container { | |||
height: inherit; | |||
min-height: inherit; | |||
max-height: inherit; | |||
width: inherit; | |||
min-width: inherit; | |||
max-width: inherit; | |||
border: 1px solid rgba($color: #000000, $alpha: 0.12); | |||
border-radius: 5px; | |||
cursor: pointer; | |||
position: relative; | |||
&::before { | |||
content: ""; | |||
position: absolute; | |||
@include positionFrom(-1px); | |||
border: 2px solid rgba($color: #000000, $alpha: 0.6); | |||
border-radius: 5px; | |||
opacity: 0; | |||
} | |||
&:hover { | |||
border-color: transparent; | |||
&::before { | |||
opacity: 1; | |||
} | |||
} | |||
&.disabled { | |||
pointer-events: none; | |||
filter: grayscale(1); | |||
color: rgba($color: #000000, $alpha: 0.6); | |||
} | |||
&.error { | |||
border-color: var(--error-color); | |||
} | |||
div.click-area { | |||
position: absolute; | |||
@include positionFrom(0); | |||
z-index: 1; | |||
} | |||
button[mat-icon-button] { | |||
position: absolute; | |||
@include positionFrom(5px); | |||
z-index: 2; | |||
} | |||
mat-icon.photo { | |||
width: 32px; | |||
height: 32px; | |||
font-size: 32px; | |||
margin-bottom: 15px; | |||
} | |||
div.image { | |||
position: absolute; | |||
@include positionFrom(0); | |||
background-size: contain; | |||
background-repeat: no-repeat; | |||
background-position: center center; | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
import { ImageComponent } from './image.component'; | |||
describe('ImageComponent', () => { | |||
let component: ImageComponent; | |||
let fixture: ComponentFixture<ImageComponent>; | |||
beforeEach(async () => { | |||
await TestBed.configureTestingModule({ | |||
declarations: [ ImageComponent ] | |||
}) | |||
.compileComponents(); | |||
}); | |||
beforeEach(() => { | |||
fixture = TestBed.createComponent(ImageComponent); | |||
component = fixture.componentInstance; | |||
fixture.detectChanges(); | |||
}); | |||
it('should create', () => { | |||
expect(component).toBeTruthy(); | |||
}); | |||
}); |
@ -0,0 +1,128 @@ | |||
import { Component, Input } from '@angular/core'; | |||
import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms'; | |||
@Component({ | |||
selector: 'app-image', | |||
templateUrl: './image.component.html', | |||
styleUrls: ['./image.component.scss'], | |||
providers: [ | |||
{ | |||
provide: NG_VALUE_ACCESSOR, | |||
useExisting: ImageComponent, | |||
multi: true | |||
}, | |||
{ | |||
provide: NG_VALIDATORS, | |||
multi: true, | |||
useExisting: ImageComponent | |||
} | |||
] | |||
}) | |||
export class ImageComponent implements ControlValueAccessor, Validator { | |||
public src: string | undefined | null; | |||
public disabled: boolean = false; | |||
public error: boolean = false; | |||
private inited: boolean = false; // inited errors ? | |||
@Input() deleteable: boolean = false; | |||
// ControlValueAccessor Implementation | |||
onChange: any = () => { }; | |||
onTouched: any = () => { }; | |||
onValidatorChange: any = () => { }; | |||
constructor() { } | |||
validate(control: AbstractControl): ValidationErrors | null { | |||
let error = null; | |||
if (('required' in (control.errors || {}) && (control.errors || {}).required == true) && (control.value == null || (control.value as string).length == 0)) { | |||
this.error = this.inited; | |||
error = { | |||
"required": true | |||
}; | |||
} else { | |||
this.error = false; | |||
} | |||
this.inited = true; | |||
return error; | |||
} | |||
registerOnValidatorChange?(onValidatorChange: any): void { | |||
this.onValidatorChange = onValidatorChange; | |||
} | |||
writeValue(value: string): void { | |||
this.src = value; | |||
} | |||
registerOnChange(onChange: any): void { | |||
this.onChange = onChange; | |||
} | |||
registerOnTouched(onTouched: any): void { | |||
this.onTouched = onTouched; | |||
} | |||
setDisabledState(disabled: boolean): void { | |||
this.disabled = disabled; | |||
} | |||
public delete(): void { | |||
this.src = null; | |||
this.onChange(null); | |||
this.onTouched(); | |||
} | |||
public async click(): Promise<void> { | |||
try { | |||
let file: File | null = await this.select(); | |||
if (file) { | |||
let base64 = await this.toBase64(file); | |||
this.src = base64; | |||
this.onChange(this.src); | |||
this.onTouched(); | |||
} | |||
} catch (error) { | |||
} | |||
} | |||
private select(): Promise<File | null> { | |||
return new Promise((resolve) => { | |||
let id = 'file-selector'; | |||
let input: HTMLInputElement = <HTMLInputElement>document.getElementById(id); | |||
if (input) document.body.removeChild(input); | |||
input = document.createElement('input'); | |||
input.type = 'file'; | |||
input.accept = [".jpg", ".png", ".jpeg", ".webp", ".svg"].join(); | |||
input.id = id; | |||
input.style.display = 'none'; | |||
document.body.appendChild(input); | |||
input.onchange = (event: any) => { | |||
resolve(event.target['files'][0]); | |||
} | |||
input.focus(); | |||
input.click(); | |||
}) | |||
} | |||
private toBase64(file: File): Promise<string | null> { | |||
return new Promise((resolve) => { | |||
const reader = new FileReader(); | |||
reader.readAsDataURL(file); | |||
reader.onload = async () => { | |||
resolve(reader.result ? reader.result.toString() : null); | |||
} | |||
}); | |||
} | |||
} |
@ -0,0 +1,18 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { CommonModule } from '@angular/common'; | |||
import { ImageComponent } from './image.component'; | |||
import { MaterialModule } from './material'; | |||
@NgModule({ | |||
declarations: [ | |||
ImageComponent | |||
], | |||
imports: [ | |||
CommonModule, | |||
MaterialModule, | |||
], | |||
exports: [ImageComponent] | |||
}) | |||
export class ImageModule { } |
@ -0,0 +1,15 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { MatButtonModule } from '@angular/material/button'; | |||
import { MatIconModule } from '@angular/material/icon'; | |||
const modules: any[] = [ | |||
MatButtonModule, | |||
MatIconModule, | |||
]; | |||
@NgModule({ | |||
imports: modules, | |||
exports: modules, | |||
}) | |||
export class MaterialModule { } |
@ -0,0 +1 @@ | |||
export const AppTitle = 'پنل مدیریت میدون'; |
@ -0,0 +1,13 @@ | |||
export interface IDrawerItem { | |||
path: string | |||
icon: string | |||
title: string | |||
} | |||
export const drawerItems: IDrawerItem[] = [ | |||
{ | |||
path: '/panel/users', | |||
icon: 'group', | |||
title: 'کاربران' | |||
} | |||
]; |
@ -0,0 +1,4 @@ | |||
export const environment = { | |||
production: true, | |||
endpoint: 'https://api.melonex.xyz' | |||
}; |
@ -0,0 +1,17 @@ | |||
// This file can be replaced during build by using the `fileReplacements` array. | |||
// `ng build` replaces `environment.ts` with `environment.prod.ts`. | |||
// The list of file replacements can be found in `angular.json`. | |||
export const environment = { | |||
production: false, | |||
endpoint: 'https://api.melonex.xyz' | |||
}; | |||
/* | |||
* For easier debugging in development mode, you can import the following file | |||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. | |||
* | |||
* This import should be commented out in production mode because it will have a negative impact | |||
* on performance if an error is thrown. | |||
*/ | |||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. |
@ -0,0 +1,18 @@ | |||
<!doctype html> | |||
<html lang="fa" dir="rtl"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>پنل مدیریت</title> | |||
<base href="/"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<link rel="icon" type="image/x-icon" href="favicon.ico"> | |||
<link rel="preconnect" href="https://fonts.gstatic.com"> | |||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> | |||
</head> | |||
<body> | |||
<app-root></app-root> | |||
</body> | |||
</html> |
@ -0,0 +1,12 @@ | |||
import { enableProdMode } from '@angular/core'; | |||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | |||
import { AppModule } from './app/app.module'; | |||
import { environment } from './environments/environment'; | |||
if (environment.production) { | |||
enableProdMode(); | |||
} | |||
platformBrowserDynamic().bootstrapModule(AppModule) | |||
.catch(err => console.error(err)); |
@ -0,0 +1,11 @@ | |||
import { NgModule } from '@angular/core'; | |||
const modules: any[] = [ | |||
]; | |||
@NgModule({ | |||
imports: modules, | |||
exports: modules, | |||
}) | |||
export class MaterialModule { } |
@ -0,0 +1,65 @@ | |||
/** | |||
* This file includes polyfills needed by Angular and is loaded before the app. | |||
* You can add your own extra polyfills to this file. | |||
* | |||
* This file is divided into 2 sections: | |||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. | |||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main | |||
* file. | |||
* | |||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that | |||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), | |||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. | |||
* | |||
* Learn more in https://angular.io/guide/browser-support | |||
*/ | |||
/*************************************************************************************************** | |||
* BROWSER POLYFILLS | |||
*/ | |||
/** | |||
* IE11 requires the following for NgClass support on SVG elements | |||
*/ | |||
// import 'classlist.js'; // Run `npm install --save classlist.js`. | |||
/** | |||
* Web Animations `@angular/platform-browser/animations` | |||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. | |||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). | |||
*/ | |||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`. | |||
/** | |||
* By default, zone.js will patch all possible macroTask and DomEvents | |||
* user can disable parts of macroTask/DomEvents patch by setting following flags | |||
* because those flags need to be set before `zone.js` being loaded, and webpack | |||
* will put import in the top of bundle, so user need to create a separate file | |||
* in this directory (for example: zone-flags.ts), and put the following flags | |||
* into that file, and then add the following code before importing zone.js. | |||
* import './zone-flags'; | |||
* | |||
* The flags allowed in zone-flags.ts are listed here. | |||
* | |||
* The following flags will work for all browsers. | |||
* | |||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame | |||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick | |||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames | |||
* | |||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js | |||
* with the following flag, it will bypass `zone.js` patch |