First version with web gui

This commit is contained in:
2020-02-21 08:42:29 +01:00
parent fb54505a40
commit 609d821501
27 changed files with 1298 additions and 36 deletions

5
.gitignore vendored
View File

@@ -8,7 +8,4 @@ BD/database*
__pycache__/
**/__pycache__/
.vscode/
cooks/*.yaml
control/
site/
admin/
cooks/*.yaml

275
admin/admin.py Normal file
View File

@@ -0,0 +1,275 @@
#!/usr/bin/python3
from flask import Flask, url_for, render_template, request, Response,redirect,make_response
import os
import jinja2
import requests
import hashlib #SHA256
import json
import base64
import configparser
from datetime import datetime
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
BUILD=1
APIC_VER=2
# Load config to comunicate with API or use default
try:
config = configparser.ConfigParser()
config.read(os.path.relpath('..\\client\\configpcm.ini'))
c_server = config['General']['server']
except:
c_server = "https://127.0.0.1:3333" #Default server api address
app = Flask(__name__,static_url_path="/assets", static_folder='assets', template_folder="templates")
def returnvalueapi(suburl,field="Name"):
global c_server
passha256=request.cookies.get('admin_logued')
if 'Password=' in suburl:
twopart = ""
elif '?' in suburl:
twopart = '&Password='+str(passha256)
else:
twopart = '?Password='+str(passha256)
r=(requests.post(c_server+suburl+twopart, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
try:
#print(suburl+twopart+"->"+r)
jsonobj = json.loads(r)
return jsonobj[field]
except:
print('Error: '+str(r))
return str({'TEXT':r,'RESULT':'ERROR'})
def returnTable(suburl,field=["Name"]):
global c_server
passha256=request.cookies.get('admin_logued')
if isinstance(field,str):
if field != 'ASIS': # Do not touch if it says that we have to return table AS IS
field=[field] # Convert to list
if 'Password=' in suburl:
twopart = ""
elif '?' in suburl:
twopart = '&Password='+str(passha256)
else:
twopart= '?Password='+str(passha256)
try:
r=(requests.post(c_server+suburl+twopart, verify=False).text).replace("\'", "\"").replace('\\"',"'").replace(': None',': "None"')
jsonobj = json.loads(r)
if field == 'ASIS':
return jsonobj
else:
listitems = []
for ite in jsonobj: # Run in array from json
to = []
for i in field:
to.append(str(ite[i]))
listitems.append(to)
return listitems
except:
return None
@app.route("/admin",methods=['GET'])
@app.route("/admin/",methods=['GET'])
def pAdminIndex(): #Admin Index page
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
table = returnTable("/get/computers",['ID_C','Name','RAM','CPUName','SOVersion','SOCaption','HDD','LastConnection','RAMFree'])
for lista in table:
lista.append(returnTable("/get/groups?ComputerID="+lista[0],["Name"])) #lista[9]¿
if lista[6] is not None:
try:
lista[6]=json.loads(base64.b64decode(lista[6]).decode('UTF-8'))
except:
lista[6]=json.loads(str("{}"))
else:
lista[6]=json.loads(str("{}"))
#print (lista[6])
try:
lista[7] = datetime.fromtimestamp(int(lista[7])).strftime('%Y-%m-%d %H:%M:%S') # LastConnection
except:
lista[7] = "Never"
return render_template('/adminindex.tmpl', title="Admin Dashboard",tablecomputers=table)
@app.route("/admin/addcomputer",methods=['GET'])
def pAdminAddcomputer():
computeradd=request.args.get('computeradd')
if computeradd is not None:
r = returnvalueapi("/add/computer?ComputerName="+computeradd,field="RESULT")
if r == "OK":
return redirect('/admin')
else:
return "ERROR"
else:
return redirect('/admin')
@app.route("/admin/delcomputer",methods=['GET'])
def pAdminDelcomputer():
computerdel=request.args.get('computerdel')
r = returnvalueapi("/del/computer?ComputerID="+computerdel,field="RESULT")
if r == "OK":
return redirect('/admin')
else:
return "ERROR"
@app.route("/admin/computer",methods=['GET'])
def pAdminComputer(): #Admin see one group
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
computerid=request.args.get('ID_C')
if computerid is not None and computerid.isnumeric():
computername = returnvalueapi('/get/computers?ComputerID='+str(computerid),"Name")
groups = returnTable("/get/groups?ComputerName="+str(computername),['ID_G','Name'])
#####IT DOESNT HAVE TEMPLATE CREATED
return render_template('/admincomputer.tmpl', title="Computer "+computername,computerid=computerid,computername=computername,groups=groups)
return redirect('/admin')
@app.route("/admin/groups",methods=['GET'])
def pAdminGroups(): #Admin Index groups
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
table = returnTable("/get/groups",['ID_G','Name'])
for lista in table:
lista.append(returnTable("/get/computersgrp?GroupID="+lista[0],['ID_C','Name'])) #lista[3]¿
lista.append(returnTable("/get/cookgrp?GroupID="+lista[0],['CookName'])) #lista[4]¿
return render_template('/admingroups.tmpl', title="Groups",tablegroups=table)
@app.route("/admin/group",methods=['GET'])
def pAdminGroup(): #Admin see one group
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
groupid=request.args.get('ID_G')
if groupid is not None and groupid.isnumeric():
groupname=returnvalueapi('/get/groups?GroupID='+str(groupid),"Name")
computers = returnTable("/get/computers",['ID_C','Name'])
datagroup = (returnTable("/get/computersgrp?GroupID="+groupid,['ID_C','Name'])) #lista[0]¿
tem = (returnTable("/get/cookgrp?GroupID="+groupid,['CookName'])) #lista[1]¿
cooksdata = []
for cook in tem:
to = []
to.append(cook[0])
to.append(returnvalueapi('/get/lastrevisioncook?CookName='+cook[0],"Revision"))
to.append(returnTable("/get/statuscook?CookName="+cook[0],'ASIS'))
cooksdata.append(to)
tem = (returnTable("/get/cookall",'ASIS')) #Lista de todas las recetas
allcooks = []
for cook in tem['CookName']:
to = []
to.append(cook)
to.append(returnvalueapi('/get/lastrevisioncook?CookName='+cook,"Revision"))
allcooks.append(to)
return render_template('/admingroup.tmpl', title="Group "+groupname,groupname=groupname,groupid=groupid,datagroup=datagroup,cooksdata=cooksdata,computers=computers,allcooks=allcooks)
return redirect('/admin/groups')
@app.route("/admin/group/addcook",methods=['GET'])
def pAdminGroupAddcook(): #Add cook form enter
cooktoadd=request.args.get('cooknameadd')
groupid=request.args.get('groupid')
if cooktoadd != None and groupid != None:
r = returnvalueapi("/add/cookgrp?CookName="+cooktoadd+"&GroupID="+groupid,field="RESULT")
if r == "OK":
return redirect('/admin/group?ID_G='+groupid)
@app.route("/admin/group/addcomputer",methods=['GET'])
def pAdminGroupAddcomputer(): #Add computer form enter
computertoadd=request.args.get('computeridadd')
groupid=request.args.get('groupid')
if computertoadd != None and groupid != None:
r = returnvalueapi("/add/grpcomputer?ComputerID="+computertoadd+"&GroupID="+groupid,field="RESULT")
if r == "OK":
return redirect('/admin/group?ID_G='+groupid)
@app.route("/admin/group/delcomputer",methods=['GET'])
def pAdminGroupDelcomputer(): #Add computer form enter
computertodel=request.args.get('computernamedel')
groupid=request.args.get('groupid')
if computertodel != None and groupid != None:
r = returnvalueapi("/del/grpcomputer?ComputerName="+computertodel+"&GroupID="+groupid,field="RESULT")
if r == "OK":
return redirect('/admin/group?ID_G='+groupid)
else:
return redirect('/admin/group?ID_G='+groupid)
@app.route("/admin/cook",methods=['GET'])
def pAdminCook(): #Admin Index groups
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
cookname=request.args.get('CookName')
if cookname != None:
groupscook=returnTable('/get/grpcook?CookName='+str(cookname),['Name'])
allgroups=returnTable('/get/groups',['ID_G','Name'])
return render_template('/admincook.tmpl', title="Cook "+cookname,cookname=cookname,allgroups=allgroups,groupscook=groupscook)
@app.route("/admin/cook/addgroup",methods=['GET'])
def pAdminCookAddgroup(): # Add cook to group
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
cookname=request.args.get('cooknameadd')
groupid=request.args.get('groupidadd')
if cookname != None and groupid != None:
returnvalueapi('/add/cookgrp?CookName='+cookname+'&GroupID='+groupid,'RESULT')
return redirect('/admin/cook?CookName='+cookname)
else:
return "Error of arguments"
@app.route("/admin/cook/delgroup",methods=['GET'])
def pAdminCookDelgroup(): # Add cook to group
if returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE') != '0':
return render_template('/login.tmpl', title="Login")
cookname=request.args.get('cooknamedel')
groupname=request.args.get('groupnamedel')
if cookname != None and groupname != None:
ret = returnvalueapi('/del/cookgrp?CookName='+cookname+'&GroupName='+groupname,'RESULT')
if ret != '0':
return "Error "+str(ret)
return redirect('/admin/cook?CookName='+cookname)
else:
return "Error of arguments"
@app.route("/",methods=['GET'])
def pIndex(): #Index page
howispasswd=returnvalueapi('/check/password?Password='+str(request.cookies.get('admin_logued')),'EXITCODE')
if howispasswd == '0': # Logued
return redirect(url_for('pAdminIndex'))
if howispasswd == '1' or howispasswd == '2': # Has to login
return render_template('/login.tmpl')
if howispasswd == '3': # No password yet
return 'Yet to do it..'
@app.route("/login",methods=['GET','POST'])
def pLogin(): #Login
if request.values.get('password'):
trpass = hashlib.sha256(request.values.get('password').encode()).hexdigest()
#print(returnvalueapi('/check/password?Password='+trpass,'EXITCODE'))
if returnvalueapi('/check/password?Password='+trpass,'EXITCODE') == '0':
res = make_response(redirect(url_for('pAdminIndex')))
res.set_cookie("admin_logued",value=trpass)
return res
else:
return render_template('/login.tmpl')
else:
return render_template('/login.tmpl')
print("Build: "+str(BUILD))
print("API Client Compatible Version: "+str(APIC_VER))
app.run(debug=True,port=3434,ssl_context='adhoc',host='0.0.0.0',threaded=True) #Default is port 3434

7
admin/assets/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
body {
font-size: .875rem;
}
.feather {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
padding: 0;
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
top: 48px; /* Height of navbar */
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link .feather {
margin-right: 4px;
color: #999;
}
.sidebar .nav-link.active {
color: #007bff;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* Navbar
*/
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}
/*
* Utilities
*/
.border-top { border-top: 1px solid #e5e5e5; }
.border-bottom { border-bottom: 1px solid #e5e5e5; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

10
admin/assets/js/Chart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
admin/assets/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

13
admin/assets/js/feather-4.24.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5
admin/assets/js/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
{# -*- coding: utf-8 -*- #} {# NOT DID ALREADY!!! #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<h2>Computer {{computername}}</h2>
<br>
<h2>Groups of {{computername}}</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Group Name</th>
<th>Cooks</th>
<th></th>
</tr>
</thead>
<tbody>
{% for item in tablegroups %} {#['ID_G','Name','Cooks[CookName]']#}
<tr>
<td><a href="/admin/group?ID_G={{item.0}}">{{item.1}}</a></td>
<td style='width:50%;'>
{% for x in item.2 %}
{{x.0}},
{% endfor %}
</td>
<td><a class="btn btn-outline-danger btn-sm" data-wgroup="{{item.1}}" data-toggle="modal" data-target="#delGroup">Delete {{computername}} from group {{item.1}}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
{% endblock %}

View File

@@ -0,0 +1,92 @@
{# -*- coding: utf-8 -*- #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<!-- Modal Delete Group From Cook -->
<div class="modal fade" id="modalDelete" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/cook/delgroup">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Delete group from cook {{cookname}}</h5>
</div>
<div class="modal-body">
<div class="form-group">
<input type="text" readonly="readonly" class="form-control" hidden="hidden" name="cooknamedel" id="cooknamedel" value="{{cookname}}"/>
<p>Are you sure?<br>
<label for="groupnamedel" class="col-form-label">Group:</label>
</div>
<input type="text" readonly="readonly" class="form-control" name="groupnamedel" id="groupnamedel">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Remove</button>
</div>
</div>
</form>
</div>
</div>
<!-- Modal Add Group To Cook -->
<div class="modal fade" id="modalAdd" tabindex="-1" role="dialog" aria-labelledby="modalAddLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/cook/addgroup">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalAddLabel">Add cook to Group {{groupname}}</h5>
</div>
<div class="modal-body">
<div class="form-group">
<p>Add group to cook {{cookname}}<br>
<input type="text" readonly="readonly" class="form-control" hidden="hidden" name="cooknameadd" id="cooknameadd" value="{{cookname}}"/>
<label for="groupidadd" class="col-form-label">Group:</label>
<select class="form-control" name="groupidadd" id="groupidadd">
{% for item in allgroups %}
<option value="{{item.0}}">{{item.1}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
</div>
</div>
<h2>Groups of cook {{cookname}}</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Group Name</th>
<th style="align:right">Delete Group from this Cook</th>
</tr>
</thead>
<tbody>
{% for item in groupscook %} {#['Name']#}
<tr>
<td style='width:64px'>{{item.0}}</td>
<td><a href="/admin/group?ID_G={{item.0}}">{{item.0}}</a></td>
<td><a class="btn btn-outline-danger btn-sm" data-wgroup="{{item.0}}" data-toggle="modal" data-target="#modalDelete">Remove group {{item.0}} from cook {{cookname}}</a></td>
</tr>
{% endfor %}
<tr>
<td>#</td>
<td colspan="2"><a class="btn btn-outline-primary btn-sm" data-toggle="modal" data-target="#modalAdd">Add group to {{cookname}}</a></td>
</tbody>
</table>
</div>
<script>
$('#modalDelete').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
var recipient = button.data('wgroup') // Extract info from data-* attributes
var modal = $(this)
modal.find('#groupnamedel').val(recipient)
})
</script>
{% endblock %}

View File

@@ -0,0 +1,150 @@
{# -*- coding: utf-8 -*- #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<!-- Modal Delete Computer From Group -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/group/delcomputer">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Delete computer from Group {{groupname}}</h5>
</div>
<div class="modal-body">
<div class="form-group">
<input type="text" readonly="readonly" class="form-control" hidden="hidden" name="groupid" id="groupid" value="{{groupid}}"/>
<p>Are you sure?<br>
<label for="computername" class="col-form-label">Computer:</label>
</div>
<input type="text" readonly="readonly" class="form-control" name="computernamedel" id="computernamedel">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</div>
</form>
</div>
</div>
<!-- Modal Add Computer To Group -->
<div class="modal fade" id="modalAdd" tabindex="-1" role="dialog" aria-labelledby="modalAddLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/group/addcomputer">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalAddLabel">Add computer to Group {{groupname}}</h5>
</div>
<div class="modal-body">
<div class="form-group">
<p>Add computer to group {{groupname}}<br>
<input type="text" readonly="readonly" class="form-control" hidden="hidden" name="groupid" value="{{groupid}}"/>
<label for="computername2" class="col-form-label">Computer:</label>
<select class="form-control" name="computeridadd" id="computeridadd">
{% for item in computers %}
<option value="{{item.0}}">{{item.1}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
</div>
</div>
<!-- Modal Add Cook To Group -->
<div class="modal fade" id="modalAddCook" tabindex="-1" role="dialog" aria-labelledby="modalAddCookLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/group/addcook">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalAddCookLabel">Add Cook to Group {{groupname}}</h5>
</div>
<div class="modal-body">
<div class="form-group">
<p>Add cook to group {{groupname}}<br>
<input type="text" readonly="readonly" class="form-control" hidden="hidden" name="groupid" value="{{groupid}}"/>
<label for="cookname" class="col-form-label">Cook name:</label>
<select class="form-control" name="cooknameadd" id="cooknameadd">
{% for item in allcooks %}
<option value="{{item.0}}">{{item.0}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
</div>
</div>
<h2>Computers of group {{groupname}}</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Computer Name</th>
<th style="align:right">Delete computer from Group</th>
</tr>
</thead>
<tbody>
{% for item in datagroup %} {#['ID_C','Name']#}
<tr>
<td style='width:64px'>{{item.0}}</td>
<td><a href="/admin/computer?ID_C={{item.0}}">{{item.1}}</a></td>
<td><a class="btn btn-outline-danger btn-sm" {#href="/admin/groupcomputerdel?ID_C={{item.0}}&ID_G={{groupid}}"#} data-wcomputer="{{item.1}}" data-toggle="modal" data-target="#exampleModal">Delete {{item.1}} from group {{groupname}}</a></td>
</tr>
{% endfor %}
<tr>
<td>#</td>
<td colspan="2"><a class="btn btn-outline-primary btn-sm" data-toggle="modal" data-target="#modalAdd">Add computer to {{groupname}}</a></td>
</tbody>
</table>
</div>
<h2>Cooks of group {{groupname}}</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>Cook Name</th>
<th style="width:64px">Cook Last Revision</th>
<th>Applied to</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="3"><a class="btn btn-outline-primary btn-sm" data-toggle="modal" data-target="#modalAddCook">Add new cook to group {{groupname}}</a></td>
</tr>
{% for item in cooksdata %} {#['CookName','LastRevision','AppliedTo']#}
<tr>
<td><a href="/admin/cook?CookName={{item.0}}">{{item.0}}</a></td>
<td>{{item.1}}</td>
<td>
{% for subitem in item.2|sort(attribute='Revision')|sort(attribute='Name') %} {# item.2 = AppliedTo #}
{{subitem.Name}}{% if subitem.Revision|int < item.1|int %}<span style="color:red">({{subitem.Revision}})</span>{% elif subitem.Error != 0 %}<span style="color:orange;font-weigth:bold;font-style:italic" title="{{subitem.ErrorDesc}}">(Error r.{{subitem.Revision}})</span>{% endif %},
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
$('#exampleModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
var recipient = button.data('wcomputer') // Extract info from data-* attributes
var modal = $(this)
//modal.find('.modal-title').text('New message to ' + recipient)
modal.find('#computernamedel').val(recipient)
})
</script>
{% endblock %}

View File

@@ -0,0 +1,35 @@
{# -*- coding: utf-8 -*- #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<h2>Groups</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Group Name</th>
<th>Cooks</th>
<th>Computers</th>
</tr>
</thead>
<tbody>
{% for item in tablegroups %} {#['ID_G','Name','Computers[ID_C,Name]','Cooks[CookName]']#}
<tr>
<td>{{item.0}}</td>
<td><a href="/admin/group?ID_G={{item.0}}">{{item.1}}</a></td>
<td style='width:50%;'>
{% for x in item.3 %}
{{x.0}},
{% endfor %}
</td>
<td>
{% for x in item.2 %}
{{x.1}},
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -0,0 +1,102 @@
{# -*- coding: utf-8 -*- #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<!-- Modal Add Computer To Database -->
<div class="modal fade" id="modalAdd" tabindex="-1" role="dialog" aria-labelledby="modalAddLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/addcomputer">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalAddLabel">Add computer to Database</h5>
</div>
<div class="modal-body">
<div class="form-group">
<p>Add computer to database<br>
<label for="computeradd" class="col-form-label">Computer:</label>
<input name="computeradd" id="computeradd" type="text"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
</div>
</div>
<!-- Modal Delete Computer To Database -->
<div class="modal fade" id="modalDel" tabindex="-1" role="dialog" aria-labelledby="modalDelLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="get" action="/admin/delcomputer">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalDelLabel">Delete computer to Database. NOT REVERSIBLE</h5>
</div>
<div class="modal-body">
<div class="form-group">
<p>Delete computer from database. NOT REVERSIBLE<br>
<label for="computerdel" class="col-form-label">Computer:</label>
<select class="form-control" name="computerdel" id="computerdel">
{% for item in tablecomputers %} {#['ID_C','Name',.....]#}
<option value="{{item.0}}">{{item.1}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">DELETE</button>
</div>
</div>
</form>
</div>
</div>
<h2>Computers</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Computer</th>
<th>Operating System</th>
<th>Groups</th>
<th>CPU</th>
<th>RAM (RAMUsed/RAM) Mb</th>
<th>HDD</th>
<th>Last ping</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="8"><a class="btn btn-outline-success btn-sm" data-toggle="modal" data-target="#modalAdd">Add computer to database</a></td>
</tr>
{% for item in tablecomputers %} {#['ID_C','Name','RAM','CPUName','SOVersion','SOCaption','HDD','LastConnection','RamFree','GroupsNames']#}
<tr>
<td>{{item.0}}</td>
<td>{{item.1}}</td>
<td>{{item.5}} {{item.4}}</td>
<td width='200px'>
{% for x in item.9 %}
{{x.0}},
{% endfor %}
</td>
<td>{{item.3}}</td>
<td>{{item.2|int - item.8|int}}/{{item.2}}</td>
<td>
{% for x in item.6 %}
{{x.Volume}}({{x.FreeSpace}}/{{x.Capacity}} GB)<br>
{% endfor %}
</td>
<td>{{item.7}}</td>
</tr>
{% endfor %}
<tr>
<td colspan="8"><a class="btn btn-outline-danger btn-sm" data-toggle="modal" data-target="#modalDel">Delete computer to database</a></td>
</tr>
</tbody>
</table>
</div>
{% endblock %}

View File

@@ -0,0 +1,84 @@
{# -*- coding: utf-8 -*- #}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<!--<link rel="icon" href="/docs/4.0/assets/img/favicons/favicon.ico">-->
<title>{{title}} - Powerful Computer Manager</title>
<link rel="canonical" href="index.html">
<!-- Bootstrap core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{{ url_for('static', filename='css/dashboard.css') }}" rel="stylesheet">
<!-- Jquery JS -->
<script src="{{ url_for('static', filename='js/jquery-3.2.1.slim.min.js') }}"></script>
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">Company name</a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">Sign out</a>
</li>
</ul>
</nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky pt-4">
<ul class="nav flex-column pt-4">
<li class="nav-item">
<a class="nav-link {% if request.path == "/admin/" or request.path == "/admin" %}active{% endif %}" href="/admin">
<span data-feather="home"></span>
Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if request.path == "/admin/groups" or request.path == "/admin/group" %}active{% endif %}" href="/admin/groups">
<span data-feather="file"></span>
Groups
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">{{title}}</h1>
<div class="btn-toolbar mb-2 mb-md-0">
</div>
</div>
{% block content %}{% endblock %}
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<!-- Icons -->
<script src="{{ url_for('static', filename='js/feather-4.24.1.min.js') }}"></script>
<script>
feather.replace()
</script>
<!-- Graphs -->
{% block scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,17 @@
{# -*- coding: utf-8 -*- #}
{% extends 'baseadmin.tmpl' %}
{% block content %}
<h2>Login</h2>
<form action="/login" method="POST">
<div class="table-responsive">
<table class="table table-striped table-sm">
<tr>
<td>Password<input type="password" name="password" id="password" /></td>
</tr>
<tr>
<td><button type="submit">Login</button></td>
</tr>
</table>
</div>
</form>
{% endblock %}

304
admin/templates/test.html Normal file
View File

@@ -0,0 +1,304 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/docs/4.0/assets/img/favicons/favicon.ico">
<title>Dashboard Template for Bootstrap</title>
<link rel="canonical" href="index.html">
<!-- Bootstrap core CSS -->
<link href="../../admin/assets/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="../../admin/assets/css/dashboard.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">Company name</a>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">Sign out</a>
</li>
</ul>
</nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#">
<span data-feather="home"></span>
Dashboard <span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file"></span>
Orders
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="shopping-cart"></span>
Products
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="users"></span>
Customers
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="bar-chart-2"></span>
Reports
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="layers"></span>
Integrations
</a>
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reports</span>
<a class="d-flex align-items-center text-muted" href="#">
<span data-feather="plus-circle"></span>
</a>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Current month
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Last quarter
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Social engagement
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Year-end sale
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<button class="btn btn-sm btn-outline-secondary">Share</button>
<button class="btn btn-sm btn-outline-secondary">Export</button>
</div>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
<span data-feather="calendar"></span>
This week
</button>
</div>
</div>
<canvas class="my-4" id="myChart" width="900" height="380"></canvas>
<h2>Section title</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>1,001</td>
<td>Lorem</td>
<td>ipsum</td>
<td>dolor</td>
<td>sit</td>
</tr>
<tr>
<td>1,002</td>
<td>amet</td>
<td>consectetur</td>
<td>adipiscing</td>
<td>elit</td>
</tr>
<tr>
<td>1,003</td>
<td>Integer</td>
<td>nec</td>
<td>odio</td>
<td>Praesent</td>
</tr>
<tr>
<td>1,003</td>
<td>libero</td>
<td>Sed</td>
<td>cursus</td>
<td>ante</td>
</tr>
<tr>
<td>1,004</td>
<td>dapibus</td>
<td>diam</td>
<td>Sed</td>
<td>nisi</td>
</tr>
<tr>
<td>1,005</td>
<td>Nulla</td>
<td>quis</td>
<td>sem</td>
<td>at</td>
</tr>
<tr>
<td>1,006</td>
<td>nibh</td>
<td>elementum</td>
<td>imperdiet</td>
<td>Duis</td>
</tr>
<tr>
<td>1,007</td>
<td>sagittis</td>
<td>ipsum</td>
<td>Praesent</td>
<td>mauris</td>
</tr>
<tr>
<td>1,008</td>
<td>Fusce</td>
<td>nec</td>
<td>tellus</td>
<td>sed</td>
</tr>
<tr>
<td>1,009</td>
<td>augue</td>
<td>semper</td>
<td>porta</td>
<td>Mauris</td>
</tr>
<tr>
<td>1,010</td>
<td>massa</td>
<td>Vestibulum</td>
<td>lacinia</td>
<td>arcu</td>
</tr>
<tr>
<td>1,011</td>
<td>eget</td>
<td>nulla</td>
<td>Class</td>
<td>aptent</td>
</tr>
<tr>
<td>1,012</td>
<td>taciti</td>
<td>sociosqu</td>
<td>ad</td>
<td>litora</td>
</tr>
<tr>
<td>1,013</td>
<td>torquent</td>
<td>per</td>
<td>conubia</td>
<td>nostra</td>
</tr>
<tr>
<td>1,014</td>
<td>per</td>
<td>inceptos</td>
<td>himenaeos</td>
<td>Curabitur</td>
</tr>
<tr>
<td>1,015</td>
<td>sodales</td>
<td>ligula</td>
<td>in</td>
<td>libero</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../../admin/assets/js/jquery-3.2.1.slim.min.js"></script>
<script src="../../admin/assets/js/popper.min.js"></script>
<script src="../../admin/assets/js/bootstrap.min.js"></script>
<!-- Icons -->
<script src="../../admin/assets/js/feather-4.24.1.min.js"></script>
<script>
feather.replace()
</script>
<!-- Graphs -->
<script src="../../admin/assets/js/Chart.min.js"></script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
</script>
</body>
</html>

66
api.py
View File

@@ -1,5 +1,5 @@
#!/usr/bin/python3
BUILD = 26
BUILD = 28
API_VER = 2
from flask import Flask, request
import random
@@ -72,7 +72,7 @@ def getComputerExists():
##
# getGroups: Know all groups or groups of a computer
# getGroups: Know all groups, or groups of a computer
# /get/groups?
# @param ComputerName/ComputerID -> Computer to see groups (Not neccesary)
# @param GroupID -> For know Name of a GroupID
@@ -138,14 +138,27 @@ def getComputersGrp():
return str(data)
##
# getCookAll: Get the list of cooks added at least to one group
# getCookAll: Get the list of all cooks (Or all added at least to one group)
# /get/cookall?
# NoParams
# @param OnlyAdded: Show only added cooks
##
@app.route("/get/cookall",methods=['POST'])
def getCookAll():
logit(request.base_url)
data = sql.select("SELECT DISTINCT CookName FROM COOKS_IDG ORDER BY CookName ASC") #All cooks
OnlyAdded =None
for key, value in request.args.to_dict().items():
if key == "OnlyAdded":
OnlyAdded= 1
if OnlyAdded is None:
x = []
for file in os.listdir("cooks"):
if file.endswith(".yaml"):
x.append(str(file.replace('.yaml','')).lower())
data = {}
data['CookName'] = x
else:
data = sql.select("SELECT DISTINCT CookName FROM COOKS_IDG ORDER BY CookName ASC") #All cooks
return str(data)
@@ -247,11 +260,12 @@ def delCookGrp(): #Delete cook from a group
if key == "GroupID":
GroupID = value
if GroupID is not None and CookName is not None:
result = sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"' AND CookName='"+CookName+"'")
return str(result)
else:
return str({'TEXT': 'Error, no Group, or CookName does\'t exists','RESULT':'ERROR'})
if GroupID is not None and CookName is not None:
result = sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"' AND CookName='"+CookName+"'")
cleanDatabase()
return str({'TEXT': '','RESULT':'0'}) #Return text str(result) is problematic (json inside json)
else:
return str({'TEXT': 'Error, no Group, or CookName doesnt exists','RESULT':'ERROR'})
##
# delEmptyPcsGroup: Delete all computers from from a group
@@ -273,7 +287,7 @@ def delEmptyPcsGroup():
result = sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"'")
return str(result)
else:
return str({'TEXT': 'Error, this group doesn\'t exists','RESULT':'ERROR'})
return str({'TEXT': 'Error, this group doesnt exists','RESULT':'ERROR'})
##
@@ -460,7 +474,6 @@ def delCook():
##
@app.route("/del/groupscook",methods=['POST'])
def delGroupsCook():
logit(request.base_url)
CookName = None, None # Initialize
for key, value in request.args.to_dict().items():
@@ -482,7 +495,6 @@ def delGroupsCook():
##
@app.route("/del/cleancook",methods=['POST'])
def delCleanCook():
logit(request.base_url)
CookName = None, None # Initialize
for key, value in request.args.to_dict().items():
@@ -505,7 +517,6 @@ def delCleanCook():
##
@app.route("/upd/group",methods=['POST'])
def updGroup():
logit(request.base_url)
GroupID, GroupNewName= None, None
for key,value in request.args.to_dict().items():
@@ -534,7 +545,6 @@ def updGroup():
##
@app.route("/add/grpcomputer",methods=['POST'])
def addGrpComputer():
logit(request.base_url)
ComputerID, GroupID= None, None
for key, value in request.args.to_dict().items():
@@ -561,7 +571,6 @@ def addGrpComputer():
##
@app.route("/del/grpcomputer",methods=['POST'])
def delGrpComputer():
logit(request.base_url)
ComputerID, GroupID= None, None
for key, value in request.args.to_dict().items():
@@ -589,7 +598,6 @@ def delGrpComputer():
##
@app.route("/del/computer",methods=['POST'])
def delComputer():
logit(request.base_url)
ComputerID= None
for key, value in request.args.to_dict().items():
@@ -669,7 +677,6 @@ def updComputer():
##
@app.route("/upd/cookname",methods=['POST'])
def updCookName():
logit(request.base_url)
CookName, CookNewName,Password= None, None,None #Initialize
for key, value in request.args.to_dict().items():
@@ -705,7 +712,6 @@ def updCookName():
##
@app.route("/load/cook",methods=['POST'])
def loadCook():
logit(request.base_url)
CookName, ComputerID, UUID= None, None, None #Initialize
for key, value in request.args.to_dict().items():
@@ -732,6 +738,15 @@ def loadCook():
data = myfile.read()
return str(yaml.safe_load(data))
##
# setCookStatus: Set status of a executed Cook
# /set/cookstatus?
# @param ComputerID/ComputerName -> ID or Name of computer
# @param CookName -> Name of the cook
# @param Revision -> Revision of cook applied
# @param Error -> Output, 0 if not error
# @param ErrorDesc -> Optional, if error ocurred, description
##
@app.route("/set/cookstatus",methods=['POST'])
def setCookStatus():
logit(request.base_url)
@@ -779,7 +794,6 @@ def setCookStatus():
##
@app.route("/check/password",methods=['POST'])
def checkPassword(): #Check password (ERROR if password not valid or no password and 1 if valid)
logit(request.base_url)
Password= None
for key, value in request.args.to_dict().items():
@@ -807,7 +821,6 @@ def checkPassword(): #Check password (ERROR if password not valid or no passwor
##
@app.route("/upd/password",methods=['POST'])
def updPassword(): #Update password (ERROR if password not valid or no password and 1 if changed)
logit(request.base_url)
OldPassword,NewPassword= None, None
for key, value in request.args.to_dict().items():
@@ -851,7 +864,14 @@ def getOptionValue():
return str({'TEXT': 'Invalid password','RESULT':'ERROR'})
else:
return sql.select("SELECT VALUE FROM OPTIONS WHERE Option='"+str(Option)+"'")[0]
def cleanDatabase():
sql.insert("DELETE FROM COOKS_STATUS WHERE CookName NOT IN (SELECT CookName FROM COOKS_IDG)")
sq = sql.select("SELECT ID_C FROM COMPUTERS")
for val in sq:
sql.insert("DELETE FROM COOKS_STATUS WHERE ID_C='"+str(val['ID_C'])+"' AND CookName NOT IN (SELECT DISTINCT CookName FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C='"+str(val['ID_C'])+"'))")
return 0
@app.route('/get/apiver',methods=['POST'])
@@ -861,5 +881,7 @@ def getApiVer():
print("Build: "+str(BUILD))
print("API Version: "+str(API_VER))
print("Cleaning database before start..")
cleanDatabase()
app.run(debug=False,port=3333,ssl_context='adhoc',host='0.0.0.0',threaded=True)

View File

@@ -1,5 +1,9 @@
# Changelog
## 2020/02/20
- Fixing more bugs
- First public beta of web gui, it doesn't have some parts but it's working
## 2019/11/21
- Fixing bugs of last change and cleaning some code
- Preparing web gui (It's hide yet)

View File

@@ -15,8 +15,8 @@ It's not error proof, then, I don't have any responsability if something crashes
- Complete translation English and Spanish
- Stabilize all
- I created a simple GUI in EasyGUI to be easy used, but if I have time, maybe will do a webUI more dynamic and visually better (Without needing to install php/apache/nginx, using python)
- Support Linux clients
- Make better WebUI to replace all easygui (Password create/change and some subpages)
# Requirements
@@ -29,17 +29,19 @@ For server you can use Linux, Mac or Windows, and in theory any environment that
- Yaml
- Python3 deps can be installed it using pip install command (pip install pyopenssl flask pyyaml)
- For controlling the program, you have to start control.pyw using Python3. It doesn't need to be started in server, but it has to be in that folder (Or with configpcm.ini file).
- It uses easygui of python3 (pip install easygui)
- For controlling the program, you have to enter in a browser and go to https://SERVERIP:3434 (By default)
- At first time yet, for putting password, you have to start control.pyw using Python3 (Needs easygui, pip install easygui). It doesn't need to be started in server, but it has to be in that folder (Or a folder with configpcm.ini file).
## Clients
- Windows:
- Windows 7 SP1 absolute minimal, but its only officially tested in 8.1 and 10, Windows 7 is being old now.
- It needs Powershell 5.0+ (Or Powershell Core 6.1+), lower versions doesnt guaranteed to work, not tested. In Windows 7 and 8.1, you have to update Powershell. (With Powershell 3.0/4.0, some commands like Get-Package doesn't work)
You can update from here: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell?view=powershell-6#upgrading-existing-windows-powershell (Download link: https://www.microsoft.com/en-us/download/details.aspx?id=54616)
- Windows 7 SP1 absolute minimal, but its only officially tested in 8.1 and 10, Windows 7 is obsolete by Microsoft.
- It needs Powershell 5.0+ (Or Powershell Core 6.2+), lower versions doesn't have some of the commands and are not guaranteed to work, not tested. In Windows 7 and 8.1, you have to update Powershell. Windows 10 have Powershell 5 by default
You can update from here:
-Powershell Core: https://github.com/PowerShell/PowerShell/releases
-Powershell 5.1: https://www.microsoft.com/en-us/download/details.aspx?id=54616 )
- Linux:
- Not supported yet, but when Windows be stable, I will try to do it in some way
- Not supported yet, but when Windows be stable, I will try to do using bash scripts
- Clients may have to be queried by DNS requests. If you don't have a domain (Samba or Windows AD), you may have to have a DNS server with local machines
@@ -48,7 +50,7 @@ For server you can use Linux, Mac or Windows, and in theory any environment that
## Server
- Copy this folder to a folder in your server
- Install dependencies
- Start the server: Start api.py using loadserver.bat (Windows) o loadserver.sh (Linux) file. You can do in systemd way, or a cron, or in a terminal of windows server,...
- Start the server: Start api.py using loadserver.bat (Windows) o loadserver.sh (Linux) file. You can do in systemd way, or a cron, or in a terminal of windows server,... It also will start the webserver gui app for controlling it (https://localhost:3434).
## Client. Example using GPO and Task Schedule
- Go to "client" folder and copy "configpcm_example.ini" to "configpcm.ini" and change their values to yours. Change lang value for having client gui translated to your lang. Here you will see:
@@ -66,4 +68,7 @@ For server you can use Linux, Mac or Windows, and in theory any environment that
- Args: -executionpolicy bypass -windowstyle hidden -noninteractive -nologo -file "\\SERVER\SysVol\DOMAINNAME\scripts\client.ps1" -startup 1
## Control app
- Start control.pyw to setup password and start adding computers, groups and cooks and configure options. You can use docs/example_cooks for examples. If anyone wants, I will setup a git repo for cooks.
- Start control.pyw to setup password
- Go to a browser and load https://SERVER_IP:3434/ , login and manage all. control.pyw is the old way, it has to work, but its deprecated
- For creating cooks, you can see the documentation (It takes 3 minutes to make something workable), and can use docs/example_cooks for examples. If anyone wants, I will setup a git repo for cooks.

View File

@@ -1,3 +1,5 @@
REM Starting Server. Will only work if python and dependencies are installed and in PATH
cd /D "%~dp0"
@start /b python3 api.py
cd admin
@start /b python3 admin.py

View File

@@ -1,3 +1,5 @@
#!/bin/bash
cd "$(dirname "$(realpath "$0")")";
python3 api.py &
cd admin
python3 admin.py

2
sql.py
View File

@@ -42,7 +42,7 @@ def insert(query):
conn.close()
if debugsql==True:
logit("SQL Ok")
return {'RESULT': 'OK'}
return str({'RESULT': 'OK'})
except:
if debugsql==True:
logit("SQL Error")