First Commit
This commit is contained in:
parent
cfe0ff3c7a
commit
d4ca69fb2b
144
README.md
144
README.md
@ -1,2 +1,144 @@
|
|||||||
# costapy
|
# CostaPy
|
||||||
Python Web Framework. Build with CherryPy and Mako.
|
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
0
config/__init__.py
Normal file
9
config/database.py
Executable file
9
config/database.py
Executable 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
24
config/directory.py
Normal 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
3
config/globalvar.py
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# Your global variables
|
||||||
|
GV_base_url = "http://localhost:81"
|
||||||
|
GV_title = "CostaPy"
|
7
config/server.py
Normal file
7
config/server.py
Normal 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
18
config/template.py
Normal 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
0
core/__init__.py
Normal file
5
core/authentication.py
Normal file
5
core/authentication.py
Normal 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
17
core/html.py
Normal 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
27
core/uploading.py
Normal 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
23
costa.py
Normal 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
19
handler.py
Executable 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
6
install.sh
Normal 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
0
modules/__init__.py
Normal file
0
modules/user/__init__.py
Normal file
0
modules/user/__init__.py
Normal file
36
modules/user/home.py
Normal file
36
modules/user/home.py
Normal 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
1
static/css/style.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* your style here */
|
22
static/lib/bootstrap/LICENSE
Normal file
22
static/lib/bootstrap/LICENSE
Normal 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.
|
1567
static/lib/bootstrap/css/bootstrap-grid.css
vendored
Normal file
1567
static/lib/bootstrap/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
static/lib/bootstrap/css/bootstrap-grid.min.css
vendored
Normal file
7
static/lib/bootstrap/css/bootstrap-grid.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
342
static/lib/bootstrap/css/bootstrap-reboot.css
vendored
Normal file
342
static/lib/bootstrap/css/bootstrap-reboot.css
vendored
Normal 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 */
|
8
static/lib/bootstrap/css/bootstrap-reboot.min.css
vendored
Normal file
8
static/lib/bootstrap/css/bootstrap-reboot.min.css
vendored
Normal 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
8975
static/lib/bootstrap/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
static/lib/bootstrap/css/bootstrap.css.map
Normal file
1
static/lib/bootstrap/css/bootstrap.css.map
Normal file
File diff suppressed because one or more lines are too long
7
static/lib/bootstrap/css/bootstrap.min.css
vendored
Normal file
7
static/lib/bootstrap/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/lib/bootstrap/css/bootstrap.min.css.map
Normal file
1
static/lib/bootstrap/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6328
static/lib/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
6328
static/lib/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
static/lib/bootstrap/js/bootstrap.bundle.js.map
Normal file
1
static/lib/bootstrap/js/bootstrap.bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
7
static/lib/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
7
static/lib/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/lib/bootstrap/js/bootstrap.bundle.min.js.map
Normal file
1
static/lib/bootstrap/js/bootstrap.bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
3894
static/lib/bootstrap/js/bootstrap.js
vendored
Normal file
3894
static/lib/bootstrap/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
static/lib/bootstrap/js/bootstrap.js.map
Normal file
1
static/lib/bootstrap/js/bootstrap.js.map
Normal file
File diff suppressed because one or more lines are too long
7
static/lib/bootstrap/js/bootstrap.min.js
vendored
Normal file
7
static/lib/bootstrap/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/lib/bootstrap/js/bootstrap.min.js.map
Normal file
1
static/lib/bootstrap/js/bootstrap.min.js.map
Normal file
File diff suppressed because one or more lines are too long
9
static/page/home.html
Executable file
9
static/page/home.html
Executable 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>
|
3
static/template/footer.html
Normal file
3
static/template/footer.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<footer class="mt-auto bg-dark text-light p-3 text-center">
|
||||||
|
© 2022 ${copyright_holder}
|
||||||
|
</footer>
|
19
static/template/template.html
Normal file
19
static/template/template.html
Normal 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
13
static/template/topnav.html
Executable 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>
|
Loading…
Reference in New Issue
Block a user