Compare commits
20 Commits
mm-logging
...
master
Author | SHA1 | Date |
---|---|---|
lza_menace | 8da92e6919 | 3 years ago |
lza_menace | b34cf05497 | 3 years ago |
lza_menace | c54c37b10e | 3 years ago |
lza_menace | 5a3bf57a32 | 3 years ago |
lza_menace | 1a48e7db77 | 3 years ago |
lza_menace | f178878fb5 | 3 years ago |
lza_menace | 952d251160 | 3 years ago |
lza_menace | a5f2422abd | 3 years ago |
lza_menace | abcf5b5dd9 | 4 years ago |
lza_menace | fbc8917a7b | 4 years ago |
lza_menace | 91ff0f8796 | 4 years ago |
lza_menace | cf20b46d7d | 4 years ago |
lza_menace | a009450caa | 4 years ago |
lza_menace | 34d47ad39f | 4 years ago |
lza_menace | 2829d698fa | 4 years ago |
lza_menace | 9428a47537 | 4 years ago |
lza_menace | 2567db144f | 4 years ago |
lza_menace | e8e97c9f1c | 4 years ago |
lza_menace | 3a9c13919d | 4 years ago |
lza_menace | a43fae4a1b | 4 years ago |
@ -1,9 +0,0 @@
|
|||||||
FROM ubuntu:19.10
|
|
||||||
WORKDIR /srv
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN apt-get update && apt-get install python3-pip -y
|
|
||||||
RUN python3 -m pip install -r requirements.txt
|
|
||||||
COPY wowstash wowstash/
|
|
||||||
COPY bin/ bin/
|
|
||||||
EXPOSE 4001
|
|
||||||
CMD ["/srv/bin/prod-container"]
|
|
@ -0,0 +1,23 @@
|
|||||||
|
.PHONY: format help
|
||||||
|
|
||||||
|
# Help system from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
setup: ## Setup Python virtual environment
|
||||||
|
python3 -m venv .venv
|
||||||
|
.venv/bin/pip install -r requirements.txt
|
||||||
|
|
||||||
|
up: ## Bring up backend containers
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
down: ## Bring down backend containers
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
dev: ## Start development server
|
||||||
|
./bin/dev
|
||||||
|
|
||||||
|
dbshell: ## Start interactive session with database
|
||||||
|
docker-compose exec db psql -U wowstash
|
@ -1,22 +0,0 @@
|
|||||||
services:
|
|
||||||
kibana:
|
|
||||||
image: docker.elastic.co/kibana/kibana:7.1.0
|
|
||||||
ports:
|
|
||||||
- 5601:5601
|
|
||||||
environment:
|
|
||||||
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
|
|
||||||
elasticsearch:
|
|
||||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.1.0
|
|
||||||
environment:
|
|
||||||
- discovery.type=single-node
|
|
||||||
- node.name=elasticsearch
|
|
||||||
- cluster.name=es-docker-cluster
|
|
||||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
||||||
ulimits:
|
|
||||||
memlock:
|
|
||||||
soft: -1
|
|
||||||
hard: -1
|
|
||||||
volumes:
|
|
||||||
- ./data/elasticsearch:/usr/share/elasticsearch/data
|
|
||||||
ports:
|
|
||||||
- 9200:9200
|
|
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: 'default'
|
||||||
|
orgId: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
disableDeletion: true
|
||||||
|
editable: true
|
||||||
|
updateIntervalSeconds: 60
|
||||||
|
allowUiUpdates: true
|
||||||
|
options:
|
||||||
|
path: /var/lib/grafana/dashboards
|
@ -0,0 +1,311 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": 1,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": null,
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 4,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"dataLinks": []
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 1,
|
||||||
|
"points": true,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"format": "time_series",
|
||||||
|
"group": [
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"$__interval",
|
||||||
|
"none"
|
||||||
|
],
|
||||||
|
"type": "time"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metricColumn": "none",
|
||||||
|
"rawQuery": false,
|
||||||
|
"rawSql": "SELECT\n $__timeGroupAlias(register_date,$__interval),\n avg(id) AS \"id\"\nFROM users\nWHERE\n $__timeFilter(register_date)\nGROUP BY 1\nORDER BY 1",
|
||||||
|
"refId": "A",
|
||||||
|
"select": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"type": "column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"avg"
|
||||||
|
],
|
||||||
|
"type": "aggregate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"type": "alias"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"table": "users",
|
||||||
|
"timeColumn": "register_date",
|
||||||
|
"timeColumnType": "timestamp",
|
||||||
|
"where": [
|
||||||
|
{
|
||||||
|
"name": "$__timeFilter",
|
||||||
|
"params": [],
|
||||||
|
"type": "macro"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "User Registrations",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": null,
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 11,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 9
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 2,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"dataLinks": []
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"format": "time_series",
|
||||||
|
"group": [
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"$__interval",
|
||||||
|
"0"
|
||||||
|
],
|
||||||
|
"type": "time"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metricColumn": "type",
|
||||||
|
"rawQuery": false,
|
||||||
|
"rawSql": "SELECT\n $__timeGroupAlias(date,$__interval,0),\n type AS metric,\n count(\"user\") AS \"id\"\nFROM events\nWHERE\n $__timeFilter(date)\nGROUP BY 1,2\nORDER BY 1,2",
|
||||||
|
"refId": "A",
|
||||||
|
"select": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"\"user\""
|
||||||
|
],
|
||||||
|
"type": "column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"count"
|
||||||
|
],
|
||||||
|
"type": "aggregate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"type": "alias"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"table": "events",
|
||||||
|
"timeColumn": "date",
|
||||||
|
"timeColumnType": "timestamp",
|
||||||
|
"where": [
|
||||||
|
{
|
||||||
|
"name": "$__timeFilter",
|
||||||
|
"params": [],
|
||||||
|
"type": "macro"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Event Activity",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": "",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": true,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": false,
|
||||||
|
"schemaVersion": 21,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Wowstash Ops",
|
||||||
|
"uid": "zvTlfCbGz",
|
||||||
|
"version": 1
|
||||||
|
}
|
@ -1,23 +1,67 @@
|
|||||||
from wowstash.library.jsonrpc import wallet
|
import click
|
||||||
from wowstash.models import Transaction
|
from flask import Blueprint, url_for
|
||||||
from wowstash.factory import db
|
|
||||||
|
|
||||||
|
import wowstash.models
|
||||||
|
from wowstash.library.docker import docker
|
||||||
|
from wowstash.models import User, PasswordReset, Event
|
||||||
|
from wowstash.factory import db, bcrypt
|
||||||
|
|
||||||
# @app.errorhandler(404)
|
|
||||||
def not_found(error):
|
|
||||||
return make_response(jsonify({
|
|
||||||
'error': 'Page not found'
|
|
||||||
}), 404)
|
|
||||||
|
|
||||||
# @app.cli.command('initdb')
|
bp = Blueprint("cli", "cli", cli_group=None)
|
||||||
def init_db():
|
|
||||||
|
|
||||||
|
@bp.cli.command('clean_containers')
|
||||||
|
def clean_containers():
|
||||||
|
docker.cleanup()
|
||||||
|
|
||||||
|
@bp.cli.command('reset_wallet')
|
||||||
|
@click.argument('user_id')
|
||||||
|
def reset_wallet(user_id):
|
||||||
|
user = User.query.get(user_id)
|
||||||
|
user.clear_wallet_data()
|
||||||
|
print(f'Wallet data cleared for user {user.id}')
|
||||||
|
|
||||||
|
@bp.cli.command('init')
|
||||||
|
def init():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
# @app.cli.command('send_transfers')
|
@bp.cli.command('list_users')
|
||||||
def send_transfers():
|
def list_users():
|
||||||
txes = Transaction.query.all()
|
users = User.query.all()
|
||||||
for i in txes:
|
for i in users:
|
||||||
print(i)
|
print(f'{i.id} - {i.email}')
|
||||||
# tx = wallet.transfer(
|
|
||||||
# 0, current_user.subaddress_index, address, amount
|
@bp.cli.command('wipe_user')
|
||||||
# )
|
@click.argument('user_id')
|
||||||
|
def wipe_user(user_id):
|
||||||
|
user = User.query.get(user_id)
|
||||||
|
if user:
|
||||||
|
events = Event.query.filter(Event.user == user.id)
|
||||||
|
for i in events:
|
||||||
|
print(f'[+] Deleting event {i.id} for user {user.id}')
|
||||||
|
db.session.delete(i)
|
||||||
|
print(f'[+] Deleting user {user.id}')
|
||||||
|
db.session.delete(user)
|
||||||
|
db.session.commit()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print('That user id does not exist')
|
||||||
|
return False
|
||||||
|
|
||||||
|
@bp.cli.command('reset_password')
|
||||||
|
@click.argument('user_email')
|
||||||
|
@click.argument('duration')
|
||||||
|
def reset_password(user_email, duration):
|
||||||
|
user = User.query.filter(User.email==user_email).first()
|
||||||
|
if not user:
|
||||||
|
click.echo('[!] Email address does not exist!')
|
||||||
|
return
|
||||||
|
|
||||||
|
pwr = PasswordReset(
|
||||||
|
user=user.id,
|
||||||
|
hash=PasswordReset().generate_hash(),
|
||||||
|
expiration_hours=duration
|
||||||
|
)
|
||||||
|
db.session.add(pwr)
|
||||||
|
db.session.commit()
|
||||||
|
click.echo(f'[+] Password reset link #{pwr.id} for {user_email} expires in {duration} hours: {url_for("auth.reset", hash=pwr.hash)}')
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
from wowstash.library.elasticsearch import send_es
|
|
||||||
from wowstash.library.mattermost import post_webhook
|
|
||||||
|
|
||||||
|
|
||||||
def capture_event(event_type, user_obj):
|
|
||||||
send_es({'type': event_type, 'user': user_obj.email})
|
|
||||||
post_webhook(f'`{event_type}` from user {user_obj.id}')
|
|
@ -1,22 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
from elasticsearch import Elasticsearch
|
|
||||||
from wowstash import config
|
|
||||||
|
|
||||||
|
|
||||||
def send_es(data):
|
|
||||||
if getattr(config, 'ELASTICSEARCH_ENABLED', False):
|
|
||||||
try:
|
|
||||||
es = Elasticsearch(
|
|
||||||
[getattr(config, 'ELASTICSEARCH_HOST', 'localhost')]
|
|
||||||
)
|
|
||||||
now = datetime.utcnow()
|
|
||||||
index_ts = now.strftime('%Y%m%d')
|
|
||||||
data['datetime'] = now
|
|
||||||
es.index(
|
|
||||||
index="{}-{}".format(
|
|
||||||
getattr(config, 'ELASTICSEARCH_INDEX_NAME', 'wowstash'),
|
|
||||||
index_ts
|
|
||||||
), body=data)
|
|
||||||
except Exception as e:
|
|
||||||
print('Could not capture event in Elasticsearch: ', e)
|
|
||||||
pass # I don't really care if this logs...
|
|
@ -0,0 +1,23 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
from wowstash.models import Event
|
||||||
|
from wowstash.factory import db
|
||||||
|
from wowstash import config
|
||||||
|
|
||||||
|
|
||||||
|
def capture_event(user_id, event_type):
|
||||||
|
event = Event(
|
||||||
|
user=user_id,
|
||||||
|
type=event_type
|
||||||
|
)
|
||||||
|
db.session.add(event)
|
||||||
|
db.session.commit()
|
||||||
|
return
|
||||||
|
|
||||||
|
def post_discord_webhook(text):
|
||||||
|
try:
|
||||||
|
r = requests.post(config.DISCORD_URL, data={"content": text})
|
||||||
|
r.raise_for_status()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
@ -1,22 +0,0 @@
|
|||||||
from requests import post as r_post
|
|
||||||
from json import dumps
|
|
||||||
from flask import current_app
|
|
||||||
from wowstash import config
|
|
||||||
|
|
||||||
|
|
||||||
def post_webhook(msg):
|
|
||||||
if getattr(config, 'MM_ENABLED', False):
|
|
||||||
try:
|
|
||||||
if current_app.config["DEBUG"]:
|
|
||||||
msg = "[DEBUG] " + msg
|
|
||||||
data = {
|
|
||||||
"text": msg,
|
|
||||||
"channel": config.MM_CHANNEL,
|
|
||||||
"username": config.MM_USERNAME,
|
|
||||||
"icon_url": config.MM_ICON
|
|
||||||
}
|
|
||||||
res = r_post(config.MM_ENDPOINT, data=dumps(data))
|
|
||||||
res.raise_for_status()
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
@ -1,117 +0,0 @@
|
|||||||
import requests
|
|
||||||
import six
|
|
||||||
import json
|
|
||||||
import operator
|
|
||||||
from tipbot import config
|
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
|
|
||||||
PICOWOW = Decimal('0.00000000001')
|
|
||||||
|
|
||||||
class Wallet(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.host = config.WALLET_HOST
|
|
||||||
self.port = config.WALLET_PORT
|
|
||||||
self.proto = config.WALLET_PROTO
|
|
||||||
self.username = config.WALLET_USER
|
|
||||||
self.password = config.WALLET_PASS
|
|
||||||
self.endpoint = '{}://{}:{}/json_rpc'.format(
|
|
||||||
self.proto, self.host, self.port
|
|
||||||
)
|
|
||||||
self.auth = requests.auth.HTTPDigestAuth(
|
|
||||||
self.username, self.password
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
r = self.height()
|
|
||||||
height = r['height']
|
|
||||||
self.connected = True
|
|
||||||
except:
|
|
||||||
self.connected = False
|
|
||||||
|
|
||||||
def make_wallet_rpc(self, method, params={}):
|
|
||||||
r = requests.get(
|
|
||||||
self.endpoint,
|
|
||||||
data=json.dumps({'method': method, 'params': params}),
|
|
||||||
auth=self.auth
|
|
||||||
)
|
|
||||||
# print(r.status_code)
|
|
||||||
if 'error' in r.json():
|
|
||||||
return r.json()['error']
|
|
||||||
else:
|
|
||||||
return r.json()['result']
|
|
||||||
|
|
||||||
def height(self):
|
|
||||||
return self.make_wallet_rpc('get_height', {})
|
|
||||||
|
|
||||||
def spend_key(self):
|
|
||||||
return self.make_wallet_rpc('query_key', {'key_type': 'spend_key'})['key']
|
|
||||||
|
|
||||||
def view_key(self):
|
|
||||||
return self.make_wallet_rpc('query_key', {'key_type': 'view_key'})['key']
|
|
||||||
|
|
||||||
def seed(self):
|
|
||||||
return self.make_wallet_rpc('query_key', {'key_type': 'mnemonic'})['key']
|
|
||||||
|
|
||||||
def accounts(self):
|
|
||||||
accounts = []
|
|
||||||
_accounts = self.make_wallet_rpc('get_accounts')
|
|
||||||
idx = 0
|
|
||||||
self.master_address = _accounts['subaddress_accounts'][0]['base_address']
|
|
||||||
for _acc in _accounts['subaddress_accounts']:
|
|
||||||
assert idx == _acc['account_index']
|
|
||||||
accounts.append(_acc['account_index'])
|
|
||||||
idx += 1
|
|
||||||
return accounts
|
|
||||||
|
|
||||||
def new_account(self, label=None):
|
|
||||||
_account = self.make_wallet_rpc('create_account', {'label': label})
|
|
||||||
return _account['account_index']
|
|
||||||
|
|
||||||
def addresses(self, account=0, addr_indices=None):
|
|
||||||
qdata = {'account_index': account}
|
|
||||||
if addr_indices:
|
|
||||||
qdata['address_index'] = addr_indices
|
|
||||||
_addresses = self.make_wallet_rpc('get_address', qdata)
|
|
||||||
addresses = [None] * (max(map(operator.itemgetter('address_index'), _addresses['addresses'])) + 1)
|
|
||||||
for _addr in _addresses['addresses']:
|
|
||||||
addresses[_addr['address_index']] = _addr['address']
|
|
||||||
return addresses
|
|
||||||
|
|
||||||
def new_address(self, account=0, label=None):
|
|
||||||
data = {'account_index': account, 'label': label}
|
|
||||||
_address = self.make_wallet_rpc('create_address', data)
|
|
||||||
return (_address['address_index'], _address['address'])
|
|
||||||
|
|
||||||
def balances(self, account=0):
|
|
||||||
data = {'account_index': account}
|
|
||||||
_balance = self.make_wallet_rpc('get_balance', data)
|
|
||||||
return (from_atomic(_balance['balance']), from_atomic(_balance['unlocked_balance']))
|
|
||||||
|
|
||||||
def transfer(self, dest_address, amount, priority, account):
|
|
||||||
data = {
|
|
||||||
'account_index': account,
|
|
||||||
'destinations': [{'address': dest_address, 'amount': to_atomic(amount)}],
|
|
||||||
'priority': priority,
|
|
||||||
'unlock_time': 0,
|
|
||||||
'get_tx_key': True,
|
|
||||||
'get_tx_hex': True,
|
|
||||||
'new_algorithm': True,
|
|
||||||
'do_not_relay': False,
|
|
||||||
'ring_size': 22
|
|
||||||
}
|
|
||||||
transfer = self.make_wallet_rpc('transfer', data)
|
|
||||||
return transfer
|
|
||||||
|
|
||||||
|
|
||||||
def to_atomic(amount):
|
|
||||||
if not isinstance(amount, (Decimal, float) + six.integer_types):
|
|
||||||
raise ValueError("Amount '{}' doesn't have numeric type. Only Decimal, int, long and "
|
|
||||||
"float (not recommended) are accepted as amounts.")
|
|
||||||
return int(amount * 10**11)
|
|
||||||
|
|
||||||
def from_atomic(amount):
|
|
||||||
return (Decimal(amount) * PICOWOW).quantize(PICOWOW)
|
|
||||||
|
|
||||||
def as_wownero(amount):
|
|
||||||
return Decimal(amount).quantize(PICOWOW)
|
|
Binary file not shown.
After Width: | Height: | Size: 216 KiB |
File diff suppressed because one or more lines are too long
@ -0,0 +1,67 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{% include 'head.html' %}
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<!-- <header class="masthead">
|
||||||
|
<div class="container h-100">
|
||||||
|
<div class="row h-100">
|
||||||
|
<div class="col-lg-12 my-auto">
|
||||||
|
<div class="header-content mx-auto">
|
||||||
|
<h1 class="mb-4">Reset your password</h1>
|
||||||
|
<p>Wownero is a privacy centric cryptocurrency and is most safely managed on your own personal devices, on your own network, and with your own copy of the blockchain. This is a publicly accessible website, and while strict security measures are implemented on our servers, your account's security <strong>cannot</strong> be guaranteed. </p>
|
||||||
|
<p>If you decide to use this site for managing your funds, you do so at your own risk and are bound by the terms and conditions of this site. Practice good operational security and do not use this site for large amounts of funds.</p>
|
||||||
|
<div>
|
||||||
|
<a href="{{ url_for('meta.faq') }}">FAQ</a> -
|
||||||
|
<a href="{{ url_for('meta.terms') }}">Terms</a> -
|
||||||
|
<a href="{{ url_for('meta.privacy') }}">Privacy</a>
|
||||||
|
</div><br>
|
||||||
|
<a href="#register" class="btn btn-outline btn-xl js-scroll-trigger">Proceed</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header> -->
|
||||||
|
|
||||||
|
<section class="section1" id="reset">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-heading text-center">
|
||||||
|
<form method="POST" action="">
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
{% for f in form %}
|
||||||
|
{% if f.name != 'csrf_token' %}
|
||||||
|
{% if f.type == 'BooleanField' %}
|
||||||
|
<div class="form-group-span">
|
||||||
|
{{ f.label }}
|
||||||
|
{{ f }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="form-group">
|
||||||
|
{{ f.label }}
|
||||||
|
{{ f }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<ul>
|
||||||
|
{% for field, errors in form.errors.items() %}
|
||||||
|
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<input type="submit" value="Reset" class="btn btn-link btn-outline-inverse btn-xl">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
|
{% include 'scripts.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{% include 'head.html' %}
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<header class="masthead">
|
||||||
|
<div class="container h-100">
|
||||||
|
<div class="row h-100">
|
||||||
|
<div class="col-lg-12 my-auto">
|
||||||
|
<div class="header-content-sm mx-auto">
|
||||||
|
<h2 class="mb-4">Error</h2>
|
||||||
|
<p>There was an error - an administrator has been notified.</p>
|
||||||
|
<p>Error: <code style="background-color: white;">{{ error.original_exception }}</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
|
{% include 'scripts.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{% include 'head.html' %}
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<header class="masthead">
|
||||||
|
<div class="container h-100">
|
||||||
|
<div class="row h-100">
|
||||||
|
<div class="col-lg-12 my-auto">
|
||||||
|
<div class="header-content mx-auto">
|
||||||
|
<h1 class="mb-4">Donate</h1>
|
||||||
|
<p>Hey, this service is provided to you for free, please consider donating some WOW since I both donate my time and money keeping the service alive and paying for hosting.</p>
|
||||||
|
<p>lza_menace: <code style="background-color: white;">Wo59kvcHiDd48sstysDqGgBAN1fECLKALKw2bPUJhS4UjX9wj2SK4e4GH6HvrBmot6cBrWNE1T65UR6a5SLbzh882c1SXEhiK</code></p>
|
||||||
|
<a href="{{ url_for('wallet.dashboard') }}?to_addr=Wo59kvcHiDd48sstysDqGgBAN1fECLKALKw2bPUJhS4UjX9wj2SK4e4GH6HvrBmot6cBrWNE1T65UR6a5SLbzh882c1SXEhiK#send" class="btn btn-outline btn-xl js-scroll-trigger">Ok, take me to my wallet</a>
|
||||||
|
<br/><br/>
|
||||||
|
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target=_blank class="btn btn-outline btn-xl js-scroll-trigger">Nah, fuck you menace.</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
|
{% include 'scripts.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
{% include 'head.html' %}
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
|
||||||
|
{% include 'navbar.html' %}
|
||||||
|
|
||||||
|
<section class="section2">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-heading text-center">
|
||||||
|
<h2>Setup Wallet</h2>
|
||||||
|
<p>Alrighty there hoss, pick an option below...</p>
|
||||||
|
|
||||||
|
<hr><br /><br />
|
||||||
|
|
||||||
|
<a class="btn btn-lg btn-link btn-outline btn-xl" href="{{ url_for('wallet.create') }}">Create new wallet</a>
|
||||||
|
|
||||||
|
<hr><br /><br />
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('wallet.setup') }}" class="send-form">
|
||||||
|
<p><strong>! WARNING !</strong><br /> If you input a mnemonic seed here I could theoretically steal your funds, even without a wallet on my server; so could a hacker if they compromised my server.</p>
|
||||||
|
<p>You <strong>can</strong> and <strong>should</strong> use a <a href="https://wownero.org/#wallets" target="_blank">wallet</a> you can run locally to ensure your funds are safe, especially if there is a lot there. Proceed at your own risk.</p>
|
||||||
|
{{ restore_form.csrf_token }}
|
||||||
|
{% for f in restore_form %}
|
||||||
|
{% if f.name != 'csrf_token' %}
|
||||||
|
<div class="form-group">
|
||||||
|
{{ f.label }}
|
||||||
|
{{ f }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<ul>
|
||||||
|
{% for field, errors in restore_form.errors.items() %}
|
||||||
|
<li>{{ restore_form[field].label }}: {{ ', '.join(errors) }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<input type="submit" value="Restore From Seed" class="btn btn-link btn-outline btn-xl">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
|
{% include 'scripts.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue