First Commit

This commit is contained in:
Dita Aji Pratama 2022-03-16 11:06:52 +07:00
parent cfe0ff3c7a
commit d4ca69fb2b
39 changed files with 21552 additions and 1 deletions

144
README.md
View File

@ -1,2 +1,144 @@
# costapy
# CostaPy
Python Web Framework. Build with CherryPy and Mako.
## Requirement & Installation
You need this libraries to use CostaPy:
- cherrypy
- mako
- mysql-connector
- bcrypt
You can install it with run this command
sh install.sh
## Usage
Use this command to start the web service
python<ver> costa.py <ip_address> <port> <service_name>
For an example like this
python3 costa.py localhost 80 My_Service
You can use nohup too and running it in the background like this
nohup python3 costa.py localhost 80 My_Service &
## Configuration
### Server (config/server.py)
tools.sessions.on
Default: True
Description: Enable sessions
engine.autoreload.on
Default: False
Description: Auto Reload when source code change. Don't use it in production.
request.show_tracebacks
Default: False
Description: Show traceback for debugging in development purposes.
### Global Variable (config/globalvar.py)
`directory.py` is the place for storing your Global Variable.
GV_base_url
Is the variable for your base URL (without `/` in the end)
GV_title
Is the variable for your web title
### Directory (config/directory.py)
`directory.py` is the place for storing your path. It is useful to calling the path more efficiently. there is 2 method that you can store your path. store it in variable for templating configuration, and store it as object for routing the url.
This is example that use for templating
html_user = "static/pages-user"
template_user = "static/template-user"
And this is example that use for routing the url
dirconfig = {
'/' :
{
'tools.sessions.on' : True ,
'tools.staticdir.root' : os.path.abspath(os.getcwd()) ,
},
'/your_dir' :
{
'tools.staticdir.on' : True ,
'tools.staticdir.dir' : './static/your-dir' ,
},
}
### Templating (config/template.py)
Templating is useful when you had more than 1 website template for difference use case. For an example, when you had user and admin in the use case, the website for user have a navbar and footer, and the website for admin have a navbar and sidebar.
Before you create a template, make sure your `directory` configuration is ready for storing templates and pages. For an example:
html_user = "static/pages-user"
template_user = "static/template-user"
To create the template, you need to insert this code in `def __init__(self)`
self.html_pages_user = html.main.get_html(directory.html_user)
self.html_template_user = html.main.get_html(directory.template_user)
if you had admin template, you just need to add the code. for the example like this
self.html_pages_user = html.main.get_html(directory.html_user)
self.html_template_user = html.main.get_html(directory.template_user)
self.html_pages_admin = html.main.get_html(directory.html_admin)
self.html_template_admin = html.main.get_html(directory.template_admin)
and then you need create function for each of your template in main class like this
def user(self, page):
params_list = {
"template" : self.html_template_user ["user.html" ] ,
"topnav" : self.html_template_user ["user-topnav.html" ] ,
"container" : self.html_pages_user [page+".html" ]
}
return params_list
### Database (config/database.py)
This is the sample template for configure it
db_default = {
'host' : 'localhost',
'user' : 'root',
'password' : '',
'database' : 'your_db',
'autocommit' : True,
}
You also can make more than 1 database configuration like this
db_default = {
'host' : 'localhost',
'user' : 'root',
'password' : '',
'database' : 'your_db',
'autocommit' : True,
}
db_other = {
'host' : 'localhost',
'user' : 'root',
'password' : '',
'database' : 'other_db',
'autocommit' : True,
}
## Handling the modules
Handling the module is in `handler.py`.

0
config/__init__.py Normal file
View File

9
config/database.py Executable file
View File

@ -0,0 +1,9 @@
# Your database list
main_db = {
'host' : 'localhost',
'user' : 'root',
'password' : '',
'database' : 'your_db',
'autocommit' : True,
}

24
config/directory.py Normal file
View File

@ -0,0 +1,24 @@
import os
# For templating
page = "static/page"
template = "static/template"
# For route
dirconfig = {
'/' :
{
'tools.sessions.on' : True ,
'tools.staticdir.root' : os.path.abspath(os.getcwd()) ,
},
'/lib' :
{
'tools.staticdir.on' : True ,
'tools.staticdir.dir' : './static/lib' ,
},
'/css' :
{
'tools.staticdir.on' : True ,
'tools.staticdir.dir' : './static/css' ,
},
}

3
config/globalvar.py Executable file
View File

@ -0,0 +1,3 @@
# Your global variables
GV_base_url = "http://localhost:81"
GV_title = "CostaPy"

7
config/server.py Normal file
View File

@ -0,0 +1,7 @@
update = {
'server.socket_host' : "hostname" ,
'server.socket_port' : "port" ,
'tools.sessions.on' : True ,
'engine.autoreload.on' : False ,
'request.show_tracebacks' : False ,
}

18
config/template.py Normal file
View File

@ -0,0 +1,18 @@
from core import html
from config import directory
class main:
def __init__(self):
# Declare a variables
self.html_page = html.main.get_html(directory.page)
self.html_template = html.main.get_html(directory.template)
def user(self, page):
params_list = {
"template" : self.html_template ["template.html" ] ,
"topnav" : self.html_template ["topnav.html" ] ,
"footer" : self.html_template ["footer.html" ] ,
"container" : self.html_page [ page+".html" ]
}
return params_list

0
core/__init__.py Normal file
View File

5
core/authentication.py Normal file
View File

@ -0,0 +1,5 @@
import cherrypy
def token_check(redirect):
if cherrypy.session.get("token") == None:
raise cherrypy.HTTPRedirect(redirect)

17
core/html.py Normal file
View File

@ -0,0 +1,17 @@
import sys
import os
class main:
def __init__(self):
pass
def get_html(location):
html_dict = {}
html_page_list = os.listdir( location )
for html_page in html_page_list:
full_path = location + "/" + html_page
html_handle = open( full_path , 'r' )
html_raw = html_handle.read()
html_dict[html_page] = html_raw
return html_dict

27
core/uploading.py Normal file
View File

@ -0,0 +1,27 @@
import os
import cherrypy
def main(file, rename, directory):
try:
upload_path = directory
upload_filename = file.filename
upload_rename = rename
upload_file = os.path.normpath(os.path.join(upload_path, upload_rename))
upload_size = 0
with open(upload_file, 'wb') as upload_result:
while True:
data = file.file.read(8192)
if not data:
break
upload_result.write(data)
upload_size += len(data)
print("UPLOAD PATH: " + str(upload_path))
print("UPLOAD FILENAME: " + str(upload_filename))
print("UPLOAD RENAME: " + str(upload_rename))
print("UPLOAD FILE: " + str(upload_file))
print("UPLOAD SIZE: " + str(upload_size))
except Exception as e:
print(f"ERROR: {e}")

23
costa.py Normal file
View File

@ -0,0 +1,23 @@
import sys
import cherrypy
import handler
from config import server
from config import directory
if __name__ == '__main__':
dirconfig = directory.dirconfig
update = server.update
if len(sys.argv) >= 3:
update["server.socket_host"] = sys.argv[1]
update["server.socket_port"] = int(sys.argv[2])
cherrypy.config.update ( update )
cherrypy.quickstart ( handler.handler(), config = dirconfig )
else:
print ("Usage : python<ver> costa.py <ip_address> <port> <service_name>")
print ("Example : python3 costa.py localhost 81 CostaPySample")

19
handler.py Executable file
View File

@ -0,0 +1,19 @@
import cherrypy
import json
import core.authentication as authentication
import config.globalvar as globalvar
import config.template as pages
import modules.user.home as user_home
class handler(pages.main):
def __init__(self):
pages.main.__init__(self)
def index(self, **kwargs):
kwargs["params_page"] = pages.main().user("home")
return user_home.main().html(kwargs)
index.exposed = True

6
install.sh Normal file
View File

@ -0,0 +1,6 @@
sudo apt-get install -y python3-pip
pip3 install --upgrade pip
pip3 install cherrypy
pip3 install mako
pip3 install mysql-connector
pip3 install bcrypt

0
modules/__init__.py Normal file
View File

0
modules/user/__init__.py Normal file
View File

36
modules/user/home.py Normal file
View File

@ -0,0 +1,36 @@
import cherrypy
from mako.template import Template
import mysql.connector as mariadb
import config.database as database
import config.globalvar as globalvar
class main:
def __init__(self):
pass
def html(self, params):
interface_template = params["params_page"]['template' ]
topnav = params["params_page"]['topnav' ]
footer = params["params_page"]['footer' ]
container = params["params_page"]['container' ]
name = "World"
return Template(interface_template).render(
GV_title = globalvar.GV_title,
GV_base_url = globalvar.GV_base_url,
topnav = Template(topnav).render(
GV_title = globalvar.GV_title,
),
footer = Template(footer).render(
copyright_holder = "Dita Aji Pratama",
),
container = Template(container).render(
GV_base_url = globalvar.GV_base_url,
greeting = "Hello " + name + ", " + "Welcome to " + globalvar.GV_title
)
)

1
static/css/style.css Normal file
View File

@ -0,0 +1 @@
/* your style here */

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2011-2020 Twitter, Inc.
Copyright (c) 2011-2020 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,342 @@
/*!
* Bootstrap Reboot v4.0.0-beta.2 (https://getbootstrap.com)
* Copyright 2011-2017 The Bootstrap Authors
* Copyright 2011-2017 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: transparent;
}
@-ms-viewport {
width: device-width;
}
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: none !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg:not(:root) {
overflow: hidden;
}
a,
area,
button,
[role="button"],
input:not([type="range"]),
label,
select,
summary,
textarea {
-ms-touch-action: manipulation;
touch-action: manipulation;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #868e96;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: .5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

View File

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.0.0-beta.2 (https://getbootstrap.com)
* Copyright 2011-2017 The Bootstrap Authors
* Copyright 2011-2017 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}[role=button],a,area,button,input:not([type=range]),label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#868e96;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

8975
static/lib/bootstrap/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3894
static/lib/bootstrap/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

9
static/page/home.html Executable file
View File

@ -0,0 +1,9 @@
<div class="container-fluid my-3">
<div class="row">
<div class="col-lg-12">
<h1>Welcome</h1>
<h3>This is your first pages</h3>
<p>${greeting}</p>
</div>
</div>
</div>

View File

@ -0,0 +1,3 @@
<footer class="mt-auto bg-dark text-light p-3 text-center">
© 2022 ${copyright_holder}
</footer>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>${GV_title}</title>
<link href="${GV_base_url}/lib/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="${GV_base_url}/css/style.css">
<script src="${GV_base_url}/lib/bootstrap/js/bootstrap.bundle.js"></script>
</head>
<body class="d-flex flex-column" style="min-height:100vh;">
${topnav}
<div class="mb-5">
${container}
</div>
${footer}
</body>
</html>

13
static/template/topnav.html Executable file
View File

@ -0,0 +1,13 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="/">${GV_title}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
</ul>
</div>
</nav>