implemented basic ui
| @ -37,17 +37,52 @@ check() { | ||||
| } | ||||
| 
 | ||||
| package() { | ||||
|     # create directories | ||||
|     install -d "$pkgdir/usr/share/doc/$pkgname" | ||||
|     install -d "$pkgdir/var/lib/$pkgname/cert" | ||||
| 
 | ||||
|     # copy docs & static files | ||||
|     cp -r "$srcdir/$pkgname/doc"/* "$pkgdir/usr/share/doc/$pkgname/" | ||||
|     install -Dm644 "$srcdir/$pkgname/README.md" "$pkgdir/usr/share/doc/$pkgname/README.md" | ||||
|     install -Dm644 "$srcdir/$pkgname/version.env" "$pkgdir/usr/share/doc/$pkgname/version.env" | ||||
| 
 | ||||
|     sed -i "s/README.md/\/usr\/share\/doc\/$pkgname\/README.md/g" "$srcdir/$pkgname/app/main.py" | ||||
| 
 | ||||
|     # copy main app python files | ||||
|     sed -i "s/join(dirname(__file__), 'cert\//join('\/var\/lib\/$pkgname', 'cert\//g" "$srcdir/$pkgname/app/main.py" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/main.py" "$pkgdir/opt/$pkgname/main.py" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/orm.py" "$pkgdir/opt/$pkgname/orm.py" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/util.py" "$pkgdir/opt/$pkgname/util.py" | ||||
| 
 | ||||
|     # copy static asset files | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/css/bootstrap.min.css" "$pkgdir/opt/$pkgname/static/assets/css/bootstrap.min.css" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/css/bootstrap-icons.min.css" "$pkgdir/opt/$pkgname/static/assets/css/bootstrap-icons.min.css" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/css/custom.css" "$pkgdir/opt/$pkgname/static/assets/css/custom.css" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/css/dashboard.css" "$pkgdir/opt/$pkgname/static/assets/css/dashboard.css" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/fonts/bootstrap-icons.woff" "$pkgdir/opt/$pkgname/static/assets/fonts/bootstrap-icons.woff" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/fonts/bootstrap-icons.woff2" "$pkgdir/opt/$pkgname/static/assets/fonts/bootstrap-icons.woff2" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/android-chrome-192x192.png" "$pkgdir/opt/$pkgname/static/assets/img/favicons/android-chrome-192x192.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/android-chrome-512x512.png" "$pkgdir/opt/$pkgname/static/assets/img/favicons/android-chrome-512x512.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/apple-touch-icon.png" "$pkgdir/opt/$pkgname/static/assets/img/favicons/apple-touch-icon.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/favicon.ico" "$pkgdir/opt/$pkgname/static/assets/img/favicons/favicon.ico" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/favicon-16x16.png" "$pkgdir/opt/$pkgname/static/assets/img/favicons/favicon-16x16.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/favicon-32x32.png" "$pkgdir/opt/$pkgname/static/assets/img/favicons/favicon-32x32.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/favicons/manifest.json" "$pkgdir/opt/$pkgname/static/assets/img/favicons/manifest.json" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/img/logo.png" "$pkgdir/opt/$pkgname/static/assets/img/logo.png" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/js/bootstrap.min.js" "$pkgdir/opt/$pkgname/static/assets/js/bootstrap.min.js" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/js/helper.js" "$pkgdir/opt/$pkgname/static/assets/js/helper.js" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/static/assets/js/popper.min.js" "$pkgdir/opt/$pkgname/static/assets/js/popper.min.js" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/components/navbar.html" "$pkgdir/opt/$pkgname/templates/components/navbar.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/components/sidebar.html" "$pkgdir/opt/$pkgname/templates/components/sidebar.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/layout/base.html" "$pkgdir/opt/$pkgname/templates/layout/base.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/layout/bootstrap.html" "$pkgdir/opt/$pkgname/templates/layout/bootstrap.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/layout/bootstrap-dashboard.html" "$pkgdir/opt/$pkgname/templates/layout/bootstrap-dashboard.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/views/dashboard.html" "$pkgdir/opt/$pkgname/templates/views/dashboard.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/views/dashboard_leases.html" "$pkgdir/opt/$pkgname/templates/views/dashboard_leases.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/views/dashboard_origins.html" "$pkgdir/opt/$pkgname/templates/views/dashboard_origins.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/views/dashboard_readme.html" "$pkgdir/opt/$pkgname/templates/views/dashboard_readme.html" | ||||
|     install -Dm755 "$srcdir/$pkgname/app/templates/views/index.html" "$pkgdir/opt/$pkgname/templates/views/index.html" | ||||
| 
 | ||||
|     # copy service files | ||||
|     install -Dm644 "$srcdir/$pkgname.default" "$pkgdir/etc/default/$pkgname" | ||||
|     install -Dm644 "$srcdir/$pkgname.service" "$pkgdir/usr/lib/systemd/system/$pkgname.service" | ||||
|     install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf" | ||||
|  | ||||
							
								
								
									
										66
									
								
								app/main.py
									
									
									
									
									
								
							
							
						
						| @ -19,6 +19,8 @@ from sqlalchemy import create_engine | ||||
| from sqlalchemy.orm import sessionmaker | ||||
| from starlette.middleware.cors import CORSMiddleware | ||||
| from starlette.responses import StreamingResponse, JSONResponse as JSONr, HTMLResponse as HTMLr, Response, RedirectResponse | ||||
| from starlette.staticfiles import StaticFiles | ||||
| from starlette.templating import Jinja2Templates | ||||
| 
 | ||||
| from orm import Origin, Lease, init as db_init, migrate | ||||
| from util import load_key, load_file | ||||
| @ -87,6 +89,8 @@ async def lifespan(_: FastAPI): | ||||
| 
 | ||||
| config = dict(openapi_url=None, docs_url=None, redoc_url=None)  # dict(openapi_url='/-/openapi.json', docs_url='/-/docs', redoc_url='/-/redoc') | ||||
| app = FastAPI(title='FastAPI-DLS', description='Minimal Delegated License Service (DLS).', version=VERSION, lifespan=lifespan, **config) | ||||
| app.mount('/static', StaticFiles(directory='static', html=True), name='static'), | ||||
| templates = Jinja2Templates(directory='templates') | ||||
| 
 | ||||
| app.debug = DEBUG | ||||
| app.add_middleware( | ||||
| @ -107,14 +111,14 @@ def __get_token(request: Request) -> dict: | ||||
| 
 | ||||
| # Endpoints | ||||
| 
 | ||||
| @app.get('/', summary='Index') | ||||
| @app.get('/', summary='* Index') | ||||
| async def index(): | ||||
|     return RedirectResponse('/-/readme') | ||||
|     return RedirectResponse('/-/') | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/', summary='* Index') | ||||
| async def _index(): | ||||
|     return RedirectResponse('/-/readme') | ||||
| async def _index(request: Request): | ||||
|     return templates.TemplateResponse(name='views/index.html', context={'request': request, 'VERSION': VERSION}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/health', summary='* Health') | ||||
| @ -142,48 +146,32 @@ async def _config(): | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/readme', summary='* Readme') | ||||
| async def _readme(): | ||||
| async def _readme(request: Request): | ||||
|     from markdown import markdown | ||||
|     content = load_file(join(dirname(__file__), '../README.md')).decode('utf-8') | ||||
|     return HTMLr(markdown(text=content, extensions=['tables', 'fenced_code', 'md_in_html', 'nl2br', 'toc'])) | ||||
|     markdown = markdown(text=content, extensions=['tables', 'fenced_code', 'md_in_html', 'nl2br', 'toc']) | ||||
|     context = {'request': request, 'markdown': markdown, 'VERSION': VERSION} | ||||
|     return templates.TemplateResponse(name='views/dashboard_readme.html', context=context) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/manage', summary='* Management UI') | ||||
| async def _manage(request: Request): | ||||
|     response = ''' | ||||
|     <!DOCTYPE html> | ||||
|     <html> | ||||
|         <head> | ||||
|             <title>FastAPI-DLS Management</title> | ||||
|         </head> | ||||
|         <body> | ||||
|             <button onclick="deleteOrigins()">delete ALL origins and their leases</button> | ||||
|             <button onclick="deleteLease()">delete specific lease</button> | ||||
|              | ||||
|             <script> | ||||
|                 function deleteOrigins() { | ||||
|                     const response = confirm('Are you sure you want to delete all origins and their leases?'); | ||||
|     return templates.TemplateResponse(name='views/manage.html', context={'request': request, 'VERSION': VERSION}) | ||||
| 
 | ||||
|                     if (response) { | ||||
|                         var xhr = new XMLHttpRequest(); | ||||
|                         xhr.open("DELETE", '/-/origins', true); | ||||
|                         xhr.send(); | ||||
|                     } | ||||
|                 } | ||||
|                 function deleteLease(lease_ref) { | ||||
|                     if(lease_ref === undefined) | ||||
|                         lease_ref = window.prompt("Please enter 'lease_ref' which should be deleted"); | ||||
|                     if(lease_ref === null || lease_ref === "") | ||||
|                         return | ||||
|                     var xhr = new XMLHttpRequest(); | ||||
|                     xhr.open("DELETE", `/-/lease/${lease_ref}`, true); | ||||
|                     xhr.send(); | ||||
|                 } | ||||
|             </script> | ||||
|         </body> | ||||
|     </html> | ||||
|     ''' | ||||
|     return HTMLr(response) | ||||
| 
 | ||||
| @app.get('/-/dashboard', summary='* Dashboard') | ||||
| async def _dashboard(request: Request): | ||||
|     return templates.TemplateResponse(name='views/dashboard.html', context={'request': request, 'VERSION': VERSION}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/dashboard/origins', summary='* Dashboard - Origins') | ||||
| async def _dashboard_origins(request: Request): | ||||
|     return templates.TemplateResponse(name='views/dashboard_origins.html', context={'request': request, 'VERSION': VERSION}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/dashboard/leases', summary='* Dashboard - Leases') | ||||
| async def _dashboard_origins(request: Request): | ||||
|     return templates.TemplateResponse(name='views/dashboard_leases.html', context={'request': request, 'VERSION': VERSION}) | ||||
| 
 | ||||
| 
 | ||||
| @app.get('/-/origins', summary='* Origins') | ||||
|  | ||||
							
								
								
									
										7
									
								
								app/static/assets/css/bootstrap-icons.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										6
									
								
								app/static/assets/css/bootstrap.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										56
									
								
								app/static/assets/css/custom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,56 @@ | ||||
| /* | ||||
|     Original: #76b900 | ||||
|     Darken 1: #5DA000 (10%) | ||||
|     Darken 2: #438600 (20%) | ||||
|     Darken 3: #2A6D00 (30%) | ||||
|     Darken 4: #105300 (40%) | ||||
|     Darken 5: #003A00 (50%) | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| .text-primary { | ||||
|     color: #76b900 !important; | ||||
| } | ||||
| 
 | ||||
| .lead { | ||||
|     color: #105300 !important; | ||||
| } | ||||
| 
 | ||||
| .navbar-green { | ||||
|     background-color: #76b900 !important; | ||||
| } | ||||
| 
 | ||||
| .navbar-brand { | ||||
|     background-color: transparent; | ||||
|     color: #ffffff; | ||||
| } | ||||
| 
 | ||||
| .navbar-brand:focus, .navbar-brand:hover { | ||||
|     color: #fcfcfc; | ||||
| } | ||||
| 
 | ||||
| .btn-primary { | ||||
|     background-color: #76b900 !important; | ||||
|     border-color: #76b900 !important; | ||||
| } | ||||
| 
 | ||||
| .btn-primary:focus, .btn-primary:hover { | ||||
|     background-color: #5DA000 !important; | ||||
|     border-color: #5DA000 !important; | ||||
| } | ||||
| 
 | ||||
| code { | ||||
|     color: #105300 !important; | ||||
| } | ||||
| 
 | ||||
| .sidebar .nav-link.active { | ||||
|     color: #76b900 !important; | ||||
| } | ||||
| 
 | ||||
| .sidebar .nav-link:focus, .sidebar .nav-link:hover { | ||||
|     color: #105300 !important; | ||||
| } | ||||
| 
 | ||||
| .navbar-nav .nav-item .nav-link { | ||||
|     color: white !important; | ||||
| } | ||||
							
								
								
									
										101
									
								
								app/static/assets/css/dashboard.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,101 @@ | ||||
| body { | ||||
|   font-size: .875rem; | ||||
| } | ||||
| 
 | ||||
| .feather { | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
|   vertical-align: text-bottom; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * Sidebar | ||||
|  */ | ||||
| 
 | ||||
| .sidebar { | ||||
|   position: fixed; | ||||
|   top: 0; | ||||
|   /* rtl:raw: | ||||
|   right: 0; | ||||
|   */ | ||||
|   bottom: 0; | ||||
|   /* rtl:remove */ | ||||
|   left: 0; | ||||
|   z-index: 100; /* Behind the navbar */ | ||||
|   padding: 48px 0 0; /* Height of navbar */ | ||||
|   box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 767.98px) { | ||||
|   .sidebar { | ||||
|     top: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .sidebar-sticky { | ||||
|   position: relative; | ||||
|   top: 0; | ||||
|   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: #727272; | ||||
| } | ||||
| 
 | ||||
| .sidebar .nav-link.active { | ||||
|   color: #2470dc; | ||||
| } | ||||
| 
 | ||||
| .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 .navbar-toggler { | ||||
|   top: .25rem; | ||||
|   right: 1rem; | ||||
| } | ||||
| 
 | ||||
| .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); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/fonts/bootstrap-icons.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/fonts/bootstrap-icons.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 27 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 51 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 733 B | 
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/favicons/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										1
									
								
								app/static/assets/img/favicons/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1 @@ | ||||
| {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} | ||||
							
								
								
									
										
											BIN
										
									
								
								app/static/assets/img/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 73 KiB | 
							
								
								
									
										6
									
								
								app/static/assets/js/bootstrap.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										94
									
								
								app/static/assets/js/helper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,94 @@ | ||||
| async function fetchOriginsWithLeases(element) { | ||||
|     let xhr = new XMLHttpRequest(); | ||||
|     xhr.open("GET", '/-/origins?leases=true', true); | ||||
|     xhr.onreadystatechange = function () { | ||||
|         if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { | ||||
|             const x = JSON.parse(xhr.response) | ||||
|             console.debug(x) | ||||
| 
 | ||||
|             element.innerHTML = '' | ||||
|             let table = document.createElement('table') | ||||
|             table.classList.add('table', 'mt-4'); | ||||
|             let thead = document.createElement('thead'); | ||||
|             thead.innerHTML = ` | ||||
|                     <tr> | ||||
|                         <th scope="col">origin</th> | ||||
|                         <th scope="col">hostname</th> | ||||
|                         <th scope="col">OS</th> | ||||
|                         <th scope="col">driver version</th> | ||||
|                         <th scope="col">leases</th> | ||||
|                     </tr>` | ||||
|             table.appendChild(thead) | ||||
|             let tbody = document.createElement('thead'); | ||||
|             x.forEach((o) => { | ||||
|                 let row = document.createElement('tr'); | ||||
|                 row.innerHTML = ` | ||||
|                         <td><code>${o.origin_ref}</code></td> | ||||
|                         <td>${o.hostname}</td> | ||||
|                         <td>${o.os_platform}</td> | ||||
|                         <td>${o.os_version}</td> | ||||
|                         <td>${o.leases.map(x => `<code title="expires: ${x.lease_expires}">${x.lease_ref}</code>`).join(', ')}</td>` | ||||
|                 tbody.appendChild(row); | ||||
|             }) | ||||
|             table.appendChild(tbody) | ||||
|             element.appendChild(table) | ||||
|         } | ||||
|     }; | ||||
|     xhr.send(); | ||||
| } | ||||
| 
 | ||||
| async function fetchLeases(element) { | ||||
|     let xhr = new XMLHttpRequest(); | ||||
|     xhr.open("GET", '/-/leases?origin=true', true); | ||||
|     xhr.onreadystatechange = function () { | ||||
|         if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { | ||||
|             const x = JSON.parse(xhr.response) | ||||
|             console.debug(x) | ||||
| 
 | ||||
|             element.innerHTML = '' | ||||
|             let table = document.createElement('table') | ||||
|             table.classList.add('table', 'mt-4'); | ||||
|             let thead = document.createElement('thead'); | ||||
|             thead.innerHTML = ` | ||||
|                     <tr> | ||||
|                         <th scope="col">lease</th> | ||||
|                         <th scope="col">created</th> | ||||
|                         <th scope="col">updated</th> | ||||
|                         <th scope="col">expires</th> | ||||
|                         <th scope="col">origin</th> | ||||
|                     </tr>` | ||||
|             table.appendChild(thead) | ||||
|             let tbody = document.createElement('thead'); | ||||
|             x.forEach((o) => { | ||||
|                 let row = document.createElement('tr'); | ||||
|                 row.innerHTML = ` | ||||
|                         <td><code>${o.lease_ref}</code></td> | ||||
|                         <td>${o.lease_created}</td> | ||||
|                         <td>${o.lease_updated}</td> | ||||
|                         <td>${o.lease_expires}</td> | ||||
|                         <td><code>${o.origin_ref}</code></td>` | ||||
|                 tbody.appendChild(row); | ||||
|             }) | ||||
|             table.appendChild(tbody) | ||||
|             element.appendChild(table) | ||||
|         } | ||||
|     }; | ||||
|     xhr.send(); | ||||
| } | ||||
| 
 | ||||
| async function deleteOrigins() { | ||||
|     let xhr = new XMLHttpRequest(); | ||||
|     xhr.open("DELETE", '/-/origins', true); | ||||
|     xhr.send(); | ||||
|     await fetchOriginsWithLeases() | ||||
| } | ||||
| 
 | ||||
| async function deleteLease(lease_ref) { | ||||
|     if (lease_ref === undefined) | ||||
|         lease_ref = window.prompt("Please enter 'lease_ref' which should be deleted"); | ||||
|     if (lease_ref === null || lease_ref === "") | ||||
|         return | ||||
|     let xhr = new XMLHttpRequest(); | ||||
|     xhr.open("DELETE", `/-/lease/${{lease_ref}}`, true); | ||||
|     xhr.send(); | ||||
| } | ||||
							
								
								
									
										5
									
								
								app/static/assets/js/popper.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										6
									
								
								app/templates/components/navbar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,6 @@ | ||||
| <header class="navbar navbar-expand-md navbar-green sticky-top bg-dark flex-md-nowrap p-0 shadow"> | ||||
|     <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="/-/">FastAPI-DLS {{ VERSION }}</a> | ||||
|     <button class="navbar-toggler position-absolute d-lg-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|         <span class="navbar-toggler-icon"></span> | ||||
|     </button> | ||||
| </header> | ||||
							
								
								
									
										58
									
								
								app/templates/components/sidebar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,58 @@ | ||||
| <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse"> | ||||
|     <div class="position-sticky pt-3"> | ||||
|         <ul class="nav flex-column"> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link {{ 'active' if request.url.path == '/-/dashboard' }}" aria-current="page" href="/-/dashboard"> | ||||
|                     <i class="bi-house-door"></i> Dashboard | ||||
|                 </a> | ||||
|             </li> | ||||
|              <li class="nav-item"> | ||||
|                 <a class="nav-link {{ 'active' if request.url.path == '/-/dashboard/origins' }}" aria-current="page" href="/-/dashboard/origins"> | ||||
|                     <i class="bi-pc-display-horizontal"></i> Origins | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link {{ 'active' if request.url.path == '/-/dashboard/leases' }}" aria-current="page" href="/-/dashboard/leases"> | ||||
|                     <i class="bi-layers"></i> Leases | ||||
|                 </a> | ||||
|             </li> | ||||
|         </ul> | ||||
| 
 | ||||
|         <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted text-uppercase"> | ||||
|             <span>Help</span> | ||||
|         </h6> | ||||
|         <ul class="nav flex-column"> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link {{ 'active' if request.url.path == '/-/readme' }}" aria-current="page" href="/-/readme"> | ||||
|                     <i class="bi-question-circle"></i> Readme | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link" aria-current="page" href="https://git.collinwebdesigns.de/oscar.krause/fastapi-dls" target="_blank"> | ||||
|                     <i class="bi-git"></i> Git Repo | ||||
|                 </a> | ||||
|             </li> | ||||
|         </ul> | ||||
| 
 | ||||
|         <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted text-uppercase"> | ||||
|             <span>Integrations</span> | ||||
|         </h6> | ||||
|         <ul class="nav flex-column"> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link" aria-current="page" href="/-/doc" target="_blank"> | ||||
|                     <i class="bi-file-text"></i> Swagger UI | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link" aria-current="page" href="/-/redoc" target="_blank"> | ||||
|                     <i class="bi-file-text"></i> Redoc | ||||
|                 </a> | ||||
|             </li> | ||||
|             <li class="nav-item"> | ||||
|                 <a class="nav-link" aria-current="page" href="/-/openapi.json"  target="_blank"> | ||||
|                     <i class="bi bi-filetype-json"></i> OpenAPI JSON | ||||
|                 </a> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </div> | ||||
| </nav> | ||||
							
								
								
									
										32
									
								
								app/templates/layouts/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| <!doctype html> | ||||
| <html lang="en" class="h-100"> | ||||
| <head> | ||||
|     {% block title %} | ||||
|         <title>FastAPI-DLS</title> | ||||
|     {% endblock %} | ||||
| 
 | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
| 
 | ||||
|     <link rel="icon" href="{{ url_for('static', path='assets/img/favicons/favicon-32x32.png') }}" sizes="32x32" type="image/png"> | ||||
|     <link rel="icon" href="{{ url_for('static', path='assets/img/favicons/favicon-16x16.png') }}" sizes="16x16" type="image/png"> | ||||
|     <link rel="manifest" href="{{ url_for('static', path='assets/img/favicons/manifest.json') }}"> | ||||
|     <link rel="icon" href="{{ url_for('static', path='assets/img/favicons/favicon.ico') }}"> | ||||
|     <link rel="apple-touch-icon" href="{{ url_for('static', path='assets/img/favicons/apple-touch-icon.png') }}" sizes="180x180"> | ||||
| 
 | ||||
|     {% block styles %} | ||||
|     {% endblock %} | ||||
| 
 | ||||
|     <link rel="stylesheet" type="text/css" href="{{ url_for('static', path='assets/css/custom.css') }}"> | ||||
| </head> | ||||
| <body class="d-flex flex-column {% block body_class %}{% endblock %}"> | ||||
| {% block body %} | ||||
| {% endblock %} | ||||
| 
 | ||||
| 
 | ||||
| <script src="{{ url_for('static', path='assets/js/helper.js') }}"></script> | ||||
| 
 | ||||
| {% block scripts %} | ||||
| {% endblock %} | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										16
									
								
								app/templates/layouts/bootstrap-dashboard.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,16 @@ | ||||
| {% extends 'layouts/bootstrap.html' %} | ||||
| 
 | ||||
| {% block body %} | ||||
| {% include 'components/navbar.html' %} | ||||
| 
 | ||||
| <div class="container-fluid"> | ||||
|     <div class="row"> | ||||
|         {% include 'components/sidebar.html' %} | ||||
| 
 | ||||
|         <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4"> | ||||
|             {% block content %} | ||||
|             {% endblock %} | ||||
|         </main> | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
							
								
								
									
										14
									
								
								app/templates/layouts/bootstrap.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | ||||
| {% extends 'layouts/base.html' %} | ||||
| 
 | ||||
| {% block styles %} | ||||
| {{ super() }} | ||||
| <link rel="stylesheet" type="text/css" href="{{ url_for('static', path='assets/css/bootstrap.min.css') }}"> | ||||
| <link rel="stylesheet" type="text/css" href="{{ url_for('static', path='assets/css/bootstrap-icons.min.css') }}"> | ||||
| <link rel="stylesheet" type="text/css" href="{{ url_for('static', path='assets/css/dashboard.css') }}"> | ||||
| 
 | ||||
| <script src="{{ url_for('static', path='assets/js/popper.min.js') }}"></script> | ||||
| <script src="{{ url_for('static', path='assets/js/bootstrap.min.js') }}"></script> | ||||
| {% endblock %} | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										41
									
								
								app/templates/views/dashboard.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,41 @@ | ||||
| {% extends 'layouts/bootstrap-dashboard.html' %} | ||||
| 
 | ||||
| {% block title %} | ||||
| <title>Dashboard</title> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
|     <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|         <h1 class="h2">Dashboard</h1> | ||||
|         <div class="btn-toolbar mb-2 mb-md-0"> | ||||
|             <div class="btn-group me-2"> | ||||
|                 <button type="button" class="btn btn-sm btn-outline-secondary" onclick="downloadClientToken()"> | ||||
|                     <i class="bi bi-download"></i> | ||||
|                     Client Token | ||||
|                 </button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="p-5 mb-4 bg-light rounded-3"> | ||||
|         <div class="container-fluid py-5"> | ||||
|             <h1 class="display-5 fw-bold">FastAPI-DLS</h1> | ||||
|             <p class="col-md-8 fs-4">Minimal Delegated License Service (DLS).</p> | ||||
| 
 | ||||
|             <a href="https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/releases" class="btn btn-primary btn-lg" target="_blank"> | ||||
|                 Releases » | ||||
|             </a> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block scripts %} | ||||
| {{ super() }} | ||||
| <script type="application/javascript"> | ||||
|     function downloadClientToken() { | ||||
|         window.open('/-/client-token', "_blank") | ||||
|     } | ||||
| </script> | ||||
| {% endblock %} | ||||
							
								
								
									
										32
									
								
								app/templates/views/dashboard_leases.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| {% extends 'layouts/bootstrap-dashboard.html' %} | ||||
| 
 | ||||
| {% block title %} | ||||
| <title>Origins</title> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
|     <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|         <h1 class="h2">Leases <small>with origin</small></h1> | ||||
|         <div class="btn-toolbar mb-2 mb-md-0"> | ||||
|             <div class="btn-group me-2"> | ||||
|                 <button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteLease();load();">delete lease</button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div id="leases" class="mt-3"></div> | ||||
| </div> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block scripts %} | ||||
| {{ super() }} | ||||
| <script type="application/javascript"> | ||||
|     function load() { | ||||
|         const leases = document.getElementById('leases') | ||||
|         fetchLeases(leases) | ||||
|     } | ||||
| 
 | ||||
|     load() | ||||
| </script> | ||||
| {% endblock %} | ||||
							
								
								
									
										28
									
								
								app/templates/views/dashboard_origins.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | ||||
| {% extends 'layouts/bootstrap-dashboard.html' %} | ||||
| 
 | ||||
| {% block title %} | ||||
| <title>Origins</title> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
|     <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|         <h1 class="h2">Origins <small>with leases</small></h1> | ||||
|         <div class="btn-toolbar mb-2 mb-md-0"> | ||||
|             <div class="btn-group me-2"> | ||||
|                 <button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteOrigins()">delete all</button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div id="origins" class="mt-3"></div> | ||||
| </div> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block scripts %} | ||||
| {{ super() }} | ||||
| <script type="application/javascript"> | ||||
|     const origins = document.getElementById('origins') | ||||
|     fetchOriginsWithLeases(origins) | ||||
| </script> | ||||
| {% endblock %} | ||||
							
								
								
									
										15
									
								
								app/templates/views/dashboard_readme.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,15 @@ | ||||
| {% extends 'layouts/bootstrap-dashboard.html' %} | ||||
| 
 | ||||
| {% block title %} | ||||
| <title>Origins</title> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block content %} | ||||
| <div> | ||||
|     <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> | ||||
|         <div class="overflow-hidden"> | ||||
|             {{ markdown|safe }} | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
							
								
								
									
										26
									
								
								app/templates/views/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,26 @@ | ||||
| {% extends 'layouts/bootstrap.html' %} | ||||
| 
 | ||||
| {% block title %} | ||||
| <title>Index</title> | ||||
| {% endblock %} | ||||
| 
 | ||||
| {% block body_class %}h-100{% endblock %} | ||||
| 
 | ||||
| {% block body %} | ||||
| <main class="flex-shrink-0"> | ||||
|     <div class="container"> | ||||
|         <h1 class="mt-5 text-primary">FastAPI-DLS</h1> | ||||
|         <p class="lead">Minimal Delegated License Service (DLS).</p> | ||||
|         <p> | ||||
|             <a href="/-/dashboard">Dashboard</a>, | ||||
|             <a href="/-/readme">Readme</a> | ||||
|         </p> | ||||
|     </div> | ||||
| </main> | ||||
| 
 | ||||
| <footer class="footer mt-auto py-3 bg-light"> | ||||
|     <div class="container"> | ||||
|         <span class="text-muted">FastAPI-DLS Version {{ VERSION }}</span> | ||||
|     </div> | ||||
| </footer> | ||||
| {% endblock %} | ||||
 Oscar Krause
						Oscar Krause