page rename, successful MySQL test
This commit is contained in:
parent
c761e740bc
commit
3c461363a4
19
README.md
19
README.md
@ -1,8 +1,8 @@
|
|||||||

|

|
||||||
|
|
||||||
HBNet is a fork of HBLink3 the extends the functionality of HBLink through several optional features, making it more of a usable application and less of a framework. HBNet aims to be complete and easy to use application that can be used to build, administrate, and run a DMR network.
|
HBNet is a fork of [HBlink3](https://github.com/HBLink-org/hblink3) the extends the functionality of HBLink through several optional features, making it more of a usable application and less of a framework. HBNet aims to be complete and easy to use application that can be used to build, administrate, and run a DMR network.
|
||||||
|
|
||||||
HBNet consists of 2 parts, web control panel, and the actual DMR server, based on HBLink.
|
HBNet consists of 2 parts, HBNet Web Server and the actual DMR server, based on HBLink.
|
||||||
|
|
||||||
|
|
||||||
### User end features:
|
### User end features:
|
||||||
@ -11,16 +11,29 @@ HBNet consists of 2 parts, web control panel, and the actual DMR server, based o
|
|||||||
|
|
||||||
* Individual passphrases for each user
|
* Individual passphrases for each user
|
||||||
|
|
||||||
|
* Automatic retrieval of DMR IDs on registration
|
||||||
|
|
||||||
|
* Monitor and change active talkgroups (WORK IN PROGRESS)
|
||||||
|
|
||||||
|
|
||||||
### Administrative features:
|
### Administrative features:
|
||||||
|
|
||||||
* Administrate multiple DMR servers through the web panel
|
* Administrate multiple DMR servers through the web panel
|
||||||
|
|
||||||
|
* Optional manual approval of new users
|
||||||
|
|
||||||
* Multiple Admin user logins
|
* Multiple Admin user logins
|
||||||
|
|
||||||
* Entirely configure HBLink in web panel
|
* Entirely configure DMR server (HBlink) in web server
|
||||||
|
|
||||||
|
* Log all peer authentication
|
||||||
|
|
||||||
|
|
||||||
|
### Other features
|
||||||
|
|
||||||
|
* SQLite or MySQL backend
|
||||||
|
|
||||||
|
* APRS and SMS features (WORK IN PROGRESS)
|
||||||
|
|
||||||
---
|
---
|
||||||
### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ###
|
### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ###
|
||||||
|
@ -1,4 +1,25 @@
|
|||||||
# HBLink User Managment Server
|
# HBNet Web Server
|
||||||
|
###############################################################################
|
||||||
|
# HBNet Web Server - Copyright (C) 2020 Eric Craw, KF7EEL <kf7eel@qsl.net>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
'''
|
||||||
|
Flask based application that is the web server for HBNet. Controls user authentication, DMR server config, etc.
|
||||||
|
'''
|
||||||
|
|
||||||
from flask import Flask, render_template_string, request, make_response, jsonify, render_template, Markup, flash, redirect, url_for, current_app
|
from flask import Flask, render_template_string, request, make_response, jsonify, render_template, Markup, flash, redirect, url_for, current_app
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
@ -124,17 +145,17 @@ def create_app():
|
|||||||
|
|
||||||
# User authentication information. The collation='NOCASE' is required
|
# User authentication information. The collation='NOCASE' is required
|
||||||
# to search case insensitively when USER_IFIND_MODE is 'nocase_collation'.
|
# to search case insensitively when USER_IFIND_MODE is 'nocase_collation'.
|
||||||
username = db.Column(db.String(100, collation='NOCASE'), nullable=False, unique=True)
|
username = db.Column(db.String(100,), nullable=False, unique=True)
|
||||||
password = db.Column(db.String(255), nullable=False, server_default='')
|
password = db.Column(db.String(255), nullable=False, server_default='')
|
||||||
email_confirmed_at = db.Column(db.DateTime())
|
email_confirmed_at = db.Column(db.DateTime())
|
||||||
email = db.Column(db.String(255, collation='NOCASE'), nullable=False, unique=True)
|
email = db.Column(db.String(255), nullable=False, unique=True)
|
||||||
|
|
||||||
# User information
|
# User information
|
||||||
first_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
first_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
last_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
last_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
dmr_ids = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
dmr_ids = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
city = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
city = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
#Used for initial approval
|
#Used for initial approval
|
||||||
initial_admin_approved = db.Column('initial_admin_approved', db.Boolean(), nullable=False, server_default='1')
|
initial_admin_approved = db.Column('initial_admin_approved', db.Boolean(), nullable=False, server_default='1')
|
||||||
# Define the relationship to Role via UserRoles
|
# Define the relationship to Role via UserRoles
|
||||||
@ -154,212 +175,214 @@ def create_app():
|
|||||||
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
|
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
|
||||||
class BurnList(db.Model):
|
class BurnList(db.Model):
|
||||||
__tablename__ = 'burn_list'
|
__tablename__ = 'burn_list'
|
||||||
|
## id = db.Column(db.Integer(), primary_key=True)
|
||||||
dmr_id = db.Column(db.Integer(), unique=True, primary_key=True)
|
dmr_id = db.Column(db.Integer(), unique=True, primary_key=True)
|
||||||
version = db.Column(db.Integer(), primary_key=True)
|
version = db.Column(db.Integer(), primary_key=True)
|
||||||
class AuthLog(db.Model):
|
class AuthLog(db.Model):
|
||||||
__tablename__ = 'auth_log'
|
__tablename__ = 'auth_log'
|
||||||
login_dmr_id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
login_time = db.Column(db.DateTime(), primary_key=True)
|
login_dmr_id = db.Column(db.Integer())
|
||||||
peer_ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
login_time = db.Column(db.DateTime())
|
||||||
server_name = db.Column(db.Integer(), primary_key=True)
|
peer_ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
login_auth_method = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server_name = db.Column(db.String(100))
|
||||||
portal_username = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
login_auth_method = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
login_type = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
portal_username = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
|
login_type = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
class mmdvmPeer(db.Model):
|
class mmdvmPeer(db.Model):
|
||||||
__tablename__ = 'MMDVM_peers'
|
__tablename__ = 'MMDVM_peers'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
loose = db.Column(db.Boolean(), nullable=False, server_default='1')
|
loose = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='127.0.0.1')
|
ip = db.Column(db.String(100), nullable=False, server_default='127.0.0.1')
|
||||||
port = db.Column(db.Integer(), primary_key=False)
|
port = db.Column(db.Integer(), primary_key=False)
|
||||||
master_ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
master_ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
master_port = db.Column(db.Integer(), primary_key=False)
|
master_port = db.Column(db.Integer(), primary_key=False)
|
||||||
passphrase = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
passphrase = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
callsign = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
callsign = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
radio_id = db.Column(db.Integer(), primary_key=False)
|
radio_id = db.Column(db.Integer(), primary_key=False)
|
||||||
rx_freq = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
rx_freq = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tx_freq = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tx_freq = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tx_power = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tx_power = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
color_code = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
color_code = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
latitude = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
latitude = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
longitude = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
longitude = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
height = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
height = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
location = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
location = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
description = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
description = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
slots = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
slots = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
url = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
url = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
group_hangtime = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
group_hangtime = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
options = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
options = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
use_acl = db.Column(db.Boolean(), nullable=False, server_default='0')
|
use_acl = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
sub_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg1_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg1_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg2_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg2_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
|
|
||||||
class xlxPeer(db.Model):
|
class xlxPeer(db.Model):
|
||||||
__tablename__ = 'XLX_peers'
|
__tablename__ = 'XLX_peers'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
loose = db.Column(db.Boolean(), nullable=False, server_default='1')
|
loose = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='127.0.0.1')
|
ip = db.Column(db.String(100), nullable=False, server_default='127.0.0.1')
|
||||||
port = db.Column(db.Integer(), primary_key=False)
|
port = db.Column(db.Integer(), primary_key=False)
|
||||||
master_ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
master_ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
master_port = db.Column(db.Integer(), primary_key=False)
|
master_port = db.Column(db.Integer(), primary_key=False)
|
||||||
passphrase = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
passphrase = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
callsign = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
callsign = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
radio_id = db.Column(db.Integer(), primary_key=False)
|
radio_id = db.Column(db.Integer(), primary_key=False)
|
||||||
rx_freq = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
rx_freq = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tx_freq = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tx_freq = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tx_power = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tx_power = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
color_code = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
color_code = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
latitude = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
latitude = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
longitude = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
longitude = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
height = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
height = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
location = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
location = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
description = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
description = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
slots = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
slots = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
url = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
url = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
group_hangtime = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
group_hangtime = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
xlxmodule = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
xlxmodule = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
options = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
options = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
use_acl = db.Column(db.Boolean(), nullable=False, server_default='0')
|
use_acl = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
sub_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg1_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg1_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg2_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg2_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
class ServerList(db.Model):
|
class ServerList(db.Model):
|
||||||
__tablename__ = 'server_list'
|
__tablename__ = 'server_list'
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), unique=True, primary_key=True)
|
name = db.Column(db.String(100), unique=True, primary_key=True)
|
||||||
secret = db.Column(db.String(255), nullable=False, server_default='')
|
secret = db.Column(db.String(255), nullable=False, server_default='')
|
||||||
## public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
## public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
id = db.Column(db.Integer(), primary_key=False)
|
id = db.Column(db.Integer(), primary_key=False)
|
||||||
ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
port = db.Column(db.Integer(), primary_key=False)
|
port = db.Column(db.Integer(), primary_key=False)
|
||||||
global_path = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='./')
|
global_path = db.Column(db.String(100), nullable=False, server_default='./')
|
||||||
global_ping_time = db.Column(db.Integer(), primary_key=False)
|
global_ping_time = db.Column(db.Integer(), primary_key=False)
|
||||||
global_max_missed = db.Column(db.Integer(), primary_key=False)
|
global_max_missed = db.Column(db.Integer(), primary_key=False)
|
||||||
global_use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
global_use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
global_reg_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='PERMIT:ALL')
|
global_reg_acl = db.Column(db.String(100), nullable=False, server_default='PERMIT:ALL')
|
||||||
global_sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='DENY:1')
|
global_sub_acl = db.Column(db.String(100), nullable=False, server_default='DENY:1')
|
||||||
global_tg1_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='PERMIT:ALL')
|
global_tg1_acl = db.Column(db.String(100), nullable=False, server_default='PERMIT:ALL')
|
||||||
global_tg2_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='PERMIT:ALL')
|
global_tg2_acl = db.Column(db.String(100), nullable=False, server_default='PERMIT:ALL')
|
||||||
ai_try_download = db.Column(db.Boolean(), nullable=False, server_default='1')
|
ai_try_download = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
ai_path = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='./')
|
ai_path = db.Column(db.String(100), nullable=False, server_default='./')
|
||||||
ai_peer_file = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='peer_ids.json')
|
ai_peer_file = db.Column(db.String(100), nullable=False, server_default='peer_ids.json')
|
||||||
ai_subscriber_file = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='subscriber_ids.json')
|
ai_subscriber_file = db.Column(db.String(100), nullable=False, server_default='subscriber_ids.json')
|
||||||
ai_tgid_file = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='talkgroup_ids.json')
|
ai_tgid_file = db.Column(db.String(100), nullable=False, server_default='talkgroup_ids.json')
|
||||||
ai_peer_url = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='https://www.radioid.net/static/rptrs.json')
|
ai_peer_url = db.Column(db.String(100), nullable=False, server_default='https://www.radioid.net/static/rptrs.json')
|
||||||
ai_subs_url = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='https://www.radioid.net/static/users.json')
|
ai_subs_url = db.Column(db.String(100), nullable=False, server_default='https://www.radioid.net/static/users.json')
|
||||||
ai_stale = db.Column(db.Integer(), primary_key=False, server_default='7')
|
ai_stale = db.Column(db.Integer(), primary_key=False, server_default='7')
|
||||||
# Pull from config file for now
|
# Pull from config file for now
|
||||||
## um_append_int = db.Column(db.Integer(), primary_key=False, server_default='2')
|
## um_append_int = db.Column(db.Integer(), primary_key=False, server_default='2')
|
||||||
um_shorten_passphrase = db.Column(db.Boolean(), nullable=False, server_default='0')
|
um_shorten_passphrase = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
um_burn_file = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='./burned_ids.txt')
|
um_burn_file = db.Column(db.String(100), nullable=False, server_default='./burned_ids.txt')
|
||||||
# Pull from config file for now
|
# Pull from config file for now
|
||||||
## um_burn_int = db.Column(db.Integer(), primary_key=False, server_default='6')
|
## um_burn_int = db.Column(db.Integer(), primary_key=False, server_default='6')
|
||||||
report_enable = db.Column(db.Boolean(), nullable=False, server_default='1')
|
report_enable = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
report_interval = db.Column(db.Integer(), primary_key=False, server_default='60')
|
report_interval = db.Column(db.Integer(), primary_key=False, server_default='60')
|
||||||
report_port = db.Column(db.Integer(), primary_key=False, server_default='4321')
|
report_port = db.Column(db.Integer(), primary_key=False, server_default='4321')
|
||||||
report_clients =db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='127.0.0.1')
|
report_clients =db.Column(db.String(100), nullable=False, server_default='127.0.0.1')
|
||||||
unit_time = db.Column(db.Integer(), primary_key=False, server_default='10080')
|
unit_time = db.Column(db.Integer(), primary_key=False, server_default='10080')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
|
|
||||||
class MasterList(db.Model):
|
class MasterList(db.Model):
|
||||||
__tablename__ = 'master_list'
|
__tablename__ = 'master_list'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
static_positions = db.Column(db.Boolean(), nullable=False, server_default='0')
|
static_positions = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
repeat = db.Column(db.Boolean(), nullable=False, server_default='1')
|
repeat = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
max_peers = db.Column(db.Integer(), primary_key=False, server_default='10')
|
max_peers = db.Column(db.Integer(), primary_key=False, server_default='10')
|
||||||
ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
port = db.Column(db.Integer(), primary_key=False)
|
port = db.Column(db.Integer(), primary_key=False)
|
||||||
enable_um = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_um = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
passphrase = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
passphrase = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
group_hang_time = db.Column(db.Integer(), primary_key=False, server_default='5')
|
group_hang_time = db.Column(db.Integer(), primary_key=False, server_default='5')
|
||||||
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
reg_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
reg_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
sub_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg1_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg1_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg2_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg2_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
|
|
||||||
|
|
||||||
class ProxyList(db.Model):
|
class ProxyList(db.Model):
|
||||||
__tablename__ = 'proxy_list'
|
__tablename__ = 'proxy_list'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
static_positions = db.Column(db.Boolean(), nullable=False, server_default='0')
|
static_positions = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
repeat = db.Column(db.Boolean(), nullable=False, server_default='1')
|
repeat = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
enable_um = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_um = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
passphrase = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
passphrase = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
external_proxy = db.Column(db.Boolean(), nullable=False, server_default='0')
|
external_proxy = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
external_port = db.Column(db.Integer(), primary_key=False)
|
external_port = db.Column(db.Integer(), primary_key=False)
|
||||||
group_hang_time = db.Column(db.Integer(), primary_key=False)
|
group_hang_time = db.Column(db.Integer(), primary_key=False)
|
||||||
internal_start_port = db.Column(db.Integer(), primary_key=False)
|
internal_start_port = db.Column(db.Integer(), primary_key=False)
|
||||||
internal_stop_port = db.Column(db.Integer(), primary_key=False)
|
internal_stop_port = db.Column(db.Integer(), primary_key=False)
|
||||||
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
reg_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
reg_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
sub_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg1_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg1_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg2_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg2_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
public_list = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
|
|
||||||
|
|
||||||
class OBP(db.Model):
|
class OBP(db.Model):
|
||||||
__tablename__ = 'OpenBridge'
|
__tablename__ = 'OpenBridge'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
network_id = db.Column(db.Integer(), primary_key=False)
|
network_id = db.Column(db.Integer(), primary_key=False)
|
||||||
ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
port = db.Column(db.Integer(), primary_key=False)
|
port = db.Column(db.Integer(), primary_key=False)
|
||||||
passphrase = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
passphrase = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
target_ip = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
target_ip = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
target_port = db.Column(db.Integer(), primary_key=False)
|
target_port = db.Column(db.Integer(), primary_key=False)
|
||||||
both_slots = db.Column(db.Boolean(), nullable=False, server_default='1')
|
both_slots = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
use_acl = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
sub_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
sub_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
tg_acl = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
tg_acl = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
enable_unit = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
notes = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
notes = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
|
|
||||||
class BridgeRules(db.Model):
|
class BridgeRules(db.Model):
|
||||||
__tablename__ = 'bridge_rules'
|
__tablename__ = 'bridge_rules'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
bridge_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
bridge_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
system_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
system_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
ts = db.Column(db.Integer(), primary_key=False)
|
ts = db.Column(db.Integer(), primary_key=False)
|
||||||
tg = db.Column(db.Integer(), primary_key=False)
|
tg = db.Column(db.Integer(), primary_key=False)
|
||||||
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
active = db.Column(db.Boolean(), nullable=False, server_default='1')
|
||||||
timeout = db.Column(db.Integer(), primary_key=False)
|
timeout = db.Column(db.Integer(), primary_key=False)
|
||||||
to_type = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
to_type = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
on = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
on = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
off = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
off = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
reset = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
reset = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
server = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
server = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
public_list = db.Column(db.Boolean(), nullable=False, server_default='0')
|
public_list = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
proxy = db.Column(db.Boolean(), nullable=False, server_default='0')
|
proxy = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
|
|
||||||
class BridgeList(db.Model):
|
class BridgeList(db.Model):
|
||||||
__tablename__ = 'bridge_list'
|
__tablename__ = 'bridge_list'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
bridge_name = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
bridge_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
description = db.Column(db.String(100, collation='NOCASE'), nullable=False, server_default='')
|
description = db.Column(db.String(100), nullable=False, server_default='')
|
||||||
public_list = db.Column(db.Boolean(), nullable=False, server_default='0')
|
public_list = db.Column(db.Boolean(), nullable=False, server_default='0')
|
||||||
tg = db.Column(db.Integer(), primary_key=False)
|
tg = db.Column(db.Integer(), primary_key=False)
|
||||||
|
|
||||||
@ -712,6 +735,7 @@ def create_app():
|
|||||||
elif request.method == 'POST': # and request.form.get('callsign') and request.form.get('subject') and request.form.get('message'):
|
elif request.method == 'POST': # and request.form.get('callsign') and request.form.get('subject') and request.form.get('message'):
|
||||||
u = User.query.filter_by(username=request.args.get('callsign')).first()
|
u = User.query.filter_by(username=request.args.get('callsign')).first()
|
||||||
msg = Message(recipients=[u.email],
|
msg = Message(recipients=[u.email],
|
||||||
|
sender=(title, MAIL_DEFAULT_SENDER),
|
||||||
subject=request.form.get('subject'),
|
subject=request.form.get('subject'),
|
||||||
body=request.form.get('message'))
|
body=request.form.get('message'))
|
||||||
mail.send(msg)
|
mail.send(msg)
|
||||||
@ -880,8 +904,9 @@ def create_app():
|
|||||||
edit_user.initial_admin_approved = True
|
edit_user.initial_admin_approved = True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
msg = Message(recipients=[edit_user.email],
|
msg = Message(recipients=[edit_user.email],
|
||||||
subject='Account Approval - ' + title,
|
sender=(title, MAIL_DEFAULT_SENDER),
|
||||||
body='''You are receiving this message because an administrator has approved your account. You may now login and view your MMDVM passphrase(s).''')
|
subject='Account Approval',
|
||||||
|
body='''You are receiving this message because an administrator has approved your account. You may now login and use ''' + title + '''.''')
|
||||||
mail.send(msg)
|
mail.send(msg)
|
||||||
content = '''<p style="text-align: center;">User approved: <strong>''' + str(request.args.get('callsign')) + '''</strong></p>\n'''
|
content = '''<p style="text-align: center;">User approved: <strong>''' + str(request.args.get('callsign')) + '''</strong></p>\n'''
|
||||||
|
|
||||||
@ -1457,7 +1482,7 @@ def create_app():
|
|||||||
content = content + '</tbody></table>'
|
content = content + '</tbody></table>'
|
||||||
return render_template('flask_user_layout.html', markup_content = Markup(content))
|
return render_template('flask_user_layout.html', markup_content = Markup(content))
|
||||||
|
|
||||||
@app.route('/self_care')
|
@app.route('/user_tg')
|
||||||
def tg_status():
|
def tg_status():
|
||||||
cu = current_user
|
cu = current_user
|
||||||
u = User.query.filter_by(username=cu.username).first()
|
u = User.query.filter_by(username=cu.username).first()
|
||||||
@ -4967,4 +4992,4 @@ def create_app():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = create_app()
|
app = create_app()
|
||||||
app.run(debug = True, port=ums_port, host=ums_host)
|
app.run(debug = True, port=hws_port, host=hws_host)
|
||||||
|
@ -1,33 +1,65 @@
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
Settings for user management portal.
|
Settings for HBNet Web Server.
|
||||||
'''
|
'''
|
||||||
# Database location
|
# Database options
|
||||||
db_location = 'sqlite:///./users.db'
|
# Using SQLite is simple and easiest. Comment out this line and uncomment the MySQL
|
||||||
|
# line to use a MySQL/MariaDB server.
|
||||||
|
db_location = 'sqlite:///hbnet.sqlite'
|
||||||
|
|
||||||
|
# Uncomment and change this line to use a MySQL DB. It is best to start with a fresh
|
||||||
|
# DB without data in it.
|
||||||
|
|
||||||
|
#db_location = 'mysql+pymysql://DB_USERNAME:DB_PASSWORD@DB_HOST:MySQL_PORT/DB_NAME'
|
||||||
|
|
||||||
|
|
||||||
|
# Title of the HBNet Web Server
|
||||||
|
title = 'HBNet DMR server'
|
||||||
|
# Port to run server
|
||||||
|
hws_port = 8080
|
||||||
|
# IP to run server on
|
||||||
|
hws_host = '127.0.0.1'
|
||||||
|
# Publicly accessible URL of the web server. THIS IS REQUIRED AND MUST BE CORRECT.
|
||||||
|
url = 'http://localhost:8080'
|
||||||
|
# Replace below with some random string such as an SHA256
|
||||||
|
secret_key = 'SUPER SECRET LONG KEY'
|
||||||
|
|
||||||
|
# Default state for newly created user accounts. Setting to False will require
|
||||||
|
# the approval of an admin user before the user can login.
|
||||||
|
default_account_state = True
|
||||||
|
|
||||||
# Legacy passphrase used in hblink.cfg
|
# Legacy passphrase used in hblink.cfg
|
||||||
legacy_passphrase = 'passw0rd'
|
#legacy_passphrase = 'passw0rd'
|
||||||
|
|
||||||
# Trim passphrases to 8 characters
|
|
||||||
use_short_passphrase = False
|
|
||||||
|
|
||||||
# Title of the Dashboard
|
# Passphrase calculation config. If REMOTE_CONFIG is not used in your DMR server config
|
||||||
title = 'MMDVM User Portal'
|
# (hblink.cfg), then the values in section [USER_MANAGER] MUST match the values below.
|
||||||
# Port to run server
|
# If REMOTE_CONFIG is enabled, the DMR server (hblink) will automatically use the values below.
|
||||||
ums_port = 8080
|
# These config options affect the generation of user passphrases.
|
||||||
# IP to run server on
|
|
||||||
ums_host = '127.0.0.1'
|
|
||||||
|
|
||||||
url = 'http://localhost:8080'
|
|
||||||
|
|
||||||
|
# Set to a value between 1 - 99. This value is used in the normal calculation.
|
||||||
append_int = 1
|
append_int = 1
|
||||||
|
|
||||||
shared_secrets = ['test']
|
# Set to a value between 1 - 99. This value is used for compromised passphrases.
|
||||||
|
|
||||||
|
|
||||||
burn_int = 5
|
burn_int = 5
|
||||||
|
|
||||||
legacy_passphrase = 'passw0rd'
|
# Set to a value between 1 - 99 This value is used in the normal calculation.
|
||||||
|
extra_int_1 = 5
|
||||||
|
|
||||||
|
# Set to a value between 1 - 99 This value is used in the normal calculation.
|
||||||
|
extra_int_2 = 8
|
||||||
|
|
||||||
|
# Set to a length of about 10 characters.
|
||||||
|
extra_1 = 'TeSt'
|
||||||
|
extra_2 = 'DmR4'
|
||||||
|
|
||||||
|
# Shorten generated passphrases
|
||||||
|
use_short_passphrase = True
|
||||||
|
|
||||||
|
# Character length of shortened passphrase
|
||||||
|
shorten_length = 6
|
||||||
|
# How often to pick character from long passphrase when shortening.
|
||||||
|
shorten_sample = 4
|
||||||
|
|
||||||
# Email settings
|
# Email settings
|
||||||
MAIL_SERVER = 'smtp.gmail.com'
|
MAIL_SERVER = 'smtp.gmail.com'
|
||||||
@ -38,12 +70,10 @@ MAIL_USERNAME = 'app@gmail.com'
|
|||||||
MAIL_PASSWORD = 'password'
|
MAIL_PASSWORD = 'password'
|
||||||
MAIL_DEFAULT_SENDER = '"' + title + '" <app@gmail.com>'
|
MAIL_DEFAULT_SENDER = '"' + title + '" <app@gmail.com>'
|
||||||
|
|
||||||
# UMS settings
|
# User settings settings
|
||||||
secret_key = 'SUPER SECRET LONG KEY'
|
|
||||||
|
|
||||||
USER_ENABLE_EMAIL = True
|
USER_ENABLE_EMAIL = True
|
||||||
USER_ENABLE_USERNAME = True # Enable username authentication
|
USER_ENABLE_USERNAME = True
|
||||||
USER_REQUIRE_RETYPE_PASSWORD = True # Simplify register form
|
USER_REQUIRE_RETYPE_PASSWORD = True
|
||||||
USER_ENABLE_CHANGE_USERNAME = False
|
USER_ENABLE_CHANGE_USERNAME = False
|
||||||
USER_ENABLE_MULTIPLE_EMAILS = True
|
USER_ENABLE_MULTIPLE_EMAILS = True
|
||||||
USER_ENABLE_CONFIRM_EMAIL = True
|
USER_ENABLE_CONFIRM_EMAIL = True
|
||||||
@ -51,12 +81,7 @@ USER_ENABLE_REGISTER = True
|
|||||||
USER_AUTO_LOGIN_AFTER_CONFIRM = False
|
USER_AUTO_LOGIN_AFTER_CONFIRM = False
|
||||||
USER_SHOW_USERNAME_DOES_NOT_EXIST = True
|
USER_SHOW_USERNAME_DOES_NOT_EXIST = True
|
||||||
|
|
||||||
# Gateway contact info displayed on about page.
|
|
||||||
contact_name = 'your name'
|
|
||||||
contact_call = 'N0CALL'
|
|
||||||
contact_email = 'email@example.org'
|
|
||||||
contact_website = 'https://hbl.ink'
|
|
||||||
|
|
||||||
# Time format for display
|
# Time format for display on some pages
|
||||||
time_format = '%H:%M:%S - %m/%d/%y'
|
time_format = '%H:%M:%S - %m/%d/%y'
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<td style="text-align: center;"><a href={{url}}/help>Help</a></td>
|
<td style="text-align: center;"><a href={{url}}/help>Help</a></td>
|
||||||
<td style="text-align: center;"><a href={{url}}/generate_passphrase>View Passphrase(s)</a></td>
|
<td style="text-align: center;"><a href={{url}}/generate_passphrase>View Passphrase(s)</a></td>
|
||||||
<td style="text-align: center;"><a href={{url}}/self_care>Self Care</a></td>
|
<td style="text-align: center;"><a href={{url}}/user_tg>Current TGs</a></td>
|
||||||
<td style="text-align: center;"><a href="{{ url_for('user.edit_user_profile') }}">Edit {{ current_user.username or current_user.email }}</a></td>
|
<td style="text-align: center;"><a href="{{ url_for('user.edit_user_profile') }}">Edit {{ current_user.username or current_user.email }}</a></td>
|
||||||
<td style="text-align: center;"><a href={{ url_for('user.logout') }}>Sign out</a></td>
|
<td style="text-align: center;"><a href={{ url_for('user.logout') }}>Sign out</a></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user