Compare commits
No commits in common. "fork" and "main" have entirely different histories.
|
@ -1,6 +0,0 @@
|
|||
.git
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
node_modules
|
||||
build
|
||||
README.md
|
2
.env.example
Normal file
|
@ -0,0 +1,2 @@
|
|||
REACT_APP_API_URL=http://stackspin_proxy:8081/api/v1
|
||||
REACT_APP_HYDRA_PUBLIC_URL=https://sso.init.stackspin.net
|
1
.envrc
|
@ -1 +0,0 @@
|
|||
export NODE_OPTIONS=--openssl-legacy-provider
|
1
.gitignore
vendored
|
@ -14,7 +14,6 @@
|
|||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
.envrc
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
|
|
11
Dockerfile
|
@ -1,11 +0,0 @@
|
|||
FROM node:14-alpine AS BUILDER
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json yarn.lock /app/
|
||||
RUN yarn install
|
||||
COPY . /app
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:latest
|
||||
COPY deployment/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=builder /app/build /usr/share/nginx/html
|
20
Makefile
|
@ -1,20 +0,0 @@
|
|||
tag = "$$(git describe --tags)"
|
||||
|
||||
build:
|
||||
$(MAKE) -C backend build
|
||||
docker build -t dashboard .
|
||||
docker tag dashboard yksflip/dashboard:$(tag)
|
||||
docker push yksflip/dashboard:$(tag)
|
||||
|
||||
rm:
|
||||
docker stack rm ${STACK_NAME}
|
||||
|
||||
deploy: rm
|
||||
DOMAIN=${DOMAIN} docker stack deploy --resolve-image always --compose-file compose.yml ${STACK_NAME}
|
||||
|
||||
update:
|
||||
docker pull yksflip/dashboard:latest
|
||||
docker service update dashboard_app --image yksflip/dashboard:latest --force
|
||||
|
||||
exec:
|
||||
docker exec -it $$(docker ps --format "{{ .Names }}" | grep dashboard) bash
|
|
@ -1,4 +0,0 @@
|
|||
.env
|
||||
.vscode
|
||||
.venv
|
||||
__pycache__
|
|
@ -1,10 +0,0 @@
|
|||
HYDRA_CLIENT_ID=
|
||||
HYDRA_CLIENT_SECRET=
|
||||
HYDRA_AUTHORIZATION_BASE_URL="https://sso.example.org/application/o/authorize/"
|
||||
HYDRA_PUBLIC_URL="https://sso.example.org/application/o/"
|
||||
TOKEN_URL="https://sso.example.org/application/o/token/"
|
||||
REDIRECT_URL="https://example.org/login-callback"
|
||||
SECRET_KEY=
|
||||
LOAD_INCLUSTER_CONFIG=false
|
||||
DATABASE_URL=sqlite:///db/database.db
|
||||
AUTHENTIK_BASEURL="https://sso.example.org/api/v3"
|
2
backend/.gitignore
vendored
|
@ -8,5 +8,3 @@ __pycache__
|
|||
.envrc
|
||||
.direnv
|
||||
run_app.local.sh
|
||||
db/
|
||||
*.db
|
||||
|
|
|
@ -15,11 +15,11 @@ ADD requirements.txt .
|
|||
# pip install the local requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# now copy all the files in this directory to /app
|
||||
# now copy all the files in this directory to /code
|
||||
ADD . .
|
||||
|
||||
# Listen to port 80 at runtime
|
||||
EXPOSE 5000
|
||||
|
||||
# Define our command to be run when launching the container
|
||||
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
||||
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:5000", "--workers", "4", "--reload", "--capture-output", "--enable-stdio-inheritance", "--log-level", "DEBUG"]
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
tag = "$$(git describe --tags)"
|
||||
|
||||
build:
|
||||
docker build -t dashboard-backend .
|
||||
docker tag dashboard-backend yksflip/dashboard-backend:$(tag)
|
||||
docker push yksflip/dashboard-backend:$(tag)
|
||||
|
||||
clean:
|
||||
rm database.db
|
||||
flask db upgrade
|
||||
|
||||
demo:
|
||||
flask cli app create nextcloud Dateiablage "https://cloud.dev.local-it.cloud"
|
||||
flask cli app create vikunja Projekte "https://vikunja.dev.local-it.cloud"
|
||||
|
||||
run:
|
||||
flask run
|
|
@ -1 +1 @@
|
|||
from .lit_auth import *
|
||||
from .auth import *
|
|
@ -1,53 +0,0 @@
|
|||
from multiprocessing import current_process
|
||||
from flask import jsonify, request
|
||||
from flask_jwt_extended import create_access_token
|
||||
from flask_cors import cross_origin
|
||||
from datetime import timedelta
|
||||
|
||||
from areas import api_v1
|
||||
from config import *
|
||||
from helpers import LITOauth, BadRequest
|
||||
|
||||
|
||||
@api_v1.route("/login", methods=["POST"])
|
||||
@cross_origin()
|
||||
def login():
|
||||
authorization_url = LITOauth.authorize()
|
||||
return jsonify({"authorizationUrl": authorization_url})
|
||||
|
||||
|
||||
@api_v1.route("/hydra/callback")
|
||||
@cross_origin()
|
||||
def hydra_callback():
|
||||
state = request.args.get("state")
|
||||
code = request.args.get("code")
|
||||
if state == None:
|
||||
raise BadRequest("Missing state query param")
|
||||
|
||||
if code == None:
|
||||
raise BadRequest("Missing code query param")
|
||||
|
||||
token = LITOauth.get_token(state, code)
|
||||
user_info = LITOauth.get_user_info()
|
||||
access_token = create_access_token(
|
||||
identity=token, expires_delta=timedelta(days=365))
|
||||
isAdmin = "admin" in user_info["groups"]
|
||||
app_roles = [
|
||||
{
|
||||
"name": "dashboard",
|
||||
"role_id": 1 if isAdmin else 2
|
||||
},
|
||||
]
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"accessToken": access_token,
|
||||
"userInfo": {
|
||||
"id": user_info["email"],
|
||||
"email": user_info["email"],
|
||||
"name": user_info["name"],
|
||||
"preferredUsername": user_info["preferred_username"],
|
||||
"app_roles": app_roles
|
||||
}
|
||||
}
|
||||
)
|
|
@ -1,39 +0,0 @@
|
|||
from flask import current_app
|
||||
from helpers.authentik_api import AuthentikApi
|
||||
from .user_service import UserService
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserService(UserService):
|
||||
@staticmethod
|
||||
def get_users():
|
||||
user_list = [User.from_authentik(u).to_dict() for u in AuthentikApi.get("/core/users")]
|
||||
return user_list
|
||||
|
||||
@staticmethod
|
||||
def get_user(id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post_user(data):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def __start_recovery_flow(email):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put_user(id, user_editing_id, data):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def delete_user(id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post_multiple_users(data):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def __insertAppRoleToUser(userId, userRes):
|
||||
pass
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
|
||||
class User:
|
||||
id = None
|
||||
uuid = None
|
||||
traits = None
|
||||
email = None
|
||||
name = None
|
||||
preferredUsername = None
|
||||
state = None
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def from_authentik(authentik_user):
|
||||
u = User()
|
||||
u.id = authentik_user["pk"]
|
||||
u.uuid = authentik_user["uid"]
|
||||
u.name = authentik_user["name"]
|
||||
u.email = authentik_user["email"]
|
||||
u.traits = {
|
||||
"name": authentik_user["name"],
|
||||
"email": authentik_user["email"],
|
||||
"app_roles": []
|
||||
}
|
||||
u.preferredUsername = authentik_user["username"]
|
||||
u.state = "active" if authentik_user["is_active"] else ""
|
||||
return u
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"traits": self.traits,
|
||||
"preferredUsername": self.preferredUsername,
|
||||
"state": self.state,
|
||||
}
|
|
@ -8,15 +8,16 @@ from helpers import KratosApi
|
|||
from helpers.auth_guard import admin_required
|
||||
|
||||
from .validation import schema, schema_multiple
|
||||
from .lit_user_service import UserService
|
||||
from .user_service import UserService
|
||||
|
||||
|
||||
@api_v1.route("/users", methods=["GET"])
|
||||
@jwt_required()
|
||||
@cross_origin()
|
||||
# @admin_required() TODO: not needed as authentik checks permissions?
|
||||
@admin_required()
|
||||
def get_users():
|
||||
return jsonify(UserService.get_users())
|
||||
res = UserService.get_users()
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
@api_v1.route("/users/<string:id>", methods=["GET"])
|
||||
|
|
|
@ -1,21 +1,10 @@
|
|||
import os
|
||||
|
||||
|
||||
def env_file(key: str):
|
||||
file_env = os.environ.get(f"{key}_FILE")
|
||||
if file_env and os.path.exists(file_env):
|
||||
return open(file_env).read().rstrip('\n')
|
||||
return os.environ.get(key)
|
||||
|
||||
|
||||
SECRET_KEY = env_file("SECRET_KEY")
|
||||
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||
HYDRA_CLIENT_ID = os.environ.get("HYDRA_CLIENT_ID")
|
||||
HYDRA_CLIENT_SECRET = env_file("HYDRA_CLIENT_SECRET")
|
||||
|
||||
HYDRA_CLIENT_SECRET = os.environ.get("HYDRA_CLIENT_SECRET")
|
||||
HYDRA_AUTHORIZATION_BASE_URL = os.environ.get("HYDRA_AUTHORIZATION_BASE_URL")
|
||||
TOKEN_URL = os.environ.get("TOKEN_URL")
|
||||
REDIRECT_URL = os.environ.get("REDIRECT_URL")
|
||||
|
||||
LOGIN_PANEL_URL = os.environ.get("LOGIN_PANEL_URL")
|
||||
|
||||
|
@ -30,6 +19,4 @@ SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|||
# Set this to "true" to load the config from a Kubernetes serviceaccount
|
||||
# running in a Kubernetes pod. Set it to "false" to load the config from the
|
||||
# `KUBECONFIG` environment variable.
|
||||
LOAD_INCLUSTER_CONFIG = os.environ.get("LOAD_INCLUSTER_CONFIG") == "true"
|
||||
|
||||
AUTHENTIK_BASEURL = os.environ.get("AUTHENTIK_BASEURL")
|
||||
LOAD_INCLUSTER_CONFIG = os.environ.get("LOAD_INCLUSTER_CONFIG").lower() == "true"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
env
|
||||
mkdir -p db
|
||||
flask db upgrade
|
||||
gunicorn app:app -b 0.0.0.0:5000 --workers "$(nproc)" --reload --capture-output --enable-stdio-inheritance --log-level DEBUG
|
|
@ -2,4 +2,3 @@ from .kratos_api import *
|
|||
from .error_handler import *
|
||||
from .hydra_oauth import *
|
||||
from .kratos_user import *
|
||||
from .lit_oauth import *
|
|
@ -12,7 +12,6 @@ def admin_required():
|
|||
def decorator(*args, **kwargs):
|
||||
verify_jwt_in_request()
|
||||
claims = get_jwt()
|
||||
|
||||
user_id = claims["user_id"]
|
||||
is_admin = RoleService.is_user_admin(user_id)
|
||||
if is_admin:
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
from typing import List
|
||||
from flask_jwt_extended import get_jwt
|
||||
import requests
|
||||
|
||||
from config import AUTHENTIK_BASEURL
|
||||
from .error_handler import AuthentikError
|
||||
|
||||
class AuthentikApi: # TODO: check if can be replaced with apispec generated api?
|
||||
@staticmethod
|
||||
def __handleError(res):
|
||||
if res.status_code >= 400:
|
||||
message = res.json()["error"]["message"]
|
||||
raise AuthentikError(message, res.status_code)
|
||||
|
||||
@staticmethod
|
||||
def __token():
|
||||
jwt = get_jwt()
|
||||
return jwt["sub"]["refresh_token"]
|
||||
|
||||
@staticmethod
|
||||
def get(url):
|
||||
try:
|
||||
res = requests.get(f"{AUTHENTIK_BASEURL}{url}", headers={
|
||||
"Authorization": f"Bearer {AuthentikApi.__token()}"})
|
||||
AuthentikApi.__handleError(res)
|
||||
if ("pagination" in res.json()):
|
||||
return AuthentikApi.__paginate(res)
|
||||
return res.json()
|
||||
except AuthentikError as err:
|
||||
raise err
|
||||
except:
|
||||
raise AuthentikError()
|
||||
|
||||
@staticmethod
|
||||
def __paginate(res: requests.Response): # TODO: test this
|
||||
results = res.json()["results"]
|
||||
for page in range(1, res.json()["pagination"]["total_pages"]):
|
||||
res = requests.get(
|
||||
f"{res.request.url}", headers=res.request.headers, params={'page': page})
|
||||
AuthentikApi.__handleError(res)
|
||||
results.append(res.json()["results"])
|
||||
return results
|
|
@ -5,8 +5,6 @@ from jsonschema import ValidationError
|
|||
class KratosError(Exception):
|
||||
pass
|
||||
|
||||
class AuthentikError(Exception):
|
||||
pass
|
||||
|
||||
class HydraError(Exception):
|
||||
pass
|
||||
|
|
|
@ -4,6 +4,7 @@ import requests
|
|||
from config import *
|
||||
from .error_handler import KratosError
|
||||
|
||||
|
||||
class KratosApi:
|
||||
@staticmethod
|
||||
def __handleError(res):
|
||||
|
|
|
@ -20,11 +20,11 @@ from config import LOAD_INCLUSTER_CONFIG
|
|||
#
|
||||
# By default this loads whatever we define in the `KUBECONFIG` env variable,
|
||||
# otherwise loads the config from default locations, similar to what kubectl
|
||||
# # does.
|
||||
# if LOAD_INCLUSTER_CONFIG:
|
||||
# config.load_incluster_config()
|
||||
# else:
|
||||
# config.load_kube_config()
|
||||
# does.
|
||||
if LOAD_INCLUSTER_CONFIG:
|
||||
config.load_incluster_config()
|
||||
else:
|
||||
config.load_kube_config()
|
||||
|
||||
def create_variables_secret(app_slug, variables_filepath):
|
||||
"""Checks if a variables secret for app_name already exists, generates it if necessary.
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
from flask import request, session
|
||||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
from config import *
|
||||
from helpers import HydraError
|
||||
|
||||
|
||||
class LITOauth:
|
||||
@staticmethod
|
||||
def authorize():
|
||||
try:
|
||||
scopes = ["openid", "email", "profile", "goauthentik.io/api"]
|
||||
oauth = OAuth2Session(HYDRA_CLIENT_ID, redirect_uri=REDIRECT_URL, scope=scopes)
|
||||
authorization_url, state = oauth.authorization_url(
|
||||
HYDRA_AUTHORIZATION_BASE_URL
|
||||
)
|
||||
return authorization_url
|
||||
except Exception as err:
|
||||
raise HydraError(str(err), 500)
|
||||
|
||||
@staticmethod
|
||||
def get_token(state, code):
|
||||
try:
|
||||
oauth = OAuth2Session(
|
||||
client_id=HYDRA_CLIENT_ID,
|
||||
redirect_uri=REDIRECT_URL,
|
||||
state=state,
|
||||
)
|
||||
token = oauth.fetch_token(
|
||||
token_url=TOKEN_URL,
|
||||
code=code,
|
||||
client_secret=HYDRA_CLIENT_SECRET,
|
||||
include_client_id=True,
|
||||
)
|
||||
|
||||
session["oauth_token"] = token
|
||||
|
||||
return token
|
||||
except Exception as err:
|
||||
raise HydraError(str(err), 500)
|
||||
|
||||
@staticmethod
|
||||
def get_user_info():
|
||||
try:
|
||||
hydra = OAuth2Session(
|
||||
client_id=HYDRA_CLIENT_ID, token=session["oauth_token"]
|
||||
)
|
||||
user_info = hydra.get("{}/userinfo".format(HYDRA_PUBLIC_URL))
|
||||
|
||||
return user_info.json()
|
||||
except Exception as err:
|
||||
raise HydraError(str(err), 500)
|
46
backend/migrations/versions/27761560bbcb_.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 27761560bbcb
|
||||
Revises:
|
||||
Create Date: 2021-12-21 06:07:14.857940
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "27761560bbcb"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"app",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=True),
|
||||
sa.Column("slug", sa.String(length=64), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("slug"),
|
||||
)
|
||||
op.create_table(
|
||||
"app_role",
|
||||
sa.Column("user_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("app_id", sa.Integer(), nullable=False),
|
||||
sa.Column("role", sa.String(length=64), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["app_id"],
|
||||
["app.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("user_id", "app_id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("app_role")
|
||||
op.drop_table("app")
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,25 @@
|
|||
"""add-velero-as-app
|
||||
|
||||
Revision ID: 3fa0c38ea1ac
|
||||
Revises: e08df0bef76f
|
||||
Create Date: 2022-10-13 09:40:44.290319
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3fa0c38ea1ac'
|
||||
down_revision = 'e08df0bef76f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add monitoring app
|
||||
op.execute(f'INSERT IGNORE INTO app (`name`, `slug`) VALUES ("Velero","velero")')
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
|
@ -0,0 +1,48 @@
|
|||
"""convert role column to table
|
||||
|
||||
Revision ID: 5f462d2d9d25
|
||||
Revises: 27761560bbcb
|
||||
Create Date: 2022-04-13 15:00:27.182898
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "5f462d2d9d25"
|
||||
down_revision = "27761560bbcb"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
role_table = op.create_table(
|
||||
"role",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(length=64), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.add_column("app_role", sa.Column("role_id", sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, "app_role", "role", ["role_id"], ["id"])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
# Insert default role "admin" as ID 1
|
||||
op.execute(sa.insert(role_table).values(id=1,name="admin"))
|
||||
# Set role_id 1 to all current "admin" users
|
||||
op.execute("UPDATE app_role SET role_id = 1 WHERE role = 'admin'")
|
||||
|
||||
# Drop old column
|
||||
op.drop_column("app_role", "role")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column(
|
||||
"app_role", sa.Column("role", mysql.VARCHAR(length=64), nullable=True)
|
||||
)
|
||||
op.drop_constraint(None, "app_role", type_="foreignkey")
|
||||
op.drop_column("app_role", "role_id")
|
||||
op.drop_table("role")
|
||||
# ### end Alembic commands ###
|
76
backend/migrations/versions/b514cca2d47b_add_user_role.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
"""update apps and add 'user' and 'no access' role
|
||||
|
||||
Revision ID: b514cca2d47b
|
||||
Revises: 5f462d2d9d25
|
||||
Create Date: 2022-06-08 17:24:51.305129
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b514cca2d47b'
|
||||
down_revision = '5f462d2d9d25'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### end Alembic commands ###
|
||||
|
||||
# Check and update app table in DB
|
||||
apps = {
|
||||
"dashboard": "Dashboard",
|
||||
"wekan": "Wekan",
|
||||
"wordpress": "WordPress",
|
||||
"nextcloud": "Nextcloud",
|
||||
"zulip": "Zulip"
|
||||
}
|
||||
# app table
|
||||
app_table = sa.table('app', sa.column('id', sa.Integer), sa.column(
|
||||
'name', sa.String), sa.column('slug', sa.String))
|
||||
|
||||
existing_apps = op.get_bind().execute(app_table.select()).fetchall()
|
||||
existing_app_slugs = [app['slug'] for app in existing_apps]
|
||||
for app_slug in apps.keys():
|
||||
if app_slug in existing_app_slugs:
|
||||
op.execute(f'UPDATE app SET `name` = "{apps.get(app_slug)}" WHERE slug = "{app_slug}"')
|
||||
else:
|
||||
op.execute(f'INSERT INTO app (`name`, slug) VALUES ("{apps.get(app_slug)}","{app_slug}")')
|
||||
|
||||
# Fetch all apps including newly created
|
||||
existing_apps = op.get_bind().execute(app_table.select()).fetchall()
|
||||
# Insert role "user" as ID 2
|
||||
op.execute("INSERT INTO `role` (id, `name`) VALUES (2, 'user')")
|
||||
# Insert role "no access" as ID 3
|
||||
op.execute("INSERT INTO `role` (id, `name`) VALUES (3, 'no access')")
|
||||
# Set role_id 2 to all current "user" users which by have NULL role ID
|
||||
op.execute("UPDATE app_role SET role_id = 2 WHERE role_id IS NULL")
|
||||
|
||||
# Add 'no access' role for all users that don't have any roles for specific apps
|
||||
app_roles_table = sa.table('app_role', sa.column('user_id', sa.String), sa.column(
|
||||
'app_id', sa.Integer), sa.column('role_id', sa.Integer))
|
||||
|
||||
app_ids = [app['id'] for app in existing_apps]
|
||||
app_roles = op.get_bind().execute(app_roles_table.select()).fetchall()
|
||||
user_ids = set([app_role['user_id'] for app_role in app_roles])
|
||||
|
||||
for user_id in user_ids:
|
||||
existing_user_app_ids = [x['app_id'] for x in list(filter(lambda role: role['user_id'] == user_id, app_roles))]
|
||||
missing_user_app_ids = [x for x in app_ids if x not in existing_user_app_ids]
|
||||
|
||||
if len(missing_user_app_ids) > 0:
|
||||
values = [{'user_id': user_id, 'app_id': app_id, 'role_id': 3} for app_id in missing_user_app_ids]
|
||||
op.bulk_insert(app_roles_table, values)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Revert all users role_id to NULL where role is 'user'
|
||||
op.execute("UPDATE app_role SET role_id = NULL WHERE role_id = 2")
|
||||
# Delete role 'user' from roles
|
||||
op.execute("DELETE FROM `role` WHERE id = 2")
|
||||
|
||||
# Delete all user app roles where role is 'no access' with role_id 3
|
||||
op.execute("DELETE FROM app_role WHERE role_id = 3")
|
||||
# Delete role 'no access' from roles
|
||||
op.execute("DELETE FROM `role` WHERE id = 3")
|
|
@ -1,51 +0,0 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: d70b750a1297
|
||||
Revises:
|
||||
Create Date: 2022-10-25 11:32:27.303354
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd70b750a1297'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('app',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=64), nullable=True),
|
||||
sa.Column('slug', sa.String(length=64), nullable=True),
|
||||
sa.Column('external', sa.Boolean(), server_default='0', nullable=False),
|
||||
sa.Column('url', sa.String(length=128), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('slug')
|
||||
)
|
||||
op.create_table('role',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=64), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('app_role',
|
||||
sa.Column('user_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('app_id', sa.Integer(), nullable=False),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['app_id'], ['app.id'], ),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
|
||||
sa.PrimaryKeyConstraint('user_id', 'app_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('app_role')
|
||||
op.drop_table('role')
|
||||
op.drop_table('app')
|
||||
# ### end Alembic commands ###
|
33
backend/migrations/versions/e08df0bef76f_.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""Add fields for external apps
|
||||
|
||||
Revision ID: e08df0bef76f
|
||||
Revises: b514cca2d47b
|
||||
Create Date: 2022-09-23 16:38:06.557307
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e08df0bef76f'
|
||||
down_revision = 'b514cca2d47b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('app', sa.Column('external', sa.Boolean(), server_default='0', nullable=False))
|
||||
op.add_column('app', sa.Column('url', sa.String(length=128), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
# Add monitoring app
|
||||
op.execute(f'INSERT IGNORE INTO app (`name`, `slug`) VALUES ("Monitoring","monitoring")')
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('app', 'url')
|
||||
op.drop_column('app', 'external')
|
||||
# ### end Alembic commands ###
|
|
@ -1,8 +1,5 @@
|
|||
alembic==1.8.1
|
||||
attrs==21.4.0
|
||||
black==22.1.0
|
||||
cachelib==0.9.0
|
||||
cachetools==5.2.0
|
||||
certifi==2021.10.8
|
||||
cffi==1.15.0
|
||||
charset-normalizer==2.0.12
|
||||
|
@ -12,43 +9,32 @@ Flask==2.0.3
|
|||
Flask-Cors==3.0.10
|
||||
flask-expects-json==1.7.0
|
||||
Flask-JWT-Extended==4.3.1
|
||||
Flask-Migrate==3.1.0
|
||||
Flask-Session==0.4.0
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
google-auth==2.14.0
|
||||
greenlet==2.0.0.post0
|
||||
gunicorn==20.1.0
|
||||
hydra-client==0.4.0
|
||||
idna==3.3
|
||||
install==1.3.5
|
||||
itsdangerous==2.1.1
|
||||
jsonschema==4.4.0
|
||||
Jinja2==3.0.3
|
||||
jinja2-base64-filters==0.1.4
|
||||
jsonschema==4.4.0
|
||||
kubernetes==24.2.0
|
||||
Mako==1.2.3
|
||||
MarkupSafe==2.1.1
|
||||
mypy-extensions==0.4.3
|
||||
oauthlib==3.2.0
|
||||
ory-kratos-client==0.9.0a2
|
||||
pathspec==0.9.0
|
||||
platformdirs==2.5.1
|
||||
pyasn1==0.4.8
|
||||
pyasn1-modules==0.2.8
|
||||
pycparser==2.21
|
||||
PyJWT==2.3.0
|
||||
PyMySQL==1.0.2
|
||||
pyrsistent==0.18.1
|
||||
python-dateutil==2.8.2
|
||||
PyYAML==6.0
|
||||
regex==2022.3.15
|
||||
requests==2.27.1
|
||||
requests-oauthlib==1.3.1
|
||||
rsa==4.9
|
||||
six==1.16.0
|
||||
SQLAlchemy==1.4.42
|
||||
tomli==1.2.3
|
||||
typing_extensions==4.1.1
|
||||
typing-extensions==4.1.1
|
||||
urllib3==1.26.8
|
||||
websocket-client==1.4.2
|
||||
Werkzeug==2.0.3
|
||||
ory-kratos-client==0.9.0a2
|
||||
pymysql
|
||||
Flask-SQLAlchemy
|
||||
hydra-client
|
||||
Flask-Migrate
|
||||
|
|
4
backend/web/static/base.js
vendored
|
@ -261,6 +261,8 @@ function render_messages(data) {
|
|||
// value: If there is already a value known, show it
|
||||
// messages: error messages related to the field
|
||||
function getFormElement(type, name, value, messages) {
|
||||
console.log('Getting form element', type, name, value, messages);
|
||||
|
||||
if (value == undefined) {
|
||||
value = '';
|
||||
}
|
||||
|
@ -348,6 +350,7 @@ function getFormInput(type, name, value, label, placeHolder, help, messages) {
|
|||
if (typeof help == 'undefined' || help == null) {
|
||||
help = '';
|
||||
}
|
||||
console.log('Messages: ', messages);
|
||||
|
||||
// Id field for help element
|
||||
var nameHelp = name + 'Help';
|
||||
|
@ -359,6 +362,7 @@ function getFormInput(type, name, value, label, placeHolder, help, messages) {
|
|||
// messages get appended to help info
|
||||
if (messages.length) {
|
||||
for (message in messages) {
|
||||
console.log('adding message', messages[message]);
|
||||
help += messages[message]['text'];
|
||||
}
|
||||
}
|
||||
|
|
3
deployment/Dockerfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
FROM nginx:latest
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
COPY . /usr/share/nginx/html
|
|
@ -27,7 +27,6 @@
|
|||
"react-helmet": "^6.1.0",
|
||||
"react-hook-form": "^7.22.0",
|
||||
"react-hot-toast": "^2.0.0",
|
||||
"react-iframe": "^1.8.0",
|
||||
"react-markdown": "^7.0.1",
|
||||
"react-redux": "^7.2.4",
|
||||
"react-router": "6.2.1",
|
||||
|
|
Before Width: | Height: | Size: 5 KiB |
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 416 416" width="416" height="416" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<symbol id="a" viewBox="-207.491 -207.49 414.981 414.981">
|
||||
<path d="m198.491 0c0-109.624-88.866-198.49-198.491-198.49-109.623 0-198.49 88.866-198.49 198.49 0 109.623 88.867 198.491 198.49 198.491 109.625 0 198.491-88.868 198.491-198.491z" fill="url(#b)" stroke="#5d6aa5" stroke-width="18"/>
|
||||
<path d="m.543 190.26c50.3-.373 104.324-20.494 143.335-66.953-1.66-39.852-72.986-80.704-132.511-125.68-49.945-37.738-126.373-73.128-178.643-87.032-49.698 95.152-13.55 186.622 48.772 238.038 29.158 25.513 73.097 41.968 119.047 41.627z" fill="url(#c)"/>
|
||||
<path d="m107.395-49.37c0-16.763-5.9-31.045-17.694-42.839-11.801-11.801-26.076-17.694-42.839-17.694h-72.175c-16.764 0-31.046 5.894-42.84 17.694-11.801 11.794-17.694 26.076-17.694 42.839v181.137c12.856 0 23.827-6.287 32.923-18.859 9.094-12.572 13.642-27.706 13.642-45.4v-116.878c0-9.312 4.656-13.969 13.97-13.969h72.175c9.312 0 13.969 4.656 13.969 13.969v55.412c0 9-4.656 13.656-13.969 13.97h-13.97c-18.007.626-33.221 5.326-45.633 14.107-12.42 8.774-18.626 19.594-18.626 32.457h78.229c16.763 0 31.038-5.901 42.839-17.694 11.794-11.801 17.694-26.076 17.694-42.84v-55.412z" fill="#757575" />
|
||||
<path d="m103.971-51.741c0-16.763-5.9-31.045-17.694-42.839-11.801-11.801-26.076-17.694-42.839-17.694h-72.175c-16.763 0-31.045 5.894-42.839 17.694-11.801 11.794-17.694 26.076-17.694 42.839v181.136c12.856 0 23.828-6.287 32.922-18.859s13.642-27.706 13.642-45.4v-116.877c0-9.313 4.656-13.969 13.969-13.969h72.175c9.312 0 13.969 4.656 13.969 13.969v55.412c0 9-4.656 13.656-13.969 13.969h-13.97c-18.007.625-33.221 5.326-45.633 14.107-12.419 8.774-18.625 19.594-18.625 32.457h78.229c16.763 0 31.038-5.901 42.839-17.694 11.794-11.801 17.694-26.076 17.694-42.839v-55.412z" fill="#fff"/>
|
||||
</symbol>
|
||||
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-113.8594" x2="113.8594" y1="162.6079" y2="-162.6081">
|
||||
<stop offset="0" stop-color="#283274"/>
|
||||
<stop offset=".7088" stop-color="#283375"/>
|
||||
<stop offset=".967" stop-color="#273f88"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" gradientUnits="userSpaceOnUse" x1="-109.1797" x2="5.1006" y1="156.1684" y2="-7.0408">
|
||||
<stop offset="0" stop-color="#bfc8e6"/>
|
||||
<stop offset=".0772" stop-color="#b6bedd"/>
|
||||
<stop offset=".2039" stop-color="#9fa4c9"/>
|
||||
<stop offset=".3642" stop-color="#7f82ae"/>
|
||||
<stop offset=".5519" stop-color="#585c92"/>
|
||||
<stop offset=".7582" stop-color="#283375"/>
|
||||
</linearGradient>
|
||||
<use xlink:href="#a" width="414.981" height="414.981" transform="matrix(1 0 0 -1 208.5918 208.3076)" x="-207.491" y="-207.49"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,7 +0,0 @@
|
|||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 200C155.228 200 200 155.228 200 100C200 44.7715 155.228 0 100 0C44.7715 0 0 44.7715 0 100C0 155.228 44.7715 200 100 200Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M81.7169 46.5946C81.7169 42.5581 84.9959 39.2859 89.0408 39.2859C116.456 39.2859 138.681 61.4642 138.681 88.8225C138.681 92.859 135.401 96.1312 131.357 96.1312C127.312 96.1312 124.033 92.859 124.033 88.8225C124.033 69.5372 108.366 53.9033 89.0408 53.9033C84.9959 53.9033 81.7169 50.6311 81.7169 46.5946Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M153.39 81.5137C157.435 81.5137 160.714 84.7859 160.714 88.8224C160.714 116.181 138.49 138.359 111.075 138.359C107.03 138.359 103.751 135.087 103.751 131.05C103.751 127.014 107.03 123.742 111.075 123.742C130.4 123.742 146.066 108.108 146.066 88.8224C146.066 84.7859 149.345 81.5137 153.39 81.5137Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.398 153.405C118.398 157.442 115.119 160.714 111.074 160.714C83.6592 160.714 61.4347 138.536 61.4347 111.177C61.4347 107.141 64.7138 103.869 68.7587 103.869C72.8035 103.869 76.0826 107.141 76.0826 111.177C76.0826 130.463 91.7489 146.097 111.074 146.097C115.119 146.097 118.398 149.369 118.398 153.405Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.6097 118.486C42.5648 118.486 39.2858 115.214 39.2858 111.178C39.2858 83.8193 61.5102 61.6409 88.9255 61.6409C92.9704 61.6409 96.2494 64.9132 96.2494 68.9497C96.2494 72.9862 92.9704 76.2584 88.9255 76.2584C69.6 76.2584 53.9337 91.8922 53.9337 111.178C53.9337 115.214 50.6546 118.486 46.6097 118.486Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -1,129 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="hedgedoc.svg"
|
||||
id="svg49"
|
||||
version="1.1"
|
||||
width="414.10999"
|
||||
viewBox="0 0 414.10999 414.11"
|
||||
stroke-miterlimit="2"
|
||||
stroke-linejoin="round"
|
||||
height="414.10999"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd">
|
||||
<metadata
|
||||
id="metadata55">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs53">
|
||||
<inkscape:path-effect
|
||||
lpeversion="1"
|
||||
is_visible="true"
|
||||
id="path-effect882"
|
||||
effect="spiro" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
inkscape:current-layer="svg49"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:cy="207.05499"
|
||||
inkscape:cx="825"
|
||||
inkscape:zoom="0.81212121"
|
||||
showgrid="false"
|
||||
id="namedview51"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10"
|
||||
gridtolerance="10"
|
||||
objecttolerance="10"
|
||||
borderopacity="1"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff" />
|
||||
<linearGradient
|
||||
x2="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(200,-420,420,200,660,1340)"
|
||||
id="a">
|
||||
<stop
|
||||
id="stop2"
|
||||
stop-color="#fdd49a"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4"
|
||||
stop-color="#dca055"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<g
|
||||
id="g31"
|
||||
transform="matrix(1.0002373,0,0,1,-50.01087,-265.89)"
|
||||
fill-rule="nonzero">
|
||||
<g
|
||||
id="g29"
|
||||
transform="matrix(0.27874,0,0,0.27874,10.207,226.1)">
|
||||
<path
|
||||
id="path23"
|
||||
fill="#b51f08"
|
||||
d="m 1553.7,961.08 75.25,-75.258 -75.25,-75.246 56.61,-90.096 -90.1,-56.616 35.15,-100.45 -100.44,-35.142 11.91,-105.75 -105.74,-11.912 -11.92,-105.75 -105.74,11.912 -35.15,-100.44 -100.43,35.145 -56.63,-90.1 -90.107,56.617 -75.242,-75.246 -75.242,75.25 -90.104,-56.612 -56.612,90.096 -100.44,-35.15 -35.142,100.44 -105.75,-11.916 -11.912,105.74 -105.74,11.913 11.917,105.74 -100.45,35.15 35.145,100.44 -90.1,56.612 56.621,90.117 -75.258,75.25 75.25,75.258 -56.613,90.107 90.1,56.61 -35.145,100.44 100.45,35.15 -11.913,105.73 105.74,11.92 11.912,105.74 105.75,-11.91 35.142,100.43 100.44,-35.13 56.617,90.1 90.096,-56.62 75.25,74.55 75.25,-74.53 90.099,56.61 56.61,-90.11 100.45,35.15 35.14,-100.44 105.75,11.91 11.91,-105.75 105.75,-11.91 -11.92,-105.74 100.45,-35.15 -35.15,-100.45 90.1,-56.61 z" />
|
||||
<path
|
||||
id="path25"
|
||||
fill="#fdd49a"
|
||||
d="m 1401.3,1004.8 c 0,-145.44 -117.9,-263.34 -263.34,-263.34 -72.24,0 -137.68,29.112 -185.25,76.225 l -0.033,-0.034 -67.096,67.1 -54.862,-54.862 c -48.267,-55.067 -119.07,-89.879 -198.04,-89.879 -145.44,0 -263.34,117.9 -263.34,263.34 0,76 32.23,144.43 83.721,192.49 l 432.78,432.55 423.22,-423.49 c 52.88,-49.77 92.25,-120 92.25,-200.1" />
|
||||
<path
|
||||
style="fill:url(#a)"
|
||||
id="path27"
|
||||
fill="url(#a)"
|
||||
d="m 885.58,884.73 -54.862,-54.862 c -48.267,-55.067 -119.07,-89.879 -198.04,-89.879 -145.44,0 -263.34,117.9 -263.34,263.34 0,76 32.23,144.43 83.721,192.49 l 432.78,432.55" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g47"
|
||||
stroke-width="0.27874">
|
||||
<path
|
||||
id="path33"
|
||||
fill="none"
|
||||
d="M 207.13,414.11 207.0592,206.82" />
|
||||
<g
|
||||
id="g45"
|
||||
fill-rule="nonzero">
|
||||
<path
|
||||
id="path35"
|
||||
fill="#010007"
|
||||
d="m 228.08,393.26 c -5.3739,-5.3937 -12.799,-8.7275 -20.996,-8.7275 -8.1998,0 -15.622,3.3338 -20.998,8.7275 0.0859,11.523 9.4517,20.839 20.996,20.839 11.544,0 20.911,-9.3184 20.998,-20.839" />
|
||||
<path
|
||||
id="path37"
|
||||
fill="#010007"
|
||||
d="m 182.56,266.33 c 0,8.8641 -7.1824,16.047 -16.048,16.047 -8.8604,0 -16.044,-7.1832 -16.044,-16.047 0,-8.8641 7.1835,-16.047 16.044,-16.047 8.8652,0 16.048,7.1832 16.048,16.047" />
|
||||
<path
|
||||
id="path39"
|
||||
fill="#fffffa"
|
||||
d="m 177.06,263.93 c 0,2.4585 -1.9919,4.4432 -4.446,4.4432 -2.4552,0 -4.4482,-1.9847 -4.4482,-4.4432 0,-2.4557 1.993,-4.4488 4.4482,-4.4488 2.4541,0 4.446,1.993 4.446,4.4488" />
|
||||
<path
|
||||
id="path41"
|
||||
fill="#010007"
|
||||
d="m 263.94,266.33 c 0,8.8641 -7.1832,16.047 -16.047,16.047 -8.8613,0 -16.046,-7.1832 -16.046,-16.047 0,-8.8641 7.1852,-16.047 16.046,-16.047 8.8641,0 16.047,7.1832 16.047,16.047" />
|
||||
<path
|
||||
id="path43"
|
||||
fill="#fffffa"
|
||||
d="m 258.44,263.93 c 0,2.4585 -1.993,4.4432 -4.446,4.4432 -2.4557,0 -4.4488,-1.9847 -4.4488,-4.4432 0,-2.4557 1.993,-4.4488 4.4488,-4.4488 2.453,0 4.446,1.993 4.446,4.4488" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5 KiB |
7
public/assets/logo-small.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="25" height="44" viewBox="0 0 25 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 35.1171C0 39.0948 4.18188 41.6853 7.74332 39.9138L22.8687 32.39C24.0424 31.8062 24.7844 30.6083 24.7844 29.2975V20.9534L1.64288 32.4649C0.636359 32.9656 0 33.9929 0 35.1171Z" fill="#2D535A"/>
|
||||
<path d="M0 22.8091C0 27.6308 5.06914 30.7709 9.38621 28.6235L24.7844 20.9641C24.7844 16.1423 19.7151 13.0021 15.398 15.1496L0 22.8091Z" fill="#54C6CC"/>
|
||||
<path d="M2.2161 21.7068C0.858395 22.3821 -0.103566 23.8187 0.458285 25.2271C1.79955 28.5895 5.84578 30.3846 9.38621 28.6235L24.7844 20.9641C24.7844 16.1423 19.7151 13.0021 15.398 15.1496L2.2161 21.7068Z" fill="#2D535A"/>
|
||||
<path d="M2.2161 21.7068C0.858395 22.3821 -0.103566 23.8187 0.458285 25.2271C1.79955 28.5895 5.84578 30.3846 9.38621 28.6235L22.5683 22.0664C23.926 21.3911 24.888 19.9545 24.3261 18.546C22.9848 15.1836 18.9385 13.3884 15.398 15.1496L2.2161 21.7068Z" fill="#1E8290"/>
|
||||
<path d="M0 22.8121L23.3077 11.2182C24.2124 10.7682 24.7844 9.8448 24.7844 8.83432C24.7844 4.77111 20.5126 2.12495 16.8747 3.93462L2.25625 11.2064C0.873945 11.894 0 13.3048 0 14.8487V22.8121Z" fill="#54C6CC"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
9
public/assets/logo.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg width="151" height="44" viewBox="0 0 151 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="151" height="44" fill="transparent"/>
|
||||
<path d="M40.8246 11.7373C37.2206 11.7373 34.89 13.5633 34.89 16.6388C34.89 19.7142 36.8842 20.8915 40.4162 21.6604L40.9688 21.7805C43.1312 22.2611 44.2604 22.7416 44.2604 24.1351C44.2604 25.4806 43.1792 26.4417 41.0168 26.4417C38.8544 26.4417 37.5329 25.4326 37.5329 23.4143V23.1741H34.4094V23.4143C34.4094 27.0664 37.1245 29.2288 41.0168 29.2288C44.9092 29.2288 47.3839 27.1145 47.3839 24.039C47.3839 20.9636 45.1014 19.7142 41.5214 18.9454L40.9688 18.8252C38.9025 18.3687 38.0135 17.7921 38.0135 16.5427C38.0135 15.2933 38.9025 14.5244 40.8246 14.5244C42.7468 14.5244 43.9241 15.2933 43.9241 17.2154V17.5998H47.0476V17.2154C47.0476 13.5633 44.4286 11.7373 40.8246 11.7373ZM47.5746 19.4259H50.554V26.2015C50.554 27.8353 51.6112 28.8925 53.1969 28.8925H56.5607V26.4417H54.2541C53.8216 26.4417 53.5814 26.2015 53.5814 25.7209V19.4259H56.849V16.9752H53.5814V13.275H50.554V16.9752H47.5746V19.4259ZM69.9725 28.8925V16.9752H66.9932V18.5849H66.7529C66.0321 17.5518 64.8788 16.6388 62.8125 16.6388C59.9774 16.6388 57.4305 19.0415 57.4305 22.9338C57.4305 26.8261 59.9774 29.2288 62.8125 29.2288C64.8788 29.2288 66.0321 28.3158 66.7529 27.2827H66.9932V28.8925H69.9725ZM63.7256 26.6339C61.8515 26.6339 60.4579 25.2884 60.4579 22.9338C60.4579 20.5792 61.8515 19.2337 63.7256 19.2337C65.5996 19.2337 66.9932 20.5792 66.9932 22.9338C66.9932 25.2884 65.5996 26.6339 63.7256 26.6339ZM71.4505 22.9338C71.4505 26.6339 74.1896 29.2288 77.6495 29.2288C80.9892 29.2288 82.9354 27.2827 83.6081 24.6637L80.6768 23.967C80.4126 25.5047 79.5236 26.5859 77.6975 26.5859C75.8715 26.5859 74.4779 25.2404 74.4779 22.9338C74.4779 20.6272 75.8715 19.2817 77.6975 19.2817C79.5236 19.2817 80.4126 20.435 80.5807 21.8766L83.512 21.2519C82.9834 18.5849 80.9652 16.6388 77.6495 16.6388C74.1896 16.6388 71.4505 19.2337 71.4505 22.9338ZM97.4406 16.9752H92.9716L88.0221 21.348V12.0737H84.9947V28.8925H88.0221V25.0241L89.68 23.6066L93.6204 28.8925H97.3445L91.8424 21.7565L97.4406 16.9752ZM98.2594 20.3149C98.2594 22.6695 100.23 23.5345 102.728 24.015L103.353 24.1351C104.843 24.4235 105.516 24.7839 105.516 25.5527C105.516 26.3216 104.843 26.9223 103.449 26.9223C102.056 26.9223 100.926 26.3456 100.614 24.6157L97.8269 25.3365C98.2354 27.8353 100.326 29.2288 103.449 29.2288C106.477 29.2288 108.447 27.8112 108.447 25.3125C108.447 22.8137 106.429 22.1409 103.738 21.6123L103.113 21.4922C101.863 21.2519 101.191 20.9156 101.191 20.1227C101.191 19.4019 101.815 18.9454 102.969 18.9454C104.122 18.9454 104.939 19.4259 105.227 20.7233L107.966 19.8824C107.39 17.9603 105.636 16.6388 102.969 16.6388C100.134 16.6388 98.2594 17.9603 98.2594 20.3149ZM109.985 33.6978H113.012V27.3547H113.252C113.925 28.3158 115.078 29.2288 117.145 29.2288C119.98 29.2288 122.527 26.8261 122.527 22.9338C122.527 19.0415 119.98 16.6388 117.145 16.6388C115.078 16.6388 113.925 17.5518 113.204 18.5849H112.964V16.9752H109.985V33.6978ZM116.231 26.6339C114.357 26.6339 112.964 25.2884 112.964 22.9338C112.964 20.5792 114.357 19.2337 116.231 19.2337C118.106 19.2337 119.499 20.5792 119.499 22.9338C119.499 25.2884 118.106 26.6339 116.231 26.6339ZM123.38 13.5153C123.38 14.7407 124.317 15.5816 125.518 15.5816C126.72 15.5816 127.657 14.7407 127.657 13.5153C127.657 12.2899 126.72 11.449 125.518 11.449C124.317 11.449 123.38 12.2899 123.38 13.5153ZM127.032 16.9752H124.005V28.8925H127.032V16.9752ZM129.249 16.9752V28.8925H132.277V22.7416C132.277 20.5311 133.358 19.2337 135.208 19.2337C136.842 19.2337 137.755 20.1227 137.755 21.9247V28.8925H140.782V21.7805C140.782 18.8252 138.932 16.7829 136.145 16.7829C133.958 16.7829 132.949 17.744 132.469 18.7531H132.228V16.9752H129.249Z" fill="#2D535A"/>
|
||||
<path d="M0 35.1171C0 39.0948 4.18188 41.6853 7.74332 39.9138L22.8687 32.39C24.0424 31.8062 24.7844 30.6083 24.7844 29.2975V20.9534L1.64288 32.4649C0.636359 32.9656 0 33.9929 0 35.1171Z" fill="#2D535A"/>
|
||||
<path d="M0 22.8091C0 27.6308 5.06914 30.7709 9.38621 28.6235L24.7844 20.9641C24.7844 16.1423 19.7151 13.0021 15.398 15.1496L0 22.8091Z" fill="#54C6CC"/>
|
||||
<path d="M2.2161 21.7068C0.858395 22.3821 -0.103566 23.8187 0.458285 25.2271C1.79955 28.5895 5.84578 30.3846 9.38621 28.6235L24.7844 20.9641C24.7844 16.1423 19.7151 13.0021 15.398 15.1496L2.2161 21.7068Z" fill="#2D535A"/>
|
||||
<path d="M2.2161 21.7068C0.858395 22.3821 -0.103566 23.8187 0.458285 25.2271C1.79955 28.5895 5.84578 30.3846 9.38621 28.6235L22.5683 22.0664C23.926 21.3911 24.888 19.9545 24.3261 18.546C22.9848 15.1836 18.9385 13.3884 15.398 15.1496L2.2161 21.7068Z" fill="#1E8290"/>
|
||||
<path d="M0 22.8121L23.3077 11.2182C24.2124 10.7682 24.7844 9.8448 24.7844 8.83432C24.7844 4.77111 20.5126 2.12495 16.8747 3.93462L2.25625 11.2064C0.873945 11.894 0 13.3048 0 14.8487V22.8121Z" fill="#54C6CC"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -1,119 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="67.732002mm"
|
||||
height="68.024376mm"
|
||||
viewBox="0 0 67.732002 68.024376"
|
||||
version="1.1"
|
||||
id="svg567"
|
||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
||||
sodipodi:docname="vikunja.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview569"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.77058782"
|
||||
inkscape:cx="365.95439"
|
||||
inkscape:cy="86.946611"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs564" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-8.3663759,-43.92885)">
|
||||
<g
|
||||
transform="matrix(0.0352769,0,0,-0.0352769,-11.620669,144.93494)"
|
||||
id="g144">
|
||||
<path
|
||||
d="m 2268.2,2512.3 c -15.884,19.573 -32.512,38.571 -49.949,56.876 -180.6,189.62 -426.24,294.06 -691.67,294.06 -256.43,0 -497.51,-99.862 -678.83,-281.18 -22.365,-22.368 -43.484,-45.656 -63.335,-69.76 -141.09,-171.28 -217.84,-384.27 -217.84,-609.07 0,-256.42 99.861,-497.5 281.18,-678.82 181.32,-181.32 422.39,-281.17 678.83,-281.17 256.43,0 497.5,99.858 678.83,281.17 181.32,181.33 281.17,422.4 281.17,678.82 0,219.21 -78.86,437.22 -218.37,609.07"
|
||||
fill="#196aff"
|
||||
id="path96"
|
||||
sodipodi:nodetypes="ccsccscscsc" />
|
||||
<path
|
||||
d="m 1823.7,1647.4 c 35.688,104.68 94.631,136.72 101.96,298.25 2.5753,56.765 -14.661,237.07 -14.661,237.07 0,0 28.013,72.294 -25.823,152.83 -83.476,124.87 -255.4,133.36 -345.69,133.36 -90.29,0 -260.21,-8.4898 -343.68,-133.36 -53.84,-80.533 -25.826,-152.83 -25.826,-152.83 0,0 -9.5356,-92.732 -16.752,-174.52 -1.6998,-19.193 0.1575,-95.115 2.4544,-113.46 4.1285,-32.869 14.481,-65.163 25.808,-96.599 14.342,-39.804 36.82,-80.194 53.983,-121.03 51.799,-123.29 8.4513,-276 11.1,-408.96 2.2383,-94.374 -20.013,-190.11 -28.731,-282.4 98.129,-33.396 202.09,-50.813 308.72,-50.813 119.83,0 236.31,21.937 344.9,63.9 -20.394,115.55 -44.092,225.15 -47.748,266.96 -10.646,126.41 -41.304,260.49 -0.01,381.6"
|
||||
fill="#ffffff"
|
||||
id="path98" />
|
||||
<path
|
||||
d="m 1162.9,2383.9 c 1.1503,-18.73 2.9966,-37.93 8.2718,-56.159 1.6558,-5.6818 4.1029,-19.683 11.415,-21.764 9.0923,-2.59 25.911,8.2571 32.288,12.957 12.356,9.096 23.925,18.459 36.285,27.651 7.8835,5.8613 16.492,10.47 24.211,16.496 8.4,6.5463 14.719,14.474 21.709,22.178 8.4843,9.3597 14.847,19.049 21.372,29.497 5.103,8.1545 37.069,13.463 42.187,21.031 5.5866,8.2755 0.9158,18.511 0.9158,28.698 0,74.164 4.4546,147.57 6.1874,220.29 1.7877,49.887 21.379,109.13 -53.44,85.744 -160.31,-49.887 -158.53,-271.26 -151.4,-386.62"
|
||||
fill="#ffffff"
|
||||
id="path100" />
|
||||
<path
|
||||
d="m 1869.1,2279.7 c -1.6118,1.7511 -4.1945,3.2384 -6.2606,4.7696 -8.9788,6.649 -17.445,13.606 -25.156,21.482 -9.411,9.6125 -19.192,18.958 -28.164,28.929 -7.8908,8.7626 -17.313,16.602 -25.006,25.574 -5.1286,5.9602 -10.081,12.305 -14.609,18.562 -2.3115,3.1761 -3.5204,6.9969 -5.3374,10.396 -2.7072,5.0114 -39.882,10.118 -36.19,15.005 6.3009,8.3304 20.328,15.397 23.702,24.94 17.243,48.667 24.771,244.53 26.852,294.51 5.3521,127.83 117.57,-6.2386 137.17,-57.682 57.008,-149.66 23.145,-258.78 -46.315,-386.62"
|
||||
fill="#ffffff"
|
||||
id="path102" />
|
||||
<path
|
||||
d="m 1716.5,1787.9 c -0.081,73.805 -9.2535,103.66 -50.407,139.75 -25.742,22.566 -55.836,31.175 -103.74,29.992 -47.902,1.1833 -82.424,-13.378 -107.32,-39.223 -37.549,-38.992 -47.436,-62.053 -47.542,-135.86 -0.059,-39.893 42.956,-128.16 55.756,-148.54 21.244,-35.93 60.562,-48.85 99.104,-46.154 38.545,-2.6962 77.867,10.224 99.096,46.154 12.811,20.372 55.107,106.95 55.052,153.87"
|
||||
fill="#f1e6d3"
|
||||
id="path104" />
|
||||
<path
|
||||
d="m 1226.6,2316.1 c -9.6162,86.088 -38.582,239.98 61.489,331.24 11.038,10.074 14.001,-24.2 15.848,-38.066 2.5386,-19.038 -0.051,-73.416 0.337,-92.583 0.7143,-36.087 8.2754,-54.964 4.7513,-71.46 -9.6272,-45.092 -17.353,-42.183 -26.559,-69.603 -18.258,-54.378 -53.235,-83.066 -55.866,-59.529"
|
||||
fill="#f1d7d4"
|
||||
id="path106" />
|
||||
<path
|
||||
d="m 1851.7,2333.1 c 10.243,-18.331 36.996,80.256 45.381,123.07 7.8945,40.307 17.925,93.869 4.059,133.91 -7.4365,21.482 -53.078,84.568 -58.463,62.892 -2.0991,-8.4183 -3.1834,-71.098 -8.345,-101.09 -6.3889,-37.109 -17.91,-73.805 -17.98,-111.54 -0.1502,-84.568 25.336,-88.055 35.347,-107.24"
|
||||
fill="#f1d7d4"
|
||||
id="path108" />
|
||||
<path
|
||||
d="m 1522,1319.7 c -2.1687,-6.583 -18.595,-11.404 -24.764,-13.393 -14.91,-4.8172 -28.145,6.9383 -36.476,16.844 -11.583,13.73 -11.276,35.629 -16.126,51.642 -2.9343,9.6822 -19.533,10.997 -24.518,2.0405 -16.628,-29.9 -81.161,26.365 -66.163,45.095 9.8872,12.353 -13.781,23.262 -23.54,11.078 -28.948,-36.16 48.92,-103.5 93.579,-85.245 1.9892,-9.0338 4.0736,-18.056 7.9091,-26.636 7.5245,-16.851 23.98,-27.739 41.011,-36.89 23.196,-12.437 68.258,9.4074 75.142,30.255 4.7916,14.503 -21.28,19.635 -26.054,5.2093"
|
||||
fill="#faeee0"
|
||||
id="path110" />
|
||||
<path
|
||||
d="m 1727.6,1538.2 c 2.3958,-10.045 2.7731,-44.073 -15.935,-25.456 -7.5904,7.5537 -22.665,3.0954 -23.28,-7.008 -1.4471,-23.387 -24.9,-23.903 -45.136,-16.833 -16.056,5.6012 -24.533,-16.588 -8.6051,-22.145 29.713,-10.367 61.961,-4.6231 74.776,17.837 10.081,-4.744 21.485,-6.0372 30.629,2.5679 15.968,15.042 18.375,36.226 13.73,55.708 -3.5278,14.763 -29.698,10.096 -26.178,-4.6707"
|
||||
fill="#faeee0"
|
||||
id="path112" />
|
||||
<path
|
||||
d="m 1775,1049.2 c -7.0043,-14.316 -19.855,-13.422 -33.637,-7.4109 -10.107,4.3996 -22.606,-2.8757 -19.558,-13.107 6.1361,-20.522 -19.786,-26.515 -37.314,-19.196 -15.426,6.4438 -28.834,-13.796 -13.243,-20.313 31.582,-13.181 71.728,-1.6485 77.552,26.134 20.313,-3.22 39.71,2.4837 49.396,22.31 6.7038,13.682 -16.404,25.471 -23.196,11.583"
|
||||
fill="#faeee0"
|
||||
id="path114" />
|
||||
<path
|
||||
d="m 1569.8,2153.3 c -3.253,-20.181 -41.095,3.3189 -50.492,9.6894 -8.2717,5.5976 -18.972,2.198 -19.983,-7.2387 -1.3518,-12.715 -18.481,-9.0227 -26.31,-7.4255 -14.807,3.0112 -27.365,12.239 -27.717,26.105 -0.3443,13.474 8.2059,27.621 12.664,40.271 2.8537,8.0776 -8.7223,16.965 -17.185,11.561 -15.239,-9.7298 -88.714,-18.481 -59.452,13.547 9.3231,10.202 -7.1325,24.797 -16.554,14.481 -13.518,-14.8 -22.661,-48.7 6.583,-55.876 15.437,-3.7842 37.776,-3.5937 56.814,0.7473 -7.9494,-25.478 -9.6162,-48.799 23.156,-65.17 22.145,-11.067 52.558,-10.99 65.445,6.1067 27.116,-14.584 69.709,-28.731 75.6,7.7626 2.088,12.906 -20.463,18.478 -22.57,5.44"
|
||||
fill="#faeee0"
|
||||
id="path116" />
|
||||
<path
|
||||
d="m 1443,1685.6 c 39.366,-3.3299 78.746,-12.272 118.48,-10.836 25.398,0.9232 51.693,4.4803 76.75,8.1949 18.203,2.6962 40.52,5.8796 52.697,19.375 1.143,-45.018 -92.513,-59.108 -128.84,-60.023 -42.143,-1.066 -89.509,17.221 -119.09,43.289"
|
||||
fill="#494949"
|
||||
id="path118" />
|
||||
<path
|
||||
d="m 1549.4,1779.5 c -4.5023,-28.896 -5.4327,-58.199 -2.7219,-87.267 0.718,-7.5977 -1.3078,-25.709 8.7956,-29.523 8.1912,-3.0955 18.317,2.7219 19.654,10.111 2.2603,12.455 -3.0405,28.164 -3.4911,40.919 -0.5312,14.939 0,29.9 1.5789,44.769 0.9451,8.814 5.8906,20.705 -4.1982,26.914 -7.4036,4.5535 -18.258,2.872 -19.617,-5.9236"
|
||||
fill="#494949"
|
||||
id="path120" />
|
||||
<path
|
||||
d="m 1626,1849.7 c -23.661,-1.0221 -45.63,-14.261 -63.397,-27.024 -16.082,10.675 -40.432,20.485 -60.694,14.774 -12.078,-3.3922 -1.11,-7.0959 3.927,-10.316 9.3085,-6.191 16.895,-14.148 23.742,-22.368 10.389,-12.598 19.624,-25.826 30.71,-37.978 7.6747,5.5243 15.001,11.104 21.672,17.558 3.0955,2.9929 28.435,36.926 32.325,42.667 2.4838,3.656 4.9895,7.4329 7.8359,10.873 2.8903,3.4802 11.07,9.0154 3.8794,11.814"
|
||||
fill="#494949"
|
||||
id="path122" />
|
||||
<path
|
||||
d="m 1326.5,2010 c 11.682,30.303 24.24,68.387 56.265,62.328 24.244,-5.1946 56.697,-86.117 35.926,-78.124 -11.254,4.3264 -20.207,41.106 -41.333,45.876 -13.422,3.0332 -32.032,-43.502 -49.993,-48.26 -8.7077,-2.3006 -4.3264,10.389 -0.8646,18.181"
|
||||
fill="#2c3844"
|
||||
id="path124" />
|
||||
<path
|
||||
d="m 1670.6,2010 c 11.686,30.303 24.244,68.387 56.268,62.328 24.24,-5.1946 56.693,-86.117 35.922,-78.124 -11.254,4.3264 -20.203,41.106 -41.333,45.876 -13.422,3.0332 -32.032,-43.502 -49.99,-48.26 -8.7077,-2.3006 -4.33,10.389 -0.8682,18.181"
|
||||
fill="#2c3844"
|
||||
id="path126" />
|
||||
<g
|
||||
transform="scale(1,-1)"
|
||||
fill="#252626"
|
||||
stroke-width="29.085"
|
||||
aria-label="Vikunja"
|
||||
id="g142" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 9 KiB |
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="126px" height="108px" viewBox="0 0 42 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>logo</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="logo" sketch:type="MSArtboardGroup">
|
||||
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 0.000000)" id="Shape">
|
||||
<path d="M27.3375,12.6 L36.72,9.72 L31.1625,13.2525 L27.3375,12.6 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M33.0525,19.62 L31.1625,13.2525 L36.72,9.72 L35.055,15.435 L33.0525,19.62 Z" fill="#E84F83" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M39.465,7.9875 L38.43,9.72 L35.055,15.435 L36.72,9.72 L39.465,7.9875 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M39.8025,9.1125 L37.1925,11.79 L38.43,9.72 L39.8025,9.1125 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M27.9,10.8225 L35.5725,10.0575 L30.24,11.7 L27.9,10.8225 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M28.1925,15.165 L31.1625,13.2525 L33.0525,19.62 L32.0625,21.645 L28.1925,15.165 Z" fill="#CA2317" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M23.76,22.725 L22.3425,5.4 L32.0625,21.645 L23.76,22.725 Z" fill="#B7DFF2" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M19.7325,27.1575 L23.76,22.725 L32.0625,21.645 L19.7325,27.1575 Z" fill="#E54011" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M0.1575,35.865 L19.7325,27.1575 L23.76,22.725 L17.37,22.0725 L0.1575,35.865 Z" fill="#FFCE33" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M0.9,28.755 L10.9575,27.225 L14.085,24.705 L12.555,24.03 L0.9,28.755 Z" fill="#D6B12D" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M4.5225,20.5425 L14.085,24.705 L17.37,22.0725 L4.5225,20.5425 Z" fill="#FFDE85" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M21.6225,11.6775 L20.4075,11.88 L17.37,22.0725 L20.655,20.0025 L21.6225,11.6775 Z" fill="#009EC6" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M23.4,18.2475 L20.655,20.0025 L22.3425,5.4 L23.4,18.2475 Z" fill="#5EAFCE" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M13.0275,13.05 L21.6225,11.6775 L22.005,8.28 L13.0275,13.05 Z" fill="#045972" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M12.105,5.085 L19.575,9.585 L22.005,8.28 L22.0725,7.8075 L12.105,5.085 Z" fill="#5A8591" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M13.5675,0.18 L20.3625,7.335 L22.0725,7.8075 L22.3425,5.4 L13.5675,0.18 Z" fill="#009EC6" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M17.37,22.0725 L23.4,18.2475 L23.76,22.725 L17.37,22.0725 Z" fill="#F39804" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.1 KiB |
4
public/env.js
vendored
|
@ -1,4 +0,0 @@
|
|||
window.env = {
|
||||
REACT_APP_API_URL: 'http://localhost:5000/api/v1',
|
||||
REACT_APP_SSO_LOGOUT_URL: 'https://login.example.org/if/flow/default-invalidation-flow/'
|
||||
};
|
|
@ -1,13 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/assets/lit_logos/favicon_lit_transp.ico" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/assets/lit_logos/lit_transp_192x192.png" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
|
@ -22,11 +24,9 @@
|
|||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<script src='%PUBLIC_URL%/env.js'></script>
|
||||
<title>Dashboard</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
|
@ -39,6 +39,5 @@
|
|||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -3,17 +3,17 @@
|
|||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/lit_logos/favicon_lit_transp.ico",
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "assets/lit_logos/lit_transp_192x192.png",
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "assets/lit_logos/lit_transp_512x512.png",
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Verwalte User und Gruppen
|
|
@ -1 +0,0 @@
|
|||
Ticketsystem
|
30
src/App.tsx
|
@ -1,42 +1,31 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from 'src/services/auth';
|
||||
import { Dashboard } from './modules';
|
||||
import { Dashboard, Users, Login, Apps, AppSingle } from './modules';
|
||||
import { Layout } from './components';
|
||||
import { AppIframe } from './modules/dashboard/AppIframe';
|
||||
import { LoginCallback } from './modules/login/LoginCallback';
|
||||
import { useApps } from './services/apps';
|
||||
import { Login } from './modules/login';
|
||||
import { Users } from './modules/users/Users';
|
||||
import { AppSingle } from './modules/apps/AppSingle';
|
||||
import { Apps } from './modules/apps/Apps';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function App() {
|
||||
const { authToken, currentUser, isAdmin } = useAuth();
|
||||
|
||||
const redirectToLogin = !authToken || !currentUser?.app_roles;
|
||||
|
||||
const ProtectedRoute = () => {
|
||||
return isAdmin ? <Outlet /> : <Navigate to="/dashboard" />;
|
||||
};
|
||||
|
||||
const { apps, loadApps } = useApps();
|
||||
|
||||
useEffect(() => {
|
||||
loadApps();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Dashboard</title>
|
||||
<title>Stackspin</title>
|
||||
<meta name="description" content="Stackspin" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon_lit_transp.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/lit_logos/lit_transp_32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/lit_logos/lit_transp_16x16" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
@ -53,9 +42,6 @@ function App() {
|
|||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
{apps.map((app) => (
|
||||
<Route key={app.name} path={app.slug} element={<AppIframe app={app} />} />
|
||||
))}
|
||||
<Route path="/users" element={<ProtectedRoute />}>
|
||||
<Route index element={<Users />} />
|
||||
</Route>
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
import React, { Fragment, useMemo } from 'react';
|
||||
import { Disclosure, Menu, Transition } from '@headlessui/react';
|
||||
import { MenuIcon, XIcon } from '@heroicons/react/outline';
|
||||
import { useAuth } from 'src/services/auth';
|
||||
import Gravatar from 'react-gravatar';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import clsx from 'clsx';
|
||||
import { useApps } from 'src/services/apps';
|
||||
import { UTILITY_APPS } from 'src/modules/dashboard/consts';
|
||||
|
||||
const navigation = [
|
||||
{ name: '', to: '/users', requiresAdmin: true },
|
||||
// { name: 'Apps', to: '/apps', requiresAdmin: true },
|
||||
];
|
||||
|
||||
function classNames(...classes: any[]) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
function filterNavigationByDashboardRole(isAdmin: boolean) {
|
||||
if (isAdmin) {
|
||||
return navigation;
|
||||
}
|
||||
|
||||
return navigation.filter((item) => !item.requiresAdmin);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface HeaderProps {}
|
||||
|
||||
const HeaderLIT: React.FC<HeaderProps> = () => {
|
||||
const { logOut, currentUser, isAdmin } = useAuth();
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const { apps } = useApps();
|
||||
const navigationItems = filterNavigationByDashboardRole(isAdmin);
|
||||
|
||||
const signOutUrl = useMemo(() => {
|
||||
// @ts-ignore
|
||||
return window.env.REACT_APP_SSO_LOGOUT_URL;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Disclosure as="nav" className="bg-white shadow relative z-10">
|
||||
{({ open }) => (
|
||||
<div className="relative">
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
|
||||
<div className="relative flex justify-between h-10">
|
||||
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
|
||||
{/* Mobile menu button */}
|
||||
<Disclosure.Button className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500">
|
||||
<span className="sr-only">Open main menu</span>
|
||||
{open ? (
|
||||
<XIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
) : (
|
||||
<MenuIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
)}
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<Link to="/" className="flex-shrink-0 flex items-center">
|
||||
<img className="block lg:hidden" src="/assets/lit_logos/lit_transp_title_52.png" alt="Local-IT" />
|
||||
<img className="hidden lg:block" src="/assets/lit_logos/lit_transp_title_52.png" alt="Local-IT" />
|
||||
</Link>
|
||||
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
{/* Current: "border-primary-500 text-gray-900", Default: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700" */}
|
||||
{apps
|
||||
.filter((app) => UTILITY_APPS.indexOf(app.slug) === -1)
|
||||
.map((app) => (
|
||||
<Link
|
||||
key={app.name}
|
||||
to={app.slug}
|
||||
className={clsx(
|
||||
'border-primary-50 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium litbutton',
|
||||
{
|
||||
'border-primary-500 litbutton-active hover:border-gray-300 inline-flex items-center px-1 pt-1 text-sm font-medium':
|
||||
pathname.includes(app.slug),
|
||||
},
|
||||
)}
|
||||
>
|
||||
{app.name}
|
||||
</Link>
|
||||
))}
|
||||
{/* {navigationItems.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.to}
|
||||
className={clsx(
|
||||
'border-primary-50 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium litbutton',
|
||||
{
|
||||
'border-primary-500 litbutton-active hover:border-gray-300 inline-flex items-center px-1 pt-1 text-sm font-medium':
|
||||
pathname.includes(item.to),
|
||||
},
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))} */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
|
||||
{/* Profile dropdown */}
|
||||
<Menu as="div" className="ml-3 relative">
|
||||
<div>
|
||||
<Menu.Button className="bg-white rounded-full flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<span className="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500 overflow-hidden">
|
||||
<Gravatar email={currentUser?.email || undefined} size={32} rating="pg" protocol="https://" />
|
||||
</span>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
onClick={() => logOut()}
|
||||
href={signOutUrl}
|
||||
className={classNames(
|
||||
active ? 'bg-gray-100 cursor-pointer' : '',
|
||||
'block px-4 py-2 text-sm text-gray-700 cursor-pointer',
|
||||
)}
|
||||
>
|
||||
Logout
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel className="sm:hidden">
|
||||
<div className="pt-2 pb-4 space-y-1">
|
||||
{apps.map((app) => (
|
||||
<Link
|
||||
key={app.name}
|
||||
to={app.slug}
|
||||
className={clsx(
|
||||
'border-transparent litbutton block pl-3 pr-4 py-2 border-l-4 litbutton text-base font-medium',
|
||||
{
|
||||
'litbutton-active border-primary-400 block pl-3 pr-4 py-2': pathname.includes(app.slug),
|
||||
},
|
||||
)}
|
||||
>
|
||||
{app.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderLIT;
|
|
@ -1 +1 @@
|
|||
export { default as Header } from './HeaderLIT';
|
||||
export { default as Header } from './Header';
|
||||
|
|
|
@ -4,4 +4,5 @@ export { Table } from './Table';
|
|||
export { Banner } from './Banner';
|
||||
export { Tabs } from './Tabs';
|
||||
export { Modal, ConfirmationModal, StepsModal } from './Modal';
|
||||
export { UserModal } from './UserModal';
|
||||
export { ProgressSteps } from './ProgressSteps';
|
||||
|
|
1
src/index.css
vendored
|
@ -1,7 +1,6 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "./lit_navigation_style.css";
|
||||
|
||||
div[tabindex] {
|
||||
flex: 1;
|
||||
|
|
57
src/lit_navigation_style.css
vendored
|
@ -1,57 +0,0 @@
|
|||
.litbutton{
|
||||
color: #755d86;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.litbutton-card{
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.litbutton-card:before,
|
||||
.litbutton:before {
|
||||
content: "[";
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
-webkit-transform: translateX(20px);
|
||||
-moz-transform: translateX(20px);
|
||||
transform: translateX(20px);
|
||||
-webkit-transition: -webkit-transform 0.3s, opacity 0.2s;
|
||||
-moz-transition: -moz-transform 0.3s, opacity 0.2s;
|
||||
transition: transform 0.3s, opacity 0.2s;
|
||||
}
|
||||
.litbutton:before{
|
||||
margin-right: 10px;
|
||||
}
|
||||
.litbutton-card:after,
|
||||
.litbutton:after {
|
||||
content: "]";
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
-webkit-transition: -webkit-transform 0.3s, opacity 0.2s;
|
||||
-moz-transition: -moz-transform 0.3s, opacity 0.2s;
|
||||
transition: transform 0.3s, opacity 0.2s;
|
||||
-webkit-transform: translateX(-20px);
|
||||
-moz-transform: translateX(-20px);
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
.litbutton:after{
|
||||
margin-left: 10px;
|
||||
|
||||
}
|
||||
.litbutton-card:hover,
|
||||
.litbutton:hover{
|
||||
color: #3a97a3;
|
||||
}
|
||||
.litbutton-card:active,
|
||||
.litbutton-active{
|
||||
color: #3a97a3;
|
||||
}
|
||||
.litbutton-card:hover:before,
|
||||
.litbutton-card:hover:after,
|
||||
.litbutton:hover:before,
|
||||
.litbutton:hover:after {
|
||||
color: #3a97a3;
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(0px);
|
||||
-moz-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import React from 'react';
|
||||
import Iframe from 'react-iframe';
|
||||
|
||||
export const AppIframe: React.FC<any> = ({ app }: { app: any }) => {
|
||||
return (
|
||||
<div className="relative">
|
||||
<div style={{ minHeight: '95vh' }}>
|
||||
<Iframe
|
||||
height="100%"
|
||||
width="100%"
|
||||
position="absolute"
|
||||
frameBorder={0}
|
||||
overflow="hidden"
|
||||
title={app.name}
|
||||
url={app.url}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { AppStatusEnum, useApps } from 'src/services/apps';
|
||||
import { useAuth } from 'src/services/auth';
|
||||
import { DashboardUtility } from './components';
|
||||
import { DashboardCard } from './components/DashboardCard/DashboardCardLIT';
|
||||
import { UTILITY_APPS } from './consts';
|
||||
|
||||
export const Dashboard: React.FC = () => {
|
||||
const { apps, loadApps, appTableLoading } = useApps();
|
||||
const { isAdmin } = useAuth();
|
||||
|
||||
// Tell React to load the apps
|
||||
useEffect(() => {
|
||||
loadApps();
|
||||
|
||||
return () => {};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="max-w-7xl mx-auto py-4 px-3 sm:px-6 lg:px-8">
|
||||
<div className="mt-6 pb-5 border-b border-gray-200 sm:flex sm:items-center sm:justify-between">
|
||||
<h1 className="text-3xl leading-6 font-bold text-gray-900">Dashboard</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-7xl mx-auto py-4 px-3 sm:px-6 lg:px-8 h-full flex-grow">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 md:gap-4 lg:grid-cols-4 mb-10">
|
||||
{!appTableLoading &&
|
||||
apps
|
||||
.filter((app) => UTILITY_APPS.indexOf(app.slug) === -1)
|
||||
.map((app) => <DashboardCard app={app} key={app.name} />)}
|
||||
</div>
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<div className="max-w-4xl mx-auto py-4 sm:px-6 lg:px-8 h-full flex-grow">
|
||||
<div className="pb-4 border-b border-gray-200 sm:flex sm:items-center">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Administration</h3>
|
||||
</div>
|
||||
|
||||
<dl className="mt-5 grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{apps
|
||||
.filter((app) => UTILITY_APPS.indexOf(app.slug) !== -1 && app.url !== null)
|
||||
.map((app) => (
|
||||
<DashboardUtility item={app} key={app.name} />
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const DashboardCard: React.FC<any> = ({ app }: { app: any }) => {
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
to={`/${app.slug}`}
|
||||
// className="mx-1 inline-flex items-center px-2.5 py-1.5 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
<div
|
||||
className="bg-white overflow-hidden shadow rounded-lg divide-y divide-gray-100 mb-4 md:mb-0"
|
||||
key={app.name}
|
||||
>
|
||||
<div className="px-4 py-5 sm:p-4">
|
||||
<div className="mr-4 flex items-center">
|
||||
<img
|
||||
className="h-16 w-16 rounded-md overflow-hidden mr-4 flex-shrink-0"
|
||||
src={app.assetSrc}
|
||||
alt={app.name}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl litbutton-card leading-8 font-bold">{app.name}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1 +1 @@
|
|||
export { DashboardCard } from './DashboardCardLIT';
|
||||
export { DashboardCard } from './DashboardCard';
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const DashboardUtility: React.FC<any> = ({ item }: { item: any }) => {
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetch(item.markdownSrc)
|
||||
.then((res) => res.text())
|
||||
.then((md) => {
|
||||
return setContent(md);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [item.markdownSrc]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/${item.slug}`}
|
||||
key={item.name}
|
||||
rel="noreferrer"
|
||||
className="bg-white rounded-lg overflow-hidden sm:p-2 flex items-center group"
|
||||
>
|
||||
<div className="w-16 h-16 flex items-center justify-center bg-primary-100 group-hover:bg-primary-200 transition-colors rounded-lg mr-4">
|
||||
{item.icon && <item.icon className="h-6 w-6 text-primary-900" aria-hidden="true" />}
|
||||
{item.assetSrc && <img className="h-6 w-6" src={item.assetSrc} alt={item.name} />}
|
||||
</div>
|
||||
<div>
|
||||
<dt className="truncate text-sm leading-5 font-medium">{item.name}</dt>
|
||||
<dd className="mt-1 text-gray-500 text-sm leading-5 font-normal">
|
||||
<ReactMarkdown>{content}</ReactMarkdown>
|
||||
</dd>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -1 +1 @@
|
|||
export { DashboardUtility } from './DashboardUtilityLIT';
|
||||
export { DashboardUtility } from './DashboardUtility';
|
||||
|
|
|
@ -10,7 +10,7 @@ export const DASHBOARD_QUICK_ACCESS = [
|
|||
];
|
||||
|
||||
/** Apps that should not be shown on the dashboard */
|
||||
export const HIDDEN_APPS = ['dashboard'];
|
||||
export const HIDDEN_APPS = ['dashboard', 'velero'];
|
||||
|
||||
/** Apps that should be shown under "Utilities" */
|
||||
export const UTILITY_APPS = ['authentik', 'zammad'];
|
||||
export const UTILITY_APPS = ['monitoring'];
|
||||
|
|
|
@ -1 +1 @@
|
|||
export { Dashboard } from './DashboardLIT';
|
||||
export { Dashboard } from './Dashboard';
|
||||
|
|
|
@ -29,8 +29,8 @@ export function Login() {
|
|||
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
<div className="flex justify-center">
|
||||
<img className="lg:block" src="assets/lit_logos/lit_transp_title_96.png" alt="Local-IT" />
|
||||
<h2 className="mt-6 text-center text-xl font-bold text-gray-900 sr-only">Einloggen</h2>
|
||||
<img className="lg:block" src="assets/logo.svg" alt="Stackspin" />
|
||||
<h2 className="mt-6 text-center text-xl font-bold text-gray-900 sr-only">Sign in</h2>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
|
@ -42,7 +42,7 @@ export function Login() {
|
|||
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
|
||||
<LockClosedIcon className="h-5 w-5 text-white group-hover:text-primary-light" aria-hidden="true" />
|
||||
</span>
|
||||
Login
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -50,7 +50,7 @@ export function LoginCallback() {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-lg text-primary-600 mt-2">Du wirst jetzt eingeloggt.</p>
|
||||
<p className="text-lg text-primary-600 mt-2">Logging You in, just a moment.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -53,11 +53,6 @@ export const Users: React.FC = () => {
|
|||
|
||||
const columns: any = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Username',
|
||||
accessor: 'preferredUsername',
|
||||
width: 'auto',
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
|
@ -80,12 +75,11 @@ export const Users: React.FC = () => {
|
|||
|
||||
if (isAdmin) {
|
||||
return (
|
||||
<div className="text-right lg:opacity-0 transition-opacity">
|
||||
<div className="text-right lg:opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button
|
||||
disabled
|
||||
onClick={() => configureModalOpen(row.original.id)}
|
||||
type="button"
|
||||
className="inline-flex items-center px-4 py-2 border border-gray-200 shadow-sm text-sm font-medium rounded-md text-gray bg-gray hover:bg-gray focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
className="inline-flex items-center px-4 py-2 border border-gray-200 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
<CogIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
|
||||
Configure
|
||||
|
@ -115,19 +109,17 @@ export const Users: React.FC = () => {
|
|||
{isAdmin && (
|
||||
<div className="mt-3 sm:mt-0 sm:ml-4">
|
||||
<button
|
||||
disabled
|
||||
onClick={() => configureModalOpen(null)}
|
||||
type="button"
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-200 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-800 mx-5 "
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-700 hover:bg-primary-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-800 mx-5 "
|
||||
>
|
||||
<PlusIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
|
||||
Add new user
|
||||
</button>
|
||||
<button
|
||||
disabled
|
||||
onClick={() => setMultipleUsersModal(true)}
|
||||
type="button"
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-200 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-800"
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-700 hover:bg-primary-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-800"
|
||||
>
|
||||
<ViewGridAddIcon className="-ml-0.5 mr-2 h-4 w-4" aria-hidden="true" />
|
||||
Add new users
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export const api = {
|
||||
// @ts-ignore
|
||||
hostname: window.env.REACT_APP_API_URL,
|
||||
hostname: process.env.REACT_APP_API_URL,
|
||||
};
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import { performApiCall } from '../api';
|
||||
import { transformApp } from './transformations';
|
||||
import { App } from './types';
|
||||
|
||||
export const fetchApps = async (): Promise<App> => {
|
||||
// @ts-ignore
|
||||
const apiUrl = window.env.REACT_APP_API_URL;
|
||||
|
||||
const res = await performApiCall({ hostname: apiUrl, path: '/apps', method: 'GET' });
|
||||
return transformApp(res);
|
||||
};
|
8
tailwind.config.js
vendored
|
@ -19,14 +19,6 @@ module.exports = {
|
|||
DEFAULT: '#54C6CC',
|
||||
dark: '#1E8290',
|
||||
},
|
||||
logo: {
|
||||
bluegray: '#3f607d',
|
||||
yellow: '#f5bd1c',
|
||||
lightviolet: '#a587bf',
|
||||
darkviolet: '#755d86',
|
||||
azure: '#3a97a3',
|
||||
lightazure: ' #b4e0e4',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|