mirror of
https://github.com/AlbrechtL/openwrt-docker.git
synced 2026-10-14 00:01:26 +00:00
Initial WIP angular web-ui commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
+42
@@ -5,3 +5,45 @@ test/tests/.pytest_cache/
|
||||
.venv
|
||||
web-backend/target
|
||||
dev_tools/*.img
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"python.testing.pytestArgs": [
|
||||
"test/tests",
|
||||
"--rootdir=test/",
|
||||
"-v",
|
||||
"-s"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# OpenwrtDockerWebGui
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.1.5.
|
||||
|
||||
## Development server
|
||||
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"openwrt-docker-web-gui": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/openwrt-docker-web-gui",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"server": "src/main.server.ts",
|
||||
"prerender": true,
|
||||
"ssr": {
|
||||
"entry": "src/server.ts"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "openwrt-docker-web-gui:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "openwrt-docker-web-gui:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OpenWrt control panel</title>
|
||||
|
||||
<!-- Add Material Design Lite CSS -->
|
||||
<link rel="stylesheet" href="./mdl/material.min.css">
|
||||
<script src="./mdl/material.min.js"></script>
|
||||
|
||||
<!-- Custom Styles for Tooltip -->
|
||||
<style>
|
||||
.container {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mdl-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f0f0f0;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container mdl-card mdl-shadow--2dp">
|
||||
<h1 class="mdl-typography--headline" style="margin-top: 0">OpenWrt</h1>
|
||||
|
||||
<div class="button-row">
|
||||
<script language="JavaScript">
|
||||
document.write('<a id="tt1" target="_blank" rel="noopener noreferrer" href="https://' + window.location.hostname + ':9000" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" ">\
|
||||
Open OpenWrt LuCI web interface </a>');
|
||||
</script>
|
||||
|
||||
<div class="mdl-tooltip mdl-tooltip--large" for="tt1" style="text-align: left;">
|
||||
Access via HTTPS to OpenWrt LuCI web interface<br>
|
||||
<br>
|
||||
Requirements<br>
|
||||
- LAN="veth"<br>
|
||||
- FORWARD_LUCI="true"<br>
|
||||
- Default OpenWrt DHCP LAN settings<br>
|
||||
- Configured "veth-openwrt0" Ethernet interface in host system<br>
|
||||
<br>
|
||||
Internally a reverse proxy is forwarding the web interface from "host-ip:9000" to "172.31.1.1:80"
|
||||
</div>
|
||||
|
||||
<h4 class="mdl-typography--title" style="margin-bottom: 0">Control</h4>
|
||||
|
||||
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="fetchJsonFromEndpoint(1)">
|
||||
Reboot
|
||||
</button>
|
||||
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="fetchJsonFromEndpoint(2)">
|
||||
Hard reset
|
||||
</button>
|
||||
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="fetchJsonFromEndpoint(3)">
|
||||
Get OpenWrt information
|
||||
</button>
|
||||
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="fetchJsonFromEndpoint(4)">
|
||||
Get container information
|
||||
</button>
|
||||
<!-- <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="fetchJsonFromEndpoint(5)">
|
||||
Hard factory reset
|
||||
</button> -->
|
||||
</div>
|
||||
|
||||
<pre id="response">Click a button to get the response...</pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const endpoints = [
|
||||
'api/reboot',
|
||||
'api/reset',
|
||||
'api/get_openwrt_info',
|
||||
'api/get_container_info',
|
||||
'api/factory_reset'
|
||||
];
|
||||
|
||||
function fetchJsonFromEndpoint(endpointNumber) {
|
||||
const endpoint = endpoints[endpointNumber - 1];
|
||||
fetch(endpoint)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
response_data = data['combined_output']
|
||||
|
||||
response_data.replace(/"\{/g, "{");
|
||||
if(isJsonString(response_data)) { // lets json show nice
|
||||
json = JSON.parse(response_data);
|
||||
document.getElementById('response').textContent = JSON.stringify(json, null, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
const regex = /\\n|\\r\\n|\\n\\r|\\r/g;
|
||||
response_data.replace(regex, '<br>');
|
||||
document.getElementById('response').textContent = response_data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('response').textContent = `Error: ${error}`;
|
||||
});
|
||||
}
|
||||
|
||||
function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg id="Capa_1" enable-background="new 0 0 511.962 511.962" height="512" viewBox="0 0 511.962 511.962" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-4.102-1.967-8.875-1.967-12.977 0l-218.985 105.063c-5.202 2.496-8.511 7.755-8.511 13.524l-.003 271.834c0 5.77 3.31 11.028 8.512 13.524l218.989 105.064c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#4e6ba6"/><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-2.051-.984-4.269-1.476-6.488-1.476v511.962c2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#28487a"/><path d="m425.812 160.441c0-2.27-.519-4.457-1.457-6.432l-336.701-.095c-.967 1.999-1.504 4.22-1.504 6.526l-.002 191.081c0 5.769 3.31 11.028 8.512 13.524l154.833 74.285c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#8dc2eb"/><path d="m424.354 154.009h-168.373v286.798c2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524l-.003-191.081c0-2.27-.52-4.458-1.458-6.432z" fill="#5e9ff6"/><path d="m417.3 146.916-154.831-74.284c-4.102-1.967-8.875-1.967-12.977 0l-154.831 74.284c-3.122 1.498-5.555 3.996-7.007 6.998l168.328 80.812 168.374-80.717c-1.448-3.044-3.9-5.579-7.056-7.093z" fill="#ecf9fd"/><path d="m417.3 146.916-154.831-74.284c-2.051-.983-4.27-1.476-6.488-1.476v163.569l168.374-80.717c-1.447-3.043-3.899-5.578-7.055-7.092z" fill="#d9f3fc"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="img/favicon.svg" type="image/x-icon">
|
||||
|
||||
<style>
|
||||
div {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
html, body, iframe { height: 99%; }
|
||||
|
||||
iframe {
|
||||
display:inline-block;
|
||||
border: solid;
|
||||
}
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 20% 79%;
|
||||
gap: 10px;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.grid-container > div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item3 {
|
||||
grid-column: 2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="grid-container">
|
||||
<div class="item1">
|
||||
<iframe src=control.html width="100%"></iframe>
|
||||
</div>
|
||||
<div class="item2">
|
||||
<iframe src="novnc/" width="100%"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,212 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2015 Google Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
All code in any directories or sub-directories that end with *.html or
|
||||
*.css is licensed under the Creative Commons Attribution International
|
||||
4.0 License, which full text can be found here:
|
||||
https://creativecommons.org/licenses/by/4.0/legalcode.
|
||||
|
||||
As an exception to this license, all html or css that is generated by
|
||||
the software at the direction of the user is copyright the user. The
|
||||
user has full ownership and control over such content, including
|
||||
whether and how they wish to license it.
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "material-design-lite",
|
||||
"version": "1.3.0",
|
||||
"homepage": "https://github.com/google/material-design-lite",
|
||||
"authors": [
|
||||
"Material Design Lite team"
|
||||
],
|
||||
"description": "Material Design Components in CSS, JS and HTML",
|
||||
"main": [
|
||||
"material.min.css",
|
||||
"material.min.js"
|
||||
],
|
||||
"keywords": [
|
||||
"material",
|
||||
"design",
|
||||
"styleguide",
|
||||
"style",
|
||||
"guide"
|
||||
],
|
||||
"license": "Apache-2",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"./lib/.bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Vendored
-9
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
-10
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,77 +0,0 @@
|
||||
{
|
||||
"name": "material-design-lite",
|
||||
"version": "1.3.0",
|
||||
"description": "Material Design Components in CSS, JS and HTML",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google",
|
||||
"repository": "google/material-design-lite",
|
||||
"main": "dist/material.min.js",
|
||||
"devDependencies": {
|
||||
"acorn": "^4.0.3",
|
||||
"babel-core": "^6.20.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"browser-sync": "^2.2.3",
|
||||
"chai": "^3.3.0",
|
||||
"chai-jquery": "^2.0.0",
|
||||
"del": "^2.0.2",
|
||||
"drool": "^0.4.0",
|
||||
"escodegen": "^1.6.1",
|
||||
"google-closure-compiler": "",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-autoprefixer": "^3.0.2",
|
||||
"gulp-cache": "^0.4.5",
|
||||
"gulp-closure-compiler": "^0.4.0",
|
||||
"gulp-concat": "^2.4.1",
|
||||
"gulp-connect": "^5.0.0",
|
||||
"gulp-css-inline-images": "^0.1.1",
|
||||
"gulp-csso": "1.0.0",
|
||||
"gulp-file": "^0.3.0",
|
||||
"gulp-flatten": "^0.3.1",
|
||||
"gulp-front-matter": "^1.2.2",
|
||||
"gulp-header": "^1.2.2",
|
||||
"gulp-if": "^2.0.0",
|
||||
"gulp-iife": "^0.3.0",
|
||||
"gulp-imagemin": "^3.1.0",
|
||||
"gulp-jscs": "^4.0.0",
|
||||
"gulp-jshint": "^2.0.4",
|
||||
"gulp-load-plugins": "^1.3.0",
|
||||
"gulp-marked": "^1.0.0",
|
||||
"gulp-mocha-phantomjs": "^0.12.0",
|
||||
"gulp-open": "^2.0.0",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-replace": "^0.5.3",
|
||||
"gulp-sass": "3.0.0",
|
||||
"gulp-shell": "^0.5.2",
|
||||
"gulp-size": "^2.0.0",
|
||||
"gulp-sourcemaps": "^2.0.1",
|
||||
"gulp-subtree": "^0.1.0",
|
||||
"gulp-tap": "^0.1.3",
|
||||
"gulp-uglify": "^2.0.0",
|
||||
"gulp-util": "^3.0.4",
|
||||
"gulp-zip": "^3.0.2",
|
||||
"humanize": "0.0.9",
|
||||
"jquery": "^3.1.1",
|
||||
"jshint": "^2.9.4",
|
||||
"jshint-stylish": "^2.2.1",
|
||||
"merge-stream": "^1.0.0",
|
||||
"mocha": "^3.0.2",
|
||||
"prismjs": "0.0.1",
|
||||
"run-sequence": "^1.0.2",
|
||||
"swig": "^1.4.2",
|
||||
"through2": "^2.0.0",
|
||||
"vinyl-paths": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "gulp && git status | grep 'working directory clean' >/dev/null || (echo 'Please commit all changes generated by building'; exit 1)"
|
||||
},
|
||||
"babel": {
|
||||
"only": "gulpfile.babel.js",
|
||||
"presets": [
|
||||
"es2015"
|
||||
]
|
||||
}
|
||||
}
|
||||
Generated
+14380
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "openwrt-docker-web-gui",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"serve:ssr:openwrt-docker-web-gui": "node dist/openwrt-docker-web-gui/server/server.mjs"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^19.1.0",
|
||||
"@angular/cdk": "^19.1.2",
|
||||
"@angular/common": "^19.1.0",
|
||||
"@angular/compiler": "^19.1.0",
|
||||
"@angular/core": "^19.1.0",
|
||||
"@angular/forms": "^19.1.0",
|
||||
"@angular/material": "^19.1.2",
|
||||
"@angular/platform-browser": "^19.1.0",
|
||||
"@angular/platform-browser-dynamic": "^19.1.0",
|
||||
"@angular/platform-server": "^19.1.0",
|
||||
"@angular/router": "^19.1.0",
|
||||
"@angular/ssr": "^19.1.5",
|
||||
"express": "^4.18.2",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.1.5",
|
||||
"@angular/cli": "^19.1.5",
|
||||
"@angular/compiler-cli": "^19.1.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/node": "^18.18.0",
|
||||
"jasmine-core": "~5.5.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.7.2"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,2 @@
|
||||
<app-side-navigation></app-side-navigation>
|
||||
<router-outlet />
|
||||
@@ -0,0 +1,29 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AppComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have the 'openwrt-docker-web-gui' title`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('openwrt-docker-web-gui');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, openwrt-docker-web-gui');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { MatToolbarModule} from '@angular/material/toolbar';
|
||||
import { SideNavigationComponent } from "./side-navigation/side-navigation.component";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [
|
||||
RouterOutlet,
|
||||
SideNavigationComponent,
|
||||
MatToolbarModule
|
||||
],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'openwrt-docker-web-gui';
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
||||
import { provideServerRendering } from '@angular/platform-server';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
const serverConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideServerRendering(),
|
||||
]
|
||||
};
|
||||
|
||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())]
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { SystemInformationComponent } from "./system-information/system-information.component";
|
||||
import { StatusComponent } from './status/status.component';
|
||||
import { ConsoleComponent } from './console/console.component';
|
||||
import { InfoComponent } from './info/info.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'status',
|
||||
component: StatusComponent,
|
||||
title: 'Status'
|
||||
},
|
||||
{
|
||||
path: 'console',
|
||||
component: ConsoleComponent,
|
||||
title: 'Console'
|
||||
},
|
||||
{
|
||||
path: 'system_information',
|
||||
component: SystemInformationComponent,
|
||||
title: 'System information'
|
||||
},
|
||||
{
|
||||
path: 'info',
|
||||
component: InfoComponent,
|
||||
title: 'info'
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,3 @@
|
||||
<div id="screen">
|
||||
<!-- This is where the remote screen will appear -->
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ConsoleComponent } from './console.component';
|
||||
|
||||
describe('ConsoleComponent', () => {
|
||||
let component: ConsoleComponent;
|
||||
let fixture: ComponentFixture<ConsoleComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ConsoleComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ConsoleComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-console',
|
||||
imports: [],
|
||||
templateUrl: './console.component.html',
|
||||
styleUrl: './console.component.scss'
|
||||
})
|
||||
export class ConsoleComponent {
|
||||
title = "vnc-client";
|
||||
|
||||
public rfb: RFB;
|
||||
|
||||
startClient() {
|
||||
console.log("Starting !!!");
|
||||
|
||||
// Read parameters specified in the URL query string
|
||||
// By default, use the host and port of server that served this file
|
||||
const host = window.location.hostname;
|
||||
const port = "6080";
|
||||
const password = "foobar"; // password of your vnc server
|
||||
const path = "websockify";
|
||||
// Build the websocket URL used to connect
|
||||
let url = "ws";
|
||||
|
||||
if (window.location.protocol === "https:") {
|
||||
url = "wss";
|
||||
} else {
|
||||
url = "ws";
|
||||
}
|
||||
|
||||
url += "://" + host;
|
||||
if (port) {
|
||||
url += ":" + port;
|
||||
}
|
||||
url += "/" + path;
|
||||
|
||||
console.log("URL: ", url);
|
||||
|
||||
// Creating a new RFB object will start a new connection
|
||||
this.rfb = new RFB(document.getElementById("screen"), url, {
|
||||
credentials: { password: password },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>info works!</p>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InfoComponent } from './info.component';
|
||||
|
||||
describe('InfoComponent', () => {
|
||||
let component: InfoComponent;
|
||||
let fixture: ComponentFixture<InfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [InfoComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(InfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-info',
|
||||
imports: [],
|
||||
templateUrl: './info.component.html',
|
||||
styleUrl: './info.component.scss'
|
||||
})
|
||||
export class InfoComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<mat-sidenav-container class="sidenav-container">
|
||||
<mat-sidenav #drawer class="sidenav" fixedInViewport
|
||||
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
|
||||
[mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||
[opened]="(isHandset$ | async) === false">
|
||||
<mat-toolbar>Menu</mat-toolbar>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item [routerLink]="['status']">Status</a>
|
||||
<a mat-list-item [routerLink]="['console']">Console</a>
|
||||
<a mat-list-item [routerLink]="['system_information']">System information</a>
|
||||
<a mat-list-item [routerLink]="['info']">Info</a>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
<mat-toolbar color="primary">
|
||||
@if (isHandset$ | async) {
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Toggle sidenav"
|
||||
mat-icon-button
|
||||
(click)="drawer.toggle()">
|
||||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
|
||||
</button>
|
||||
}
|
||||
<span>OpenWrt</span>
|
||||
<span class="example-spacer"></span>
|
||||
<mat-slide-toggle labelPosition="before">Expert mode</mat-slide-toggle>
|
||||
</mat-toolbar>
|
||||
<div class="content-container">
|
||||
<router-outlet />
|
||||
</div>
|
||||
|
||||
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
@@ -0,0 +1,26 @@
|
||||
.sidenav-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidenav {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.sidenav .mat-toolbar {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.mat-toolbar.mat-primary {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.example-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { SideNavigationComponent } from './side-navigation.component';
|
||||
|
||||
describe('SideNavigationComponent', () => {
|
||||
let component: SideNavigationComponent;
|
||||
let fixture: ComponentFixture<SideNavigationComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SideNavigationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should compile', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-navigation',
|
||||
templateUrl: './side-navigation.component.html',
|
||||
styleUrl: './side-navigation.component.scss',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatListModule,
|
||||
MatIconModule,
|
||||
AsyncPipe,
|
||||
MatSlideToggleModule,
|
||||
RouterOutlet,
|
||||
RouterModule,
|
||||
]
|
||||
})
|
||||
export class SideNavigationComponent {
|
||||
private breakpointObserver = inject(BreakpointObserver);
|
||||
|
||||
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
|
||||
.pipe(
|
||||
map(result => result.matches),
|
||||
shareReplay()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<span>status works!</span>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { StatusComponent } from './status.component';
|
||||
|
||||
describe('StatusComponent', () => {
|
||||
let component: StatusComponent;
|
||||
let fixture: ComponentFixture<StatusComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [StatusComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(StatusComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-status',
|
||||
imports: [],
|
||||
templateUrl: './status.component.html',
|
||||
styleUrl: './status.component.scss'
|
||||
})
|
||||
export class StatusComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable, of as observableOf, merge } from 'rxjs';
|
||||
|
||||
// TODO: Replace this with your own data model type
|
||||
export interface SystemInformationItem {
|
||||
name: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
// TODO: replace this with real data from your application
|
||||
const EXAMPLE_DATA: SystemInformationItem[] = [
|
||||
{id: 1, name: 'Hydrogen'},
|
||||
{id: 2, name: 'Helium'},
|
||||
{id: 3, name: 'Lithium'},
|
||||
{id: 4, name: 'Beryllium'},
|
||||
{id: 5, name: 'Boron'},
|
||||
{id: 6, name: 'Carbon'},
|
||||
{id: 7, name: 'Nitrogen'},
|
||||
{id: 8, name: 'Oxygen'},
|
||||
{id: 9, name: 'Fluorine'},
|
||||
{id: 10, name: 'Neon'},
|
||||
{id: 11, name: 'Sodium'},
|
||||
{id: 12, name: 'Magnesium'},
|
||||
{id: 13, name: 'Aluminum'},
|
||||
{id: 14, name: 'Silicon'},
|
||||
{id: 15, name: 'Phosphorus'},
|
||||
{id: 16, name: 'Sulfur'},
|
||||
{id: 17, name: 'Chlorine'},
|
||||
{id: 18, name: 'Argon'},
|
||||
{id: 19, name: 'Potassium'},
|
||||
{id: 20, name: 'Calcium'},
|
||||
];
|
||||
|
||||
/**
|
||||
* Data source for the SystemInformation view. This class should
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class SystemInformationDataSource extends DataSource<SystemInformationItem> {
|
||||
data: SystemInformationItem[] = EXAMPLE_DATA;
|
||||
paginator: MatPaginator | undefined;
|
||||
sort: MatSort | undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect this data source to the table. The table will only update when
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
connect(): Observable<SystemInformationItem[]> {
|
||||
if (this.paginator && this.sort) {
|
||||
// Combine everything that affects the rendered data into one update
|
||||
// stream for the data-table to consume.
|
||||
return merge(observableOf(this.data), this.paginator.page, this.sort.sortChange)
|
||||
.pipe(map(() => {
|
||||
return this.getPagedData(this.getSortedData([...this.data ]));
|
||||
}));
|
||||
} else {
|
||||
throw Error('Please set the paginator and sort on the data source before connecting.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the table is being destroyed. Use this function, to clean up
|
||||
* any open connections or free any held resources that were set up during connect.
|
||||
*/
|
||||
disconnect(): void {}
|
||||
|
||||
/**
|
||||
* Paginate the data (client-side). If you're using server-side pagination,
|
||||
* this would be replaced by requesting the appropriate data from the server.
|
||||
*/
|
||||
private getPagedData(data: SystemInformationItem[]): SystemInformationItem[] {
|
||||
if (this.paginator) {
|
||||
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
||||
return data.splice(startIndex, this.paginator.pageSize);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the data (client-side). If you're using server-side sorting,
|
||||
* this would be replaced by requesting the appropriate data from the server.
|
||||
*/
|
||||
private getSortedData(data: SystemInformationItem[]): SystemInformationItem[] {
|
||||
if (!this.sort || !this.sort.active || this.sort.direction === '') {
|
||||
return data;
|
||||
}
|
||||
|
||||
return data.sort((a, b) => {
|
||||
const isAsc = this.sort?.direction === 'asc';
|
||||
switch (this.sort?.active) {
|
||||
case 'name': return compare(a.name, b.name, isAsc);
|
||||
case 'id': return compare(+a.id, +b.id, isAsc);
|
||||
default: return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Simple sort comparator for example ID/Name columns (for client-side sorting). */
|
||||
function compare(a: string | number, b: string | number, isAsc: boolean): number {
|
||||
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="mat-elevation-z2">
|
||||
<table mat-table class="full-width-table" matSort aria-label="Elements">
|
||||
<!-- Id Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Id</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator
|
||||
[length]="dataSource.data.length"
|
||||
[pageIndex]="0"
|
||||
[pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]"
|
||||
aria-label="Select page">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.full-width-table {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { SystemInformationComponent } from './system-information.component';
|
||||
|
||||
describe('SystemInformationComponent', () => {
|
||||
let component: SystemInformationComponent;
|
||||
let fixture: ComponentFixture<SystemInformationComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SystemInformationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should compile', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { MatTableModule, MatTable } from '@angular/material/table';
|
||||
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSortModule, MatSort } from '@angular/material/sort';
|
||||
import { SystemInformationDataSource, SystemInformationItem } from './system-information-datasource';
|
||||
|
||||
@Component({
|
||||
selector: 'app-system-information',
|
||||
templateUrl: './system-information.component.html',
|
||||
styleUrl: './system-information.component.scss',
|
||||
standalone: true,
|
||||
imports: [MatTableModule, MatPaginatorModule, MatSortModule]
|
||||
})
|
||||
export class SystemInformationComponent implements AfterViewInit {
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
@ViewChild(MatTable) table!: MatTable<SystemInformationItem>;
|
||||
dataSource = new SystemInformationDataSource();
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
displayedColumns = ['id', 'name'];
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.dataSource.sort = this.sort;
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.table.dataSource = this.dataSource;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>OpenwrtDockerWebGui</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { config } from './app/app.config.server';
|
||||
|
||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
||||
|
||||
export default bootstrap;
|
||||
@@ -0,0 +1,6 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
@@ -0,0 +1,65 @@
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { CommonEngine, isMainModule } from '@angular/ssr/node';
|
||||
import express from 'express';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import bootstrap from './main.server';
|
||||
|
||||
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
|
||||
const browserDistFolder = resolve(serverDistFolder, '../browser');
|
||||
const indexHtml = join(serverDistFolder, 'index.server.html');
|
||||
|
||||
const app = express();
|
||||
const commonEngine = new CommonEngine();
|
||||
|
||||
/**
|
||||
* Example Express Rest API endpoints can be defined here.
|
||||
* Uncomment and define endpoints as necessary.
|
||||
*
|
||||
* Example:
|
||||
* ```ts
|
||||
* app.get('/api/**', (req, res) => {
|
||||
* // Handle API request
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serve static files from /browser
|
||||
*/
|
||||
app.get(
|
||||
'**',
|
||||
express.static(browserDistFolder, {
|
||||
maxAge: '1y',
|
||||
index: 'index.html'
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Handle all other requests by rendering the Angular application.
|
||||
*/
|
||||
app.get('**', (req, res, next) => {
|
||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
||||
|
||||
commonEngine
|
||||
.render({
|
||||
bootstrap,
|
||||
documentFilePath: indexHtml,
|
||||
url: `${protocol}://${headers.host}${originalUrl}`,
|
||||
publicPath: browserDistFolder,
|
||||
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
|
||||
})
|
||||
.then((html) => res.send(html))
|
||||
.catch((err) => next(err));
|
||||
});
|
||||
|
||||
/**
|
||||
* Start the server if this module is the main entry point.
|
||||
* The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
|
||||
*/
|
||||
if (isMainModule(import.meta.url)) {
|
||||
const port = process.env['PORT'] || 4000;
|
||||
app.listen(port, () => {
|
||||
console.log(`Node Express server listening on http://localhost:${port}`);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
|
||||
@use "@angular/material" as mat;
|
||||
|
||||
@include mat.core();
|
||||
|
||||
// Customize the entire app. Change :root to your selector if you want to scope the styles.
|
||||
:root {
|
||||
@include mat.sidenav-overrides((
|
||||
container-divider-color: var(--mat-sys-outline-variant),
|
||||
container-shape: 0px,
|
||||
));
|
||||
|
||||
@include mat.toolbar-overrides((
|
||||
container-background-color: var(--mat-sys-tertiary-container),
|
||||
));
|
||||
}
|
||||
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
@include mat.theme((
|
||||
color: mat.$azure-palette,
|
||||
typography: Roboto,
|
||||
density: 0
|
||||
));
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--mat-sys-surface);
|
||||
color: var(--mat-sys-on-surface);
|
||||
}
|
||||
|
||||
// html {
|
||||
// @include mat.theme((
|
||||
// color: (
|
||||
// theme-type: light,
|
||||
// primary: mat.$azure-palette,
|
||||
// tertiary: mat.$blue-palette,
|
||||
// ),
|
||||
// typography: Roboto,
|
||||
// density: 0,
|
||||
// ));
|
||||
// }
|
||||
@@ -0,0 +1,19 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/main.server.ts",
|
||||
"src/server.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user