504 lines
30 KiB
HTML
504 lines
30 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Users{% endblock %}
|
||
|
||
{% block styles %}
|
||
<style>
|
||
.user-details-content p {
|
||
margin-bottom: 0.5rem;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid #dee2e6;
|
||
padding-bottom: 0.5rem;
|
||
}
|
||
.user-details-content p:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.user-details-content strong {
|
||
margin-right: 10px;
|
||
}
|
||
.user-details-actions .btn {
|
||
margin-right: 5px;
|
||
margin-bottom: 5px;
|
||
}
|
||
.form-check-label i {
|
||
width: 16px;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="content-header">
|
||
<div class="container-fluid">
|
||
<div class="row mb-2">
|
||
<div class="col-sm-6">
|
||
<h1 class="m-0">Users <small class="font-weight-light"></small></h1>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="content"
|
||
data-service-status-url="{{ url_for('server_services_status_api') }}"
|
||
data-bulk-remove-url="{{ url_for('bulk_remove_users_api') }}"
|
||
data-remove-user-url-template="{{ url_for('remove_user_api', username='U') }}"
|
||
data-bulk-add-url="{{ url_for('add_bulk_users_api') }}"
|
||
data-add-user-url="{{ url_for('add_user_api') }}"
|
||
data-edit-user-url-template="{{ url_for('edit_user_api', username='U') }}"
|
||
data-reset-user-url-template="{{ url_for('reset_user_api', username='U') }}"
|
||
data-user-uri-url-template="{{ url_for('show_user_uri_api', username='U') }}"
|
||
data-bulk-uri-url="{{ url_for('show_multiple_user_uris_api') }}"
|
||
data-users-base-url="{{ url_for('users') }}"
|
||
data-get-user-url-template="{{ url_for('get_user_api', username='U') }}"
|
||
data-search-url="{{ url_for('search_users') }}">
|
||
<div class="container-fluid">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3 class="card-title">User List {% if total_users is defined %}(<span id="user-total-count">{{ total_users }}</span>){% endif %}</h3>
|
||
<div class="card-tools d-flex align-items-center flex-wrap">
|
||
|
||
<!-- Mobile Filter Dropdown -->
|
||
<div class="dropdown d-md-none mr-2 mb-2">
|
||
<button class="btn btn-sm btn-default dropdown-toggle" type="button" id="filterDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||
<i class="fas fa-filter"></i>
|
||
</button>
|
||
<div class="dropdown-menu" aria-labelledby="filterDropdown">
|
||
<a class="dropdown-item filter-button" href="#" data-filter="all"><i class="fas fa-list fa-fw mr-2"></i> All</a>
|
||
<a class="dropdown-item filter-button" href="#" data-filter="on-hold"><i class="fas fa-pause-circle fa-fw mr-2 text-warning"></i> Hold</a>
|
||
<a class="dropdown-item filter-button" href="#" data-filter="online"><i class="fas fa-wifi fa-fw mr-2 text-info"></i> Online</a>
|
||
<a class="dropdown-item filter-button" href="#" data-filter="enable"><i class="fas fa-check fa-fw mr-2 text-success"></i> Enable</a>
|
||
<a class="dropdown-item filter-button" href="#" data-filter="disable"><i class="fas fa-ban fa-fw mr-2 text-danger"></i> Disable</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Desktop Filter Buttons -->
|
||
<div class="mr-2 mb-2 d-none d-md-block">
|
||
<button type="button" class="btn btn-sm btn-default filter-button" data-filter="all">
|
||
<i class="fas fa-list"></i> All
|
||
</button>
|
||
</div>
|
||
<div class="mr-2 mb-2 d-none d-md-block">
|
||
<button type="button" class="btn btn-sm btn-warning filter-button" data-filter="on-hold">
|
||
<i class="fas fa-pause-circle"></i> Hold
|
||
</button>
|
||
</div>
|
||
<div class="mr-2 mb-2 d-none d-md-block">
|
||
<button type="button" class="btn btn-sm btn-info filter-button" data-filter="online">
|
||
<i class="fas fa-wifi"></i> Online
|
||
</button>
|
||
</div>
|
||
<div class="mr-2 mb-2 d-none d-md-block">
|
||
<button type="button" class="btn btn-sm btn-success filter-button" data-filter="enable">
|
||
<i class="fas fa-check"></i> Enable
|
||
</button>
|
||
</div>
|
||
<div class="mr-2 mb-2 d-none d-md-block">
|
||
<button type="button" class="btn btn-sm btn-danger filter-button" data-filter="disable">
|
||
<i class="fas fa-ban"></i> Disable
|
||
</button>
|
||
</div>
|
||
|
||
<div class="input-group input-group-sm" style="width: 200px;">
|
||
<input type="text" id="searchInput" class="form-control float-right" placeholder="Search">
|
||
<div class="input-group-append">
|
||
<button type="button" class="btn btn-default" id="searchButton">
|
||
<i class="fas fa-search"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-primary ml-2" data-toggle="modal" data-target="#addUserModal">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
<button type="button" class="btn btn-sm btn-info ml-2" id="showSelectedLinks">
|
||
<i class="fas fa-link"></i>
|
||
</button>
|
||
<button type="button" class="btn btn-sm btn-danger ml-2" id="deleteSelected">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body table-responsive p-0">
|
||
<table class="table table-bordered table-hover" id="userTable">
|
||
<thead>
|
||
<tr>
|
||
<th>
|
||
<input type="checkbox" id="selectAll">
|
||
</th>
|
||
<th>#</th>
|
||
<th>Username</th>
|
||
<th class="d-none d-md-table-cell">Status</th>
|
||
<th class="d-none d-md-table-cell">Traffic Usage</th>
|
||
<th class="d-none d-md-table-cell text-nowrap">Day Usage</th>
|
||
<th class="d-none d-md-table-cell text-nowrap">Expiry Date</th>
|
||
<th class="d-none d-md-table-cell">Enable</th>
|
||
<th class="d-none d-md-table-cell">Note</th>
|
||
<th class="d-none d-md-table-cell text-nowrap requires-iplimit-service" style="display: none;">Unlimited IP</th>
|
||
<th class="d-none d-md-table-cell text-nowrap">Configs</th>
|
||
<th class="d-none d-md-table-cell text-nowrap">Actions</th>
|
||
<th class="d-md-none text-center">Details</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="userTableBody">
|
||
{% set offset = (current_page - 1) * limit %}
|
||
{% include 'users_rows.html' %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer clearfix" id="paginationContainer">
|
||
<div class="row">
|
||
<div class="col-sm-12 col-md-5 d-flex align-items-center">
|
||
<div class="dataTables_info" role="status" aria-live="polite">
|
||
Showing {{ ((current_page - 1) * limit) + 1 }} to {{ [((current_page - 1) * limit) + limit, total_users] | min }} of {{ total_users }} users
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-12 col-md-7">
|
||
<div class="d-flex justify-content-end align-items-center">
|
||
<div class="mr-3">
|
||
<form id="limit-form" method="get" class="form-inline">
|
||
<label for="limit-select" class="mr-2">Per Page:</label>
|
||
<select id="limit-select" class="form-control form-control-sm">
|
||
<option value="10">10</option>
|
||
<option value="25">25</option>
|
||
<option value="50" selected>50</option>
|
||
<option value="100">100</option>
|
||
<option value="1000">1000</option>
|
||
</select>
|
||
</form>
|
||
</div>
|
||
{% if total_pages > 1 %}
|
||
<nav aria-label="Page navigation">
|
||
<ul class="pagination pagination-sm m-0">
|
||
<li class="page-item {% if current_page == 1 %}disabled{% endif %}">
|
||
<a class="page-link" href="{{ url_for('users_paginated', page=current_page - 1) }}" aria-label="Previous">
|
||
<span aria-hidden="true">«</span>
|
||
</a>
|
||
</li>
|
||
|
||
{% set page_start = [1, current_page - 2] | max %}
|
||
{% set page_end = [total_pages, current_page + 2] | min %}
|
||
|
||
{% if page_start > 1 %}
|
||
<li class="page-item"><a class="page-link" href="{{ url_for('users_paginated', page=1) }}">1</a></li>
|
||
{% if page_start > 2 %}
|
||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||
{% endif %}
|
||
{% endif %}
|
||
|
||
{% for page_num in range(page_start, page_end + 1) %}
|
||
<li class="page-item {% if page_num == current_page %}active{% endif %}">
|
||
<a class="page-link" href="{{ url_for('users_paginated', page=page_num) }}">{{ page_num }}</a>
|
||
</li>
|
||
{% endfor %}
|
||
|
||
{% if page_end < total_pages %}
|
||
{% if page_end < total_pages - 1 %}
|
||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||
{% endif %}
|
||
<li class="page-item"><a class="page-link" href="{{ url_for('users_paginated', page=total_pages) }}">{{ total_pages }}</a></li>
|
||
{% endif %}
|
||
|
||
<li class="page-item {% if current_page == total_pages %}disabled{% endif %}">
|
||
<a class="page-link" href="{{ url_for('users_paginated', page=current_page + 1) }}" aria-label="Next">
|
||
<span aria-hidden="true">»</span>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!-- Add User Modal -->
|
||
<div class="modal fade" id="addUserModal" tabindex="-1" role="dialog" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="addUserModalLabel">Add User</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<ul class="nav nav-tabs" id="addUserTab" role="tablist">
|
||
<li class="nav-item">
|
||
<a class="nav-link active" id="single-user-tab" data-toggle="tab" href="#single-user" role="tab" aria-controls="single-user" aria-selected="true">Add User</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" id="bulk-users-tab" data-toggle="tab" href="#bulk-users" role="tab" aria-controls="bulk-users" aria-selected="false">Bulk Add</a>
|
||
</li>
|
||
</ul>
|
||
<div class="tab-content" id="addUserTabContent">
|
||
<div class="tab-pane fade show active" id="single-user" role="tabpanel" aria-labelledby="single-user-tab">
|
||
<form id="addUserForm" class="mt-3">
|
||
<div class="form-group">
|
||
<label for="addUsername">Username</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||
</div>
|
||
<input type="text" class="form-control" id="addUsername" name="username" required>
|
||
</div>
|
||
<small class="form-text text-danger" id="addUsernameError"></small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="addTrafficLimit">Traffic Limit (GB)</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-database"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addTrafficLimit" name="traffic_limit" required min="0">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="addExpirationDays">Expiration Days</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addExpirationDays" name="expiration_days" required min="0">
|
||
</div>
|
||
<small class="form-text text-danger" id="addExpirationDaysError"></small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="addNote">Note (Optional)</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-sticky-note"></i></span>
|
||
</div>
|
||
<input type="text" class="form-control" id="addNote" name="note">
|
||
</div>
|
||
</div>
|
||
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
||
<input type="checkbox" class="form-check-input" id="addUnlimited" name="unlimited">
|
||
<label class="form-check-label" for="addUnlimited">
|
||
<i class="fas fa-shield-alt text-primary mr-2"></i>Unlimited IP
|
||
</label>
|
||
</div>
|
||
<button type="submit" class="btn btn-primary" id="addSubmitButton">Add User</button>
|
||
</form>
|
||
</div>
|
||
<div class="tab-pane fade" id="bulk-users" role="tabpanel" aria-labelledby="bulk-users-tab">
|
||
<form id="addBulkUsersForm" class="mt-3">
|
||
<div class="form-group">
|
||
<label for="addBulkPrefix">Username Prefix</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-users"></i></span>
|
||
</div>
|
||
<input type="text" class="form-control" id="addBulkPrefix" name="prefix" required>
|
||
</div>
|
||
<small class="form-text text-danger" id="addBulkPrefixError"></small>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group col-md-6">
|
||
<label for="addBulkCount">Number of Users</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-hashtag"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addBulkCount" name="count" value="10" required min="1">
|
||
</div>
|
||
</div>
|
||
<div class="form-group col-md-6">
|
||
<label for="addBulkStartNumber">Start Number</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-play-circle"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addBulkStartNumber" name="start_number" value="1" required min="0">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group col-md-6">
|
||
<label for="addBulkTrafficLimit">Traffic Limit (GB)</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-database"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addBulkTrafficLimit" name="traffic_gb" required min="0">
|
||
</div>
|
||
</div>
|
||
<div class="form-group col-md-6">
|
||
<label for="addBulkExpirationDays">Expiration Days</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="addBulkExpirationDays" name="expiration_days" required min="0">
|
||
</div>
|
||
<small class="form-text text-danger" id="addBulkExpirationDaysError"></small>
|
||
</div>
|
||
</div>
|
||
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
||
<input type="checkbox" class="form-check-input" id="addBulkUnlimited" name="unlimited">
|
||
<label class="form-check-label" for="addBulkUnlimited">
|
||
<i class="fas fa-shield-alt text-primary mr-2"></i>Unlimited IP
|
||
</label>
|
||
</div>
|
||
<button type="submit" class="btn btn-primary" id="addBulkSubmitButton">Add Bulk Users</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit User Modal -->
|
||
<div class="modal fade" id="editUserModal" tabindex="-1" role="dialog" aria-labelledby="editUserModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="editUserModalLabel">Edit User</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="editUserForm">
|
||
<div class="form-group">
|
||
<label for="editUsername">Username</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||
</div>
|
||
<input type="text" class="form-control" id="editUsername" readonly>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="editPassword">Password
|
||
<i class="fas fa-info-circle text-muted ml-1"
|
||
data-toggle="tooltip"
|
||
data-placement="top"
|
||
title="Changing the password disconnects the user and invalidates all existing configuration and subscription links. New links must be shared with the user to restore access.">
|
||
</i>
|
||
</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-key"></i></span>
|
||
</div>
|
||
<input type="text" class="form-control" id="editPassword" name="new_password" placeholder="Leave empty to keep current password">
|
||
<div class="input-group-append">
|
||
<button class="btn btn-outline-secondary" type="button" id="generatePasswordBtn"><i class="fas fa-sync-alt"></i></button>
|
||
</div>
|
||
</div>
|
||
<small class="form-text text-danger" id="editPasswordError"></small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="editTrafficLimit">Traffic Limit (GB)</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-database"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="editTrafficLimit" name="new_traffic_limit" min="0">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="editExpirationDays">Expiration Days</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
|
||
</div>
|
||
<input type="number" class="form-control" id="editExpirationDays" name="new_expiration_days" min="0">
|
||
</div>
|
||
<small class="form-text text-danger" id="editExpirationDaysError"></small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="editNote">Note</label>
|
||
<div class="input-group">
|
||
<div class="input-group-prepend">
|
||
<span class="input-group-text"><i class="fas fa-sticky-note"></i></span>
|
||
</div>
|
||
<textarea class="form-control" id="editNote" name="note" rows="3"></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="form-check">
|
||
<input type="checkbox" class="form-check-input" id="editBlocked" name="blocked">
|
||
<label class="form-check-label" for="editBlocked">
|
||
<i class="fas fa-ban text-danger mr-2"></i>Blocked
|
||
</label>
|
||
</div>
|
||
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
||
<input type="checkbox" class="form-check-input" id="editUnlimitedIp" name="unlimited_ip">
|
||
<label class="form-check-label" for="editUnlimitedIp">
|
||
<i class="fas fa-shield-alt text-primary mr-2"></i>Unlimited IP
|
||
</label>
|
||
</div>
|
||
<input type="hidden" id="originalUsername" name="username">
|
||
<button type="submit" class="btn btn-primary mt-3" id="editSubmitButton">Save Changes</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- QR Code Modal -->
|
||
<div class="modal fade" id="qrcodeModal" tabindex="-1" role="dialog" aria-labelledby="qrcodeModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="qrcodeModalLabel">QR Codes</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body text-center">
|
||
<div id="qrcodesContainer" class="mx-auto"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Show Links Modal -->
|
||
<div class="modal fade" id="showLinksModal" tabindex="-1" role="dialog" aria-labelledby="showLinksModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="showLinksModalLabel">Extract User Links</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>Select the link types you want to extract for the selected users:</p>
|
||
<div class="form-group">
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="extractIPv4" value="ipv4">
|
||
<label class="form-check-label" for="extractIPv4">IPv4</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="extractIPv6" value="ipv6">
|
||
<label class="form-check-label" for="extractIPv6">IPv6</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="extractNormalSub" value="normal_sub" checked>
|
||
<label class="form-check-label" for="extractNormalSub">Normal SUB</label>
|
||
</div>
|
||
<div class="form-check form-check-inline">
|
||
<input class="form-check-input" type="checkbox" id="extractNodes" value="nodes">
|
||
<label class="form-check-label" for="extractNodes">Nodes</label>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn btn-primary mb-3" id="extractLinksButton">Extract Links</button>
|
||
<textarea id="linksTextarea" class="form-control" rows="10" readonly placeholder="Extracted links will appear here..."></textarea>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||
<button type="button" class="btn btn-success" id="copyExtractedLinksButton">Copy</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block javascripts %}
|
||
<script src="https://cdn.jsdelivr.net/npm/qr-code-styling/lib/qr-code-styling.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||
<script src="{{ url_for('assets', path='js/users.js') }}"></script>
|
||
{% endblock %} |