feat: Initial commit 🎉

Set up the foundational structure for the MikroWizard deployment repository, including:
- Docker Compose configuration for MikroFront, MikroMan, PostgreSQL, and Redis Stack.
- `prepare.sh` script for host environment preparation.
- Database initialization script (`init-db.sql`).
- `.env` template for centralized configuration.

This commit marks the beginning of a streamlined deployment process for MikroWizard!
This commit is contained in:
sepehr 2024-12-16 13:45:30 +03:00
commit a8f029ee38
80 changed files with 3429 additions and 0 deletions

9
.env Normal file
View file

@ -0,0 +1,9 @@
MW_DB_NAME=mikrowizard_db
MW_DB_USER=mikrowizard_user
MW_DB_PASSWORD=securepassword
MW_SERVER_IP=127.0.0.1
MW_RAD_SECRET=your_rad_secret
MW_encryptKey=bN0PJaVMpV7e4NGE8cLF3FECgY_nofYDuBtlLxX7pWg=
CONF_PATH=/opt/mikrowizard/conf
FIRMWARE_PATH=/opt/mikrowizard/firmware
BACKUPS_PATH=/opt/mikrowizard/backups

21
LICENSE.txt Normal file
View file

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

148
README.md Normal file
View file

@ -0,0 +1,148 @@
# MikroWizard Deployment
This repository provides a full Docker-based setup for deploying MikroWizard services, including **MikroMan** (backend) and **MikroFront** (frontend), along with the necessary database and Redis stack.
---
## Repository Structure
```
├── mikrofront/ # MikroFront service files
├── mikroman/ # MikroMan service files
├── docker-compose.yml # Main Docker Compose file
├── init-db.sql # Database initialization script
├── prepare.sh # Script to prepare host machine
├── .env # Configuration file for environment variables
```
---
## Prerequisites
1. **Docker**: Ensure Docker is installed on your machine.
- Installation guide: [Docker Documentation](https://docs.docker.com/get-docker/)
2. **Docker Compose**: Ensure Docker Compose is installed.
- Installation guide: [Docker Compose Documentation](https://docs.docker.com/compose/install/)
---
## Setup Instructions
### Step 1: Clone the Repository
```bash
git clone <repository-url> mikrowizard
cd mikrowizard
```
### Step 2: Configure Environment Variables
Edit the `.env` file to set the appropriate values for your environment:
```env
DB_NAME=mikrowizard
DB_USER=postgres
DB_PASSWORD=your_password
DB_HOST=host.docker.internal
SERVER_IP=127.0.0.1
RAD_SECRET=mysecret
```
Ensure you replace `your_password` and other placeholders with actual values.
### Step 3: Prepare Host Machine
Run the `prepare.sh` script to create required directories and files:
```bash
chmod +x prepare.sh
./prepare.sh
```
This script will:
- Create the `conf`, `firmware`, and `backups` folders on the host machine.
- Ensure proper permissions.
- Create the needed configuration Files
### Step 4: Start the Services
Use Docker Compose to build and start the services:
```bash
docker-compose up --build
```
This command will:
- Build the Docker images for MikroMan and MikroFront.
- Start the PostgreSQL database, Redis stack, and MikroWizard services.
### Step 5: Access the Application
- **Frontend**: Open [http://localhost](http://localhost) in your browser.
- **Backend**: Accessible through configured API endpoints.
---
## Database Initialization
1. The database is initialized automatically using the `init-db.sql` script during container startup.
2. Ensure the database extensions and migrations are applied:
- UUID extension is enabled.
- `dbmigrate.py` script creates required tables.
---
## Customization
### Environment Variables
All environment variables are centralized in the `.env` file. Update these values to customize your deployment.
### Volume Mapping
Host directories `conf`, `firmware`, and `backups` are mapped to container paths:
- `conf`: Configuration files.
- `firmware`: Stores firmware files.
- `backups`: Stores database and system backups.
---
## Troubleshooting
### Common Issues
1. **Database Connection Errors**:
- Verify the `DB_HOST` in `.env` points to `host.docker.internal` or the appropriate host.
2. **Permission Denied**:
- Ensure you have execution permissions for `prepare.sh`.
### Logs
Check logs for debugging:
```bash
docker-compose logs -f
```
---
## Contributions
Contributions are welcome! Submit issues or pull requests to improve the deployment process.
---
## License
This repository is licensed under the MIT License.
---
## Contact
For any inquiries, contact the MikroWizard team at [info@mikrowizard.com](mailto\:info@mikrowizard.com).

69
docker-compose.yml Normal file
View file

@ -0,0 +1,69 @@
version: '3.9'
services:
postgres:
image: postgres:latest
container_name: postgres
environment:
POSTGRES_USER: ${MW_DB_USER}
POSTGRES_PASSWORD: ${MW_DB_PASSWORD}
POSTGRES_DB: ${MW_DB_NAME}
PGUSER: ${MW_DB_USER}
volumes:
- db_data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: [ "CMD-SHELL", "pg_isready -q -U ${MW_DB_USER} -d ${MW_DB_NAME}" ]
interval: 5s
timeout: 5s
retries: 50
redis:
image: redis/redis-stack-server:latest
ports:
- "6379:6379"
mikroman:
build:
context: ./mikroman
dockerfile: Dockerfile
network_mode: "host"
entrypoint: bash -c "cd /app && ./init.sh"
environment:
- MW_SERVER_IP=${MW_SERVER_IP}
- MW_RAD_SECRET=${MW_RAD_SECRET}
- MW_DB_PASSWORD=${MW_DB_PASSWORD}
- MW_DB_USER=${MW_DB_USER}
- MW_DB_NAME=${MW_DB_NAME}
- MW_encryptKey=${MW_encryptKey}
volumes:
- ${CONF_PATH}:/conf
- ${FIRMWARE_PATH}:/conf/firmware
- ${BACKUPS_PATH}:/conf/backups
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
postgres:
condition: service_healthy
env_file: .env
mikrofront:
build:
context: ./mikrofront
dockerfile: Dockerfile
ports:
- "80:80"
volumes:
- ./mikrofront/nginx.conf:/etc/nginx/conf.d/default.conf
- ${CONF_PATH}:/conf
depends_on:
- mikroman
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
db_data:

2
init-db.sql Normal file
View file

@ -0,0 +1,2 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

27
mikrofront/Dockerfile Normal file
View file

@ -0,0 +1,27 @@
FROM nginx:latest
RUN apt-get update && apt-get -y install cron
RUN touch /var/log/cron.log
COPY reqs.txt /reqs.txt
RUN set -ex \
&& buildDeps=' \
build-essential \
gcc \
' \
&& deps=' \
htop \
' \
&& apt-get install -y python3 python3-dev pip $buildDeps $deps --no-install-recommends && pip install -r /reqs.txt --break-system-packages
COPY front-update.py /
COPY mwcrontab /etc/cron.d/mwcrontab
RUN chmod 0644 /etc/cron.d/mwcrontab
RUN crontab /etc/cron.d/mwcrontab
COPY dist/mikrofront /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD cron;nginx -g "daemon off;"

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkmikrowizard=self.webpackChunkmikrowizard||[]).push([[118],{5118:(v,i,t)=>{t.r(i),t.d(i,{PagesModule:()=>f});var m=t(177),c=t(7062),r=t(3042),d=t(5050),g=t(2234),a=t(4438);const h=[{path:"404",component:r.X,data:{title:"Page 404"}},{path:"500",component:d.y,data:{title:"Page 500"}},{path:"login",component:g.X,data:{title:"Login Page"}}];let u=(()=>{class o{static#t=this.\u0275fac=function(s){return new(s||o)};static#o=this.\u0275mod=a.$C({type:o});static#a=this.\u0275inj=a.G2t({imports:[c.iI.forChild(h),c.iI]})}return o})();var n=t(8921),e=t(4662),l=t(9417);let f=(()=>{class o{static#t=this.\u0275fac=function(s){return new(s||o)};static#o=this.\u0275mod=a.$C({type:o});static#a=this.\u0275inj=a.G2t({imports:[m.MD,u,n.Dw2,n.tmq,n.pc9,e.op,n.tHK,l.YN,l.X1]})}return o})()}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="1500"
height="333"
viewBox="-1 0 1500 333"
id="svg13"
sodipodi:docname="logo-no-background.svg"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs13">
<linearGradient
id="linearGradient13"
inkscape:collect="always">
<stop
style="stop-color:#f7daa2;stop-opacity:1;"
offset="0"
id="stop13" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop14" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient14"
x1="4.25"
y1="-18"
x2="289.23"
y2="-18"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="namedview13"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.90066667"
inkscape:cx="783.30866"
inkscape:cy="208.17913"
inkscape:window-width="1920"
inkscape:window-height="991"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg10" />
<g
transform="translate(-0.90909091,0.37614089)"
id="g12">
<svg
viewBox="0 0 396 88"
data-background-color="#303c54"
preserveAspectRatio="xMidYMid"
height="333"
width="1500"
version="1.1"
id="svg12">
<g
id="tight-bounds"
transform="translate(0.24,-0.0994006)">
<svg
viewBox="0 0 395.52 88.19865"
height="88.198647"
width="395.51999"
version="1.1"
id="svg11">
<g
id="g10"
style="display:inline">
<svg
viewBox="0 0 540.23295 120.46879"
height="88.198647"
width="395.51999"
version="1.1"
id="svg10">
<g
transform="translate(144.71295,34.560291)"
id="g3"
style="display:inline;fill:url(#linearGradient14)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg3"
style="fill:url(#linearGradient14)">
<g
id="textblocktransform"
style="fill:url(#linearGradient14)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
id="textblock"
version="1.1"
style="fill:url(#linearGradient14)">
<g
id="g2"
style="fill:url(#linearGradient14)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg2"
style="fill:url(#linearGradient14)">
<g
id="g1"
style="fill:url(#linearGradient14)">
<svg
width="395.51999"
viewBox="4.25 -36.5 284.98 37"
height="51.348209"
data-palette-color="#ffffff"
version="1.1"
id="svg1"
style="fill:url(#linearGradient14)">
<path
d="m 39.45,0 h -4.5 V -27.1 L 23.25,-6.25 H 20.5 L 8.75,-27.1 V 0 H 4.25 V -35.5 H 9 l 12.85,23.05 12.9,-23.05 h 4.7 z m 12.2,0 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z M 81.9,0 H 77.15 L 68.1,-12.7 63.15,-8.05 V 0 h -4.4 v -36.5 h 4.4 v 23.85 l 13.5,-13.4 H 81.4 L 71,-15.25 Z m 17.59,-26.25 v 4 q -3.3,0.05 -5.82,1.67 -2.53,1.63 -3.58,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.58,-4.38 2.22,-1.67 4.67,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z M 114.64,0.5 v 0 q -2.95,0 -5.35,-1.08 -2.4,-1.07 -4.15,-2.95 -1.75,-1.87 -2.7,-4.32 -0.95,-2.45 -0.95,-5.15 v 0 q 0,-2.75 0.95,-5.2 0.95,-2.45 2.7,-4.33 1.75,-1.87 4.18,-2.95 2.42,-1.07 5.37,-1.07 v 0 q 2.9,0 5.3,1.07 2.4,1.08 4.18,2.95 1.77,1.88 2.72,4.33 0.95,2.45 0.95,5.2 v 0 q 0,2.7 -0.95,5.15 -0.95,2.45 -2.7,4.32 -1.75,1.88 -4.17,2.95 -2.43,1.08 -5.38,1.08 z m -8.65,-13.45 v 0 q 0,2.7 1.18,4.9 1.17,2.2 3.12,3.47 1.95,1.28 4.35,1.28 v 0 q 2.4,0 4.38,-1.3 1.97,-1.3 3.15,-3.53 1.17,-2.22 1.17,-4.92 v 0 q 0,-2.7 -1.17,-4.9 -1.18,-2.2 -3.15,-3.5 -1.98,-1.3 -4.38,-1.3 v 0 q -2.4,0 -4.35,1.32 -1.95,1.33 -3.12,3.53 -1.18,2.2 -1.18,4.95 z m 43.6,-6.9 -6.15,-15.45 h 4.25 l 4.7,12.35 4.75,-12.35 h 4.25 l -6.15,15.45 6.1,14.65 12,-30.3 h 4.9 L 163.39,0 h -3.9 L 152.44,-16.75 145.34,0 h -3.9 l -14.8,-35.5 h 4.85 l 12.05,30.3 z m 37,19.85 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z m 5.04,31 v -2.85 l 16.45,-20.05 h -16 v -3.2 h 20.95 v 2.85 L 196.68,-3.2 h 16.4 V 0 Z m 24.65,-7.55 v 0 q 0,-2.5 1.43,-4.33 1.42,-1.82 3.92,-2.82 2.5,-1 5.8,-1 v 0 q 1.75,0 3.7,0.27 1.95,0.28 3.45,0.83 v 0 -1.9 q 0,-3 -1.8,-4.73 -1.8,-1.72 -5.1,-1.72 v 0 q -2.15,0 -4.12,0.77 -1.98,0.78 -4.18,2.23 v 0 l -1.6,-3.1 q 2.55,-1.75 5.1,-2.63 2.55,-0.87 5.3,-0.87 v 0 q 5,0 7.9,2.77 2.9,2.78 2.9,7.73 v 0 V -5 q 0,0.8 0.33,1.17 0.32,0.38 1.07,0.43 v 0 3.4 q -0.65,0.1 -1.12,0.15 -0.48,0.05 -0.78,0.05 v 0 q -1.55,0 -2.32,-0.85 -0.78,-0.85 -0.88,-1.8 v 0 l -0.1,-1.65 q -1.7,2.2 -4.45,3.4 -2.75,1.2 -5.45,1.2 v 0 q -2.6,0 -4.65,-1.08 -2.05,-1.07 -3.2,-2.9 -1.15,-1.82 -1.15,-4.07 z m 17,1.85 v 0 q 0.6,-0.7 0.95,-1.43 0.35,-0.72 0.35,-1.22 v 0 -3.25 q -1.55,-0.6 -3.25,-0.93 -1.7,-0.32 -3.35,-0.32 v 0 q -3.35,0 -5.42,1.32 -2.08,1.33 -2.08,3.63 v 0 q 0,1.25 0.68,2.42 0.67,1.18 2,1.93 1.32,0.75 3.27,0.75 v 0 q 2.05,0 3.9,-0.83 1.85,-0.82 2.95,-2.07 z m 26.55,-20.55 v 4 q -3.3,0.05 -5.83,1.67 -2.52,1.63 -3.57,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.57,-4.38 2.23,-1.67 4.68,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z m 2.2,13.25 v 0 q 0,-3.7 1.52,-6.78 1.53,-3.07 4.2,-4.92 2.68,-1.85 6.13,-1.85 v 0 q 3.15,0 5.65,1.65 2.5,1.65 3.9,4 v 0 -15.6 h 4.4 V -5 q 0,0.8 0.32,1.17 0.33,0.38 1.08,0.43 v 0 3.4 q -1.25,0.2 -1.95,0.2 v 0 q -1.3,0 -2.28,-0.9 -0.97,-0.9 -0.97,-2 v 0 -2.2 q -1.55,2.5 -4.15,3.95 -2.6,1.45 -5.4,1.45 v 0 q -2.7,0 -4.98,-1.08 -2.27,-1.07 -3.95,-2.97 -1.67,-1.9 -2.6,-4.33 -0.92,-2.42 -0.92,-5.12 z m 21.4,3.85 v 0 -7.5 q -0.6,-1.7 -1.95,-3.08 -1.35,-1.37 -3.03,-2.2 -1.67,-0.82 -3.32,-0.82 v 0 q -1.95,0 -3.53,0.82 -1.57,0.83 -2.7,2.2 -1.12,1.38 -1.72,3.13 -0.6,1.75 -0.6,3.65 v 0 q 0,1.95 0.67,3.7 0.68,1.75 1.9,3.1 1.23,1.35 2.85,2.1 1.63,0.75 3.53,0.75 v 0 q 1.2,0 2.47,-0.45 1.28,-0.45 2.43,-1.25 1.15,-0.8 1.95,-1.88 0.8,-1.07 1.05,-2.27 z"
opacity="1"
fill="#ffffff"
class="wordmark-text-0"
data-fill-palette-color="primary"
id="text-0"
style="fill:url(#linearGradient14)" />
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<g
id="g9"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg9"
inkscape:label="svg9"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g4"
style="fill:#f7daa2;fill-opacity:1">
<svg
version="1.1"
id="svg4"
width="100%"
height="100%"
style="fill:#f7daa2;fill-opacity:1" />
</g>
<g
id="icon-0"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg8"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g5"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<path
d="M 0,60.234 C 0,26.967 26.968,0 60.234,0 c 33.267,0 60.234,26.968 60.235,60.234 0,33.267 -26.968,60.234 -60.235,60.235 C 26.967,120.469 0,93.501 0,60.234 Z m 60.234,55.604 c 30.709,0 55.604,-24.895 55.604,-55.604 0,-30.709 -24.895,-55.604 -55.604,-55.603 -30.709,0 -55.604,24.895 -55.603,55.603 0,30.709 24.895,55.604 55.603,55.604 z"
data-fill-palette-color="accent"
fill="#ffffff"
stroke="transparent"
id="path4"
style="fill:#f7daa2;fill-opacity:1" />
</g>
<g
transform="translate(30.690719,29.558553)"
id="g8"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<svg
viewBox="0 0 59.087355 61.351687"
height="61.351688"
width="59.087357"
version="1.1"
id="svg7"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g7"
style="fill:#f7daa2;fill-opacity:1">
<svg
version="1.1"
x="0"
y="0"
viewBox="5.021 3.2976885 89.957 93.404312"
enable-background="new 0 0 100 100"
xml:space="preserve"
height="61.351688"
width="59.087357"
class="icon-cg-0"
data-fill-palette-color="accent"
id="cg-0"
style="fill:#f7daa2;fill-opacity:1"><path
d="m 68.84,60.533 c 0.076,2.981 0.114,6.083 0.114,9.313 0,5.141 -5.936,9.197 -13.67,9.5 -0.473,3.237 -3.26,5.735 -6.627,5.735 -3.37,0 -6.159,-2.502 -6.628,-5.743 -6.134,-0.38 -10.984,-4.486 -10.984,-9.492 0,-2.465 0.556,-5.571 1.404,-9.558 -16.468,2.988 -27.428,9.916 -27.428,17.445 0,10.459 20.178,18.969 44.979,18.969 24.801,0 44.978,-8.51 44.978,-18.969 C 94.979,70.48 84.525,63.641 68.84,60.533 Z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path5"
style="fill:#f7daa2;fill-opacity:1" /><path
d="m 43.952,78.375 c 0,2.595 2.111,4.706 4.705,4.706 2.595,0 4.705,-2.111 4.705,-4.706 0,-2.595 -2.11,-4.705 -4.705,-4.705 -2.594,0 -4.705,2.11 -4.705,4.705 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path6"
style="fill:#f7daa2;fill-opacity:1" /><path
d="M 66.954,69.846 C 66.954,48.265 65.589,18.326 53.781,7.529 50.439,4.473 46.504,3.098 41.768,3.321 41.673,27.762 37.84,45.108 35.293,56.634 c -1.256,5.688 -2.249,10.18 -2.249,13.212 0,3.918 3.959,7.143 8.996,7.494 0.5,-3.206 3.272,-5.67 6.617,-5.67 3.347,0 6.121,2.468 6.618,5.678 6.517,-0.283 11.679,-3.535 11.679,-7.502 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path7"
style="fill:#f7daa2;fill-opacity:1" /></svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<defs
id="defs10" />
</svg>
<rect
width="395.51999"
height="88.198647"
fill="none"
stroke="none"
visibility="hidden"
id="rect11"
x="0"
y="0" />
</g>
</svg>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

@ -0,0 +1,325 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="337"
height="337"
viewBox="-1 0 337 337"
id="svg13"
sodipodi:docname="logo-MIkroWizard-small-color.svg"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs13">
<linearGradient
id="linearGradient13"
inkscape:collect="always">
<stop
style="stop-color:#f7daa2;stop-opacity:1;"
offset="0"
id="stop13" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop14" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient14"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient15"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient16"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient17"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient18"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient19"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient20"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient21"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient22"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
</defs>
<sodipodi:namedview
id="namedview13"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.90066667"
inkscape:cx="713.91562"
inkscape:cy="208.17913"
inkscape:window-width="1920"
inkscape:window-height="991"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg10" />
<g
transform="translate(-0.90909091,0.37614089)"
id="g12">
<svg
viewBox="0 0 396 88"
data-background-color="#303c54"
preserveAspectRatio="xMidYMid"
height="333"
width="1500"
version="1.1"
id="svg12">
<g
id="tight-bounds"
transform="translate(0.24,-0.0994006)">
<svg
viewBox="0 0 395.52 88.19865"
height="88.198647"
width="395.51999"
version="1.1"
id="svg11">
<g
id="g10"
style="display:inline">
<svg
viewBox="0 0 540.23295 120.46879"
height="88.198647"
width="395.51999"
version="1.1"
id="svg10">
<g
transform="translate(144.71295,34.560291)"
id="g3"
style="display:inline;fill:url(#linearGradient14)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg3"
style="fill:url(#linearGradient22)">
<g
id="textblocktransform"
style="fill:url(#linearGradient21)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
id="textblock"
version="1.1"
style="fill:url(#linearGradient20)">
<g
id="g2"
style="fill:url(#linearGradient19)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg2"
style="fill:url(#linearGradient18)">
<g
id="g1"
style="fill:url(#linearGradient17)">
<svg
width="395.51999"
viewBox="4.25 -36.5 284.98 37"
height="51.348209"
data-palette-color="#ffffff"
version="1.1"
id="svg1"
style="fill:url(#linearGradient16)">
<path
d="m 39.45,0 h -4.5 V -27.1 L 23.25,-6.25 H 20.5 L 8.75,-27.1 V 0 H 4.25 V -35.5 H 9 l 12.85,23.05 12.9,-23.05 h 4.7 z m 12.2,0 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z M 81.9,0 H 77.15 L 68.1,-12.7 63.15,-8.05 V 0 h -4.4 v -36.5 h 4.4 v 23.85 l 13.5,-13.4 H 81.4 L 71,-15.25 Z m 17.59,-26.25 v 4 q -3.3,0.05 -5.82,1.67 -2.53,1.63 -3.58,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.58,-4.38 2.22,-1.67 4.67,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z M 114.64,0.5 v 0 q -2.95,0 -5.35,-1.08 -2.4,-1.07 -4.15,-2.95 -1.75,-1.87 -2.7,-4.32 -0.95,-2.45 -0.95,-5.15 v 0 q 0,-2.75 0.95,-5.2 0.95,-2.45 2.7,-4.33 1.75,-1.87 4.18,-2.95 2.42,-1.07 5.37,-1.07 v 0 q 2.9,0 5.3,1.07 2.4,1.08 4.18,2.95 1.77,1.88 2.72,4.33 0.95,2.45 0.95,5.2 v 0 q 0,2.7 -0.95,5.15 -0.95,2.45 -2.7,4.32 -1.75,1.88 -4.17,2.95 -2.43,1.08 -5.38,1.08 z m -8.65,-13.45 v 0 q 0,2.7 1.18,4.9 1.17,2.2 3.12,3.47 1.95,1.28 4.35,1.28 v 0 q 2.4,0 4.38,-1.3 1.97,-1.3 3.15,-3.53 1.17,-2.22 1.17,-4.92 v 0 q 0,-2.7 -1.17,-4.9 -1.18,-2.2 -3.15,-3.5 -1.98,-1.3 -4.38,-1.3 v 0 q -2.4,0 -4.35,1.32 -1.95,1.33 -3.12,3.53 -1.18,2.2 -1.18,4.95 z m 43.6,-6.9 -6.15,-15.45 h 4.25 l 4.7,12.35 4.75,-12.35 h 4.25 l -6.15,15.45 6.1,14.65 12,-30.3 h 4.9 L 163.39,0 h -3.9 L 152.44,-16.75 145.34,0 h -3.9 l -14.8,-35.5 h 4.85 l 12.05,30.3 z m 37,19.85 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z m 5.04,31 v -2.85 l 16.45,-20.05 h -16 v -3.2 h 20.95 v 2.85 L 196.68,-3.2 h 16.4 V 0 Z m 24.65,-7.55 v 0 q 0,-2.5 1.43,-4.33 1.42,-1.82 3.92,-2.82 2.5,-1 5.8,-1 v 0 q 1.75,0 3.7,0.27 1.95,0.28 3.45,0.83 v 0 -1.9 q 0,-3 -1.8,-4.73 -1.8,-1.72 -5.1,-1.72 v 0 q -2.15,0 -4.12,0.77 -1.98,0.78 -4.18,2.23 v 0 l -1.6,-3.1 q 2.55,-1.75 5.1,-2.63 2.55,-0.87 5.3,-0.87 v 0 q 5,0 7.9,2.77 2.9,2.78 2.9,7.73 v 0 V -5 q 0,0.8 0.33,1.17 0.32,0.38 1.07,0.43 v 0 3.4 q -0.65,0.1 -1.12,0.15 -0.48,0.05 -0.78,0.05 v 0 q -1.55,0 -2.32,-0.85 -0.78,-0.85 -0.88,-1.8 v 0 l -0.1,-1.65 q -1.7,2.2 -4.45,3.4 -2.75,1.2 -5.45,1.2 v 0 q -2.6,0 -4.65,-1.08 -2.05,-1.07 -3.2,-2.9 -1.15,-1.82 -1.15,-4.07 z m 17,1.85 v 0 q 0.6,-0.7 0.95,-1.43 0.35,-0.72 0.35,-1.22 v 0 -3.25 q -1.55,-0.6 -3.25,-0.93 -1.7,-0.32 -3.35,-0.32 v 0 q -3.35,0 -5.42,1.32 -2.08,1.33 -2.08,3.63 v 0 q 0,1.25 0.68,2.42 0.67,1.18 2,1.93 1.32,0.75 3.27,0.75 v 0 q 2.05,0 3.9,-0.83 1.85,-0.82 2.95,-2.07 z m 26.55,-20.55 v 4 q -3.3,0.05 -5.83,1.67 -2.52,1.63 -3.57,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.57,-4.38 2.23,-1.67 4.68,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z m 2.2,13.25 v 0 q 0,-3.7 1.52,-6.78 1.53,-3.07 4.2,-4.92 2.68,-1.85 6.13,-1.85 v 0 q 3.15,0 5.65,1.65 2.5,1.65 3.9,4 v 0 -15.6 h 4.4 V -5 q 0,0.8 0.32,1.17 0.33,0.38 1.08,0.43 v 0 3.4 q -1.25,0.2 -1.95,0.2 v 0 q -1.3,0 -2.28,-0.9 -0.97,-0.9 -0.97,-2 v 0 -2.2 q -1.55,2.5 -4.15,3.95 -2.6,1.45 -5.4,1.45 v 0 q -2.7,0 -4.98,-1.08 -2.27,-1.07 -3.95,-2.97 -1.67,-1.9 -2.6,-4.33 -0.92,-2.42 -0.92,-5.12 z m 21.4,3.85 v 0 -7.5 q -0.6,-1.7 -1.95,-3.08 -1.35,-1.37 -3.03,-2.2 -1.67,-0.82 -3.32,-0.82 v 0 q -1.95,0 -3.53,0.82 -1.57,0.83 -2.7,2.2 -1.12,1.38 -1.72,3.13 -0.6,1.75 -0.6,3.65 v 0 q 0,1.95 0.67,3.7 0.68,1.75 1.9,3.1 1.23,1.35 2.85,2.1 1.63,0.75 3.53,0.75 v 0 q 1.2,0 2.47,-0.45 1.28,-0.45 2.43,-1.25 1.15,-0.8 1.95,-1.88 0.8,-1.07 1.05,-2.27 z"
opacity="1"
fill="#ffffff"
class="wordmark-text-0"
data-fill-palette-color="primary"
id="text-0"
style="fill:url(#linearGradient15)" />
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<g
id="g9"
style="display:inline;fill:#f7daa2;fill-opacity:1"
transform="translate(0,0.72190669)">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg9"
inkscape:label="svg9"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g4"
style="fill:#f7daa2;fill-opacity:1">
<svg
version="1.1"
id="svg4"
width="100%"
height="100%"
style="fill:#f7daa2;fill-opacity:1" />
</g>
<g
id="icon-0"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg8"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g5"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<path
d="M 0,60.234 C 0,26.967 26.968,0 60.234,0 c 33.267,0 60.234,26.968 60.235,60.234 0,33.267 -26.968,60.234 -60.235,60.235 C 26.967,120.469 0,93.501 0,60.234 Z m 60.234,55.604 c 30.709,0 55.604,-24.895 55.604,-55.604 0,-30.709 -24.895,-55.604 -55.604,-55.603 -30.709,0 -55.604,24.895 -55.603,55.603 0,30.709 24.895,55.604 55.603,55.604 z"
data-fill-palette-color="accent"
fill="#ffffff"
stroke="transparent"
id="path4"
style="fill:#f7daa2;fill-opacity:1" />
</g>
<g
transform="translate(30.690719,29.558553)"
id="g8"
style="display:inline;fill:#f7daa2;fill-opacity:1">
<svg
viewBox="0 0 59.087355 61.351687"
height="61.351688"
width="59.087357"
version="1.1"
id="svg7"
style="fill:#f7daa2;fill-opacity:1">
<g
id="g7"
style="fill:#f7daa2;fill-opacity:1">
<svg
version="1.1"
x="0"
y="0"
viewBox="5.021 3.2976885 89.957 93.404312"
enable-background="new 0 0 100 100"
xml:space="preserve"
height="61.351688"
width="59.087357"
class="icon-cg-0"
data-fill-palette-color="accent"
id="cg-0"
style="fill:#f7daa2;fill-opacity:1"><path
d="m 68.84,60.533 c 0.076,2.981 0.114,6.083 0.114,9.313 0,5.141 -5.936,9.197 -13.67,9.5 -0.473,3.237 -3.26,5.735 -6.627,5.735 -3.37,0 -6.159,-2.502 -6.628,-5.743 -6.134,-0.38 -10.984,-4.486 -10.984,-9.492 0,-2.465 0.556,-5.571 1.404,-9.558 -16.468,2.988 -27.428,9.916 -27.428,17.445 0,10.459 20.178,18.969 44.979,18.969 24.801,0 44.978,-8.51 44.978,-18.969 C 94.979,70.48 84.525,63.641 68.84,60.533 Z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path5"
style="fill:#f7daa2;fill-opacity:1" /><path
d="m 43.952,78.375 c 0,2.595 2.111,4.706 4.705,4.706 2.595,0 4.705,-2.111 4.705,-4.706 0,-2.595 -2.11,-4.705 -4.705,-4.705 -2.594,0 -4.705,2.11 -4.705,4.705 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path6"
style="fill:#f7daa2;fill-opacity:1" /><path
d="M 66.954,69.846 C 66.954,48.265 65.589,18.326 53.781,7.529 50.439,4.473 46.504,3.098 41.768,3.321 41.673,27.762 37.84,45.108 35.293,56.634 c -1.256,5.688 -2.249,10.18 -2.249,13.212 0,3.918 3.959,7.143 8.996,7.494 0.5,-3.206 3.272,-5.67 6.617,-5.67 3.347,0 6.121,2.468 6.618,5.678 6.517,-0.283 11.679,-3.535 11.679,-7.502 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path7"
style="fill:#f7daa2;fill-opacity:1" /></svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<defs
id="defs10" />
</svg>
<rect
width="395.51999"
height="88.198647"
fill="none"
stroke="none"
visibility="hidden"
id="rect11"
x="0"
y="0" />
</g>
</svg>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,325 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="337"
height="337"
viewBox="-1 0 337 337"
id="svg13"
sodipodi:docname="logo-MIkroWizard-small-white.svg"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs13">
<linearGradient
id="linearGradient13"
inkscape:collect="always">
<stop
style="stop-color:#f7daa2;stop-opacity:1;"
offset="0"
id="stop13" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop14" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient14"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient15"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient16"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient17"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient18"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient19"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient20"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient21"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13"
id="linearGradient22"
gradientUnits="userSpaceOnUse"
x1="4.25"
y1="-18"
x2="289.23001"
y2="-18" />
</defs>
<sodipodi:namedview
id="namedview13"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.90066667"
inkscape:cx="713.91562"
inkscape:cy="208.17913"
inkscape:window-width="1920"
inkscape:window-height="991"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg10" />
<g
transform="translate(-0.90909091,0.37614089)"
id="g12">
<svg
viewBox="0 0 396 88"
data-background-color="#303c54"
preserveAspectRatio="xMidYMid"
height="333"
width="1500"
version="1.1"
id="svg12">
<g
id="tight-bounds"
transform="translate(0.24,-0.0994006)">
<svg
viewBox="0 0 395.52 88.19865"
height="88.198647"
width="395.51999"
version="1.1"
id="svg11">
<g
id="g10"
style="display:inline">
<svg
viewBox="0 0 540.23295 120.46879"
height="88.198647"
width="395.51999"
version="1.1"
id="svg10">
<g
transform="translate(144.71295,34.560291)"
id="g3"
style="display:inline;fill:url(#linearGradient14)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg3"
style="fill:url(#linearGradient22)">
<g
id="textblocktransform"
style="fill:url(#linearGradient21)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
id="textblock"
version="1.1"
style="fill:url(#linearGradient20)">
<g
id="g2"
style="fill:url(#linearGradient19)">
<svg
viewBox="0 0 395.52 51.348211"
height="51.348209"
width="395.51999"
version="1.1"
id="svg2"
style="fill:url(#linearGradient18)">
<g
id="g1"
style="fill:url(#linearGradient17)">
<svg
width="395.51999"
viewBox="4.25 -36.5 284.98 37"
height="51.348209"
data-palette-color="#ffffff"
version="1.1"
id="svg1"
style="fill:url(#linearGradient16)">
<path
d="m 39.45,0 h -4.5 V -27.1 L 23.25,-6.25 H 20.5 L 8.75,-27.1 V 0 H 4.25 V -35.5 H 9 l 12.85,23.05 12.9,-23.05 h 4.7 z m 12.2,0 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z M 81.9,0 H 77.15 L 68.1,-12.7 63.15,-8.05 V 0 h -4.4 v -36.5 h 4.4 v 23.85 l 13.5,-13.4 H 81.4 L 71,-15.25 Z m 17.59,-26.25 v 4 q -3.3,0.05 -5.82,1.67 -2.53,1.63 -3.58,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.58,-4.38 2.22,-1.67 4.67,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z M 114.64,0.5 v 0 q -2.95,0 -5.35,-1.08 -2.4,-1.07 -4.15,-2.95 -1.75,-1.87 -2.7,-4.32 -0.95,-2.45 -0.95,-5.15 v 0 q 0,-2.75 0.95,-5.2 0.95,-2.45 2.7,-4.33 1.75,-1.87 4.18,-2.95 2.42,-1.07 5.37,-1.07 v 0 q 2.9,0 5.3,1.07 2.4,1.08 4.18,2.95 1.77,1.88 2.72,4.33 0.95,2.45 0.95,5.2 v 0 q 0,2.7 -0.95,5.15 -0.95,2.45 -2.7,4.32 -1.75,1.88 -4.17,2.95 -2.43,1.08 -5.38,1.08 z m -8.65,-13.45 v 0 q 0,2.7 1.18,4.9 1.17,2.2 3.12,3.47 1.95,1.28 4.35,1.28 v 0 q 2.4,0 4.38,-1.3 1.97,-1.3 3.15,-3.53 1.17,-2.22 1.17,-4.92 v 0 q 0,-2.7 -1.17,-4.9 -1.18,-2.2 -3.15,-3.5 -1.98,-1.3 -4.38,-1.3 v 0 q -2.4,0 -4.35,1.32 -1.95,1.33 -3.12,3.53 -1.18,2.2 -1.18,4.95 z m 43.6,-6.9 -6.15,-15.45 h 4.25 l 4.7,12.35 4.75,-12.35 h 4.25 l -6.15,15.45 6.1,14.65 12,-30.3 h 4.9 L 163.39,0 h -3.9 L 152.44,-16.75 145.34,0 h -3.9 l -14.8,-35.5 h 4.85 l 12.05,30.3 z m 37,19.85 h -4.4 v -26.1 h 4.4 z m 0,-31 h -4.4 v -5.5 h 4.4 z m 5.04,31 v -2.85 l 16.45,-20.05 h -16 v -3.2 h 20.95 v 2.85 L 196.68,-3.2 h 16.4 V 0 Z m 24.65,-7.55 v 0 q 0,-2.5 1.43,-4.33 1.42,-1.82 3.92,-2.82 2.5,-1 5.8,-1 v 0 q 1.75,0 3.7,0.27 1.95,0.28 3.45,0.83 v 0 -1.9 q 0,-3 -1.8,-4.73 -1.8,-1.72 -5.1,-1.72 v 0 q -2.15,0 -4.12,0.77 -1.98,0.78 -4.18,2.23 v 0 l -1.6,-3.1 q 2.55,-1.75 5.1,-2.63 2.55,-0.87 5.3,-0.87 v 0 q 5,0 7.9,2.77 2.9,2.78 2.9,7.73 v 0 V -5 q 0,0.8 0.33,1.17 0.32,0.38 1.07,0.43 v 0 3.4 q -0.65,0.1 -1.12,0.15 -0.48,0.05 -0.78,0.05 v 0 q -1.55,0 -2.32,-0.85 -0.78,-0.85 -0.88,-1.8 v 0 l -0.1,-1.65 q -1.7,2.2 -4.45,3.4 -2.75,1.2 -5.45,1.2 v 0 q -2.6,0 -4.65,-1.08 -2.05,-1.07 -3.2,-2.9 -1.15,-1.82 -1.15,-4.07 z m 17,1.85 v 0 q 0.6,-0.7 0.95,-1.43 0.35,-0.72 0.35,-1.22 v 0 -3.25 q -1.55,-0.6 -3.25,-0.93 -1.7,-0.32 -3.35,-0.32 v 0 q -3.35,0 -5.42,1.32 -2.08,1.33 -2.08,3.63 v 0 q 0,1.25 0.68,2.42 0.67,1.18 2,1.93 1.32,0.75 3.27,0.75 v 0 q 2.05,0 3.9,-0.83 1.85,-0.82 2.95,-2.07 z m 26.55,-20.55 v 4 q -3.3,0.05 -5.83,1.67 -2.52,1.63 -3.57,4.48 v 0 V 0 h -4.4 v -26.1 h 4.1 v 6.05 q 1.35,-2.7 3.57,-4.38 2.23,-1.67 4.68,-1.87 v 0 q 0.5,0 0.85,0 0.35,0 0.6,0.05 z m 2.2,13.25 v 0 q 0,-3.7 1.52,-6.78 1.53,-3.07 4.2,-4.92 2.68,-1.85 6.13,-1.85 v 0 q 3.15,0 5.65,1.65 2.5,1.65 3.9,4 v 0 -15.6 h 4.4 V -5 q 0,0.8 0.32,1.17 0.33,0.38 1.08,0.43 v 0 3.4 q -1.25,0.2 -1.95,0.2 v 0 q -1.3,0 -2.28,-0.9 -0.97,-0.9 -0.97,-2 v 0 -2.2 q -1.55,2.5 -4.15,3.95 -2.6,1.45 -5.4,1.45 v 0 q -2.7,0 -4.98,-1.08 -2.27,-1.07 -3.95,-2.97 -1.67,-1.9 -2.6,-4.33 -0.92,-2.42 -0.92,-5.12 z m 21.4,3.85 v 0 -7.5 q -0.6,-1.7 -1.95,-3.08 -1.35,-1.37 -3.03,-2.2 -1.67,-0.82 -3.32,-0.82 v 0 q -1.95,0 -3.53,0.82 -1.57,0.83 -2.7,2.2 -1.12,1.38 -1.72,3.13 -0.6,1.75 -0.6,3.65 v 0 q 0,1.95 0.67,3.7 0.68,1.75 1.9,3.1 1.23,1.35 2.85,2.1 1.63,0.75 3.53,0.75 v 0 q 1.2,0 2.47,-0.45 1.28,-0.45 2.43,-1.25 1.15,-0.8 1.95,-1.88 0.8,-1.07 1.05,-2.27 z"
opacity="1"
fill="#ffffff"
class="wordmark-text-0"
data-fill-palette-color="primary"
id="text-0"
style="fill:url(#linearGradient15)" />
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<g
id="g9"
style="display:inline;fill:#ffffff;fill-opacity:1"
transform="translate(0,0.72190669)">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg9"
inkscape:label="svg9"
style="fill:#ffffff;fill-opacity:1">
<g
id="g4"
style="fill:#ffffff;fill-opacity:1">
<svg
version="1.1"
id="svg4"
width="100%"
height="100%"
style="fill:#ffffff;fill-opacity:1" />
</g>
<g
id="icon-0"
style="display:inline;fill:#ffffff;fill-opacity:1">
<svg
viewBox="0 0 120.46879 120.46879"
height="120.4688"
width="120.4688"
version="1.1"
id="svg8"
style="fill:#ffffff;fill-opacity:1">
<g
id="g5"
style="display:inline;fill:#ffffff;fill-opacity:1">
<path
d="M 0,60.234 C 0,26.967 26.968,0 60.234,0 c 33.267,0 60.234,26.968 60.235,60.234 0,33.267 -26.968,60.234 -60.235,60.235 C 26.967,120.469 0,93.501 0,60.234 Z m 60.234,55.604 c 30.709,0 55.604,-24.895 55.604,-55.604 0,-30.709 -24.895,-55.604 -55.604,-55.603 -30.709,0 -55.604,24.895 -55.603,55.603 0,30.709 24.895,55.604 55.603,55.604 z"
data-fill-palette-color="accent"
fill="#ffffff"
stroke="transparent"
id="path4"
style="fill:#ffffff;fill-opacity:1" />
</g>
<g
transform="translate(30.690719,29.558553)"
id="g8"
style="display:inline;fill:#ffffff;fill-opacity:1">
<svg
viewBox="0 0 59.087355 61.351687"
height="61.351688"
width="59.087357"
version="1.1"
id="svg7"
style="fill:#ffffff;fill-opacity:1">
<g
id="g7"
style="fill:#ffffff;fill-opacity:1">
<svg
version="1.1"
x="0"
y="0"
viewBox="5.021 3.2976885 89.957 93.404312"
enable-background="new 0 0 100 100"
xml:space="preserve"
height="61.351688"
width="59.087357"
class="icon-cg-0"
data-fill-palette-color="accent"
id="cg-0"
style="fill:#ffffff;fill-opacity:1"><path
d="m 68.84,60.533 c 0.076,2.981 0.114,6.083 0.114,9.313 0,5.141 -5.936,9.197 -13.67,9.5 -0.473,3.237 -3.26,5.735 -6.627,5.735 -3.37,0 -6.159,-2.502 -6.628,-5.743 -6.134,-0.38 -10.984,-4.486 -10.984,-9.492 0,-2.465 0.556,-5.571 1.404,-9.558 -16.468,2.988 -27.428,9.916 -27.428,17.445 0,10.459 20.178,18.969 44.979,18.969 24.801,0 44.978,-8.51 44.978,-18.969 C 94.979,70.48 84.525,63.641 68.84,60.533 Z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path5"
style="fill:#ffffff;fill-opacity:1" /><path
d="m 43.952,78.375 c 0,2.595 2.111,4.706 4.705,4.706 2.595,0 4.705,-2.111 4.705,-4.706 0,-2.595 -2.11,-4.705 -4.705,-4.705 -2.594,0 -4.705,2.11 -4.705,4.705 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path6"
style="fill:#ffffff;fill-opacity:1" /><path
d="M 66.954,69.846 C 66.954,48.265 65.589,18.326 53.781,7.529 50.439,4.473 46.504,3.098 41.768,3.321 41.673,27.762 37.84,45.108 35.293,56.634 c -1.256,5.688 -2.249,10.18 -2.249,13.212 0,3.918 3.959,7.143 8.996,7.494 0.5,-3.206 3.272,-5.67 6.617,-5.67 3.347,0 6.121,2.468 6.618,5.678 6.517,-0.283 11.679,-3.535 11.679,-7.502 z"
fill="#ffffff"
data-fill-palette-color="accent"
id="path7"
style="fill:#ffffff;fill-opacity:1" /></svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
</svg>
</g>
<defs
id="defs10" />
</svg>
<rect
width="395.51999"
height="88.198647"
fill="none"
stroke="none"
visibility="hidden"
id="rect11"
x="0"
y="0" />
</g>
</svg>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -0,0 +1,39 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 60 60" capture-installed="true">
<path
id="motionPath"
fill="none"
stroke-width="0"
stroke-miterlimit="10"
d="
M 23, 23
m -17, 0
a 17,17 0 1,0 34,0
a 17,17 0 1,0 -34,0"
/>
<g fill="none" fill-rule="evenodd" transform="translate(1 1)" stroke-width="0" >
<circle id="circle" cx="0" cy="0" r="5" fill="#4FC1EA" fill-opacity="1" ></circle>
<circle id="circle2" cx="0" cy="0" r="4.5" fill="#4FC1EA" fill-opacity=".9" ></circle>
<circle id="circle3" cx="0" cy="0" r="4" fill="#4FC1EA" fill-opacity=".6" ></circle>
<circle id="circle4" cx="0" cy="0" r="3.5" fill="#4FC1EA" fill-opacity=".8" ></circle>
<circle id="circle5" cx="0" cy="0" r="3" fill="#4FC1EA" fill-opacity=".4" ></circle>
<circle id="circle6" cx="0" cy="0" r="2.5" fill="#4FC1EA" fill-opacity=".7" ></circle>
</g>
<animateMotion href="#circle" dur="1s" begin="0s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
<animateMotion href="#circle2" dur="1s" begin=".09s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
<animateMotion href="#circle3" dur="1s" begin=".18s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
<animateMotion href="#circle4" dur="1s" begin=".26s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
<animateMotion href="#circle5" dur="1s" begin=".34s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
<animateMotion href="#circle6" dur="1s" begin=".40s" fill="freeze" repeatCount="indefinite" >
<mpath href="#motionPath" />
</animateMotion>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,90 @@
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
base: #282c34
mono-1: #abb2bf
mono-2: #818896
mono-3: #5c6370
hue-1: #56b6c2
hue-2: #61aeee
hue-3: #c678dd
hue-4: #98c379
hue-5: #e06c75
hue-5-2: #be5046
hue-6: #d19a66
hue-6-2: #e6c07b
*/
.hljs {
color: #abb2bf;
background: #282c34
}
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75
}
.hljs-literal {
color: #56b6c2
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta .hljs-string {
color: #98c379
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee
}
.hljs-built_in,
.hljs-title.class_,
.hljs-class .hljs-title {
color: #e6c07b
}
.hljs-emphasis {
font-style: italic
}
.hljs-strong {
font-weight: bold
}
.hljs-link {
text-decoration: underline
}

View file

@ -0,0 +1,358 @@
/*!
Highlight.js v11.10.0 (git: 366a8bd012)
(c) 2006-2024 Josh Goebel <hello@joshgoebel.com> and other contributors
License: BSD-3-Clause
*/
var hljs=function(){"use strict";function e(t){
return t instanceof Map?t.clear=t.delete=t.set=()=>{
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
throw Error("set is read-only")
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
})),t}class t{constructor(e){
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
;class o{constructor(e,t){
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
if(e.startsWith("language:"))return e.replace("language:","language-")
;if(e.includes(".")){const n=e.split(".")
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
this.buffer+=`<span class="${e}">`}}const r=(e={})=>{const t={children:[]}
;return Object.assign(t,e),t};class a{constructor(){
this.rootNode=r(),this.stack=[this.rootNode]}get top(){
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
this.top.children.push(e)}openNode(e){const t=r({scope:e})
;this.add(t),this.stack.push(t)}closeNode(){
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
this.closeNode()}__addSublanguage(e,t){const n=e.root
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
return new o(this,this.options).value()}finalize(){
return this.closeAllNodes(),!0}}function l(e){
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
const t=e[e.length-1]
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
s+=i.substring(0,e.index),
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={
begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'",
illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n",
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
contains:[]},n);s.contains.push({scope:"doctag",
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({
__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N,
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w,
PHRASAL_WORDS_MODE:{
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
SHEBANG:(e={})=>{const t=/^#![ ]*\//
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
if(e.match){
if(e.begin||e.end)throw Error("begin & end are not supported with match")
;e.begin=e.match,delete e.match}}function P(e,t){
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
},e.relevance=0,delete n.beforeMatch
},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword"
;function $(e,t,n=C){const i=Object.create(null)
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
Object.assign(i,$(e[n],t,n))})),i;function s(e,n){
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{
console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{
z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0)
},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={}
;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1])
;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
}),(e=>{if(Array.isArray(e.begin)){
if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
K
;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"),
K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
if(Array.isArray(e.end)){
if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
K
;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"),
K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){
function t(t,n){
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
}class n{constructor(){
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
addRule(e,t){
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
;const t=this.matcherRe.exec(e);if(!t)return null
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
this.rules=[],this.multiRegexes=[],
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
;let n=t.exec(e)
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
return n&&(this.regexIndex+=n.position+1,
this.regexIndex===this.count&&this.considerAll()),n}}
if(e.compilerExtensions||(e.compilerExtensions=[]),
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o
;if(o.isCompiled)return a
;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))),
o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null
;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords),
c=o.keywords.$pattern,
delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)),
a.keywordPatternRe=t(c,!0),
r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/),
o.end&&(a.endRe=t(a.end)),
a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)),
o.illegal&&(a.illegalRe=t(o.illegal)),
o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{
starts:e.starts?i(e.starts):null
}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a)
})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){
return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{
const i=Object.create(null),s=Object.create(null),o=[];let r=!0
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
;"object"==typeof t?(i=e,
n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."),
G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o)
;const r=o.result?o.result:E(o.language,o.code,n)
;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
;for(;t;){n+=R.substring(e,t.index)
;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){
const[e,i]=o
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{
const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
if(!i[N.subLanguage])return void M.addText(R)
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language)
})():l(),R=""}function u(e,t){
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
function h(e,t){
return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope),
e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N
;N.endScope&&N.endScope._wrap?(g(),
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t),
g(),o.excludeEnd&&(R=t));do{
N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent
}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length}
let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0
;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){
if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`)
;throw t.languageName=e,t.badRule=w.rule,t}return 1}
if(w=o,"begin"===o.type)return(e=>{
const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]]
;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o)
;if("illegal"===o.type&&!s){
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e}
if("illegal"===o.type&&""===a)return 1
;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches")
;return R+=a,a.length}const _=O(e)
;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[]
;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope)
;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{
if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A
;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e)
;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e,
value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){
if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n),
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A,
context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{
language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1)))
;s.unshift(n);const o=s.sort(((e,t)=>{
if(e.relevance!==t.relevance)return t.relevance-e.relevance
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r
;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
;return t||(X(a.replace("{}",n[1])),
X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
;if(N("before:highlightElement",{el:e,language:n
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
console.warn("The element with unescaped HTML:"),
console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML)
;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
;e.classList.add("hljs"),e.classList.add("language-"+i)
})(e,n,o.language),e.result={language:o.language,re:o.relevance,
relevance:o.relevance},o.secondBest&&(e.secondBest={
language:o.secondBest.language,relevance:o.secondBest.relevance
}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){
"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0
}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
s[e.toLowerCase()]=t}))}function k(e){const t=O(e)
;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{
e[n]&&e[n](t)}))}
"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_,
highlightElement:w,
highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"),
G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)},
initHighlighting:()=>{
_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
initHighlightingOnLoad:()=>{
_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
if(W("Language definition for '{}' could not be registered.".replace("{}",e)),
!r)throw t;W(t),s=l}
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{
languageName:e})},unregisterLanguage:e=>{delete i[e]
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v,
autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
e["before:highlightBlock"](Object.assign({block:t.el},t))
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)},
removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{
r=!1},n.safeMode=()=>{r=!0},n.versionString="11.10.0",n.regex={concat:h,
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n
},ne=te({});return ne.newInstance=()=>te({}),ne}()
;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `routeros` grammar compiled for Highlight.js 11.10.0 */
(()=>{var e=(()=>{"use strict";return e=>{
const r="foreach do while for if from to step else on-error and or not in",n="true false yes no nothing nil null",i={
className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{(.*?)\}/
}]},s={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,i,{
className:"variable",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]}]},t={
className:"string",begin:/'/,end:/'/};return{name:"MikroTik RouterOS script",
aliases:["mikrotik"],case_insensitive:!0,keywords:{$pattern:/:?[\w-]+/,
literal:n,
keyword:r+" :"+r.split(" ").join(" :")+" :"+"global local beep delay put len typeof pick log time set find environment terminal error execute parse resolve toarray tobool toid toip toip6 tonum tostr totime".split(" ").join(" :")
},contains:[{variants:[{begin:/\/\*/,end:/\*\//},{begin:/\/\//,end:/$/},{
begin:/<\//,end:/>/}],illegal:/./},e.COMMENT("^#","$"),s,t,i,{
begin:/[\w-]+=([^\s{}[\]()>]+)/,relevance:0,returnBegin:!0,contains:[{
className:"attribute",begin:/[^=]+/},{begin:/=/,endsWithParent:!0,relevance:0,
contains:[s,t,i,{className:"literal",begin:"\\b("+n.split(" ").join("|")+")\\b"
},{begin:/("[^"]*"|[^\s{}[\]]+)/}]}]},{className:"number",begin:/\*[0-9a-fA-F]+/
},{
begin:"\\b(add|remove|enable|disable|set|get|print|export|edit|find|run|debug|error|info|warning)([\\s[(\\]|])",
returnBegin:!0,contains:[{className:"built_in",begin:/\w+/}]},{
className:"built_in",variants:[{
begin:"(\\.\\./|/|\\s)((traffic-flow|traffic-generator|firewall|scheduler|aaa|accounting|address-list|address|align|area|bandwidth-server|bfd|bgp|bridge|client|clock|community|config|connection|console|customer|default|dhcp-client|dhcp-server|discovery|dns|e-mail|ethernet|filter|firmware|gps|graphing|group|hardware|health|hotspot|identity|igmp-proxy|incoming|instance|interface|ip|ipsec|ipv6|irq|l2tp-server|lcd|ldp|logging|mac-server|mac-winbox|mangle|manual|mirror|mme|mpls|nat|nd|neighbor|network|note|ntp|ospf|ospf-v3|ovpn-server|page|peer|pim|ping|policy|pool|port|ppp|pppoe-client|pptp-server|prefix|profile|proposal|proxy|queue|radius|resource|rip|ripng|route|routing|screen|script|security-profiles|server|service|service-port|settings|shares|smb|sms|sniffer|snmp|snooper|socks|sstp-server|system|tool|tracking|type|upgrade|upnp|user-manager|users|user|vlan|secret|vrrp|watchdog|web-access|wireless|pptp|pppoe|lan|wan|layer7-protocol|lease|simple|raw);?\\s)+"
},{begin:/\.\./,relevance:0}]}]}}})();hljs.registerLanguage("routeros",e)})();/*! `xml` grammar compiled for Highlight.js 11.10.0 */
(()=>{var e=(()=>{"use strict";return e=>{
const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={
className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/,
contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
},i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{
className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={
endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:"attr",
begin:/[\p{L}0-9._:-]+/u,relevance:0},{begin:/=\s*/,relevance:0,contains:[{
className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[s]},{
begin:/'/,end:/'/,contains:[s]},{begin:/[^\s"'=<>`]+/}]}]}]};return{
name:"HTML, XML",
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin:/<![a-z]/,
end:/>/,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{
className:"meta",begin:/<![a-z]/,end:/>/,contains:[t,i,l,c]}]}]
},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\[CDATA\[/,end:/\]\]>/,
relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,
relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",
begin:/<style(?=\s|>)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{
end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",
begin:/<script(?=\s|>)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{
end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{
className:"tag",begin:/<>|<\/>/},{className:"tag",
begin:a.concat(/</,a.lookahead(a.concat(n,a.either(/\/>/,/>/,/\s/)))),
end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{
className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{
className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}
})();hljs.registerLanguage("xml",e)})();

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkmikrowizard=self.webpackChunkmikrowizard||[]).push([[76],{7630:(h,p,r)=>{r.d(p,{y:()=>a});var t=r(4438),n=r(8921),l=r(177);const u=["*"];function _(i,s){1&i&&t.nrm(0,"i",8)}function f(i,s){1&i&&t.nrm(0,"i",9)}function m(i,s){1&i&&t.nrm(0,"i",10)}let a=(()=>{class i extends n.d3n{constructor(e,o,c,d){super(e,o,c,d),this.hostElement=e,this.renderer=o,this.toasterService=c,this.changeDetectorRef=d,this.closeButton=!0,this.title="",this.body=""}static#t=this.\u0275fac=function(o){return new(o||i)(t.rXU(t.aKT),t.rXU(t.sFG),t.rXU(n.W9m),t.rXU(t.gRc))};static#e=this.\u0275cmp=t.VBU({type:i,selectors:[["app-toast-simple"]],inputs:{closeButton:"closeButton",title:"title",body:"body"},standalone:!0,features:[t.Jv_([{provide:n.d3n,useExisting:(0,t.Rfq)(()=>i)}]),t.Vt3,t.aNF],ngContentSelectors:u,decls:12,vars:7,consts:[["toastBody",""],[3,"closeButton"],["style","color:#e55353","class","fa-solid fa-xmark mx-1",4,"ngIf"],["style","color:#3399ff","class","fa-solid fa-exclamation mx-1",4,"ngIf"],["style","color:#f9b115","class","fa-solid fa-triangle-exclamation mx-1",4,"ngIf"],[2,"line-height","1"],[3,"cToastClose"],[1,"mb-1",2,"color","#fff"],[1,"fa-solid","fa-xmark","mx-1",2,"color","#e55353"],[1,"fa-solid","fa-exclamation","mx-1",2,"color","#3399ff"],[1,"fa-solid","fa-triangle-exclamation","mx-1",2,"color","#f9b115"]],template:function(o,c){if(1&o&&(t.NAR(),t.qex(0),t.j41(1,"c-toast-header",1),t.DNE(2,_,1,0,"i",2)(3,f,1,0,"i",3)(4,m,1,0,"i",4),t.j41(5,"strong",5),t.EFF(6),t.k0s()(),t.j41(7,"c-toast-body",6,0)(9,"p",7),t.EFF(10),t.k0s(),t.SdG(11),t.k0s(),t.bVm()),2&o){const d=t.sdS(8);t.R7$(),t.Y8G("closeButton",c.closeButton),t.R7$(),t.Y8G("ngIf","danger"==c.color),t.R7$(),t.Y8G("ngIf","info"==c.color),t.R7$(),t.Y8G("ngIf","warning"==c.color),t.R7$(2),t.JRh(c.title),t.R7$(),t.Y8G("cToastClose",d.toast),t.R7$(3),t.SpI("",c.body," ")}},dependencies:[n.eY7,n.jS2,n.T5C,l.MD,l.bT],styles:["[_nghost-%COMP%]{display:block;overflow:hidden}"]})}return i})()},3801:(h,p,r)=>{r.d(p,{FQ:()=>m,dF:()=>f});var t=r(177),n=r(4438);class l{constructor(i,s){this._document=s;const e=this._textarea=this._document.createElement("textarea"),o=e.style;o.position="fixed",o.top=o.opacity="0",o.left="-999em",e.setAttribute("aria-hidden","true"),e.value=i,e.readOnly=!0,(this._document.fullscreenElement||this._document.body).appendChild(e)}copy(){const i=this._textarea;let s=!1;try{if(i){const e=this._document.activeElement;i.select(),i.setSelectionRange(0,i.value.length),s=this._document.execCommand("copy"),e&&e.focus()}}catch{}return s}destroy(){const i=this._textarea;i&&(i.remove(),this._textarea=void 0)}}let u=(()=>{class a{constructor(s){this._document=s}copy(s){const e=this.beginCopy(s),o=e.copy();return e.destroy(),o}beginCopy(s){return new l(s,this._document)}static#t=this.\u0275fac=function(e){return new(e||a)(n.KVO(t.qQ))};static#e=this.\u0275prov=n.jDH({token:a,factory:a.\u0275fac,providedIn:"root"})}return a})();const _=new n.nKC("CDK_COPY_TO_CLIPBOARD_CONFIG");let f=(()=>{class a{constructor(s,e,o){this._clipboard=s,this._ngZone=e,this.text="",this.attempts=1,this.copied=new n.bkB,this._pending=new Set,o&&null!=o.attempts&&(this.attempts=o.attempts)}copy(s=this.attempts){if(s>1){let e=s;const o=this._clipboard.beginCopy(this.text);this._pending.add(o);const c=()=>{const d=o.copy();d||! --e||this._destroyed?(this._currentTimeout=null,this._pending.delete(o),o.destroy(),this.copied.emit(d)):this._currentTimeout=this._ngZone.runOutsideAngular(()=>setTimeout(c,1))};c()}else this.copied.emit(this._clipboard.copy(this.text))}ngOnDestroy(){this._currentTimeout&&clearTimeout(this._currentTimeout),this._pending.forEach(s=>s.destroy()),this._pending.clear(),this._destroyed=!0}static#t=this.\u0275fac=function(e){return new(e||a)(n.rXU(u),n.rXU(n.SKi),n.rXU(_,8))};static#e=this.\u0275dir=n.FsC({type:a,selectors:[["","cdkCopyToClipboard",""]],hostBindings:function(e,o){1&e&&n.bIt("click",function(){return o.copy()})},inputs:{text:[n.Mj6.None,"cdkCopyToClipboard","text"],attempts:[n.Mj6.None,"cdkCopyToClipboardAttempts","attempts"]},outputs:{copied:"cdkCopyToClipboardCopied"}})}return a})(),m=(()=>{class a{static#t=this.\u0275fac=function(e){return new(e||a)};static#e=this.\u0275mod=n.$C({type:a});static#o=this.\u0275inj=n.G2t({})}return a})()}}]);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

31
mikrofront/dist/mikrofront/index.html vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
(()=>{"use strict";var e,m={},v={};function a(e){var f=v[e];if(void 0!==f)return f.exports;var r=v[e]={exports:{}};return m[e](r,r.exports,a),r.exports}a.m=m,e=[],a.O=(f,r,n,i)=>{if(!r){var t=1/0;for(d=0;d<e.length;d++){for(var[r,n,i]=e[d],l=!0,o=0;o<r.length;o++)(!1&i||t>=i)&&Object.keys(a.O).every(p=>a.O[p](r[o]))?r.splice(o--,1):(l=!1,i<t&&(t=i));if(l){e.splice(d--,1);var u=n();void 0!==u&&(f=u)}}return f}i=i||0;for(var d=e.length;d>0&&e[d-1][2]>i;d--)e[d]=e[d-1];e[d]=[r,n,i]},a.d=(e,f)=>{for(var r in f)a.o(f,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:f[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((f,r)=>(a.f[r](e,f),f),[])),a.u=e=>(76===e?"common":e)+"."+{71:"6dd96d705d742310",76:"a4ef334c5064572b",80:"117bc609dee9efd1",84:"6bde8408b4fba658",118:"752d2f2f0022849f",130:"15d81349c878944b",182:"550b6560498517dc",184:"751c328b2ed4f7a7",204:"17b1994c1608037c",325:"f9ddbfb6c670511e",381:"071d8c2d068b86cb",385:"90a9729209634334",390:"0263e82f7304a035",391:"94be3a07e48126fb",435:"50aa5b7f2908d6f4",467:"45b165e0dfec22c9",573:"6779ebc34aa91ab9",578:"f4499d6cb3ea793c",631:"bd7178888196a3c5",633:"7075e3558e175ba4",650:"2dd5206d4b1e71a2",703:"0005b6cdd5939585",813:"c9f3904bb7739380",858:"5a69ea3e2ca72256",981:"7bb57a106e9d8927",983:"18b3499f015bcd56",987:"96f768e3ee10d3ce"}[e]+".js",a.miniCssF=e=>{},a.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="mikrowizard:";a.l=(r,n,i,d)=>{if(e[r])e[r].push(n);else{var t,l;if(void 0!==i)for(var o=document.getElementsByTagName("script"),u=0;u<o.length;u++){var c=o[u];if(c.getAttribute("src")==r||c.getAttribute("data-webpack")==f+i){t=c;break}}t||(l=!0,(t=document.createElement("script")).type="module",t.charset="utf-8",t.timeout=120,a.nc&&t.setAttribute("nonce",a.nc),t.setAttribute("data-webpack",f+i),t.src=a.tu(r)),e[r]=[n];var b=(g,p)=>{t.onerror=t.onload=null,clearTimeout(s);var h=e[r];if(delete e[r],t.parentNode&&t.parentNode.removeChild(t),h&&h.forEach(y=>y(p)),g)return g(p)},s=setTimeout(b.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=b.bind(null,t.onerror),t.onload=b.bind(null,t.onload),l&&document.head.appendChild(t)}}})(),a.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;a.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),a.tu=e=>a.tt().createScriptURL(e),a.p="",(()=>{var e={121:0};a.f.j=(n,i)=>{var d=a.o(e,n)?e[n]:void 0;if(0!==d)if(d)i.push(d[2]);else if(121!=n){var t=new Promise((c,b)=>d=e[n]=[c,b]);i.push(d[2]=t);var l=a.p+a.u(n),o=new Error;a.l(l,c=>{if(a.o(e,n)&&(0!==(d=e[n])&&(e[n]=void 0),d)){var b=c&&("load"===c.type?"missing":c.type),s=c&&c.target&&c.target.src;o.message="Loading chunk "+n+" failed.\n("+b+": "+s+")",o.name="ChunkLoadError",o.type=b,o.request=s,d[1](o)}},"chunk-"+n,n)}else e[n]=0},a.O.j=n=>0===e[n];var f=(n,i)=>{var o,u,[d,t,l]=i,c=0;if(d.some(s=>0!==e[s])){for(o in t)a.o(t,o)&&(a.m[o]=t[o]);if(l)var b=l(a)}for(n&&n(i);c<d.length;c++)a.o(e,u=d[c])&&e[u]&&e[u][0](),e[u]=0;return a.O(b)},r=self.webpackChunkmikrowizard=self.webpackChunkmikrowizard||[];r.forEach(f.bind(null,0)),r.push=f.bind(null,r.push.bind(r))})()})();

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version": "1.0.2", "name": "MikroWizard"}

200
mikrofront/front-update.py Normal file
View file

@ -0,0 +1,200 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# mule1.py: independent worker process
# - a TCP server as an example
import time
import datetime
from pathlib import Path
import requests
import logging
import os
import hashlib
import zipfile
import subprocess
import json
from cryptography.fernet import Fernet
import psutil
import sys
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("updater")
log.setLevel(logging.INFO)
API_URL="http://host.docker.internal:8181"
Config_File="/conf/server-conf.json"
Version_File="/usr/share/nginx/html/version.json"
# Example usage
def check_sha256(filename, expect):
"""Check if the file with the name "filename" matches the SHA-256 sum
in "expect"."""
h = hashlib.sha256()
# This will raise an exception if the file doesn't exist. Catching
# and handling it is left as an exercise for the reader.
try:
with open(filename, 'rb') as fh:
# Read and hash the file in 4K chunks. Reading the whole
# file at once might consume a lot of memory if it is
# large.
while True:
data = fh.read(4096)
if len(data) == 0:
break
else:
h.update(data)
return expect == h.hexdigest()
except Exception as e:
return False
def crypt_data(text,key):
# Encryption: Encrypting password using Fernet symmetric encryption
key = Fernet.generate_key()
cipher_suite = Fernet(key)
# Encrypting
encrypted_password = cipher_suite.encrypt(text.encode()).decode()
return encrypted_password
def decrypt_data(text,key):
# Encryption: Decrypting password using Fernet symmetric encryption
cipher_suite = Fernet(key)
# Decrypting password
decrypted_password = cipher_suite.decrypt(text.encode()).decode()
return decrypted_password
def extract_zip_reload(filename,dst):
"""Extract the contents of the zip file "filename" to the directory
"dst". Then reload the updated modules."""
with zipfile.ZipFile(filename, 'r') as zip_ref:
zip_ref.extractall(dst)
# run db migrate
# dir ="/usr/share/nginx/html/"
# cmd = "cd {}; PYTHONPATH={}py PYSRV_CONFIG_PATH={} python3 scripts/dbmigrate.py".format(dir, dir, "/conf/server-conf.json")
# p = subprocess.Popen(cmd, shell=True)
# (output, err) = p.communicate()
#This makes the wait possible
# p_status = p.wait()
#touch server reload file /app/reload
os.remove(filename)
# Path('/app/reload').touch()
def load_config_file():
try:
with open(Config_File, 'r') as fh:
config = json.load(fh)
return config
except Exception as e:
log.error(e)
return False
def get_serial_from_api():
url=API_URL+"/api/get_version"
config=load_config_file()
key=False
if config:
key=config.get('PYSRV_CRYPT_KEY',False)
else:
return False
if not key:
return False
try:
response = requests.get(url)
response = response.json()
return json.loads(decrypt_data(response['result'],key))
except Exception as e:
log.error(e)
return False
def get_version_from_file():
try:
with open(Version_File, 'r') as fh:
version = json.load(fh)
return version.get('version', '0.0.0')
except Exception as e:
log.error(e)
return '0.0.0'
def main():
while True:
pcount=0
for process in psutil.process_iter():
if '/front-update.py' in process.cmdline():
pcount=pcount+1
if pcount>=2:
print("Already running")
exit()
try:
next_hour = (time.time() // 3600 + 1) * 3600
sleep_time = next_hour - time.time()
res=get_serial_from_api()
hwid=res['serial']
username=res['username']
version=get_version_from_file()
params={
"serial_number": hwid,
"username": username.strip(),
"front":True,
"version": version
}
url="https://mikrowizard.com/wp-json/mikrowizard/v1/get_update"
# send post request to server mikrowizard.com with params in json
response = requests.post(url, json=params)
# get response from server
res = response
try:
if res.status_code == 200:
res=res.json()
if 'token' in res:
params={
"token":res['token'],
"file_name":res['filename'],
"username":username.strip(),
"front":True
}
log.info("Update available/Downloading...")
else:
log.info("Update not available")
time.sleep(sleep_time)
continue
except Exception as e:
log.error(e)
# check if filename exist in /app/py and checksum is same then dont continue
if check_sha256("/usr/share/nginx/"+res['filename'], res['sha256']):
log.error("Checksum match, File exist")
extract_zip_reload("/usr/share/nginx/"+res['filename'],"/usr/share/nginx/")
time.sleep(sleep_time)
continue
download_url="https://mikrowizard.com/wp-json/mikrowizard/v1/download_update"
# send post request to server mikrowizard.com with params in json
r = requests.post(download_url,json=params,stream=True)
if "invalid" in r.text or r.text=='false':
log.error(r)
log.error("Invalid response")
time.sleep(30)
continue
with open("/usr/share/nginx/"+res['filename'], 'wb') as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
if check_sha256("/usr/share/nginx/"+res['filename'], res['sha256']):
log.error("Update downloaded")
log.error("/usr/share/nginx/"+res['filename'])
extract_zip_reload("/usr/share/nginx/"+res['filename'],"/usr/share/nginx/")
else:
log.error("Checksum not match")
os.remove("/usr/share/nginx/"+res['filename'])
time.sleep(sleep_time)
except Exception as e:
log.error(e)
time.sleep(30)
if __name__ == '__main__':
main()

3
mikrofront/mwcrontab Normal file
View file

@ -0,0 +1,3 @@
# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
* * * * * /usr/bin/python3 /front-update.py > /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.

38
mikrofront/nginx.conf Normal file
View file

@ -0,0 +1,38 @@
server {
listen 80;
sendfile on;
default_type application/octet-stream;
gzip on;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 256;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 9;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html =404;
}
location /api {
proxy_pass http://host.docker.internal:8181;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /api/frontver {
add_header Cache-Control 'no-store';
add_header Cache-Control 'no-cache';
expires 0;
index version.json;
alias /usr/share/nginx/html;
}
}

3
mikrofront/reqs.txt Normal file
View file

@ -0,0 +1,3 @@
cryptography==3.4.8
Requests==2.32.2
psutil==5.9.0

66
mikroman/Dockerfile Normal file
View file

@ -0,0 +1,66 @@
FROM python:3.11-slim-bullseye
WORKDIR /app
RUN apt-get update
RUN apt-get -y install cron
RUN apt-get install -y iputils-ping
RUN apt-get install -y net-tools
RUN apt-get install -y git
RUN touch /var/log/cron.log
RUN git clone https://github.com/MikroWizard/mikroman.git /app
RUN set -ex \
&& buildDeps=' \
build-essential \
gcc \
' \
&& deps=' \
htop \
' \
&& apt-get install -y $buildDeps git $deps --no-install-recommends && rm -rf /var/lib/apt/lists/* \
&& pip install uWSGI==2.0.22 \
&& pip install -r /app/reqs.txt
RUN mkdir -p /conf
RUN cp /app/conf/loginscript.sh /etc/profile
COPY server-conf.json /conf/
ARG AM_I_IN_A_DOCKER_CONTAINER=Yes
COPY init.sh /app/
COPY initpy.py /app/
RUN chmod +x /app/init.sh
# background spooler dir
RUN mkdir /tmp/pysrv_spooler
# we don't need this file with Docker but uwsgi looks for it
RUN echo `date +%s` >/app/VERSION
EXPOSE 80
# our server config file
# - you should write your own config file and put OUTSIDE the repository
# since the config file contains secrets
# - here I use the sample template from repo
# - it is also possible to override the config with env variables, either here
# or in Amazon ECS or Kubernetes configuration
#COPY /app/real-server-config.json /app/real-server-config.json
# ENV PYSRV_DATABASE_HOST host.docker.internal
# ENV PYSRV_REDIS_HOST host.docker.internal
# ENV PYSRV_DATABASE_PASSWORD x
# build either a production or dev image
ARG BUILDMODE=production
ENV ENVBUILDMODE=$BUILDMODE
RUN echo "BUILDMODE $ENVBUILDMODE"
# run in shell mode with ENV expansion
RUN cd /app && bash init.sh

67
mikroman/init.sh Normal file
View file

@ -0,0 +1,67 @@
#!/bin/bash
# test if it is in build mode!
if [[ -z "${AM_I_IN_A_DOCKER_CONTAINER}" ]]; then
echo "Starting Mikroman ..."
else
exit 0
fi
CONTAINER_ALREADY_STARTED="CONTAINER_ALREADY_STARTED_PLACEHOLDER"
if [ ! -e $CONTAINER_ALREADY_STARTED ]; then
echo "-- Initializing the mikroman for first run --"
# YOUR_JUST_ONCE_LOGIC_HERE
cd /app && export PYTHONPATH=/app/py && export PYSRV_CONFIG_PATH=/conf/server-conf.json && python3 scripts/dbmigrate.py
cat << EOF1 | tee init.sql >/dev/null
INSERT INTO public.tasks( signal, name, status) VALUES ( 100, 'check-update', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 110, 'upgrade-firmware', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 120, 'backup', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 130, 'scanner', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 140, 'downloader', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 150, 'firmware-service', false);
INSERT INTO public.tasks( signal, name, status) VALUES ( 160, 'snipet-exec', false);
INSERT INTO public.sysconfig( key, value) VALUES ( 'scan_mode', 'mac');
INSERT INTO public.sysconfig( key, value) VALUES ( 'mac_scan_interval', '5');
INSERT INTO public.sysconfig( key, value) VALUES ( 'ip_scan_interval', '4');
INSERT INTO public.sysconfig( key, value) VALUES ( 'old_firmware_action', 'keep');
INSERT INTO public.sysconfig( key, value) VALUES ( 'default_user', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'default_password', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'old_version', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'latest_version', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'default_ip', '${MW_SERVER_IP}');
INSERT INTO public.sysconfig( key, value) VALUES ( 'rad_secret', '${MW_RAD_SECRET}');
INSERT INTO public.sysconfig( key, value) VALUES ( 'system_url', 'http://${MW_SERVER_IP}');
INSERT INTO public.sysconfig( key, value) VALUES ( 'force_perms', 'True');
INSERT INTO public.sysconfig( key, value) VALUES ( 'force_radius', 'True');
INSERT INTO public.sysconfig( key, value) VALUES ( 'force_syslog', 'True');
INSERT INTO public.sysconfig( key, value) VALUES ( 'safe_install', 'True');
INSERT INTO public.sysconfig( key, value) VALUES ( 'timezone', 'UTC');
INSERT INTO public.sysconfig( key, value) VALUES ( 'username', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'install_date', '');
INSERT INTO public.sysconfig( key, value) VALUES ( 'all_ip', '');
INSERT INTO public.device_groups( id, name ) VALUES (1, 'Default');
INSERT INTO public.users(username, first_name, last_name,email, role) VALUES ('system', 'system', '','system@localhost', 'disabled');
INSERT INTO public.users(id,username, password, first_name, last_name, email, role,adminperms) VALUES ('37cc36e0-afec-4545-9219-94655805868b','mikrowizard', '$pbkdf2-sha256$29000$yVnr/d9b6917j7G2tlYqRQ$.8fbnLorUGGt6z8SZK9t7Q5WHrRnmKIYL.RW5IkyZLo', 'admin','admin','admin@localhost', 'admin','{"device": "full", "device_group": "full", "task": "full", "backup": "full", "snippet": "full", "accounting": "full", "authentication": "full", "users": "full", "permissions": "full", "settings": "full", "system_backup": "full"}');
INSERT INTO public.permissions(id, name, perms) VALUES (1, 'full', '{"api": true, "ftp": true, "password": true, "read": true, "romon": true, "sniff": true, "telnet": true, "tikapp": true, "winbox": true, "dude": true, "local": true, "policy": true, "reboot": true, "rest-api": true, "sensitive": true, "ssh": true, "test": true, "web": true, "write": true}');
INSERT INTO public.permissions(id, name, perms) VALUES (2, 'read', '{"api": true, "ftp": false, "password": true, "read": true, "romon": true, "sniff": true, "telnet": true, "tikapp": true, "winbox": true, "dude": false, "local": true, "policy": false, "reboot": true, "rest-api": true, "sensitive": true, "ssh": true, "test": true, "web": true, "write": false}');
INSERT INTO public.permissions(id, name, perms) VALUES (3, 'write', '{"api": true, "dude": false, "ftp": false, "local": true, "password": true, "policy": false, "read": true, "reboot": true, "rest-api": true, "romon": true, "sensitive": true, "sniff": true, "ssh": true, "telnet": true, "test": true, "tikapp": true, "web": true, "winbox": true, "write": true}');
INSERT INTO public.user_group_perm_rel(group_id, user_id, perm_id) VALUES ( 1, '37cc36e0-afec-4545-9219-94655805868b', 1);
EOF1
# Run the Python script
python3 /app/initpy.py
# Check if the Python script ran successfully
if [ $? -ne 0 ]; then
echo "An error occurred while executing the SQL commands."
else
touch $CONTAINER_ALREADY_STARTED
echo "SQL commands executed successfully."
fi
cron
uwsgi --ini /app/conf/uwsgi.ini:uwsgi-production --touch-reload=/app/reload
else
cron
uwsgi --ini /app/conf/uwsgi.ini:uwsgi-production --touch-reload=/app/reload
fi

44
mikroman/initpy.py Normal file
View file

@ -0,0 +1,44 @@
import json
import psycopg2
# Step 1: Read connection details from server.json
with open('/conf/server-conf.json') as f:
config = json.load(f)
# Step 2: Connect to the PostgreSQL database
try:
conn = psycopg2.connect(
dbname=config['PYSRV_DATABASE_NAME'],
user=config['PYSRV_DATABASE_USER'],
password=config['PYSRV_DATABASE_PASSWORD'],
host=config['PYSRV_DATABASE_HOST_POSTGRESQL'],
port=config['PYSRV_DATABASE_PORT']
)
cursor = conn.cursor()
print("Connected to the database successfully.")
# Step 3: Read the SQL commands from the SQL file
with open('init.sql', 'r') as sql_file:
sql_commands = sql_file.read()
# Step 4: Execute the SQL commands
cursor.execute(sql_commands)
if cursor.description: # Check if there are results
# Fetch all results
results = cursor.fetchall()
# Print each row of results
for row in results:
print(row)
conn.commit() # Commit the changes if it's not a SELECT query
print("Executed SQL commands successfully.")
except Exception as e:
print(f"An error occurred: {e}")
exit(1)
finally:
# Close the database connection
if cursor:
cursor.close()
if conn:
conn.close()
print("Database connection closed.")

28
mikroman/reqs.txt Normal file
View file

@ -0,0 +1,28 @@
Flask==2.3.2
Flask-Session2==1.3.1
passlib==1.7.4
peewee==3.16.2
peewee-migrate==1.7.1
psycopg2-binary==2.9.6
pytz==2022.7.1
redis==4.5.4
uwsgidecorators==1.1.0
beautifulsoup4==4.10.0
#chardet==4.0.0
click==8.1.7
#click==8.0.3
cron_descriptor==1.4.0
cron_validator==1.0.8
cryptography==3.4.8
feedparser==6.0.11
librouteros==3.2.1
nagiosplugin==1.3.3
paramiko==2.9.3
pexpect==4.9.0
pycryptodome==3.20.0
pyrad==2.4
python_crontab==3.0.0
Requests==2.32.2
#setuptools==59.6.0
uvloop==0.19.0
netifaces==0.11.0

17
mikroman/server-conf.json Normal file
View file

@ -0,0 +1,17 @@
{
"name": "python server config template - rename me",
"PYSRV_IS_PRODUCTION": "1",
"PYSRV_DATABASE_HOST": "127.0.0.1",
"PYSRV_DATABASE_HOST_POSTGRESQL": "127.0.0.1",
"PYSRV_DATABASE_PORT": "5432",
"PYSRV_DATABASE_NAME": "mikrowizard_db",
"PYSRV_DATABASE_USER": "mikrowizard_user",
"PYSRV_DATABASE_PASSWORD": "securepassword",
"PYSRV_CRYPT_KEY": "bN0PJaVMpV7e4NGE8cLF3FECgY_nofYDuBtlLxX7pWg=",
"PYSRV_BACKUP_FOLDER":"/backups/",
"PYSRV_FIRM_FOLDER":"/firms/",
"PYSRV_COOKIE_HTTPS_ONLY": false,
"PYSRV_REDIS_HOST": "127.0.0.1:6379",
"PYSRV_DOMAIN_NAME": "",
"PYSRV_CORS_ALLOW_ORIGIN": "*"
}

View file

@ -0,0 +1,14 @@
{
"name": "python server config template - rename me",
"PYSRV_IS_PRODUCTION": "1",
"PYSRV_DATABASE_HOST": "127.0.0.1",
"PYSRV_DATABASE_PORT": "5432",
"PYSRV_DATABASE_NAME": "${MW_DB_NAME}",
"PYSRV_DATABASE_USER": "${MW_DB_USER}",
"PYSRV_DATABASE_PASSWORD": "${MW_DB_PASSWORD}",
"PYSRV_CRYPT_KEY": "${MW_encryptKey}",
"PYSRV_COOKIE_HTTPS_ONLY": false,
"PYSRV_REDIS_HOST": "127.0.0.1:6379",
"PYSRV_CORS_ALLOW_ORIGIN": "*"
}

62
prepare.sh Executable file
View file

@ -0,0 +1,62 @@
#!/bin/bash
# Load environment variables
set -a
source .env
set +a
export MW_encryptKey=$(python3 -c 'import os; import base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode("utf-8"))')
# Create directories dynamically based on the environment variables
sudo mkdir -p "${CONF_PATH}"
sudo mkdir -p "${FIRMWARE_PATH}"
sudo mkdir -p "${BACKUPS_PATH}"
sed -i "s/.*MW_encryptKey.*/MW_encryptKey=${MW_encryptKey}/" .env
echo "Required directories created:"
echo "Conf: ${CONF_PATH}"
echo "Firmware: ${FIRMWARE_PATH}"
echo "Backups: ${BACKUPS_PATH}"
echo "Your configuration:"
cat << EOF1 | sudo tee ${CONF_PATH}/server-conf.json >/dev/null
{
"name": "python server config template - rename me",
"PYSRV_IS_PRODUCTION": "1",
"PYSRV_DATABASE_HOST": "127.0.0.1",
"PYSRV_DATABASE_HOST_POSTGRESQL": "127.0.0.1",
"PYSRV_DATABASE_PORT": "5432",
"PYSRV_DATABASE_NAME": "${MW_DB_NAME}",
"PYSRV_DATABASE_USER": "${MW_DB_USER}",
"PYSRV_DATABASE_PASSWORD": "${MW_DB_PASSWORD}",
"PYSRV_CRYPT_KEY": "${MW_encryptKey}",
"PYSRV_BACKUP_FOLDER":"/backups/",
"PYSRV_FIRM_FOLDER":"/firms/",
"PYSRV_COOKIE_HTTPS_ONLY": false,
"PYSRV_REDIS_HOST": "127.0.0.1:6379",
"PYSRV_DOMAIN_NAME": "",
"PYSRV_CORS_ALLOW_ORIGIN": "*"
}
EOF1
cat << EOF2 | sudo tee ./mikroman/server-conf.json >/dev/null
{
"name": "python server config template - rename me",
"PYSRV_IS_PRODUCTION": "1",
"PYSRV_DATABASE_HOST": "127.0.0.1",
"PYSRV_DATABASE_HOST_POSTGRESQL": "127.0.0.1",
"PYSRV_DATABASE_PORT": "5432",
"PYSRV_DATABASE_NAME": "${MW_DB_NAME}",
"PYSRV_DATABASE_USER": "${MW_DB_USER}",
"PYSRV_DATABASE_PASSWORD": "${MW_DB_PASSWORD}",
"PYSRV_CRYPT_KEY": "${MW_encryptKey}",
"PYSRV_BACKUP_FOLDER":"/backups/",
"PYSRV_FIRM_FOLDER":"/firms/",
"PYSRV_COOKIE_HTTPS_ONLY": false,
"PYSRV_REDIS_HOST": "127.0.0.1:6379",
"PYSRV_DOMAIN_NAME": "",
"PYSRV_CORS_ALLOW_ORIGIN": "*"
}
EOF2
echo "Stored in : ${CONF_PATH}/server-conf.json"