Compare commits
No commits in common. "dev" and "master" have entirely different histories.
@ -1,45 +1,13 @@
|
|||||||
name: Build on push
|
name: Build dev on push
|
||||||
run-name: Build on push
|
run-name: Build dev on push
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
|
||||||
runs-on: [runner]
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
|
||||||
steps:
|
|
||||||
- name: Clone Repository
|
|
||||||
uses: https://github.com/actions/checkout@v3
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get Date and Build Number
|
|
||||||
run: |
|
|
||||||
DATE=$(date +'%Y.%m.%d')
|
|
||||||
git fetch --tags
|
|
||||||
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
|
|
||||||
BUILD_NUMBER=$(($TAG_COUNT + 1))
|
|
||||||
BUILD_VERSION="${DATE}.${BUILD_NUMBER}-dev"
|
|
||||||
echo "$BUILD_VERSION" > version.txt
|
|
||||||
|
|
||||||
- name: Create Git Tag for Build
|
|
||||||
run: |
|
|
||||||
git config user.name "ci"
|
|
||||||
git config user.email "dev@sh-edraft.de"
|
|
||||||
git tag ${{ env.BUILD_VERSION }}
|
|
||||||
git push origin ${{ env.BUILD_VERSION }}
|
|
||||||
|
|
||||||
- name: Upload build version artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
path: version.txt
|
|
||||||
|
|
||||||
build-api:
|
build-api:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -47,16 +15,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: |
|
run: |
|
||||||
cd api
|
cd api
|
||||||
echo "VERSION = \"$(cat version.txt)\"" > version.py
|
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-api-dev:$(cat ../version.txt) .
|
||||||
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-api:$(cat ../version.txt) .
|
|
||||||
|
|
||||||
- name: Login to registry git.sh-edraft.de
|
- name: Login to registry git.sh-edraft.de
|
||||||
uses: https://github.com/docker/login-action@v1
|
uses: https://github.com/docker/login-action@v1
|
||||||
@ -67,11 +29,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Push image
|
- name: Push image
|
||||||
run: |
|
run: |
|
||||||
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-api:$(cat version.txt)
|
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-api-dev:$(cat version.txt)
|
||||||
|
|
||||||
build-redirector:
|
build-redirector:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -79,15 +40,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: |
|
run: |
|
||||||
cd api
|
cd api
|
||||||
docker build -f dockerfile_redirector -t git.sh-edraft.de/sh-edraft.de/open-redirect-redirector:$(cat ../version.txt) .
|
docker build -f dockerfile_redirector -t git.sh-edraft.de/sh-edraft.de/open-redirect-redirector-dev:$(cat ../version.txt) .
|
||||||
|
|
||||||
- name: Login to registry git.sh-edraft.de
|
- name: Login to registry git.sh-edraft.de
|
||||||
uses: https://github.com/docker/login-action@v1
|
uses: https://github.com/docker/login-action@v1
|
||||||
@ -98,11 +54,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Push image
|
- name: Push image
|
||||||
run: |
|
run: |
|
||||||
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-redirector:$(cat version.txt)
|
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-redirector-dev:$(cat version.txt)
|
||||||
|
|
||||||
build-web:
|
build-web:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -110,11 +65,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Prepare web build
|
- name: Prepare web build
|
||||||
run: |
|
run: |
|
||||||
cd web
|
cd web
|
||||||
@ -128,7 +78,7 @@ jobs:
|
|||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: |
|
run: |
|
||||||
cd web
|
cd web
|
||||||
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-web:$(cat ../version.txt) .
|
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-web-dev:$(cat ../version.txt) .
|
||||||
|
|
||||||
- name: Login to registry git.sh-edraft.de
|
- name: Login to registry git.sh-edraft.de
|
||||||
uses: https://github.com/docker/login-action@v1
|
uses: https://github.com/docker/login-action@v1
|
||||||
@ -139,4 +89,4 @@ jobs:
|
|||||||
|
|
||||||
- name: Push image
|
- name: Push image
|
||||||
run: |
|
run: |
|
||||||
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-web:$(cat version.txt)
|
docker push git.sh-edraft.de/sh-edraft.de/open-redirect-web-dev:$(cat version.txt)
|
||||||
|
@ -6,40 +6,8 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
|
||||||
runs-on: [runner]
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
|
||||||
steps:
|
|
||||||
- name: Clone Repository
|
|
||||||
uses: https://github.com/actions/checkout@v3
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get Date and Build Number
|
|
||||||
run: |
|
|
||||||
DATE=$(date +'%Y.%m.%d')
|
|
||||||
git fetch --tags
|
|
||||||
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
|
|
||||||
BUILD_NUMBER=$(($TAG_COUNT + 1))
|
|
||||||
BUILD_VERSION="${DATE}.${BUILD_NUMBER}"
|
|
||||||
echo "$BUILD_VERSION" > version.txt
|
|
||||||
|
|
||||||
- name: Create Git Tag for Build
|
|
||||||
run: |
|
|
||||||
git config user.name "ci"
|
|
||||||
git config user.email "dev@sh-edraft.de"
|
|
||||||
git tag ${{ env.BUILD_VERSION }}
|
|
||||||
git push origin ${{ env.BUILD_VERSION }}
|
|
||||||
|
|
||||||
- name: Upload build version artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
path: version.txt
|
|
||||||
|
|
||||||
build-api:
|
build-api:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -47,15 +15,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: |
|
run: |
|
||||||
cd api
|
cd api
|
||||||
echo "VERSION = \"$(cat version.txt)\"" > version.py
|
|
||||||
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-api:$(cat ../version.txt) .
|
docker build -t git.sh-edraft.de/sh-edraft.de/open-redirect-api:$(cat ../version.txt) .
|
||||||
|
|
||||||
- name: Login to registry git.sh-edraft.de
|
- name: Login to registry git.sh-edraft.de
|
||||||
@ -71,7 +33,6 @@ jobs:
|
|||||||
|
|
||||||
build-redirector:
|
build-redirector:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -79,11 +40,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: |
|
run: |
|
||||||
cd api
|
cd api
|
||||||
@ -102,7 +58,6 @@ jobs:
|
|||||||
|
|
||||||
build-web:
|
build-web:
|
||||||
runs-on: [runner]
|
runs-on: [runner]
|
||||||
needs: prepare
|
|
||||||
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
@ -110,11 +65,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
token: ${{ secrets.CI_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Download build version artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: version
|
|
||||||
|
|
||||||
- name: Prepare web build
|
- name: Prepare web build
|
||||||
run: |
|
run: |
|
||||||
cd web
|
cd web
|
||||||
|
@ -52,11 +52,7 @@ class RouteUserExtension:
|
|||||||
if not user_id:
|
if not user_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
user = await userDao.find_by_keycloak_id(user_id)
|
return await userDao.find_by_keycloak_id(user_id)
|
||||||
if user is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return user
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_dev_user(cls) -> Optional[User]:
|
async def get_dev_user(cls) -> Optional[User]:
|
||||||
@ -71,56 +67,6 @@ class RouteUserExtension:
|
|||||||
user = await cls.get_dev_user()
|
user = await cls.get_dev_user()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _flatten_groups(cls, groups):
|
|
||||||
flat_list = []
|
|
||||||
for group in groups:
|
|
||||||
flat_list.append(group)
|
|
||||||
if "subGroups" in group and group["subGroups"]:
|
|
||||||
flat_list.extend(cls._flatten_groups(group["subGroups"]))
|
|
||||||
return flat_list
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def _map_keycloak_groups_with_roles(cls, user: User):
|
|
||||||
try:
|
|
||||||
roles = {x.name: x for x in await roleDao.get_all()}
|
|
||||||
groups = cls._flatten_groups(Keycloak.admin.get_groups(full_hierarchy=True))
|
|
||||||
groups_with_role = [x["name"] for x in groups if x["name"] in roles.keys()]
|
|
||||||
|
|
||||||
user_groups_with_role = [
|
|
||||||
x["name"]
|
|
||||||
for x in Keycloak.admin.get_user_groups(user.keycloak_id)
|
|
||||||
if x["name"] in roles.keys()
|
|
||||||
]
|
|
||||||
user_roles = set(
|
|
||||||
x.name for x in await user.roles if x.name in groups_with_role
|
|
||||||
)
|
|
||||||
missing_groups = set(user_groups_with_role) - set(user_roles)
|
|
||||||
missing_roles = set(user_roles) - set(user_groups_with_role)
|
|
||||||
|
|
||||||
if len(missing_groups) > 0:
|
|
||||||
await roleUserDao.create_many(
|
|
||||||
[
|
|
||||||
RoleUser(0, (await roleDao.get_by_name(group)).id, user.id)
|
|
||||||
for group in missing_groups
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(missing_roles) > 0:
|
|
||||||
await roleUserDao.delete_many(
|
|
||||||
[
|
|
||||||
await roleUserDao.get_single_by(
|
|
||||||
[
|
|
||||||
{RoleUser.role_id: roles[role].id},
|
|
||||||
{RoleUser.user_id: user.id},
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for role in missing_roles
|
|
||||||
]
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Failed to map user groups", e)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _create_user(cls, kc_user: KeycloakUser):
|
async def _create_user(cls, kc_user: KeycloakUser):
|
||||||
try:
|
try:
|
||||||
@ -155,11 +101,8 @@ class RouteUserExtension:
|
|||||||
|
|
||||||
user = await cls.get_user()
|
user = await cls.get_user()
|
||||||
if user is None:
|
if user is None:
|
||||||
u_id = await cls._create_user(KeycloakUser(user_info))
|
await cls._create_user(KeycloakUser(user_info))
|
||||||
await cls._map_keycloak_groups_with_roles(await userDao.get_by_id(u_id))
|
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
await cls._map_keycloak_groups_with_roles(user)
|
|
||||||
|
|
||||||
if user.deleted:
|
if user.deleted:
|
||||||
return False
|
return False
|
||||||
|
@ -69,10 +69,8 @@ class QueryABC(ObjectType):
|
|||||||
):
|
):
|
||||||
if len(permissions) > 0:
|
if len(permissions) > 0:
|
||||||
user = await Route.get_authenticated_user_or_api_key_or_default()
|
user = await Route.get_authenticated_user_or_api_key_or_default()
|
||||||
perms = await user.permissions
|
|
||||||
has_perms = [await user.has_permission(x) for x in permissions]
|
|
||||||
if user is not None and all(
|
if user is not None and all(
|
||||||
has_perms
|
[await user.has_permission(x) for x in permissions]
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
from api_graphql.abc.db_model_filter_abc import DbModelFilterABC
|
|
||||||
from api_graphql.abc.filter.string_filter import StringFilter
|
|
||||||
|
|
||||||
|
|
||||||
class DomainFilter(DbModelFilterABC):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
obj: dict,
|
|
||||||
):
|
|
||||||
DbModelFilterABC.__init__(self, obj)
|
|
||||||
|
|
||||||
self.add_field("name", StringFilter)
|
|
||||||
self.add_field("description", StringFilter)
|
|
@ -1,53 +0,0 @@
|
|||||||
type DomainResult {
|
|
||||||
totalCount: Int
|
|
||||||
count: Int
|
|
||||||
nodes: [Domain]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Domain implements DbModel {
|
|
||||||
id: ID
|
|
||||||
name: String
|
|
||||||
|
|
||||||
shortUrls: [ShortUrl]
|
|
||||||
|
|
||||||
deleted: Boolean
|
|
||||||
editor: User
|
|
||||||
createdUtc: String
|
|
||||||
updatedUtc: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input DomainSort {
|
|
||||||
id: SortOrder
|
|
||||||
name: SortOrder
|
|
||||||
|
|
||||||
deleted: SortOrder
|
|
||||||
editorId: SortOrder
|
|
||||||
createdUtc: SortOrder
|
|
||||||
updatedUtc: SortOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
input DomainFilter {
|
|
||||||
id: IntFilter
|
|
||||||
name: StringFilter
|
|
||||||
|
|
||||||
deleted: BooleanFilter
|
|
||||||
editor: IntFilter
|
|
||||||
createdUtc: DateFilter
|
|
||||||
updatedUtc: DateFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
type DomainMutation {
|
|
||||||
create(input: DomainCreateInput!): Domain
|
|
||||||
update(input: DomainUpdateInput!): Domain
|
|
||||||
delete(id: ID!): Boolean
|
|
||||||
restore(id: ID!): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
input DomainCreateInput {
|
|
||||||
name: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input DomainUpdateInput {
|
|
||||||
id: ID!
|
|
||||||
name: String
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ type Group implements DbModel {
|
|||||||
name: String
|
name: String
|
||||||
|
|
||||||
shortUrls: [ShortUrl]
|
shortUrls: [ShortUrl]
|
||||||
roles: [Role]
|
|
||||||
|
|
||||||
deleted: Boolean
|
deleted: Boolean
|
||||||
editor: User
|
editor: User
|
||||||
@ -46,11 +45,9 @@ type GroupMutation {
|
|||||||
|
|
||||||
input GroupCreateInput {
|
input GroupCreateInput {
|
||||||
name: String!
|
name: String!
|
||||||
roles: [ID]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input GroupUpdateInput {
|
input GroupUpdateInput {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String
|
name: String
|
||||||
roles: [ID]
|
|
||||||
}
|
}
|
@ -5,6 +5,5 @@ type Mutation {
|
|||||||
role: RoleMutation
|
role: RoleMutation
|
||||||
|
|
||||||
group: GroupMutation
|
group: GroupMutation
|
||||||
domain: DomainMutation
|
|
||||||
shortUrl: ShortUrlMutation
|
shortUrl: ShortUrlMutation
|
||||||
}
|
}
|
@ -11,7 +11,6 @@ type Query {
|
|||||||
userHasAnyPermission(permissions: [String]!): Boolean
|
userHasAnyPermission(permissions: [String]!): Boolean
|
||||||
notExistingUsersFromKeycloak: KeycloakUserResult
|
notExistingUsersFromKeycloak: KeycloakUserResult
|
||||||
|
|
||||||
domains(filter: [DomainFilter], sort: [DomainSort], skip: Int, take: Int): DomainResult
|
|
||||||
groups(filter: [GroupFilter], sort: [GroupSort], skip: Int, take: Int): GroupResult
|
groups(filter: [GroupFilter], sort: [GroupSort], skip: Int, take: Int): GroupResult
|
||||||
shortUrls(filter: [ShortUrlFilter], sort: [ShortUrlSort], skip: Int, take: Int): ShortUrlResult
|
shortUrls(filter: [ShortUrlFilter], sort: [ShortUrlSort], skip: Int, take: Int): ShortUrlResult
|
||||||
}
|
}
|
@ -11,7 +11,6 @@ type ShortUrl implements DbModel {
|
|||||||
description: String
|
description: String
|
||||||
visits: Int
|
visits: Int
|
||||||
group: Group
|
group: Group
|
||||||
domain: Domain
|
|
||||||
loadingScreen: Boolean
|
loadingScreen: Boolean
|
||||||
|
|
||||||
deleted: Boolean
|
deleted: Boolean
|
||||||
@ -56,7 +55,6 @@ input ShortUrlCreateInput {
|
|||||||
targetUrl: String!
|
targetUrl: String!
|
||||||
description: String
|
description: String
|
||||||
groupId: ID
|
groupId: ID
|
||||||
domainId: ID
|
|
||||||
loadingScreen: Boolean
|
loadingScreen: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +64,5 @@ input ShortUrlUpdateInput {
|
|||||||
targetUrl: String
|
targetUrl: String
|
||||||
description: String
|
description: String
|
||||||
groupId: ID
|
groupId: ID
|
||||||
domainId: ID
|
|
||||||
loadingScreen: Boolean
|
loadingScreen: Boolean
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
from api_graphql.abc.input_abc import InputABC
|
|
||||||
|
|
||||||
|
|
||||||
class DomainCreateInput(InputABC):
|
|
||||||
|
|
||||||
def __init__(self, src: dict):
|
|
||||||
InputABC.__init__(self, src)
|
|
||||||
|
|
||||||
self._name = self.option("name", str, required=True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return self._name
|
|
@ -1,18 +0,0 @@
|
|||||||
from api_graphql.abc.input_abc import InputABC
|
|
||||||
|
|
||||||
|
|
||||||
class DomainUpdateInput(InputABC):
|
|
||||||
|
|
||||||
def __init__(self, src: dict):
|
|
||||||
InputABC.__init__(self, src)
|
|
||||||
|
|
||||||
self._id = self.option("id", int, required=True)
|
|
||||||
self._name = self.option("name", str)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self) -> int:
|
|
||||||
return self._id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return self._name
|
|
@ -7,12 +7,7 @@ class GroupCreateInput(InputABC):
|
|||||||
InputABC.__init__(self, src)
|
InputABC.__init__(self, src)
|
||||||
|
|
||||||
self._name = self.option("name", str, required=True)
|
self._name = self.option("name", str, required=True)
|
||||||
self._roles = self.option("roles", list[int])
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
|
||||||
def roles(self) -> list[int]:
|
|
||||||
return self._roles
|
|
||||||
|
@ -8,7 +8,6 @@ class GroupUpdateInput(InputABC):
|
|||||||
|
|
||||||
self._id = self.option("id", int, required=True)
|
self._id = self.option("id", int, required=True)
|
||||||
self._name = self.option("name", str)
|
self._name = self.option("name", str)
|
||||||
self._roles = self.option("roles", list[int])
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> int:
|
def id(self) -> int:
|
||||||
@ -17,7 +16,3 @@ class GroupUpdateInput(InputABC):
|
|||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
|
||||||
def roles(self) -> list[int]:
|
|
||||||
return self._roles
|
|
||||||
|
@ -12,7 +12,6 @@ class ShortUrlCreateInput(InputABC):
|
|||||||
self._target_url = self.option("targetUrl", str, required=True)
|
self._target_url = self.option("targetUrl", str, required=True)
|
||||||
self._description = self.option("description", str)
|
self._description = self.option("description", str)
|
||||||
self._group_id = self.option("groupId", int)
|
self._group_id = self.option("groupId", int)
|
||||||
self._domain_id = self.option("domainId", int)
|
|
||||||
self._loading_screen = self.option("loadingScreen", bool)
|
self._loading_screen = self.option("loadingScreen", bool)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -31,10 +30,6 @@ class ShortUrlCreateInput(InputABC):
|
|||||||
def group_id(self) -> Optional[int]:
|
def group_id(self) -> Optional[int]:
|
||||||
return self._group_id
|
return self._group_id
|
||||||
|
|
||||||
@property
|
|
||||||
def domain_id(self) -> Optional[int]:
|
|
||||||
return self._domain_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loading_screen(self) -> Optional[str]:
|
def loading_screen(self) -> Optional[str]:
|
||||||
return self._loading_screen
|
return self._loading_screen
|
||||||
|
@ -13,7 +13,6 @@ class ShortUrlUpdateInput(InputABC):
|
|||||||
self._target_url = self.option("targetUrl", str)
|
self._target_url = self.option("targetUrl", str)
|
||||||
self._description = self.option("description", str)
|
self._description = self.option("description", str)
|
||||||
self._group_id = self.option("groupId", int)
|
self._group_id = self.option("groupId", int)
|
||||||
self._domain_id = self.option("domainId", int)
|
|
||||||
self._loading_screen = self.option("loadingScreen", bool)
|
self._loading_screen = self.option("loadingScreen", bool)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -36,10 +35,6 @@ class ShortUrlUpdateInput(InputABC):
|
|||||||
def group_id(self) -> Optional[int]:
|
def group_id(self) -> Optional[int]:
|
||||||
return self._group_id
|
return self._group_id
|
||||||
|
|
||||||
@property
|
|
||||||
def domain_id(self) -> Optional[int]:
|
|
||||||
return self._domain_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loading_screen(self) -> Optional[str]:
|
def loading_screen(self) -> Optional[str]:
|
||||||
return self._loading_screen
|
return self._loading_screen
|
||||||
|
@ -33,15 +33,6 @@ class Mutation(MutationABC):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add_mutation_type(
|
|
||||||
"domain",
|
|
||||||
"Domain",
|
|
||||||
require_any_permission=[
|
|
||||||
Permissions.domains_create,
|
|
||||||
Permissions.domains_update,
|
|
||||||
Permissions.domains_delete,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
self.add_mutation_type(
|
self.add_mutation_type(
|
||||||
"group",
|
"group",
|
||||||
"Group",
|
"Group",
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
from api_graphql.abc.mutation_abc import MutationABC
|
|
||||||
from api_graphql.input.domain_create_input import DomainCreateInput
|
|
||||||
from api_graphql.input.domain_update_input import DomainUpdateInput
|
|
||||||
from api_graphql.input.group_create_input import GroupCreateInput
|
|
||||||
from api_graphql.input.group_update_input import GroupUpdateInput
|
|
||||||
from core.logger import APILogger
|
|
||||||
from data.schemas.public.domain_dao import domainDao
|
|
||||||
from data.schemas.public.group import Group
|
|
||||||
from service.permission.permissions_enum import Permissions
|
|
||||||
|
|
||||||
logger = APILogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainMutation(MutationABC):
|
|
||||||
def __init__(self):
|
|
||||||
MutationABC.__init__(self, "Domain")
|
|
||||||
|
|
||||||
self.mutation(
|
|
||||||
"create",
|
|
||||||
self.resolve_create,
|
|
||||||
DomainCreateInput,
|
|
||||||
require_any_permission=[Permissions.domains_create],
|
|
||||||
)
|
|
||||||
self.mutation(
|
|
||||||
"update",
|
|
||||||
self.resolve_update,
|
|
||||||
DomainUpdateInput,
|
|
||||||
require_any_permission=[Permissions.domains_update],
|
|
||||||
)
|
|
||||||
self.mutation(
|
|
||||||
"delete",
|
|
||||||
self.resolve_delete,
|
|
||||||
require_any_permission=[Permissions.domains_delete],
|
|
||||||
)
|
|
||||||
self.mutation(
|
|
||||||
"restore",
|
|
||||||
self.resolve_restore,
|
|
||||||
require_any_permission=[Permissions.domains_delete],
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def resolve_create(obj: GroupCreateInput, *_):
|
|
||||||
logger.debug(f"create domain: {obj.__dict__}")
|
|
||||||
|
|
||||||
domain = Group(
|
|
||||||
0,
|
|
||||||
obj.name,
|
|
||||||
)
|
|
||||||
nid = await domainDao.create(domain)
|
|
||||||
return await domainDao.get_by_id(nid)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def resolve_update(obj: GroupUpdateInput, *_):
|
|
||||||
logger.debug(f"update domain: {input}")
|
|
||||||
|
|
||||||
if obj.name is not None:
|
|
||||||
domain = await domainDao.get_by_id(obj.id)
|
|
||||||
domain.name = obj.name
|
|
||||||
await domainDao.update(domain)
|
|
||||||
|
|
||||||
return await domainDao.get_by_id(obj.id)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def resolve_delete(*_, id: str):
|
|
||||||
logger.debug(f"delete domain: {id}")
|
|
||||||
domain = await domainDao.get_by_id(id)
|
|
||||||
await domainDao.delete(domain)
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def resolve_restore(*_, id: str):
|
|
||||||
logger.debug(f"restore domain: {id}")
|
|
||||||
domain = await domainDao.get_by_id(id)
|
|
||||||
await domainDao.restore(domain)
|
|
||||||
return True
|
|
@ -1,13 +1,9 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from api_graphql.abc.mutation_abc import MutationABC
|
from api_graphql.abc.mutation_abc import MutationABC
|
||||||
from api_graphql.input.group_create_input import GroupCreateInput
|
from api_graphql.input.group_create_input import GroupCreateInput
|
||||||
from api_graphql.input.group_update_input import GroupUpdateInput
|
from api_graphql.input.group_update_input import GroupUpdateInput
|
||||||
from core.logger import APILogger
|
from core.logger import APILogger
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from data.schemas.public.group_role_assignment import GroupRoleAssignment
|
|
||||||
from data.schemas.public.group_role_assignment_dao import groupRoleAssignmentDao
|
|
||||||
from service.permission.permissions_enum import Permissions
|
from service.permission.permissions_enum import Permissions
|
||||||
|
|
||||||
logger = APILogger(__name__)
|
logger = APILogger(__name__)
|
||||||
@ -41,61 +37,25 @@ class GroupMutation(MutationABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _handle_group_role_assignments(gid: int, roles: Optional[list[int]]):
|
async def resolve_create(obj: GroupCreateInput, *_):
|
||||||
if roles is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
existing_roles = await groupDao.get_roles(gid)
|
|
||||||
existing_role_ids = {role.id for role in existing_roles}
|
|
||||||
|
|
||||||
new_role_ids = set(roles)
|
|
||||||
|
|
||||||
roles_to_add = new_role_ids - existing_role_ids
|
|
||||||
roles_to_remove = existing_role_ids - new_role_ids
|
|
||||||
|
|
||||||
if roles_to_add:
|
|
||||||
group_role_assignments = [
|
|
||||||
GroupRoleAssignment(0, gid, role_id) for role_id in roles_to_add
|
|
||||||
]
|
|
||||||
await groupRoleAssignmentDao.create_many(group_role_assignments)
|
|
||||||
|
|
||||||
if roles_to_remove:
|
|
||||||
assignments_to_remove = await groupRoleAssignmentDao.find_by(
|
|
||||||
[
|
|
||||||
{GroupRoleAssignment.group_id: gid},
|
|
||||||
{GroupRoleAssignment.role_id: {"in": roles_to_remove}},
|
|
||||||
]
|
|
||||||
)
|
|
||||||
await groupRoleAssignmentDao.delete_many(assignments_to_remove)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def resolve_create(cls, obj: GroupCreateInput, *_):
|
|
||||||
logger.debug(f"create group: {obj.__dict__}")
|
logger.debug(f"create group: {obj.__dict__}")
|
||||||
|
|
||||||
group = Group(
|
group = Group(
|
||||||
0,
|
0,
|
||||||
obj.name,
|
obj.name,
|
||||||
)
|
)
|
||||||
gid = await groupDao.create(group)
|
nid = await groupDao.create(group)
|
||||||
|
return await groupDao.get_by_id(nid)
|
||||||
|
|
||||||
await cls._handle_group_role_assignments(gid, obj.roles)
|
@staticmethod
|
||||||
|
async def resolve_update(obj: GroupUpdateInput, *_):
|
||||||
return await groupDao.get_by_id(gid)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def resolve_update(cls, obj: GroupUpdateInput, *_):
|
|
||||||
logger.debug(f"update group: {input}")
|
logger.debug(f"update group: {input}")
|
||||||
|
|
||||||
if await groupDao.find_by_id(obj.id) is None:
|
|
||||||
raise ValueError(f"Group with id {obj.id} not found")
|
|
||||||
|
|
||||||
if obj.name is not None:
|
if obj.name is not None:
|
||||||
group = await groupDao.get_by_id(obj.id)
|
group = await groupDao.get_by_id(obj.id)
|
||||||
group.name = obj.name
|
group.name = obj.name
|
||||||
await groupDao.update(group)
|
await groupDao.update(group)
|
||||||
|
|
||||||
await cls._handle_group_role_assignments(obj.id, obj.roles)
|
|
||||||
|
|
||||||
return await groupDao.get_by_id(obj.id)
|
return await groupDao.get_by_id(obj.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -4,7 +4,6 @@ from api_graphql.abc.mutation_abc import MutationABC
|
|||||||
from api_graphql.input.short_url_create_input import ShortUrlCreateInput
|
from api_graphql.input.short_url_create_input import ShortUrlCreateInput
|
||||||
from api_graphql.input.short_url_update_input import ShortUrlUpdateInput
|
from api_graphql.input.short_url_update_input import ShortUrlUpdateInput
|
||||||
from core.logger import APILogger
|
from core.logger import APILogger
|
||||||
from data.schemas.public.domain_dao import domainDao
|
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from data.schemas.public.short_url import ShortUrl
|
from data.schemas.public.short_url import ShortUrl
|
||||||
from data.schemas.public.short_url_dao import shortUrlDao
|
from data.schemas.public.short_url_dao import shortUrlDao
|
||||||
@ -50,7 +49,6 @@ class ShortUrlMutation(MutationABC):
|
|||||||
obj.target_url,
|
obj.target_url,
|
||||||
obj.description,
|
obj.description,
|
||||||
obj.group_id,
|
obj.group_id,
|
||||||
obj.domain_id,
|
|
||||||
obj.loading_screen,
|
obj.loading_screen,
|
||||||
)
|
)
|
||||||
nid = await shortUrlDao.create(short_url)
|
nid = await shortUrlDao.create(short_url)
|
||||||
@ -76,16 +74,6 @@ class ShortUrlMutation(MutationABC):
|
|||||||
if group_by_id is None:
|
if group_by_id is None:
|
||||||
raise NotFound(f"Group with id {obj.group_id} does not exist")
|
raise NotFound(f"Group with id {obj.group_id} does not exist")
|
||||||
short_url.group_id = obj.group_id
|
short_url.group_id = obj.group_id
|
||||||
else:
|
|
||||||
short_url.group_id = None
|
|
||||||
|
|
||||||
if obj.domain_id is not None:
|
|
||||||
domain_by_id = await domainDao.find_by_id(obj.domain_id)
|
|
||||||
if domain_by_id is None:
|
|
||||||
raise NotFound(f"Domain with id {obj.domain_id} does not exist")
|
|
||||||
short_url.domain_id = obj.domain_id
|
|
||||||
else:
|
|
||||||
short_url.domain_id = None
|
|
||||||
|
|
||||||
if obj.loading_screen is not None:
|
if obj.loading_screen is not None:
|
||||||
short_url.loading_screen = obj.loading_screen
|
short_url.loading_screen = obj.loading_screen
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
|
||||||
from data.schemas.public.domain import Domain
|
|
||||||
from data.schemas.public.group import Group
|
|
||||||
from data.schemas.public.short_url import ShortUrl
|
|
||||||
from data.schemas.public.short_url_dao import shortUrlDao
|
|
||||||
|
|
||||||
|
|
||||||
class DomainQuery(DbModelQueryABC):
|
|
||||||
def __init__(self):
|
|
||||||
DbModelQueryABC.__init__(self, "Domain")
|
|
||||||
|
|
||||||
self.set_field("name", lambda x, *_: x.name)
|
|
||||||
self.set_field("shortUrls", self._get_urls)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _get_urls(domain: Domain, *_):
|
|
||||||
return await shortUrlDao.find_by({ShortUrl.domain_id: domain.id})
|
|
@ -1,11 +1,7 @@
|
|||||||
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
from api_graphql.abc.db_model_query_abc import DbModelQueryABC
|
||||||
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
|
||||||
from api_graphql.require_any_resolvers import group_by_assignment_resolver
|
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
|
||||||
from data.schemas.public.short_url import ShortUrl
|
from data.schemas.public.short_url import ShortUrl
|
||||||
from data.schemas.public.short_url_dao import shortUrlDao
|
from data.schemas.public.short_url_dao import shortUrlDao
|
||||||
from service.permission.permissions_enum import Permissions
|
|
||||||
|
|
||||||
|
|
||||||
class GroupQuery(DbModelQueryABC):
|
class GroupQuery(DbModelQueryABC):
|
||||||
@ -13,19 +9,8 @@ class GroupQuery(DbModelQueryABC):
|
|||||||
DbModelQueryABC.__init__(self, "Group")
|
DbModelQueryABC.__init__(self, "Group")
|
||||||
|
|
||||||
self.set_field("name", lambda x, *_: x.name)
|
self.set_field("name", lambda x, *_: x.name)
|
||||||
self.field(
|
self.set_field("shortUrls", self._get_urls)
|
||||||
ResolverFieldBuilder("shortUrls")
|
|
||||||
.with_resolver(self._get_urls)
|
|
||||||
.with_require_any([
|
|
||||||
Permissions.groups,
|
|
||||||
], [group_by_assignment_resolver])
|
|
||||||
)
|
|
||||||
self.set_field("roles", self._get_roles)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _get_urls(group: Group, *_):
|
async def _get_urls(group: Group, *_):
|
||||||
return await shortUrlDao.find_by({ShortUrl.group_id: group.id})
|
return await shortUrlDao.find_by({ShortUrl.group_id: group.id})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _get_roles(group: Group, *_):
|
|
||||||
return await groupDao.get_roles(group.id)
|
|
||||||
|
@ -9,6 +9,5 @@ class ShortUrlQuery(DbModelQueryABC):
|
|||||||
self.set_field("targetUrl", lambda x, *_: x.target_url)
|
self.set_field("targetUrl", lambda x, *_: x.target_url)
|
||||||
self.set_field("description", lambda x, *_: x.description)
|
self.set_field("description", lambda x, *_: x.description)
|
||||||
self.set_field("group", lambda x, *_: x.group)
|
self.set_field("group", lambda x, *_: x.group)
|
||||||
self.set_field("domain", lambda x, *_: x.domain)
|
|
||||||
self.set_field("visits", lambda x, *_: x.visit_count)
|
self.set_field("visits", lambda x, *_: x.visit_count)
|
||||||
self.set_field("loadingScreen", lambda x, *_: x.loading_screen)
|
self.set_field("loadingScreen", lambda x, *_: x.loading_screen)
|
||||||
|
@ -5,13 +5,11 @@ from api_graphql.abc.sort_abc import Sort
|
|||||||
from api_graphql.field.dao_field_builder import DaoFieldBuilder
|
from api_graphql.field.dao_field_builder import DaoFieldBuilder
|
||||||
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
from api_graphql.field.resolver_field_builder import ResolverFieldBuilder
|
||||||
from api_graphql.filter.api_key_filter import ApiKeyFilter
|
from api_graphql.filter.api_key_filter import ApiKeyFilter
|
||||||
from api_graphql.filter.domain_filter import DomainFilter
|
|
||||||
from api_graphql.filter.group_filter import GroupFilter
|
from api_graphql.filter.group_filter import GroupFilter
|
||||||
from api_graphql.filter.permission_filter import PermissionFilter
|
from api_graphql.filter.permission_filter import PermissionFilter
|
||||||
from api_graphql.filter.role_filter import RoleFilter
|
from api_graphql.filter.role_filter import RoleFilter
|
||||||
from api_graphql.filter.short_url_filter import ShortUrlFilter
|
from api_graphql.filter.short_url_filter import ShortUrlFilter
|
||||||
from api_graphql.filter.user_filter import UserFilter
|
from api_graphql.filter.user_filter import UserFilter
|
||||||
from api_graphql.require_any_resolvers import group_by_assignment_resolver
|
|
||||||
from data.schemas.administration.api_key import ApiKey
|
from data.schemas.administration.api_key import ApiKey
|
||||||
from data.schemas.administration.api_key_dao import apiKeyDao
|
from data.schemas.administration.api_key_dao import apiKeyDao
|
||||||
from data.schemas.administration.user import User
|
from data.schemas.administration.user import User
|
||||||
@ -20,8 +18,6 @@ from data.schemas.permission.permission import Permission
|
|||||||
from data.schemas.permission.permission_dao import permissionDao
|
from data.schemas.permission.permission_dao import permissionDao
|
||||||
from data.schemas.permission.role import Role
|
from data.schemas.permission.role import Role
|
||||||
from data.schemas.permission.role_dao import roleDao
|
from data.schemas.permission.role_dao import roleDao
|
||||||
from data.schemas.public.domain import Domain
|
|
||||||
from data.schemas.public.domain_dao import domainDao
|
|
||||||
from data.schemas.public.group import Group
|
from data.schemas.public.group import Group
|
||||||
from data.schemas.public.group_dao import groupDao
|
from data.schemas.public.group_dao import groupDao
|
||||||
from data.schemas.public.short_url import ShortUrl
|
from data.schemas.public.short_url import ShortUrl
|
||||||
@ -52,15 +48,7 @@ class Query(QueryABC):
|
|||||||
.with_dao(roleDao)
|
.with_dao(roleDao)
|
||||||
.with_filter(RoleFilter)
|
.with_filter(RoleFilter)
|
||||||
.with_sort(Sort[Role])
|
.with_sort(Sort[Role])
|
||||||
.with_require_any_permission(
|
.with_require_any_permission([Permissions.roles])
|
||||||
[
|
|
||||||
Permissions.roles,
|
|
||||||
Permissions.users_create,
|
|
||||||
Permissions.users_update,
|
|
||||||
Permissions.groups_create,
|
|
||||||
Permissions.groups_update,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.field(
|
self.field(
|
||||||
@ -93,38 +81,25 @@ class Query(QueryABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.field(
|
self.field(
|
||||||
DaoFieldBuilder("domains")
|
DaoFieldBuilder("groups")
|
||||||
.with_dao(domainDao)
|
.with_dao(groupDao)
|
||||||
.with_filter(DomainFilter)
|
.with_filter(GroupFilter)
|
||||||
.with_sort(Sort[Domain])
|
.with_sort(Sort[Group])
|
||||||
.with_require_any_permission(
|
.with_require_any_permission(
|
||||||
[
|
[
|
||||||
Permissions.domains,
|
Permissions.groups,
|
||||||
Permissions.short_urls_create,
|
Permissions.short_urls_create,
|
||||||
Permissions.short_urls_update,
|
Permissions.short_urls_update,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.field(
|
# partially public to load redirect if not resolved/redirected by api
|
||||||
DaoFieldBuilder("groups")
|
|
||||||
.with_dao(groupDao)
|
|
||||||
.with_filter(GroupFilter)
|
|
||||||
.with_sort(Sort[Group])
|
|
||||||
.with_require_any(
|
|
||||||
[
|
|
||||||
Permissions.groups,
|
|
||||||
Permissions.short_urls_create,
|
|
||||||
Permissions.short_urls_update,
|
|
||||||
],
|
|
||||||
[group_by_assignment_resolver]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.field(
|
self.field(
|
||||||
DaoFieldBuilder("shortUrls")
|
DaoFieldBuilder("shortUrls")
|
||||||
.with_dao(shortUrlDao)
|
.with_dao(shortUrlDao)
|
||||||
.with_filter(ShortUrlFilter)
|
.with_filter(ShortUrlFilter)
|
||||||
.with_sort(Sort[ShortUrl])
|
.with_sort(Sort[ShortUrl])
|
||||||
.with_require_any([Permissions.short_urls], [group_by_assignment_resolver])
|
.with_require_any_permission([Permissions.short_urls])
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
from api_graphql.service.collection_result import CollectionResult
|
|
||||||
from api_graphql.service.query_context import QueryContext
|
|
||||||
from data.schemas.public.group_dao import groupDao
|
|
||||||
from service.permission.permissions_enum import Permissions
|
|
||||||
|
|
||||||
|
|
||||||
async def group_by_assignment_resolver(ctx: QueryContext) -> bool:
|
|
||||||
if not isinstance(ctx.data, CollectionResult):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if ctx.has_permission(Permissions.short_urls_by_assignment):
|
|
||||||
groups = [await x.group for x in ctx.data.nodes]
|
|
||||||
role_ids = {x.id for x in await ctx.user.roles}
|
|
||||||
filtered_groups = [
|
|
||||||
g.id for g in groups if
|
|
||||||
g is not None and (roles := await groupDao.get_roles(g.id)) and all(r.id in role_ids for r in roles)
|
|
||||||
]
|
|
||||||
|
|
||||||
ctx.data.nodes = [node for node in ctx.data.nodes if (await node.group) is not None and (await node.group).id in filtered_groups]
|
|
||||||
return True
|
|
||||||
|
|
||||||
return True
|
|
@ -14,7 +14,7 @@ class QueryContext:
|
|||||||
self,
|
self,
|
||||||
data: Any,
|
data: Any,
|
||||||
user: Optional[User],
|
user: Optional[User],
|
||||||
user_permissions: Optional[list[Permissions]],
|
user_permissions: Optional[list[Permission]],
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
@ -23,7 +23,7 @@ class QueryContext:
|
|||||||
self._user = user
|
self._user = user
|
||||||
if user_permissions is None:
|
if user_permissions is None:
|
||||||
user_permissions = []
|
user_permissions = []
|
||||||
self._user_permissions: list[str] = [x.value for x in user_permissions]
|
self._user_permissions: list[str] = [x.name for x in user_permissions]
|
||||||
|
|
||||||
self._resolve_info = None
|
self._resolve_info = None
|
||||||
for arg in args:
|
for arg in args:
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
from collections.abc import Awaitable
|
from collections.abc import Awaitable
|
||||||
from typing import Callable, Union, Optional, Coroutine, Any
|
from typing import Callable, Union, Optional
|
||||||
|
|
||||||
from api_graphql.service.query_context import QueryContext
|
from api_graphql.service.query_context import QueryContext
|
||||||
from service.permission.permissions_enum import Permissions
|
from service.permission.permissions_enum import Permissions
|
||||||
|
|
||||||
TRequireAnyPermissions = Optional[list[Permissions]]
|
TRequireAnyPermissions = Optional[list[Permissions]]
|
||||||
TRequireAnyResolvers = list[
|
TRequireAnyResolvers = list[
|
||||||
Union[
|
Union[Callable[[QueryContext], bool], Awaitable[[QueryContext], bool]]
|
||||||
Callable[[QueryContext], bool],
|
|
||||||
Awaitable[[QueryContext], bool],
|
|
||||||
Callable[[QueryContext], Coroutine[Any, Any, bool]]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
TRequireAny = tuple[TRequireAnyPermissions, TRequireAnyResolvers]
|
TRequireAny = tuple[TRequireAnyPermissions, TRequireAnyResolvers]
|
||||||
|
@ -35,12 +35,12 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return self._table_name
|
return self._table_name
|
||||||
|
|
||||||
def attribute(
|
def attribute(
|
||||||
self,
|
self,
|
||||||
attr_name: Attribute,
|
attr_name: Attribute,
|
||||||
attr_type: type,
|
attr_type: type,
|
||||||
db_name: str = None,
|
db_name: str = None,
|
||||||
ignore=False,
|
ignore=False,
|
||||||
primary_key=False,
|
primary_key=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add an attribute for db and object mapping to the data access object
|
Add an attribute for db and object mapping to the data access object
|
||||||
@ -118,11 +118,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return self.to_object(result[0])
|
return self.to_object(result[0])
|
||||||
|
|
||||||
async def get_by(
|
async def get_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> list[T_DBM]:
|
) -> list[T_DBM]:
|
||||||
"""
|
"""
|
||||||
Get all objects by the given filters
|
Get all objects by the given filters
|
||||||
@ -143,11 +143,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return [self.to_object(x) for x in result]
|
return [self.to_object(x) for x in result]
|
||||||
|
|
||||||
async def get_single_by(
|
async def get_single_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> T_DBM:
|
) -> T_DBM:
|
||||||
"""
|
"""
|
||||||
Get a single object by the given filters
|
Get a single object by the given filters
|
||||||
@ -168,11 +168,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
async def find_by(
|
async def find_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> list[Optional[T_DBM]]:
|
) -> list[Optional[T_DBM]]:
|
||||||
"""
|
"""
|
||||||
Find all objects by the given filters
|
Find all objects by the given filters
|
||||||
@ -192,11 +192,11 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return [self.to_object(x) for x in result]
|
return [self.to_object(x) for x in result]
|
||||||
|
|
||||||
async def find_single_by(
|
async def find_single_by(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> Optional[T_DBM]:
|
) -> Optional[T_DBM]:
|
||||||
"""
|
"""
|
||||||
Find a single object by the given filters
|
Find a single object by the given filters
|
||||||
@ -296,7 +296,7 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
await self._db.execute(query)
|
await self._db.execute(query)
|
||||||
|
|
||||||
async def _build_delete_statement(
|
async def _build_delete_statement(
|
||||||
self, obj: T_DBM, hard_delete: bool = False
|
self, obj: T_DBM, hard_delete: bool = False
|
||||||
) -> str:
|
) -> str:
|
||||||
if hard_delete:
|
if hard_delete:
|
||||||
return f"""
|
return f"""
|
||||||
@ -370,9 +370,6 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
if isinstance(value, NoneType):
|
if isinstance(value, NoneType):
|
||||||
return "NULL"
|
return "NULL"
|
||||||
|
|
||||||
if value is None:
|
|
||||||
return "NULL"
|
|
||||||
|
|
||||||
if isinstance(value, Enum):
|
if isinstance(value, Enum):
|
||||||
return str(value.value)
|
return str(value.value)
|
||||||
|
|
||||||
@ -406,21 +403,21 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
return cast_type(value)
|
return cast_type(value)
|
||||||
|
|
||||||
def _build_conditional_query(
|
def _build_conditional_query(
|
||||||
self,
|
self,
|
||||||
filters: AttributeFilters = None,
|
filters: AttributeFilters = None,
|
||||||
sorts: AttributeSorts = None,
|
sorts: AttributeSorts = None,
|
||||||
take: int = None,
|
take: int = None,
|
||||||
skip: int = None,
|
skip: int = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
query = f"SELECT * FROM {self._table_name}"
|
query = f"SELECT * FROM {self._table_name}"
|
||||||
|
|
||||||
if filters is not None and (not isinstance(filters, list) or len(filters) > 0):
|
if filters and len(filters) > 0:
|
||||||
query += f" WHERE {self._build_conditions(filters)}"
|
query += f" WHERE {self._build_conditions(filters)}"
|
||||||
if sorts is not None and (not isinstance(sorts, list) or len(sorts) > 0):
|
if sorts and len(sorts) > 0:
|
||||||
query += f" ORDER BY {self._build_order_by(sorts)}"
|
query += f" ORDER BY {self._build_order_by(sorts)}"
|
||||||
if take is not None:
|
if take:
|
||||||
query += f" LIMIT {take}"
|
query += f" LIMIT {take}"
|
||||||
if skip is not None:
|
if skip:
|
||||||
query += f" OFFSET {skip}"
|
query += f" OFFSET {skip}"
|
||||||
return query
|
return query
|
||||||
|
|
||||||
@ -455,21 +452,14 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sub_conditions.append(
|
sub_conditions.append(
|
||||||
self._get_value_validation_sql(db_name, value)
|
f"{db_name} = {self._get_value_sql(value)}"
|
||||||
)
|
)
|
||||||
conditions.append(f"({' OR '.join(sub_conditions)})")
|
conditions.append(f"({' OR '.join(sub_conditions)})")
|
||||||
else:
|
else:
|
||||||
conditions.append(self._get_value_validation_sql(db_name, values))
|
conditions.append(f"{db_name} = {self._get_value_sql(values)}")
|
||||||
|
|
||||||
return " AND ".join(conditions)
|
return " AND ".join(conditions)
|
||||||
|
|
||||||
def _get_value_validation_sql(self, field: str, value: Any):
|
|
||||||
value = self._get_value_sql(value)
|
|
||||||
|
|
||||||
if value == "NULL":
|
|
||||||
return f"{field} IS NULL"
|
|
||||||
return f"{field} = {value}"
|
|
||||||
|
|
||||||
def _build_condition(self, db_name: str, operator: str, value: Any) -> str:
|
def _build_condition(self, db_name: str, operator: str, value: Any) -> str:
|
||||||
"""
|
"""
|
||||||
Build individual SQL condition based on the operator
|
Build individual SQL condition based on the operator
|
||||||
|
@ -24,25 +24,18 @@ def get_value(
|
|||||||
|
|
||||||
if key in source:
|
if key in source:
|
||||||
value = source[key]
|
value = source[key]
|
||||||
if isinstance(
|
if isinstance(value, cast_type):
|
||||||
value,
|
|
||||||
cast_type if not hasattr(cast_type, "__origin__") else cast_type.__origin__,
|
|
||||||
):
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if cast_type == bool:
|
if cast_type == bool:
|
||||||
return value.lower() in ["true", "1"]
|
return value.lower() in ["true", "1"]
|
||||||
|
|
||||||
if (
|
if cast_type == list:
|
||||||
cast_type
|
|
||||||
if not hasattr(cast_type, "__origin__")
|
|
||||||
else cast_type.__origin__
|
|
||||||
) == list:
|
|
||||||
subtype = (
|
subtype = (
|
||||||
cast_type.__args__[0] if hasattr(cast_type, "__args__") else None
|
cast_type.__args__[0] if hasattr(cast_type, "__args__") else None
|
||||||
)
|
)
|
||||||
value = value.split(list_delimiter)
|
value = ast.literal_eval(value)
|
||||||
return [
|
return [
|
||||||
subtype(item) if subtype is not None else item for item in value
|
subtype(item) if subtype is not None else item for item in value
|
||||||
]
|
]
|
||||||
|
@ -42,9 +42,17 @@ class User(DbModelABC):
|
|||||||
|
|
||||||
@async_property
|
@async_property
|
||||||
async def permissions(self):
|
async def permissions(self):
|
||||||
from data.schemas.administration.user_dao import userDao
|
from data.schemas.permission.role_user_dao import roleUserDao
|
||||||
|
from data.schemas.permission.role_permission_dao import rolePermissionDao
|
||||||
|
from data.schemas.permission.permission_dao import permissionDao
|
||||||
|
|
||||||
return await userDao.get_permissions(self.id)
|
x = [
|
||||||
|
rp.permission_id
|
||||||
|
for x in await roleUserDao.get_by_user_id(self.id)
|
||||||
|
for rp in await rolePermissionDao.get_by_role_id(x.role_id)
|
||||||
|
]
|
||||||
|
|
||||||
|
return await permissionDao.get_by({"id": {"in": x}})
|
||||||
|
|
||||||
async def has_permission(self, permission: Permissions) -> bool:
|
async def has_permission(self, permission: Permissions) -> bool:
|
||||||
from data.schemas.administration.user_dao import userDao
|
from data.schemas.administration.user_dao import userDao
|
||||||
|
@ -33,30 +33,13 @@ class UserDao(DbModelDaoABC[User]):
|
|||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM permission.role_users ru
|
FROM permission.role_users ru
|
||||||
JOIN permission.role_permissions rp ON ru.roleId = rp.roleId
|
JOIN permission.role_permissions rp ON ru.roleId = rp.roleId
|
||||||
WHERE ru.userId = {user_id}
|
WHERE ru.userId = {user_id} AND rp.permissionId = {p.id};
|
||||||
AND rp.permissionId = {p.id}
|
|
||||||
AND ru.deleted = FALSE
|
|
||||||
AND rp.deleted = FALSE;
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
if result is None or len(result) == 0:
|
if result is None or len(result) == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return result[0]["count"] > 0
|
return True
|
||||||
|
|
||||||
async def get_permissions(self, user_id: int) -> list[Permissions]:
|
|
||||||
result = await self._db.select_map(
|
|
||||||
f"""
|
|
||||||
SELECT p.*
|
|
||||||
FROM permission.permissions p
|
|
||||||
JOIN permission.role_permissions rp ON p.id = rp.permissionId
|
|
||||||
JOIN permission.role_users ru ON rp.roleId = ru.roleId
|
|
||||||
WHERE ru.userId = {user_id}
|
|
||||||
AND rp.deleted = FALSE
|
|
||||||
AND ru.deleted = FALSE;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
return [Permissions(p["name"]) for p in result]
|
|
||||||
|
|
||||||
|
|
||||||
userDao = UserDao()
|
userDao = UserDao()
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from core.database.abc.db_model_abc import DbModelABC
|
|
||||||
from core.typing import SerialId
|
|
||||||
|
|
||||||
|
|
||||||
class Domain(DbModelABC):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
id: SerialId,
|
|
||||||
name: str,
|
|
||||||
deleted: bool = False,
|
|
||||||
editor_id: Optional[SerialId] = None,
|
|
||||||
created: Optional[datetime] = None,
|
|
||||||
updated: Optional[datetime] = None,
|
|
||||||
):
|
|
||||||
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, value: str):
|
|
||||||
self._name = value
|
|
@ -1,22 +0,0 @@
|
|||||||
from core.logger import DBLogger
|
|
||||||
from data.schemas.public.domain import Domain
|
|
||||||
from data.schemas.public.group import Group
|
|
||||||
|
|
||||||
logger = DBLogger(__name__)
|
|
||||||
|
|
||||||
from core.database.abc.db_model_dao_abc import DbModelDaoABC
|
|
||||||
|
|
||||||
|
|
||||||
class DomainDao(DbModelDaoABC[Group]):
|
|
||||||
def __init__(self):
|
|
||||||
DbModelDaoABC.__init__(self, __name__, Group, "public.domains")
|
|
||||||
self.attribute(Domain.name, str)
|
|
||||||
|
|
||||||
async def get_by_name(self, name: str) -> Group:
|
|
||||||
result = await self._db.select_map(
|
|
||||||
f"SELECT * FROM {self._table_name} WHERE Name = '{name}'"
|
|
||||||
)
|
|
||||||
return self.to_object(result[0])
|
|
||||||
|
|
||||||
|
|
||||||
domainDao = DomainDao()
|
|
@ -17,19 +17,5 @@ class GroupDao(DbModelDaoABC[Group]):
|
|||||||
)
|
)
|
||||||
return self.to_object(result[0])
|
return self.to_object(result[0])
|
||||||
|
|
||||||
async def get_roles(self, group_id: int):
|
|
||||||
result = await self._db.select_map(
|
|
||||||
f"""
|
|
||||||
SELECT r.*
|
|
||||||
FROM permission.roles r
|
|
||||||
JOIN public.group_role_assignments gra ON r.id = gra.roleId
|
|
||||||
WHERE gra.groupId = {group_id}
|
|
||||||
AND gra.deleted = FALSE
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
from data.schemas.permission.role_dao import roleDao
|
|
||||||
|
|
||||||
return [roleDao.to_object(x) for x in result]
|
|
||||||
|
|
||||||
|
|
||||||
groupDao = GroupDao()
|
groupDao = GroupDao()
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from async_property import async_property
|
|
||||||
|
|
||||||
from core.database.abc.db_model_abc import DbModelABC
|
|
||||||
from core.typing import SerialId
|
|
||||||
|
|
||||||
|
|
||||||
class GroupRoleAssignment(DbModelABC):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
id: SerialId,
|
|
||||||
group_id: SerialId,
|
|
||||||
role_id: SerialId,
|
|
||||||
deleted: bool = False,
|
|
||||||
editor_id: Optional[SerialId] = None,
|
|
||||||
created: Optional[datetime] = None,
|
|
||||||
updated: Optional[datetime] = None,
|
|
||||||
):
|
|
||||||
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
|
|
||||||
self._group_id = group_id
|
|
||||||
self._role_id = role_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_id(self) -> SerialId:
|
|
||||||
return self._group_id
|
|
||||||
|
|
||||||
@async_property
|
|
||||||
async def group(self):
|
|
||||||
from data.schemas.public.group_dao import groupDao
|
|
||||||
|
|
||||||
return await groupDao.get_by_id(self._group_id)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def role_id(self) -> SerialId:
|
|
||||||
return self._role_id
|
|
||||||
|
|
||||||
@async_property
|
|
||||||
async def role(self):
|
|
||||||
from data.schemas.permission.role_dao import roleDao
|
|
||||||
|
|
||||||
return await roleDao.get_by_id(self._role_id)
|
|
@ -1,37 +0,0 @@
|
|||||||
from core.logger import DBLogger
|
|
||||||
from data.schemas.public.group_role_assignment import GroupRoleAssignment
|
|
||||||
|
|
||||||
logger = DBLogger(__name__)
|
|
||||||
|
|
||||||
from core.database.abc.db_model_dao_abc import DbModelDaoABC
|
|
||||||
|
|
||||||
|
|
||||||
class GroupRoleAssignmentDao(DbModelDaoABC[GroupRoleAssignment]):
|
|
||||||
def __init__(self):
|
|
||||||
DbModelDaoABC.__init__(
|
|
||||||
self, __name__, GroupRoleAssignment, "public.group_role_assignments"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute(GroupRoleAssignment.group_id, int)
|
|
||||||
self.attribute(GroupRoleAssignment.role_id, int)
|
|
||||||
|
|
||||||
async def get_by_group_id(
|
|
||||||
self, gid: int, with_deleted=False
|
|
||||||
) -> list[GroupRoleAssignment]:
|
|
||||||
f = [{GroupRoleAssignment.group_id: gid}]
|
|
||||||
if not with_deleted:
|
|
||||||
f.append({GroupRoleAssignment.deleted: False})
|
|
||||||
|
|
||||||
return await self.find_by(f)
|
|
||||||
|
|
||||||
async def get_by_role_id(
|
|
||||||
self, rid: int, with_deleted=False
|
|
||||||
) -> list[GroupRoleAssignment]:
|
|
||||||
f = [{GroupRoleAssignment.role_id: rid}]
|
|
||||||
if not with_deleted:
|
|
||||||
f.append({GroupRoleAssignment.deleted: False})
|
|
||||||
|
|
||||||
return await self.find_by(f)
|
|
||||||
|
|
||||||
|
|
||||||
groupRoleAssignmentDao = GroupRoleAssignmentDao()
|
|
@ -16,7 +16,6 @@ class ShortUrl(DbModelABC):
|
|||||||
target_url: str,
|
target_url: str,
|
||||||
description: Optional[str],
|
description: Optional[str],
|
||||||
group_id: Optional[SerialId],
|
group_id: Optional[SerialId],
|
||||||
domain_id: Optional[SerialId],
|
|
||||||
loading_screen: Optional[str] = None,
|
loading_screen: Optional[str] = None,
|
||||||
deleted: bool = False,
|
deleted: bool = False,
|
||||||
editor_id: Optional[SerialId] = None,
|
editor_id: Optional[SerialId] = None,
|
||||||
@ -28,7 +27,6 @@ class ShortUrl(DbModelABC):
|
|||||||
self._target_url = target_url
|
self._target_url = target_url
|
||||||
self._description = description
|
self._description = description
|
||||||
self._group_id = group_id
|
self._group_id = group_id
|
||||||
self._domain_id = domain_id
|
|
||||||
self._loading_screen = loading_screen
|
self._loading_screen = loading_screen
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -72,23 +70,6 @@ class ShortUrl(DbModelABC):
|
|||||||
|
|
||||||
return await groupDao.get_by_id(self._group_id)
|
return await groupDao.get_by_id(self._group_id)
|
||||||
|
|
||||||
@property
|
|
||||||
def domain_id(self) -> SerialId:
|
|
||||||
return self._domain_id
|
|
||||||
|
|
||||||
@domain_id.setter
|
|
||||||
def domain_id(self, value: SerialId):
|
|
||||||
self._domain_id = value
|
|
||||||
|
|
||||||
@async_property
|
|
||||||
async def domain(self) -> Optional[Group]:
|
|
||||||
if self._domain_id is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
from data.schemas.public.domain_dao import domainDao
|
|
||||||
|
|
||||||
return await domainDao.get_by_id(self._domain_id)
|
|
||||||
|
|
||||||
@async_property
|
@async_property
|
||||||
async def visit_count(self) -> int:
|
async def visit_count(self) -> int:
|
||||||
from data.schemas.public.short_url_visit_dao import shortUrlVisitDao
|
from data.schemas.public.short_url_visit_dao import shortUrlVisitDao
|
||||||
|
@ -13,7 +13,6 @@ class ShortUrlDao(DbModelDaoABC[ShortUrl]):
|
|||||||
self.attribute(ShortUrl.target_url, str)
|
self.attribute(ShortUrl.target_url, str)
|
||||||
self.attribute(ShortUrl.description, str)
|
self.attribute(ShortUrl.description, str)
|
||||||
self.attribute(ShortUrl.group_id, int)
|
self.attribute(ShortUrl.group_id, int)
|
||||||
self.attribute(ShortUrl.domain_id, int)
|
|
||||||
self.attribute(ShortUrl.loading_screen, bool)
|
self.attribute(ShortUrl.loading_screen, bool)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
CREATE
|
|
||||||
SCHEMA IF NOT EXISTS public;
|
|
||||||
|
|
||||||
-- groups
|
|
||||||
CREATE TABLE IF NOT EXISTS public.domains
|
|
||||||
(
|
|
||||||
Id SERIAL PRIMARY KEY,
|
|
||||||
Name VARCHAR(255) NOT NULL,
|
|
||||||
-- for history
|
|
||||||
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
EditorId INT NULL REFERENCES administration.users (Id),
|
|
||||||
CreatedUtc timestamptz NOT NULL DEFAULT NOW(),
|
|
||||||
UpdatedUtc timestamptz NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.domains_history
|
|
||||||
(
|
|
||||||
LIKE public.domains
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TRIGGER domains_history_trigger
|
|
||||||
BEFORE INSERT OR UPDATE OR DELETE
|
|
||||||
ON public.domains
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION public.history_trigger_function();
|
|
||||||
|
|
||||||
ALTER TABLE public.short_urls
|
|
||||||
ADD COLUMN domainId INT NULL REFERENCES public.domains (Id);
|
|
||||||
|
|
||||||
ALTER TABLE public.short_urls_history
|
|
||||||
ADD COLUMN domainId INT NULL REFERENCES public.domains (Id);
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
CREATE
|
|
||||||
SCHEMA IF NOT EXISTS public;
|
|
||||||
|
|
||||||
-- groups
|
|
||||||
CREATE TABLE IF NOT EXISTS public.group_role_assignments
|
|
||||||
(
|
|
||||||
Id SERIAL PRIMARY KEY,
|
|
||||||
GroupId INT NOT NULL REFERENCES public.groups (Id),
|
|
||||||
RoleId INT NOT NULL REFERENCES permission.roles (Id),
|
|
||||||
-- for history
|
|
||||||
Deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
EditorId INT NULL REFERENCES administration.users (Id),
|
|
||||||
CreatedUtc timestamptz NOT NULL DEFAULT NOW(),
|
|
||||||
UpdatedUtc timestamptz NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.group_role_assignments_history
|
|
||||||
(
|
|
||||||
LIKE public.group_role_assignments
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TRIGGER group_role_assignment_history_trigger
|
|
||||||
BEFORE INSERT OR UPDATE OR DELETE
|
|
||||||
ON public.group_role_assignments
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION public.history_trigger_function();
|
|
||||||
|
|
@ -25,8 +25,7 @@ class PermissionSeeder(DataSeederABC):
|
|||||||
possible_permissions = [permission.value for permission in Permissions]
|
possible_permissions = [permission.value for permission in Permissions]
|
||||||
|
|
||||||
if len(permissions) == len(possible_permissions):
|
if len(permissions) == len(possible_permissions):
|
||||||
logger.info("Permissions already existing")
|
logger.info("Permissions already completed")
|
||||||
await self._update_missing_descriptions()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.warning("Permissions incomplete")
|
logger.warning("Permissions incomplete")
|
||||||
@ -42,7 +41,6 @@ class PermissionSeeder(DataSeederABC):
|
|||||||
if permission not in permission_names
|
if permission not in permission_names
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
await self._update_missing_descriptions()
|
|
||||||
|
|
||||||
await self._add_missing_to_role()
|
await self._add_missing_to_role()
|
||||||
await self._add_missing_to_api_key()
|
await self._add_missing_to_api_key()
|
||||||
@ -80,28 +78,3 @@ class PermissionSeeder(DataSeederABC):
|
|||||||
if permission.id not in [x.permission_id for x in admin_permissions]
|
if permission.id not in [x.permission_id for x in admin_permissions]
|
||||||
]
|
]
|
||||||
await apiKeyPermissionDao.create_many(to_assign)
|
await apiKeyPermissionDao.create_many(to_assign)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _update_missing_descriptions():
|
|
||||||
permissions = {
|
|
||||||
permission.name: permission
|
|
||||||
for permission in await permissionDao.find_by(
|
|
||||||
[{Permission.description: None}]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
to_update = []
|
|
||||||
|
|
||||||
if len(permissions) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
for key in PERMISSION_DESCRIPTIONS:
|
|
||||||
if key.value not in permissions:
|
|
||||||
continue
|
|
||||||
|
|
||||||
permissions[key.value].description = PERMISSION_DESCRIPTIONS[key]
|
|
||||||
to_update.append(permissions[key.value])
|
|
||||||
|
|
||||||
if len(to_update) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
await permissionDao.update_many(to_update)
|
|
||||||
|
@ -24,35 +24,16 @@ class Redirector(Flask):
|
|||||||
|
|
||||||
app = Redirector(__name__)
|
app = Redirector(__name__)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return render_template("404.html"), 404
|
return render_template("404.html"), 404
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<path:path>")
|
@app.route("/<path:path>")
|
||||||
async def _handle_request(path: str):
|
async def _handle_request(path: str):
|
||||||
short_url = await _find_short_url_by_url(path)
|
short_url = await _find_short_url_by_url(path)
|
||||||
if short_url is None:
|
if short_url is None:
|
||||||
return render_template("404.html"), 404
|
return render_template("404.html"), 404
|
||||||
|
|
||||||
domains = Environment.get("DOMAINS", list[str], [])
|
|
||||||
domain = await short_url.domain
|
|
||||||
logger.debug(
|
|
||||||
f"Domain: {domain.name if domain is not None else None}, request.host: {request.host}"
|
|
||||||
)
|
|
||||||
|
|
||||||
host = request.host
|
|
||||||
if ":" in host:
|
|
||||||
host = host.split(":")[0]
|
|
||||||
|
|
||||||
domain_strict_mode = Environment.get("DOMAIN_STRICT_MODE", bool, False)
|
|
||||||
if domain is not None and (
|
|
||||||
domain.name not in domains
|
|
||||||
or (domain_strict_mode and not host.endswith(domain.name))
|
|
||||||
):
|
|
||||||
return render_template("404.html"), 404
|
|
||||||
|
|
||||||
user_agent = request.headers.get("User-Agent", "").lower()
|
user_agent = request.headers.get("User-Agent", "").lower()
|
||||||
|
|
||||||
if "wheregoes" in user_agent or "someothertool" in user_agent:
|
if "wheregoes" in user_agent or "someothertool" in user_agent:
|
||||||
@ -78,7 +59,6 @@ async def _handle_short_url(path: str, short_url: ShortUrl):
|
|||||||
|
|
||||||
return _do_redirect(short_url.target_url)
|
return _do_redirect(short_url.target_url)
|
||||||
|
|
||||||
|
|
||||||
async def _track_visit(short_url: ShortUrl):
|
async def _track_visit(short_url: ShortUrl):
|
||||||
try:
|
try:
|
||||||
await shortUrlVisitDao.create(
|
await shortUrlVisitDao.create(
|
||||||
@ -87,7 +67,6 @@ async def _track_visit(short_url: ShortUrl):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to update short url {short_url.short_url} with error", e)
|
logger.error(f"Failed to update short url {short_url.short_url} with error", e)
|
||||||
|
|
||||||
|
|
||||||
async def _find_short_url_by_url(url: str) -> ShortUrl:
|
async def _find_short_url_by_url(url: str) -> ShortUrl:
|
||||||
return await shortUrlDao.find_single_by({ShortUrl.short_url: url})
|
return await shortUrlDao.find_single_by({ShortUrl.short_url: url})
|
||||||
|
|
||||||
|
@ -31,12 +31,6 @@ class Permissions(Enum):
|
|||||||
"""
|
"""
|
||||||
Public
|
Public
|
||||||
"""
|
"""
|
||||||
# domains
|
|
||||||
domains = "domains"
|
|
||||||
domains_create = "domains.create"
|
|
||||||
domains_update = "domains.update"
|
|
||||||
domains_delete = "domains.delete"
|
|
||||||
|
|
||||||
# groups
|
# groups
|
||||||
groups = "groups"
|
groups = "groups"
|
||||||
groups_create = "groups.create"
|
groups_create = "groups.create"
|
||||||
@ -45,7 +39,6 @@ class Permissions(Enum):
|
|||||||
|
|
||||||
# short_urls
|
# short_urls
|
||||||
short_urls = "short_urls"
|
short_urls = "short_urls"
|
||||||
short_urls_by_assignment = "short_urls.by_assignment"
|
|
||||||
short_urls_create = "short_urls.create"
|
short_urls_create = "short_urls.create"
|
||||||
short_urls_update = "short_urls.update"
|
short_urls_update = "short_urls.update"
|
||||||
short_urls_delete = "short_urls.delete"
|
short_urls_delete = "short_urls.delete"
|
||||||
@ -53,6 +46,4 @@ class Permissions(Enum):
|
|||||||
|
|
||||||
PERMISSION_DESCRIPTIONS = {
|
PERMISSION_DESCRIPTIONS = {
|
||||||
Permissions.users_update: "Edit users, including changing their roles",
|
Permissions.users_update: "Edit users, including changing their roles",
|
||||||
Permissions.short_urls: "See all URLs",
|
|
||||||
Permissions.short_urls_by_assignment: "See all short urls assigned to a group by role",
|
|
||||||
}
|
}
|
||||||
|
1
version.txt
Normal file
1
version.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.1.0
|
@ -1,4 +1,4 @@
|
|||||||
<main *ngIf="isLoggedIn && !hideUI; else home" [class]="theme">
|
<main *ngIf="isLoggedIn && !hideUI; else home">
|
||||||
<app-header></app-header>
|
<app-header></app-header>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { Component, OnDestroy } from '@angular/core';
|
import { Component, OnDestroy } from "@angular/core";
|
||||||
import { SidebarService } from 'src/app/service/sidebar.service';
|
import { SidebarService } from "src/app/service/sidebar.service";
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { GuiService } from 'src/app/service/gui.service';
|
import { GuiService } from "src/app/service/gui.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: "app-root",
|
||||||
templateUrl: './app.component.html',
|
templateUrl: "./app.component.html",
|
||||||
styleUrl: './app.component.scss',
|
styleUrl: "./app.component.scss",
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnDestroy {
|
export class AppComponent implements OnDestroy {
|
||||||
theme = 'open-redirect';
|
|
||||||
showSidebar = false;
|
showSidebar = false;
|
||||||
hideUI = false;
|
hideUI = false;
|
||||||
isLoggedIn = false;
|
isLoggedIn = false;
|
||||||
@ -20,27 +19,23 @@ export class AppComponent implements OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private sidebar: SidebarService,
|
private sidebar: SidebarService,
|
||||||
private auth: AuthService,
|
private auth: AuthService,
|
||||||
private gui: GuiService
|
private gui: GuiService,
|
||||||
) {
|
) {
|
||||||
this.auth.loadUser();
|
this.auth.loadUser();
|
||||||
|
|
||||||
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(user => {
|
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe((user) => {
|
||||||
this.isLoggedIn = user !== null && user !== undefined;
|
this.isLoggedIn = user !== null && user !== undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sidebar.visible$
|
this.sidebar.visible$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(visible => {
|
.subscribe((visible) => {
|
||||||
this.showSidebar = visible;
|
this.showSidebar = visible;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gui.hideGui$.pipe(takeUntil(this.unsubscribe$)).subscribe(hide => {
|
this.gui.hideGui$.pipe(takeUntil(this.unsubscribe$)).subscribe((hide) => {
|
||||||
this.hideUI = hide;
|
this.hideUI = hide;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.gui.theme$.pipe(takeUntil(this.unsubscribe$)).subscribe(theme => {
|
|
||||||
this.theme = theme;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@ -17,18 +17,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<p-button
|
|
||||||
type="button"
|
|
||||||
icon="pi pi-palette"
|
|
||||||
class="btn icon-btn p-button-text"
|
|
||||||
(onClick)="themeMenu.toggle($event)"></p-button>
|
|
||||||
<p-menu
|
|
||||||
#themeMenu
|
|
||||||
[popup]="true"
|
|
||||||
[model]="themeList"
|
|
||||||
class="lang-menu"></p-menu>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<p-button
|
<p-button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { MenuItem, PrimeNGConfig } from 'primeng/api';
|
import { MenuItem, PrimeNGConfig } from "primeng/api";
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from "rxjs";
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from "rxjs/operators";
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { GuiService } from 'src/app/service/gui.service';
|
import { GuiService } from "src/app/service/gui.service";
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
import { AuthService } from 'src/app/service/auth.service';
|
import { AuthService } from "src/app/service/auth.service";
|
||||||
import { MenuElement } from 'src/app/model/view/menu-element';
|
import { MenuElement } from "src/app/model/view/menu-element";
|
||||||
import { SidebarService } from 'src/app/service/sidebar.service';
|
import { SidebarService } from "src/app/service/sidebar.service";
|
||||||
import { SettingsService } from 'src/app/service/settings.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-header',
|
selector: "app-header",
|
||||||
templateUrl: './header.component.html',
|
templateUrl: "./header.component.html",
|
||||||
styleUrls: ['./header.component.scss'],
|
styleUrls: ["./header.component.scss"],
|
||||||
})
|
})
|
||||||
export class HeaderComponent implements OnInit, OnDestroy {
|
export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
langList: MenuItem[] = [];
|
langList: MenuItem[] = [];
|
||||||
themeList: MenuItem[] = [];
|
|
||||||
|
|
||||||
userMenuList!: MenuItem[];
|
userMenuList!: MenuItem[];
|
||||||
user: User | null = null;
|
user: User | null = null;
|
||||||
private unsubscribe$ = new Subject<void>();
|
private unsubscribe$ = new Subject<void>();
|
||||||
@ -32,30 +29,22 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
private guiService: GuiService,
|
private guiService: GuiService,
|
||||||
private auth: AuthService,
|
private auth: AuthService,
|
||||||
private sidebarService: SidebarService,
|
private sidebarService: SidebarService,
|
||||||
private settings: SettingsService
|
|
||||||
) {
|
) {
|
||||||
this.guiService.isMobile$
|
this.guiService.isMobile$
|
||||||
.pipe(takeUntil(this.unsubscribe$))
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
.subscribe(isMobile => {
|
.subscribe((isMobile) => {
|
||||||
this.isMobile = isMobile;
|
this.isMobile = isMobile;
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
this.sidebarService.hide();
|
this.sidebarService.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(async user => {
|
this.auth.user$
|
||||||
this.user = user;
|
.pipe(takeUntil(this.unsubscribe$))
|
||||||
await this.initMenuLists();
|
.subscribe(async (user) => {
|
||||||
});
|
this.user = user;
|
||||||
|
await this.initMenuLists();
|
||||||
this.themeList = this.settings.settings.themes.map(theme => {
|
});
|
||||||
return {
|
|
||||||
label: theme.label,
|
|
||||||
command: () => {
|
|
||||||
this.guiService.theme$.next(theme.name);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -80,17 +69,17 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
async initLangMenuList() {
|
async initLangMenuList() {
|
||||||
this.langList = [
|
this.langList = [
|
||||||
{
|
{
|
||||||
label: 'English',
|
label: "English",
|
||||||
command: () => {
|
command: () => {
|
||||||
this.translate('en');
|
this.translate("en");
|
||||||
this.setLang('en');
|
this.setLang("en");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Deutsch',
|
label: "Deutsch",
|
||||||
command: () => {
|
command: () => {
|
||||||
this.translate('de');
|
this.translate("de");
|
||||||
this.setLang('de');
|
this.setLang("de");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -107,13 +96,13 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
separator: true,
|
separator: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.translateService.instant('header.logout'),
|
label: this.translateService.instant("header.logout"),
|
||||||
command: () => {
|
command: () => {
|
||||||
this.auth.logout().then(() => {
|
this.auth.logout().then(() => {
|
||||||
console.log('logout');
|
console.log("logout");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: 'pi pi-sign-out',
|
icon: "pi pi-sign-out",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -121,19 +110,19 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||||||
translate(lang: string) {
|
translate(lang: string) {
|
||||||
this.translateService.use(lang);
|
this.translateService.use(lang);
|
||||||
this.translateService
|
this.translateService
|
||||||
.get('primeng')
|
.get("primeng")
|
||||||
.subscribe(res => this.config.setTranslation(res));
|
.subscribe((res) => this.config.setTranslation(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLang() {
|
async loadLang() {
|
||||||
const lang = 'en';
|
const lang = "en";
|
||||||
this.setLang(lang);
|
this.setLang(lang);
|
||||||
this.translate(lang);
|
this.translate(lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLang(lang: string) {
|
setLang(lang: string) {
|
||||||
// this.settings.setSetting(`lang`, lang);
|
// this.settings.setSetting(`lang`, lang);
|
||||||
console.log('setLang', lang);
|
console.log("setLang", lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSidebar() {
|
toggleSidebar() {
|
||||||
|
@ -1,36 +1,30 @@
|
|||||||
export enum PermissionsEnum {
|
export enum PermissionsEnum {
|
||||||
// Administration
|
// Administration
|
||||||
apiKeys = 'api_keys',
|
apiKeys = "api_keys",
|
||||||
apiKeysCreate = 'api_keys.create',
|
apiKeysCreate = "api_keys.create",
|
||||||
apiKeysUpdate = 'api_keys.update',
|
apiKeysUpdate = "api_keys.update",
|
||||||
apiKeysDelete = 'api_keys.delete',
|
apiKeysDelete = "api_keys.delete",
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
users = 'users',
|
users = "users",
|
||||||
usersCreate = 'users.create',
|
usersCreate = "users.create",
|
||||||
usersUpdate = 'users.update',
|
usersUpdate = "users.update",
|
||||||
usersDelete = 'users.delete',
|
usersDelete = "users.delete",
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
roles = 'roles',
|
roles = "roles",
|
||||||
rolesCreate = 'roles.create',
|
rolesCreate = "roles.create",
|
||||||
rolesUpdate = 'roles.update',
|
rolesUpdate = "roles.update",
|
||||||
rolesDelete = 'roles.delete',
|
rolesDelete = "roles.delete",
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
domains = 'domains',
|
groups = "groups",
|
||||||
domainsCreate = 'domains.create',
|
groupsCreate = "groups.create",
|
||||||
domainsUpdate = 'domains.update',
|
groupsUpdate = "groups.update",
|
||||||
domainsDelete = 'domains.delete',
|
groupsDelete = "groups.delete",
|
||||||
|
|
||||||
groups = 'groups',
|
shortUrls = "short_urls",
|
||||||
groupsCreate = 'groups.create',
|
shortUrlsCreate = "short_urls.create",
|
||||||
groupsUpdate = 'groups.update',
|
shortUrlsUpdate = "short_urls.update",
|
||||||
groupsDelete = 'groups.delete',
|
shortUrlsDelete = "short_urls.delete",
|
||||||
|
|
||||||
shortUrls = 'short_urls',
|
|
||||||
shortUrlsByAssignment = 'short_urls.by_assignment',
|
|
||||||
shortUrlsCreate = 'short_urls.create',
|
|
||||||
shortUrlsUpdate = 'short_urls.update',
|
|
||||||
shortUrlsDelete = 'short_urls.delete',
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
|
||||||
|
|
||||||
export interface Domain extends DbModel {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DomainCreateInput {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DomainUpdateInput {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
@ -1,18 +1,14 @@
|
|||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
import { DbModel } from 'src/app/model/entities/db-model';
|
||||||
import { Role } from 'src/app/model/entities/role';
|
|
||||||
|
|
||||||
export interface Group extends DbModel {
|
export interface Group extends DbModel {
|
||||||
name: string;
|
name: string;
|
||||||
roles: Role[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupCreateInput {
|
export interface GroupCreateInput {
|
||||||
name: string;
|
name: string;
|
||||||
roles: Role[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupUpdateInput {
|
export interface GroupUpdateInput {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
roles: Role[];
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { DbModel } from 'src/app/model/entities/db-model';
|
import { DbModel } from 'src/app/model/entities/db-model';
|
||||||
import { Group } from 'src/app/model/entities/group';
|
import { Group } from 'src/app/model/entities/group';
|
||||||
import { Domain } from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
export interface ShortUrl extends DbModel {
|
export interface ShortUrl extends DbModel {
|
||||||
shortUrl: string;
|
shortUrl: string;
|
||||||
@ -9,7 +8,6 @@ export interface ShortUrl extends DbModel {
|
|||||||
loadingScreen: boolean;
|
loadingScreen: boolean;
|
||||||
visits: number;
|
visits: number;
|
||||||
group?: Group;
|
group?: Group;
|
||||||
domain?: Domain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShortUrlDto {
|
export interface ShortUrlDto {
|
||||||
@ -24,7 +22,6 @@ export interface ShortUrlCreateInput {
|
|||||||
description: string;
|
description: string;
|
||||||
loadingScreen: boolean;
|
loadingScreen: boolean;
|
||||||
groupId: number;
|
groupId: number;
|
||||||
domainId: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShortUrlUpdateInput {
|
export interface ShortUrlUpdateInput {
|
||||||
@ -34,5 +31,4 @@ export interface ShortUrlUpdateInput {
|
|||||||
description: string;
|
description: string;
|
||||||
loadingScreen: boolean;
|
loadingScreen: boolean;
|
||||||
groupId: number;
|
groupId: number;
|
||||||
domainId: number;
|
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,6 @@ import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
|||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
|
||||||
path: 'domains',
|
|
||||||
loadChildren: () =>
|
|
||||||
import('src/app/modules/admin/domains/domains.module').then(
|
|
||||||
m => m.DomainsModule
|
|
||||||
),
|
|
||||||
canActivate: [PermissionGuard],
|
|
||||||
data: { permissions: [PermissionsEnum.domains] },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'groups',
|
path: 'groups',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
@ -31,12 +22,7 @@ const routes: Routes = [
|
|||||||
m => m.ShortUrlsModule
|
m => m.ShortUrlsModule
|
||||||
),
|
),
|
||||||
canActivate: [PermissionGuard],
|
canActivate: [PermissionGuard],
|
||||||
data: {
|
data: { permissions: [PermissionsEnum.shortUrls] },
|
||||||
permissions: [
|
|
||||||
PermissionsEnum.shortUrls,
|
|
||||||
PermissionsEnum.shortUrlsByAssignment,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'administration',
|
path: 'administration',
|
||||||
|
@ -1,73 +1,67 @@
|
|||||||
<app-form-page
|
<app-form-page
|
||||||
*ngIf="node"
|
*ngIf="node"
|
||||||
[formGroup]="form"
|
[formGroup]="form"
|
||||||
[isUpdate]="isUpdate"
|
[isUpdate]="isUpdate"
|
||||||
(onSave)="save()"
|
(onSave)="save()"
|
||||||
(onClose)="close()">
|
(onClose)="close()">
|
||||||
<ng-template formPageHeader let-isUpdate>
|
<ng-template formPageHeader let-isUpdate>
|
||||||
<h2>
|
<h2>
|
||||||
{{ 'common.role' | translate }}
|
{{ 'common.role' | translate }}
|
||||||
{{
|
{{
|
||||||
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
||||||
| translate
|
| translate
|
||||||
}}
|
}}
|
||||||
</h2>
|
</h2>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template formPageContent>
|
<ng-template formPageContent>
|
||||||
<div class="form-page-input">
|
<div class="form-page-input">
|
||||||
<p class="label">{{ 'common.id' | translate }}</p>
|
<p class="label">{{ 'common.id' | translate }}</p>
|
||||||
<input pInputText class="value" type="number" formControlName="id"/>
|
<input pInputText class="value" type="number" formControlName="id" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-page-input">
|
<div class="form-page-input">
|
||||||
<p class="label">{{ 'common.name' | translate }}</p>
|
<p class="label">{{ 'common.name' | translate }}</p>
|
||||||
<input pInputText class="value" type="text" formControlName="name"/>
|
<input pInputText class="value" type="text" formControlName="name" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-page-input">
|
<div class="form-page-input">
|
||||||
<p class="label">{{ 'common.description' | translate }}</p>
|
<p class="label">{{ 'common.description' | translate }}</p>
|
||||||
<input
|
<input
|
||||||
pInputText
|
pInputText
|
||||||
class="value"
|
class="value"
|
||||||
type="text"
|
type="text"
|
||||||
formControlName="description"/>
|
formControlName="description" />
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
<div *ngFor="let group of Object.keys(permissionGroups)">
|
<div *ngFor="let group of Object.keys(permissionGroups)">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<label class="flex-1" for="roles.permission_groups.{{ group }}">
|
<label class="flex-1" for="roles.permission_groups.{{ group }}">
|
||||||
<h3>
|
<h3>
|
||||||
{{ 'permissions.' + group | translate }}
|
{{ 'permissions.' + group | translate }}
|
||||||
</h3>
|
</h3>
|
||||||
</label>
|
</label>
|
||||||
<form #form="ngForm">
|
<form #form="ngForm">
|
||||||
<p-inputSwitch
|
<p-inputSwitch
|
||||||
name="roles.permission_groups.{{ group }}"
|
name="roles.permission_groups.{{ group }}"
|
||||||
(onChange)="toggleGroup($event, group)"
|
(onChange)="toggleGroup($event, group)"
|
||||||
[ngModel]="isGroupChecked(group)"></p-inputSwitch>
|
[ngModel]="isGroupChecked(group)"></p-inputSwitch>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngFor="let permission of permissionGroups[group]"
|
*ngFor="let permission of permissionGroups[group]"
|
||||||
class="flex flex-col">
|
class="flex items-center justify-between w-full">
|
||||||
<div class="flex items-center justify-between w-full">
|
<label class="flex-1" for="{{ permission.name }}">
|
||||||
<label class="flex-1" for="{{ permission.name }}">
|
{{ 'permissions.' + permission.name | translate }}
|
||||||
{{ 'permissions.' + permission.name | translate }}
|
</label>
|
||||||
</label>
|
<p-inputSwitch [formControlName]="permission.name">
|
||||||
<p-inputSwitch class="flex items-center justify-center" [formControlName]="permission.name">
|
>
|
||||||
</p-inputSwitch>
|
</p-inputSwitch>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="permission.description">
|
|
||||||
<p class="text-sm text-gray-500">
|
|
||||||
{{ permission.description }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
</app-form-page>
|
</app-form-page>
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import {
|
import {
|
||||||
Permission,
|
Permission,
|
||||||
Role,
|
Role,
|
||||||
RoleCreateInput,
|
RoleCreateInput,
|
||||||
RoleUpdateInput,
|
RoleUpdateInput,
|
||||||
} from 'src/app/model/entities/role';
|
} from "src/app/model/entities/role";
|
||||||
import { InputSwitchChangeEvent } from 'primeng/inputswitch';
|
import { InputSwitchChangeEvent } from "primeng/inputswitch";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from "rxjs";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import { RolesDataService } from 'src/app/modules/admin/administration/roles/roles.data.service';
|
import { RolesDataService } from "src/app/modules/admin/administration/roles/roles.data.service";
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-role-form-page',
|
selector: "app-role-form-page",
|
||||||
templateUrl: './role-form-page.component.html',
|
templateUrl: "./role-form-page.component.html",
|
||||||
styleUrl: './role-form-page.component.scss',
|
styleUrl: "./role-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class RoleFormPageComponent extends FormPageBase<
|
export class RoleFormPageComponent extends FormPageBase<
|
||||||
Role,
|
Role,
|
||||||
@ -27,10 +26,7 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
permissionGroups: { [key: string]: Permission[] } = {};
|
permissionGroups: { [key: string]: Permission[] } = {};
|
||||||
allPermissions: Permission[] = [];
|
allPermissions: Permission[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(private toast: ToastService) {
|
||||||
private toast: ToastService,
|
|
||||||
private translate: TranslateService
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.initializePermissions().then(() => {
|
this.initializePermissions().then(() => {
|
||||||
if (!this.nodeId) {
|
if (!this.nodeId) {
|
||||||
@ -40,7 +36,7 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataService.loadById(this.nodeId).subscribe(role => {
|
this.dataService.loadById(this.nodeId).subscribe((role) => {
|
||||||
this.node = role;
|
this.node = role;
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -49,17 +45,12 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
async initializePermissions() {
|
async initializePermissions() {
|
||||||
const permissions = await firstValueFrom(
|
const permissions = await firstValueFrom(
|
||||||
this.dataService.getAllPermissions()
|
this.dataService.getAllPermissions(),
|
||||||
);
|
);
|
||||||
this.allPermissions = permissions.map(x => {
|
this.allPermissions = permissions;
|
||||||
const key = `permission_descriptions.${x.name}`;
|
|
||||||
const description = this.translate.instant(key);
|
|
||||||
x.description = description === key ? undefined : description;
|
|
||||||
return x;
|
|
||||||
});
|
|
||||||
this.permissionGroups = permissions.reduce(
|
this.permissionGroups = permissions.reduce(
|
||||||
(acc, p) => {
|
(acc, p) => {
|
||||||
const group = p.name.includes('.') ? p.name.split('.')[0] : p.name;
|
const group = p.name.includes(".") ? p.name.split(".")[0] : p.name;
|
||||||
|
|
||||||
if (!acc[group]) {
|
if (!acc[group]) {
|
||||||
acc[group] = [];
|
acc[group] = [];
|
||||||
@ -67,10 +58,10 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
acc[group].push(p);
|
acc[group].push(p);
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{} as { [key: string]: Permission[] }
|
{} as { [key: string]: Permission[] },
|
||||||
);
|
);
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.addControl(p.name, new FormControl<boolean>(false));
|
this.form.addControl(p.name, new FormControl<boolean>(false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -85,48 +76,48 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
name: new FormControl<string | undefined>(undefined, Validators.required),
|
name: new FormControl<string | undefined>(undefined, Validators.required),
|
||||||
description: new FormControl<string | undefined>(undefined),
|
description: new FormControl<string | undefined>(undefined),
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: Role) {
|
setForm(node?: Role) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['name'].setValue(node?.name);
|
this.form.controls["name"].setValue(node?.name);
|
||||||
this.form.controls['description'].setValue(node?.description);
|
this.form.controls["description"].setValue(node?.description);
|
||||||
|
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
const permissions = node.permissions ?? [];
|
const permissions = node.permissions ?? [];
|
||||||
|
|
||||||
permissions.forEach(p => {
|
permissions.forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(true);
|
this.form.controls[p.name].setValue(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): RoleCreateInput {
|
getCreateInput(): RoleCreateInput {
|
||||||
return {
|
return {
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
: (this.form.controls["name"].value ?? undefined),
|
||||||
description: this.form.controls['description'].pristine
|
description: this.form.controls["description"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['description'].value ?? undefined),
|
: (this.form.controls["description"].value ?? undefined),
|
||||||
permissions: this.allPermissions.filter(
|
permissions: this.allPermissions.filter(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): RoleUpdateInput {
|
getUpdateInput(): RoleUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['name'].value,
|
: this.form.controls["name"].value,
|
||||||
description: this.form.controls['description'].pristine
|
description: this.form.controls["description"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['description'].value,
|
: this.form.controls["description"].value,
|
||||||
permissions: this.allPermissions.filter(
|
permissions: this.allPermissions.filter(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -134,7 +125,7 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
create(role: RoleCreateInput): void {
|
create(role: RoleCreateInput): void {
|
||||||
this.dataService.create(role).subscribe(() => {
|
this.dataService.create(role).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -142,20 +133,20 @@ export class RoleFormPageComponent extends FormPageBase<
|
|||||||
update(role: RoleUpdateInput): void {
|
update(role: RoleUpdateInput): void {
|
||||||
this.dataService.update(role).subscribe(() => {
|
this.dataService.update(role).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
toggleGroup(event: InputSwitchChangeEvent, group: string) {
|
||||||
this.permissionGroups[group].forEach(p => {
|
this.permissionGroups[group].forEach((p) => {
|
||||||
this.form.controls[p.name].setValue(event.checked);
|
this.form.controls[p.name].setValue(event.checked);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroupChecked(group: string) {
|
isGroupChecked(group: string) {
|
||||||
return this.permissionGroups[group].every(
|
return this.permissionGroups[group].every(
|
||||||
p => this.form.controls[p.name].value
|
(p) => this.form.controls[p.name].value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import {
|
import {
|
||||||
Permission,
|
Permission,
|
||||||
Role,
|
Role,
|
||||||
RoleCreateInput,
|
RoleCreateInput,
|
||||||
RoleUpdateInput,
|
RoleUpdateInput,
|
||||||
} from 'src/app/model/entities/role';
|
} from "src/app/model/entities/role";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RolesDataService
|
export class RolesDataService
|
||||||
@ -32,7 +32,7 @@ export class RolesDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ export class RolesDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<Role>> {
|
): Observable<QueryResult<Role>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ roles: QueryResult<Role> }>({
|
.query<{ roles: QueryResult<Role> }>({
|
||||||
@ -75,12 +75,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.roles));
|
.pipe(map((result) => result.data.roles));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<Role> {
|
loadById(id: number): Observable<Role> {
|
||||||
@ -112,12 +112,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.roles.nodes[0]));
|
.pipe(map((result) => result.data.roles.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: RoleCreateInput): Observable<Role | undefined> {
|
create(object: RoleCreateInput): Observable<Role | undefined> {
|
||||||
@ -145,17 +145,17 @@ export class RolesDataService
|
|||||||
input: {
|
input: {
|
||||||
name: object.name,
|
name: object.name,
|
||||||
description: object.description,
|
description: object.description,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.create));
|
.pipe(map((result) => result.data?.role.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: RoleUpdateInput): Observable<Role | undefined> {
|
update(object: RoleUpdateInput): Observable<Role | undefined> {
|
||||||
@ -184,17 +184,17 @@ export class RolesDataService
|
|||||||
id: object.id,
|
id: object.id,
|
||||||
name: object.name,
|
name: object.name,
|
||||||
description: object.description,
|
description: object.description,
|
||||||
permissions: object.permissions?.map(x => x.id),
|
permissions: object.permissions?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.update));
|
.pipe(map((result) => result.data?.role.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: Role): Observable<boolean> {
|
delete(object: Role): Observable<boolean> {
|
||||||
@ -212,12 +212,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.delete ?? false));
|
.pipe(map((result) => result.data?.role.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: Role): Observable<boolean> {
|
restore(object: Role): Observable<boolean> {
|
||||||
@ -235,12 +235,12 @@ export class RolesDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.role.restore ?? false));
|
.pipe(map((result) => result.data?.role.restore ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllPermissions(): Observable<Permission[]> {
|
getAllPermissions(): Observable<Permission[]> {
|
||||||
@ -252,19 +252,18 @@ export class RolesDataService
|
|||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
description
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.permissions.nodes));
|
.pipe(map((result) => result.data.permissions.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import {
|
import {
|
||||||
NotExistingUser,
|
NotExistingUser,
|
||||||
User,
|
User,
|
||||||
UserCreateInput,
|
UserCreateInput,
|
||||||
UserUpdateInput,
|
UserUpdateInput,
|
||||||
} from 'src/app/model/auth/user';
|
} from "src/app/model/auth/user";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
import { UsersDataService } from 'src/app/modules/admin/administration/users/users.data.service';
|
import { UsersDataService } from "src/app/modules/admin/administration/users/users.data.service";
|
||||||
import { CommonDataService } from 'src/app/modules/shared/service/common-data.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-form-page',
|
selector: "app-user-form-page",
|
||||||
templateUrl: './user-form-page.component.html',
|
templateUrl: "./user-form-page.component.html",
|
||||||
styleUrl: './user-form-page.component.scss',
|
styleUrl: "./user-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class UserFormPageComponent extends FormPageBase<
|
export class UserFormPageComponent extends FormPageBase<
|
||||||
User,
|
User,
|
||||||
@ -26,17 +25,14 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
notExistingUsers: NotExistingUser[] = [];
|
notExistingUsers: NotExistingUser[] = [];
|
||||||
roles: Role[] = [];
|
roles: Role[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(private toast: ToastService) {
|
||||||
private toast: ToastService,
|
|
||||||
private cds: CommonDataService
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.cds.getAllRoles().subscribe(roles => {
|
this.dataService.getAllRoles().subscribe((roles) => {
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.nodeId) {
|
if (!this.nodeId) {
|
||||||
this.dataService.getNotExistingUsersFromKeycloak().subscribe(users => {
|
this.dataService.getNotExistingUsersFromKeycloak().subscribe((users) => {
|
||||||
this.notExistingUsers = users;
|
this.notExistingUsers = users;
|
||||||
this.node = this.new();
|
this.node = this.new();
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
@ -45,7 +41,7 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataService.loadById(this.nodeId).subscribe(user => {
|
this.dataService.loadById(this.nodeId).subscribe((user) => {
|
||||||
this.node = user;
|
this.node = user;
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -63,47 +59,47 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
email: new FormControl<string | undefined>(undefined),
|
email: new FormControl<string | undefined>(undefined),
|
||||||
roles: new FormControl<Role[]>([]),
|
roles: new FormControl<Role[]>([]),
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
this.form.controls['username'].disable();
|
this.form.controls["username"].disable();
|
||||||
this.form.controls['email'].disable();
|
this.form.controls["email"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: User) {
|
setForm(node?: User) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['username'].setValue(node?.username);
|
this.form.controls["username"].setValue(node?.username);
|
||||||
this.form.controls['email'].setValue(node?.email);
|
this.form.controls["email"].setValue(node?.email);
|
||||||
this.form.controls['roles'].setValue(node?.roles ?? []);
|
this.form.controls["roles"].setValue(node?.roles ?? []);
|
||||||
|
|
||||||
if (this.notExistingUsers.length > 0) {
|
if (this.notExistingUsers.length > 0) {
|
||||||
this.form.controls['id'].enable();
|
this.form.controls["id"].enable();
|
||||||
this.form.controls['keycloakId'].reset(undefined, { required: true });
|
this.form.controls["keycloakId"].reset(undefined, { required: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): UserCreateInput {
|
getCreateInput(): UserCreateInput {
|
||||||
return {
|
return {
|
||||||
keycloakId: this.form.controls['keycloakId'].pristine
|
keycloakId: this.form.controls["keycloakId"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['keycloakId'].value,
|
: this.form.controls["keycloakId"].value,
|
||||||
roles: this.form.controls['roles'].pristine
|
roles: this.form.controls["roles"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['roles'].value,
|
: this.form.controls["roles"].value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): UserUpdateInput {
|
getUpdateInput(): UserUpdateInput {
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
roles: this.form.controls['roles'].pristine
|
roles: this.form.controls["roles"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: this.form.controls['roles'].value,
|
: this.form.controls["roles"].value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
create(user: UserCreateInput): void {
|
create(user: UserCreateInput): void {
|
||||||
this.dataService.create(user).subscribe(() => {
|
this.dataService.create(user).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -111,7 +107,7 @@ export class UserFormPageComponent extends FormPageBase<
|
|||||||
update(user: UserUpdateInput): void {
|
update(user: UserUpdateInput): void {
|
||||||
this.dataService.update(user).subscribe(() => {
|
this.dataService.update(user).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import {
|
import {
|
||||||
NotExistingUser,
|
NotExistingUser,
|
||||||
User,
|
User,
|
||||||
UserCreateInput,
|
UserCreateInput,
|
||||||
UserUpdateInput,
|
UserUpdateInput,
|
||||||
} from 'src/app/model/auth/user';
|
} from "src/app/model/auth/user";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
import { Role } from "src/app/model/entities/role";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersDataService
|
export class UsersDataService
|
||||||
@ -33,7 +33,7 @@ export class UsersDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ export class UsersDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<User>> {
|
): Observable<QueryResult<User>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ users: QueryResult<User> }>({
|
.query<{ users: QueryResult<User> }>({
|
||||||
@ -77,12 +77,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.users));
|
.pipe(map((result) => result.data.users));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<User> {
|
loadById(id: number): Observable<User> {
|
||||||
@ -115,12 +115,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.users.nodes[0]));
|
.pipe(map((result) => result.data.users.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: UserCreateInput): Observable<User | undefined> {
|
create(object: UserCreateInput): Observable<User | undefined> {
|
||||||
@ -145,17 +145,17 @@ export class UsersDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
keycloakId: object.keycloakId,
|
keycloakId: object.keycloakId,
|
||||||
roles: object.roles?.map(x => x.id),
|
roles: object.roles?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.create));
|
.pipe(map((result) => result.data?.user.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: UserUpdateInput): Observable<User | undefined> {
|
update(object: UserUpdateInput): Observable<User | undefined> {
|
||||||
@ -180,17 +180,17 @@ export class UsersDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
id: object.id,
|
id: object.id,
|
||||||
roles: object.roles?.map(x => x.id),
|
roles: object.roles?.map((x) => x.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.update));
|
.pipe(map((result) => result.data?.user.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: User): Observable<boolean> {
|
delete(object: User): Observable<boolean> {
|
||||||
@ -208,12 +208,12 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.delete ?? false));
|
.pipe(map((result) => result.data?.user.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: User): Observable<boolean> {
|
restore(object: User): Observable<boolean> {
|
||||||
@ -231,12 +231,35 @@ export class UsersDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.user.restore ?? false));
|
.pipe(map((result) => result.data?.user.restore ?? false));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllRoles(): Observable<Role[]> {
|
||||||
|
return this.apollo
|
||||||
|
.query<{ roles: QueryResult<Role> }>({
|
||||||
|
query: gql`
|
||||||
|
query getRoles {
|
||||||
|
roles {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
catchError((err) => {
|
||||||
|
this.spinner.hide();
|
||||||
|
throw err;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.pipe(map((result) => result.data.roles.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNotExistingUsersFromKeycloak(): Observable<NotExistingUser[]> {
|
getNotExistingUsersFromKeycloak(): Observable<NotExistingUser[]> {
|
||||||
@ -254,12 +277,12 @@ export class UsersDataService
|
|||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.notExistingUsersFromKeycloak.nodes));
|
.pipe(map((result) => result.data.notExistingUsersFromKeycloak.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
|
||||||
import {
|
|
||||||
DB_MODEL_COLUMNS,
|
|
||||||
ID_COLUMN,
|
|
||||||
PageColumns,
|
|
||||||
} from 'src/app/core/base/page.columns';
|
|
||||||
import { TableColumn } from 'src/app/modules/shared/components/table/table.model';
|
|
||||||
import { Domain } from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DomainsColumns extends PageColumns<Domain> {
|
|
||||||
get(): TableColumn<Domain>[] {
|
|
||||||
return [
|
|
||||||
ID_COLUMN,
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
label: 'common.name',
|
|
||||||
type: 'text',
|
|
||||||
filterable: true,
|
|
||||||
value: (row: Domain) => row.name,
|
|
||||||
},
|
|
||||||
...DB_MODEL_COLUMNS,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static provide(): Provider[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
provide: PageColumns,
|
|
||||||
useClass: DomainsColumns,
|
|
||||||
},
|
|
||||||
DomainsColumns,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import {
|
|
||||||
Create,
|
|
||||||
Delete,
|
|
||||||
PageDataService,
|
|
||||||
Restore,
|
|
||||||
Update,
|
|
||||||
} from 'src/app/core/base/page.data.service';
|
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
|
||||||
import { catchError, map } from 'rxjs/operators';
|
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
|
||||||
import {
|
|
||||||
Domain,
|
|
||||||
DomainCreateInput,
|
|
||||||
DomainUpdateInput,
|
|
||||||
} from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DomainsDataService
|
|
||||||
extends PageDataService<Domain>
|
|
||||||
implements
|
|
||||||
Create<Domain, DomainCreateInput>,
|
|
||||||
Update<Domain, DomainUpdateInput>,
|
|
||||||
Delete<Domain>,
|
|
||||||
Restore<Domain>
|
|
||||||
{
|
|
||||||
constructor(
|
|
||||||
private spinner: SpinnerService,
|
|
||||||
private apollo: Apollo
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
load(
|
|
||||||
filter?: Filter[] | undefined,
|
|
||||||
sort?: Sort[] | undefined,
|
|
||||||
skip?: number | undefined,
|
|
||||||
take?: number | undefined
|
|
||||||
): Observable<QueryResult<Domain>> {
|
|
||||||
return this.apollo
|
|
||||||
.query<{ domains: QueryResult<Domain> }>({
|
|
||||||
query: gql`
|
|
||||||
query getDomains(
|
|
||||||
$filter: [DomainFilter]
|
|
||||||
$sort: [DomainSort]
|
|
||||||
$skip: Int
|
|
||||||
$take: Int
|
|
||||||
) {
|
|
||||||
domains(filter: $filter, sort: $sort, skip: $skip, take: $take) {
|
|
||||||
count
|
|
||||||
totalCount
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
|
|
||||||
...DB_MODEL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${DB_MODEL_FRAGMENT}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
filter: filter,
|
|
||||||
sort: sort,
|
|
||||||
skip: skip,
|
|
||||||
take: take,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data.domains));
|
|
||||||
}
|
|
||||||
|
|
||||||
loadById(id: number): Observable<Domain> {
|
|
||||||
return this.apollo
|
|
||||||
.query<{ domains: QueryResult<Domain> }>({
|
|
||||||
query: gql`
|
|
||||||
query getDomain($id: Int) {
|
|
||||||
domain(filter: { id: { equal: $id } }) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
|
|
||||||
...DB_MODEL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${DB_MODEL_FRAGMENT}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
id: id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data.domains.nodes[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
create(object: DomainCreateInput): Observable<Domain | undefined> {
|
|
||||||
return this.apollo
|
|
||||||
.mutate<{ domain: { create: Domain } }>({
|
|
||||||
mutation: gql`
|
|
||||||
mutation createDomain($input: DomainCreateInput!) {
|
|
||||||
domain {
|
|
||||||
create(input: $input) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
|
|
||||||
...DB_MODEL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${DB_MODEL_FRAGMENT}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
name: object.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data?.domain.create));
|
|
||||||
}
|
|
||||||
|
|
||||||
update(object: DomainUpdateInput): Observable<Domain | undefined> {
|
|
||||||
return this.apollo
|
|
||||||
.mutate<{ domain: { update: Domain } }>({
|
|
||||||
mutation: gql`
|
|
||||||
mutation updateDomain($input: DomainUpdateInput!) {
|
|
||||||
domain {
|
|
||||||
update(input: $input) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
|
|
||||||
...DB_MODEL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${DB_MODEL_FRAGMENT}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
id: object.id,
|
|
||||||
name: object.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data?.domain.update));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(object: Domain): Observable<boolean> {
|
|
||||||
return this.apollo
|
|
||||||
.mutate<{ domain: { delete: boolean } }>({
|
|
||||||
mutation: gql`
|
|
||||||
mutation deleteDomain($id: ID!) {
|
|
||||||
domain {
|
|
||||||
delete(id: $id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
id: object.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data?.domain.delete ?? false));
|
|
||||||
}
|
|
||||||
|
|
||||||
restore(object: Domain): Observable<boolean> {
|
|
||||||
return this.apollo
|
|
||||||
.mutate<{ domain: { restore: boolean } }>({
|
|
||||||
mutation: gql`
|
|
||||||
mutation restoreDomain($id: ID!) {
|
|
||||||
domain {
|
|
||||||
restore(id: $id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
id: object.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data?.domain.restore ?? false));
|
|
||||||
}
|
|
||||||
|
|
||||||
static provide(): Provider[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
provide: PageDataService,
|
|
||||||
useClass: DomainsDataService,
|
|
||||||
},
|
|
||||||
DomainsDataService,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
|
||||||
import { PermissionGuard } from 'src/app/core/guard/permission.guard';
|
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
|
||||||
import { DomainsPage } from 'src/app/modules/admin/domains/domains.page';
|
|
||||||
import { DomainFormPageComponent } from 'src/app/modules/admin/domains/form-page/domain-form-page.component';
|
|
||||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
|
||||||
import { DomainsColumns } from 'src/app/modules/admin/domains/domains.columns';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
title: 'Domains',
|
|
||||||
component: DomainsPage,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
component: DomainFormPageComponent,
|
|
||||||
canActivate: [PermissionGuard],
|
|
||||||
data: {
|
|
||||||
permissions: [PermissionsEnum.apiKeysCreate],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'edit/:id',
|
|
||||||
component: DomainFormPageComponent,
|
|
||||||
canActivate: [PermissionGuard],
|
|
||||||
data: {
|
|
||||||
permissions: [PermissionsEnum.apiKeysUpdate],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [DomainsPage, DomainFormPageComponent],
|
|
||||||
imports: [CommonModule, SharedModule, RouterModule.forChild(routes)],
|
|
||||||
providers: [DomainsDataService.provide(), DomainsColumns.provide()],
|
|
||||||
})
|
|
||||||
export class DomainsModule {}
|
|
@ -1,19 +0,0 @@
|
|||||||
<app-table
|
|
||||||
[rows]="result.nodes"
|
|
||||||
[columns]="columns"
|
|
||||||
[rowsPerPageOptions]="rowsPerPageOptions"
|
|
||||||
[totalCount]="result.totalCount"
|
|
||||||
[requireAnyPermissions]="requiredPermissions"
|
|
||||||
countHeaderTranslation="domain.count_header"
|
|
||||||
[loading]="loading"
|
|
||||||
[(filter)]="filter"
|
|
||||||
[(sort)]="sort"
|
|
||||||
[(skip)]="skip"
|
|
||||||
[(take)]="take"
|
|
||||||
(load)="load()"
|
|
||||||
[create]="true"
|
|
||||||
[update]="true"
|
|
||||||
(delete)="delete($event)"
|
|
||||||
(restore)="restore($event)"></app-table>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
|
@ -1,51 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
||||||
import { ApiKeysPage } from "src/app/modules/admin/administration/api-keys/api-keys.page";
|
|
||||||
import { SharedModule } from "src/app/modules/shared/shared.module";
|
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
|
||||||
import { AuthService } from "src/app/service/auth.service";
|
|
||||||
import { KeycloakService } from "keycloak-angular";
|
|
||||||
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
|
||||||
import { ToastService } from "src/app/service/toast.service";
|
|
||||||
import { ConfirmationService, MessageService } from "primeng/api";
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
|
||||||
import { of } from "rxjs";
|
|
||||||
import { PageDataService } from "src/app/core/base/page.data.service";
|
|
||||||
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
|
||||||
|
|
||||||
describe("ApiKeysComponent", () => {
|
|
||||||
let component: ApiKeysPage;
|
|
||||||
let fixture: ComponentFixture<ApiKeysPage>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [ApiKeysPage],
|
|
||||||
imports: [SharedModule, TranslateModule.forRoot()],
|
|
||||||
providers: [
|
|
||||||
AuthService,
|
|
||||||
KeycloakService,
|
|
||||||
ErrorHandlingService,
|
|
||||||
ToastService,
|
|
||||||
MessageService,
|
|
||||||
ConfirmationService,
|
|
||||||
{
|
|
||||||
provide: ActivatedRoute,
|
|
||||||
useValue: {
|
|
||||||
snapshot: { params: of({}) },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: PageDataService,
|
|
||||||
useClass: ApiKeysDataService,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ApiKeysPage);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create", () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,72 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { PageBase } from 'src/app/core/base/page-base';
|
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
|
||||||
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
|
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
|
||||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
|
||||||
import { DomainsColumns } from 'src/app/modules/admin/domains/domains.columns';
|
|
||||||
import { Domain } from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-domains',
|
|
||||||
templateUrl: './domains.page.html',
|
|
||||||
styleUrl: './domains.page.scss',
|
|
||||||
})
|
|
||||||
export class DomainsPage extends PageBase<
|
|
||||||
Domain,
|
|
||||||
DomainsDataService,
|
|
||||||
DomainsColumns
|
|
||||||
> {
|
|
||||||
constructor(
|
|
||||||
private toast: ToastService,
|
|
||||||
private confirmation: ConfirmationDialogService
|
|
||||||
) {
|
|
||||||
super(true, {
|
|
||||||
read: [PermissionsEnum.domains],
|
|
||||||
create: [PermissionsEnum.domainsCreate],
|
|
||||||
update: [PermissionsEnum.domainsUpdate],
|
|
||||||
delete: [PermissionsEnum.domainsDelete],
|
|
||||||
restore: [PermissionsEnum.domainsDelete],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
load(): void {
|
|
||||||
this.loading = true;
|
|
||||||
this.dataService
|
|
||||||
.load(this.filter, this.sort, this.skip, this.take)
|
|
||||||
.subscribe(result => {
|
|
||||||
this.result = result;
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(domain: Domain): void {
|
|
||||||
this.confirmation.confirmDialog({
|
|
||||||
header: 'dialog.delete.header',
|
|
||||||
message: 'dialog.delete.message',
|
|
||||||
accept: () => {
|
|
||||||
this.loading = true;
|
|
||||||
this.dataService.delete(domain).subscribe(() => {
|
|
||||||
this.toast.success('action.deleted');
|
|
||||||
this.load();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
messageParams: { entity: domain.name },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
restore(domain: Domain): void {
|
|
||||||
this.confirmation.confirmDialog({
|
|
||||||
header: 'dialog.restore.header',
|
|
||||||
message: 'dialog.restore.message',
|
|
||||||
accept: () => {
|
|
||||||
this.loading = true;
|
|
||||||
this.dataService.restore(domain).subscribe(() => {
|
|
||||||
this.toast.success('action.restored');
|
|
||||||
this.load();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
messageParams: { entity: domain.name },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<app-form-page
|
|
||||||
*ngIf="node"
|
|
||||||
[formGroup]="form"
|
|
||||||
[isUpdate]="isUpdate"
|
|
||||||
(onSave)="save()"
|
|
||||||
(onClose)="close()">
|
|
||||||
<ng-template formPageHeader let-isUpdate>
|
|
||||||
<h2>
|
|
||||||
{{ 'common.group' | translate }}
|
|
||||||
{{
|
|
||||||
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
|
||||||
| translate
|
|
||||||
}}
|
|
||||||
</h2>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template formPageContent>
|
|
||||||
<div class="form-page-input">
|
|
||||||
<p class="label">{{ 'common.id' | translate }}</p>
|
|
||||||
<input pInputText class="value" type="number" formControlName="id"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-page-input">
|
|
||||||
<p class="label">{{ 'common.name' | translate }}</p>
|
|
||||||
<input
|
|
||||||
pInputText
|
|
||||||
class="value"
|
|
||||||
type="text"
|
|
||||||
formControlName="name"/>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</app-form-page>
|
|
@ -1,50 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
||||||
import { RoleFormPageComponent } from "src/app/modules/admin/administration/roles/form-page/role-form-page.component";
|
|
||||||
import { SharedModule } from "src/app/modules/shared/shared.module";
|
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
|
||||||
import { AuthService } from "src/app/service/auth.service";
|
|
||||||
import { ErrorHandlingService } from "src/app/service/error-handling.service";
|
|
||||||
import { ToastService } from "src/app/service/toast.service";
|
|
||||||
import { ConfirmationService, MessageService } from "primeng/api";
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
|
||||||
import { of } from "rxjs";
|
|
||||||
import { ApiKeysDataService } from "src/app/modules/admin/administration/api-keys/api-keys.data.service";
|
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
|
||||||
|
|
||||||
describe("ApiKeyFormpageComponent", () => {
|
|
||||||
let component: RoleFormPageComponent;
|
|
||||||
let fixture: ComponentFixture<RoleFormPageComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
declarations: [RoleFormPageComponent],
|
|
||||||
imports: [
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
SharedModule,
|
|
||||||
TranslateModule.forRoot(),
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
AuthService,
|
|
||||||
ErrorHandlingService,
|
|
||||||
ToastService,
|
|
||||||
MessageService,
|
|
||||||
ConfirmationService,
|
|
||||||
{
|
|
||||||
provide: ActivatedRoute,
|
|
||||||
useValue: {
|
|
||||||
snapshot: { params: of({}) },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ApiKeysDataService,
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RoleFormPageComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create", () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,93 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
|
||||||
import {
|
|
||||||
Domain,
|
|
||||||
DomainCreateInput,
|
|
||||||
DomainUpdateInput,
|
|
||||||
} from 'src/app/model/entities/domain';
|
|
||||||
import { DomainsDataService } from 'src/app/modules/admin/domains/domains.data.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-domain-form-page',
|
|
||||||
templateUrl: './domain-form-page.component.html',
|
|
||||||
styleUrl: './domain-form-page.component.scss',
|
|
||||||
})
|
|
||||||
export class DomainFormPageComponent extends FormPageBase<
|
|
||||||
Domain,
|
|
||||||
DomainCreateInput,
|
|
||||||
DomainUpdateInput,
|
|
||||||
DomainsDataService
|
|
||||||
> {
|
|
||||||
constructor(private toast: ToastService) {
|
|
||||||
super();
|
|
||||||
if (!this.nodeId) {
|
|
||||||
this.node = this.new();
|
|
||||||
this.setForm(this.node);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dataService
|
|
||||||
.load([{ id: { equal: this.nodeId } }])
|
|
||||||
.subscribe(apiKey => {
|
|
||||||
this.node = apiKey.nodes[0];
|
|
||||||
this.setForm(this.node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
new(): Domain {
|
|
||||||
return {} as Domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildForm() {
|
|
||||||
this.form = new FormGroup({
|
|
||||||
id: new FormControl<number | undefined>(undefined),
|
|
||||||
name: new FormControl<string | undefined>(undefined, Validators.required),
|
|
||||||
});
|
|
||||||
this.form.controls['id'].disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
setForm(node?: Domain) {
|
|
||||||
this.form.controls['id'].setValue(node?.id);
|
|
||||||
this.form.controls['name'].setValue(node?.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCreateInput(): DomainCreateInput {
|
|
||||||
return {
|
|
||||||
name: this.form.controls['name'].pristine
|
|
||||||
? undefined
|
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getUpdateInput(): DomainUpdateInput {
|
|
||||||
if (!this.node?.id) {
|
|
||||||
throw new Error('Node id is missing');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: this.form.controls['id'].value,
|
|
||||||
name: this.form.controls['name'].pristine
|
|
||||||
? undefined
|
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
create(apiKey: DomainCreateInput): void {
|
|
||||||
this.dataService.create(apiKey).subscribe(() => {
|
|
||||||
this.spinner.hide();
|
|
||||||
this.toast.success('action.created');
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update(apiKey: DomainUpdateInput): void {
|
|
||||||
this.dataService.update(apiKey).subscribe(() => {
|
|
||||||
this.spinner.hide();
|
|
||||||
this.toast.success('action.created');
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,13 +27,5 @@
|
|||||||
type="text"
|
type="text"
|
||||||
formControlName="name"/>
|
formControlName="name"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
|
||||||
<p-multiSelect
|
|
||||||
[options]="roles"
|
|
||||||
formControlName="roles"
|
|
||||||
optionLabel="name"
|
|
||||||
placeholder="{{ 'user.assign_roles' | translate }}"
|
|
||||||
display="chip"
|
|
||||||
[showClear]="true" />
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-form-page>
|
</app-form-page>
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from "@angular/core";
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { FormPageBase } from 'src/app/core/base/form-page-base';
|
import { FormPageBase } from "src/app/core/base/form-page-base";
|
||||||
import {
|
import {
|
||||||
Group,
|
Group,
|
||||||
GroupCreateInput,
|
GroupCreateInput,
|
||||||
GroupUpdateInput,
|
GroupUpdateInput,
|
||||||
} from 'src/app/model/entities/group';
|
} from "src/app/model/entities/group";
|
||||||
import { GroupsDataService } from 'src/app/modules/admin/groups/groups.data.service';
|
import { GroupsDataService } from "src/app/modules/admin/groups/groups.data.service";
|
||||||
import { Role } from 'src/app/model/entities/role';
|
|
||||||
import { CommonDataService } from 'src/app/modules/shared/service/common-data.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-group-form-page',
|
selector: "app-group-form-page",
|
||||||
templateUrl: './group-form-page.component.html',
|
templateUrl: "./group-form-page.component.html",
|
||||||
styleUrl: './group-form-page.component.scss',
|
styleUrl: "./group-form-page.component.scss",
|
||||||
})
|
})
|
||||||
export class GroupFormPageComponent extends FormPageBase<
|
export class GroupFormPageComponent extends FormPageBase<
|
||||||
Group,
|
Group,
|
||||||
@ -22,17 +20,8 @@ export class GroupFormPageComponent extends FormPageBase<
|
|||||||
GroupUpdateInput,
|
GroupUpdateInput,
|
||||||
GroupsDataService
|
GroupsDataService
|
||||||
> {
|
> {
|
||||||
roles: Role[] = [];
|
constructor(private toast: ToastService) {
|
||||||
|
|
||||||
constructor(
|
|
||||||
private toast: ToastService,
|
|
||||||
private cds: CommonDataService
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.cds.getAllRoles().subscribe(roles => {
|
|
||||||
this.roles = roles;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.nodeId) {
|
if (!this.nodeId) {
|
||||||
this.node = this.new();
|
this.node = this.new();
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
@ -42,7 +31,7 @@ export class GroupFormPageComponent extends FormPageBase<
|
|||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.load([{ id: { equal: this.nodeId } }])
|
.load([{ id: { equal: this.nodeId } }])
|
||||||
.subscribe(apiKey => {
|
.subscribe((apiKey) => {
|
||||||
this.node = apiKey.nodes[0];
|
this.node = apiKey.nodes[0];
|
||||||
this.setForm(this.node);
|
this.setForm(this.node);
|
||||||
});
|
});
|
||||||
@ -56,48 +45,40 @@ export class GroupFormPageComponent extends FormPageBase<
|
|||||||
this.form = new FormGroup({
|
this.form = new FormGroup({
|
||||||
id: new FormControl<number | undefined>(undefined),
|
id: new FormControl<number | undefined>(undefined),
|
||||||
name: new FormControl<string | undefined>(undefined, Validators.required),
|
name: new FormControl<string | undefined>(undefined, Validators.required),
|
||||||
roles: new FormControl<Role[]>([]),
|
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls["id"].disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm(node?: Group) {
|
setForm(node?: Group) {
|
||||||
this.form.controls['id'].setValue(node?.id);
|
this.form.controls["id"].setValue(node?.id);
|
||||||
this.form.controls['name'].setValue(node?.name);
|
this.form.controls["name"].setValue(node?.name);
|
||||||
this.form.controls['roles'].setValue(node?.roles ?? []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): GroupCreateInput {
|
getCreateInput(): GroupCreateInput {
|
||||||
return {
|
return {
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
: (this.form.controls["name"].value ?? undefined),
|
||||||
roles: this.form.controls['roles'].pristine
|
|
||||||
? undefined
|
|
||||||
: this.form.controls['roles'].value,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateInput(): GroupUpdateInput {
|
getUpdateInput(): GroupUpdateInput {
|
||||||
if (!this.node?.id) {
|
if (!this.node?.id) {
|
||||||
throw new Error('Node id is missing');
|
throw new Error("Node id is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.form.controls['id'].value,
|
id: this.form.controls["id"].value,
|
||||||
name: this.form.controls['name'].pristine
|
name: this.form.controls["name"].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['name'].value ?? undefined),
|
: (this.form.controls["name"].value ?? undefined),
|
||||||
roles: this.form.controls['roles'].pristine
|
|
||||||
? undefined
|
|
||||||
: this.form.controls['roles'].value,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
create(apiKey: GroupCreateInput): void {
|
create(apiKey: GroupCreateInput): void {
|
||||||
this.dataService.create(apiKey).subscribe(() => {
|
this.dataService.create(apiKey).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -105,7 +86,7 @@ export class GroupFormPageComponent extends FormPageBase<
|
|||||||
update(apiKey: GroupUpdateInput): void {
|
update(apiKey: GroupUpdateInput): void {
|
||||||
this.dataService.update(apiKey).subscribe(() => {
|
this.dataService.update(apiKey).subscribe(() => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
this.toast.success('action.created');
|
this.toast.success("action.created");
|
||||||
this.close();
|
this.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { Injectable, Provider } from '@angular/core';
|
import { Injectable, Provider } from "@angular/core";
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from "rxjs";
|
||||||
import {
|
import {
|
||||||
Create,
|
Create,
|
||||||
Delete,
|
Delete,
|
||||||
PageDataService,
|
PageDataService,
|
||||||
Restore,
|
Restore,
|
||||||
Update,
|
Update,
|
||||||
} from 'src/app/core/base/page.data.service';
|
} from "src/app/core/base/page.data.service";
|
||||||
import { Filter } from 'src/app/model/graphql/filter/filter.model';
|
import { Filter } from "src/app/model/graphql/filter/filter.model";
|
||||||
import { Sort } from 'src/app/model/graphql/filter/sort.model';
|
import { Sort } from "src/app/model/graphql/filter/sort.model";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
import { QueryResult } from "src/app/model/entities/query-result";
|
||||||
import { DB_MODEL_FRAGMENT } from 'src/app/model/graphql/db-model.query';
|
import { DB_MODEL_FRAGMENT } from "src/app/model/graphql/db-model.query";
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from "rxjs/operators";
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
import { SpinnerService } from "src/app/service/spinner.service";
|
||||||
import {
|
import {
|
||||||
Group,
|
Group,
|
||||||
GroupCreateInput,
|
GroupCreateInput,
|
||||||
GroupUpdateInput,
|
GroupUpdateInput,
|
||||||
} from 'src/app/model/entities/group';
|
} from "src/app/model/entities/group";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GroupsDataService
|
export class GroupsDataService
|
||||||
@ -31,7 +31,7 @@ export class GroupsDataService
|
|||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private spinner: SpinnerService,
|
private spinner: SpinnerService,
|
||||||
private apollo: Apollo
|
private apollo: Apollo,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ export class GroupsDataService
|
|||||||
filter?: Filter[] | undefined,
|
filter?: Filter[] | undefined,
|
||||||
sort?: Sort[] | undefined,
|
sort?: Sort[] | undefined,
|
||||||
skip?: number | undefined,
|
skip?: number | undefined,
|
||||||
take?: number | undefined
|
take?: number | undefined,
|
||||||
): Observable<QueryResult<Group>> {
|
): Observable<QueryResult<Group>> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ groups: QueryResult<Group> }>({
|
.query<{ groups: QueryResult<Group> }>({
|
||||||
@ -57,10 +57,6 @@ export class GroupsDataService
|
|||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
roles {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
...DB_MODEL
|
...DB_MODEL
|
||||||
}
|
}
|
||||||
@ -77,12 +73,12 @@ export class GroupsDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.groups));
|
.pipe(map((result) => result.data.groups));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadById(id: number): Observable<Group> {
|
loadById(id: number): Observable<Group> {
|
||||||
@ -93,10 +89,6 @@ export class GroupsDataService
|
|||||||
group(filter: { id: { equal: $id } }) {
|
group(filter: { id: { equal: $id } }) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
roles {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
...DB_MODEL
|
...DB_MODEL
|
||||||
}
|
}
|
||||||
@ -109,12 +101,12 @@ export class GroupsDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data.groups.nodes[0]));
|
.pipe(map((result) => result.data.groups.nodes[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(object: GroupCreateInput): Observable<Group | undefined> {
|
create(object: GroupCreateInput): Observable<Group | undefined> {
|
||||||
@ -137,17 +129,16 @@ export class GroupsDataService
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
name: object.name,
|
name: object.name,
|
||||||
roles: object.roles?.map(x => x.id),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.group.create));
|
.pipe(map((result) => result.data?.group.create));
|
||||||
}
|
}
|
||||||
|
|
||||||
update(object: GroupUpdateInput): Observable<Group | undefined> {
|
update(object: GroupUpdateInput): Observable<Group | undefined> {
|
||||||
@ -171,17 +162,16 @@ export class GroupsDataService
|
|||||||
input: {
|
input: {
|
||||||
id: object.id,
|
id: object.id,
|
||||||
name: object.name,
|
name: object.name,
|
||||||
roles: object.roles?.map(x => x.id),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.group.update));
|
.pipe(map((result) => result.data?.group.update));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(object: Group): Observable<boolean> {
|
delete(object: Group): Observable<boolean> {
|
||||||
@ -199,12 +189,12 @@ export class GroupsDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.group.delete ?? false));
|
.pipe(map((result) => result.data?.group.delete ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(object: Group): Observable<boolean> {
|
restore(object: Group): Observable<boolean> {
|
||||||
@ -222,12 +212,12 @@ export class GroupsDataService
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => {
|
catchError((err) => {
|
||||||
this.spinner.hide();
|
this.spinner.hide();
|
||||||
throw err;
|
throw err;
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.pipe(map(result => result.data?.group.restore ?? false));
|
.pipe(map((result) => result.data?.group.restore ?? false));
|
||||||
}
|
}
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
(onClose)="close()">
|
(onClose)="close()">
|
||||||
<ng-template formPageHeader let-isUpdate>
|
<ng-template formPageHeader let-isUpdate>
|
||||||
<h2>
|
<h2>
|
||||||
{{ 'common.short_url' | translate }}
|
{{ 'common.group' | translate }}
|
||||||
{{
|
{{
|
||||||
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
(isUpdate ? 'sidebar.header.update' : 'sidebar.header.create')
|
||||||
| translate
|
| translate
|
||||||
@ -65,20 +65,5 @@
|
|||||||
></p-dropdown>
|
></p-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-page-input">
|
|
||||||
<p class="label">{{ 'common.domain' | translate }}</p>
|
|
||||||
<div
|
|
||||||
class="value">
|
|
||||||
<p-dropdown
|
|
||||||
[options]="domains"
|
|
||||||
formControlName="domainId"
|
|
||||||
[showClear]="true"
|
|
||||||
[filter]="true"
|
|
||||||
filterBy="name"
|
|
||||||
optionLabel="name"
|
|
||||||
optionValue="id"
|
|
||||||
></p-dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-form-page>
|
</app-form-page>
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
} from 'src/app/model/entities/short-url';
|
} from 'src/app/model/entities/short-url';
|
||||||
import { ShortUrlsDataService } from 'src/app/modules/admin/short-urls/short-urls.data.service';
|
import { ShortUrlsDataService } from 'src/app/modules/admin/short-urls/short-urls.data.service';
|
||||||
import { Group } from 'src/app/model/entities/group';
|
import { Group } from 'src/app/model/entities/group';
|
||||||
import { Domain } from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-short-url-form-page',
|
selector: 'app-short-url-form-page',
|
||||||
@ -23,16 +22,12 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
ShortUrlsDataService
|
ShortUrlsDataService
|
||||||
> {
|
> {
|
||||||
groups: Group[] = [];
|
groups: Group[] = [];
|
||||||
domains: Domain[] = [];
|
|
||||||
|
|
||||||
constructor(private toast: ToastService) {
|
constructor(private toast: ToastService) {
|
||||||
super();
|
super();
|
||||||
this.dataService.getAllGroups().subscribe(groups => {
|
this.dataService.getAllGroups().subscribe(groups => {
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
});
|
});
|
||||||
this.dataService.getAllDomains().subscribe(domains => {
|
|
||||||
this.domains = domains;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.nodeId) {
|
if (!this.nodeId) {
|
||||||
this.node = this.new();
|
this.node = this.new();
|
||||||
@ -67,7 +62,6 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
description: new FormControl<string | undefined>(undefined),
|
description: new FormControl<string | undefined>(undefined),
|
||||||
loadingScreen: new FormControl<boolean | undefined>(undefined),
|
loadingScreen: new FormControl<boolean | undefined>(undefined),
|
||||||
groupId: new FormControl<number | undefined>(undefined),
|
groupId: new FormControl<number | undefined>(undefined),
|
||||||
domainId: new FormControl<number | undefined>(undefined),
|
|
||||||
});
|
});
|
||||||
this.form.controls['id'].disable();
|
this.form.controls['id'].disable();
|
||||||
}
|
}
|
||||||
@ -92,7 +86,6 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
this.form.controls['description'].setValue(node?.description);
|
this.form.controls['description'].setValue(node?.description);
|
||||||
this.form.controls['loadingScreen'].setValue(node?.loadingScreen);
|
this.form.controls['loadingScreen'].setValue(node?.loadingScreen);
|
||||||
this.form.controls['groupId'].setValue(node?.group?.id);
|
this.form.controls['groupId'].setValue(node?.group?.id);
|
||||||
this.form.controls['domainId'].setValue(node?.domain?.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateInput(): ShortUrlCreateInput {
|
getCreateInput(): ShortUrlCreateInput {
|
||||||
@ -110,9 +103,6 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
groupId: this.form.controls['groupId'].pristine
|
groupId: this.form.controls['groupId'].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['groupId'].value ?? undefined),
|
: (this.form.controls['groupId'].value ?? undefined),
|
||||||
domainId: this.form.controls['domainId'].pristine
|
|
||||||
? undefined
|
|
||||||
: (this.form.controls['domainId'].value ?? undefined),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +128,6 @@ export class ShortUrlFormPageComponent extends FormPageBase<
|
|||||||
groupId: this.form.controls['groupId'].pristine
|
groupId: this.form.controls['groupId'].pristine
|
||||||
? undefined
|
? undefined
|
||||||
: (this.form.controls['groupId'].value ?? undefined),
|
: (this.form.controls['groupId'].value ?? undefined),
|
||||||
domainId: this.form.controls['domainId'].pristine
|
|
||||||
? undefined
|
|
||||||
: (this.form.controls['domainId'].value ?? undefined),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
ShortUrlUpdateInput,
|
ShortUrlUpdateInput,
|
||||||
} from 'src/app/model/entities/short-url';
|
} from 'src/app/model/entities/short-url';
|
||||||
import { Group } from 'src/app/model/entities/group';
|
import { Group } from 'src/app/model/entities/group';
|
||||||
import { Domain } from 'src/app/model/entities/domain';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShortUrlsDataService
|
export class ShortUrlsDataService
|
||||||
@ -67,10 +66,6 @@ export class ShortUrlsDataService
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
domain {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
...DB_MODEL
|
...DB_MODEL
|
||||||
}
|
}
|
||||||
@ -111,10 +106,6 @@ export class ShortUrlsDataService
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
domain {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
|
|
||||||
...DB_MODEL
|
...DB_MODEL
|
||||||
}
|
}
|
||||||
@ -159,7 +150,6 @@ export class ShortUrlsDataService
|
|||||||
description: object.description,
|
description: object.description,
|
||||||
loadingScreen: object.loadingScreen,
|
loadingScreen: object.loadingScreen,
|
||||||
groupId: object.groupId,
|
groupId: object.groupId,
|
||||||
domainId: object.domainId,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -197,7 +187,6 @@ export class ShortUrlsDataService
|
|||||||
description: object.description,
|
description: object.description,
|
||||||
loadingScreen: object.loadingScreen,
|
loadingScreen: object.loadingScreen,
|
||||||
groupId: object.groupId,
|
groupId: object.groupId,
|
||||||
domainId: object.domainId,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -279,29 +268,6 @@ export class ShortUrlsDataService
|
|||||||
.pipe(map(result => result.data.groups.nodes));
|
.pipe(map(result => result.data.groups.nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllDomains() {
|
|
||||||
return this.apollo
|
|
||||||
.query<{ domains: QueryResult<Domain> }>({
|
|
||||||
query: gql`
|
|
||||||
query getGroups {
|
|
||||||
domains {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data.domains.nodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
static provide(): Provider[] {
|
static provide(): Provider[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -22,10 +22,6 @@
|
|||||||
<div class="pi pi-{{ url.loadingScreen ? 'check-circle' : 'times-circle' }}"></div>
|
<div class="pi pi-{{ url.loadingScreen ? 'check-circle' : 'times-circle' }}"></div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-container" *ngIf="url.domain">
|
|
||||||
<span class="grid-label font-bold">{{ 'common.domain' | translate }}:</span>
|
|
||||||
<span class="grid-value">{{ url.domain?.name }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
@ -109,7 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="bg2 rounded-xl p-3" *ngIf="shortUrlsWithoutGroup.length > 0">
|
<div class="bg2 rounded-xl p-3">
|
||||||
<div class="flex flex-wrap gap-1.5">
|
<div class="flex flex-wrap gap-1.5">
|
||||||
<ng-template *ngFor="let url of shortUrlsWithoutGroup" [ngTemplateOutlet]="shortUrl"
|
<ng-template *ngFor="let url of shortUrlsWithoutGroup" [ngTemplateOutlet]="shortUrl"
|
||||||
[ngTemplateOutletContext]="{ $implicit: url }"></ng-template>
|
[ngTemplateOutletContext]="{ $implicit: url }"></ng-template>
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Role } from 'src/app/model/entities/role';
|
|
||||||
import { QueryResult } from 'src/app/model/entities/query-result';
|
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
|
||||||
import { catchError, map } from 'rxjs/operators';
|
|
||||||
import { SpinnerService } from 'src/app/service/spinner.service';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class CommonDataService {
|
|
||||||
constructor(
|
|
||||||
private spinner: SpinnerService,
|
|
||||||
private apollo: Apollo
|
|
||||||
) {}
|
|
||||||
|
|
||||||
getAllRoles(): Observable<Role[]> {
|
|
||||||
return this.apollo
|
|
||||||
.query<{ roles: QueryResult<Role> }>({
|
|
||||||
query: gql`
|
|
||||||
query getRoles {
|
|
||||||
roles {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
catchError(err => {
|
|
||||||
this.spinner.hide();
|
|
||||||
throw err;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(map(result => result.data.roles.nodes));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,16 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { User } from 'src/app/model/auth/user';
|
import { User } from "src/app/model/auth/user";
|
||||||
import { BehaviorSubject, concatWith, firstValueFrom, Observable } from 'rxjs';
|
import { BehaviorSubject, firstValueFrom, Observable } from "rxjs";
|
||||||
import { Apollo, gql } from 'apollo-angular';
|
import { Apollo, gql } from "apollo-angular";
|
||||||
import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum';
|
import { PermissionsEnum } from "src/app/model/auth/permissionsEnum";
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from "keycloak-angular";
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from "rxjs/operators";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
|
|
||||||
const log = new Logger('AuthService');
|
const log = new Logger("AuthService");
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
protected anyPermissionForAdminPage = [
|
protected anyPermissionForAdminPage = [
|
||||||
@ -23,7 +23,7 @@ export class AuthService {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apollo: Apollo,
|
private apollo: Apollo,
|
||||||
private keycloakService: KeycloakService
|
private keycloakService: KeycloakService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private requestUser() {
|
private requestUser() {
|
||||||
@ -60,11 +60,11 @@ export class AuthService {
|
|||||||
permission,
|
permission,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(map(result => result.data.userHasPermission));
|
.pipe(map((result) => result.data.userHasPermission));
|
||||||
}
|
}
|
||||||
|
|
||||||
private userHasAnyPermission(
|
private userHasAnyPermission(
|
||||||
permissions: PermissionsEnum[]
|
permissions: PermissionsEnum[],
|
||||||
): Observable<boolean> {
|
): Observable<boolean> {
|
||||||
return this.apollo
|
return this.apollo
|
||||||
.query<{ userHasAnyPermission: boolean }>({
|
.query<{ userHasAnyPermission: boolean }>({
|
||||||
@ -77,7 +77,7 @@ export class AuthService {
|
|||||||
permissions,
|
permissions,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.pipe(map(result => result.data.userHasAnyPermission));
|
.pipe(map((result) => result.data.userHasAnyPermission));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUser() {
|
loadUser() {
|
||||||
@ -88,8 +88,8 @@ export class AuthService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.requestUser().subscribe(result => {
|
this.requestUser().subscribe((result) => {
|
||||||
log.info('User loaded');
|
log.info("User loaded");
|
||||||
this.user$.next(result.data.user);
|
this.user$.next(result.data.user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -111,15 +111,18 @@ export class AuthService {
|
|||||||
if (!this.user$.value) return false;
|
if (!this.user$.value) return false;
|
||||||
|
|
||||||
const userPermissions = this.user$.value.roles
|
const userPermissions = this.user$.value.roles
|
||||||
.map(role => (role.permissions ?? []).map(p => p.name))
|
.map((role) => (role.permissions ?? []).map((p) => p.name))
|
||||||
.flat();
|
.flat();
|
||||||
return permissions.some(permission => userPermissions.includes(permission));
|
return permissions.every((permission) =>
|
||||||
|
userPermissions.includes(permission),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasAnyPermissionLazy(permissions: PermissionsEnum[]): Promise<boolean> {
|
async hasAnyPermissionLazy(permissions: PermissionsEnum[]): Promise<boolean> {
|
||||||
if (this.user$.value && this.user$.value.roles) {
|
if (this.user$.value && this.user$.value.roles) {
|
||||||
return this.hasAnyPermission(permissions);
|
return this.hasAnyPermission(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await firstValueFrom(this.userHasAnyPermission(permissions));
|
return await firstValueFrom(this.userHasAnyPermission(permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +130,7 @@ export class AuthService {
|
|||||||
if (!this.user$.value) return false;
|
if (!this.user$.value) return false;
|
||||||
|
|
||||||
const permissions = this.user$.value.roles
|
const permissions = this.user$.value.roles
|
||||||
.map(role => (role.permissions ?? []).map(p => p.name))
|
.map((role) => (role.permissions ?? []).map((p) => p.name))
|
||||||
.flat();
|
.flat();
|
||||||
return permissions.includes(permission);
|
return permissions.includes(permission);
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from "src/app/service/logger.service";
|
||||||
import { ErrorHandler, Injectable } from '@angular/core';
|
import { ErrorHandler, Injectable } from "@angular/core";
|
||||||
import { ToastService } from 'src/app/service/toast.service';
|
import { ToastService } from "src/app/service/toast.service";
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from "@angular/common/http";
|
||||||
import { ApolloError } from '@apollo/client/errors';
|
import { ApolloError } from "@apollo/client/errors";
|
||||||
import { MissingPermissionException } from 'src/app/model/utils/error';
|
import { MissingPermissionException } from "src/app/model/utils/error";
|
||||||
import { GraphQLError } from 'graphql/error/GraphQLError';
|
import { GraphQLError } from "graphql/error/GraphQLError";
|
||||||
import { ToastOptions } from 'src/app/model/utils/toast-options';
|
import { ToastOptions } from "src/app/model/utils/toast-options";
|
||||||
|
|
||||||
const logger = new Logger('ErrorHandler');
|
const logger = new Logger("ErrorHandler");
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class ErrorHandlingService implements ErrorHandler {
|
export class ErrorHandlingService implements ErrorHandler {
|
||||||
constructor(
|
constructor(
|
||||||
private t: TranslateService,
|
private t: TranslateService,
|
||||||
private toast: ToastService
|
private toast: ToastService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handleError(error: HttpErrorResponse | ApolloError) {
|
handleError(error: HttpErrorResponse | ApolloError) {
|
||||||
@ -30,7 +30,7 @@ export class ErrorHandlingService implements ErrorHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.handleHttpError(error);
|
// this.handleHttpError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleHttpError(e: HttpErrorResponse) {
|
private handleHttpError(e: HttpErrorResponse) {
|
||||||
@ -40,14 +40,14 @@ export class ErrorHandlingService implements ErrorHandler {
|
|||||||
error?: string;
|
error?: string;
|
||||||
options?: ToastOptions;
|
options?: ToastOptions;
|
||||||
} = {
|
} = {
|
||||||
summary: this.t.instant('common.error'),
|
summary: this.t.instant("common.error"),
|
||||||
detail: e.message,
|
detail: e.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (e.status === 401) {
|
if (e.status === 401) {
|
||||||
toast = {
|
toast = {
|
||||||
summary: this.t.instant('common.error'),
|
summary: this.t.instant("common.error"),
|
||||||
detail: this.t.instant('error.unauthorized'),
|
detail: this.t.instant("error.unauthorized"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,15 +57,15 @@ export class ErrorHandlingService implements ErrorHandler {
|
|||||||
.permissions;
|
.permissions;
|
||||||
|
|
||||||
toast = {
|
toast = {
|
||||||
summary: this.t.instant('common.error'),
|
summary: this.t.instant("common.error"),
|
||||||
detail: this.t.instant('error.missing_permissions', {
|
detail: this.t.instant("error.missing_permissions", {
|
||||||
permissions: missingPermissions.join(', '),
|
permissions: missingPermissions.join(", "),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
toast = {
|
toast = {
|
||||||
summary: this.t.instant('common.error'),
|
summary: this.t.instant("common.error"),
|
||||||
detail: this.t.instant('error.permission_denied'),
|
detail: this.t.instant("error.permission_denied"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ export class ErrorHandlingService implements ErrorHandler {
|
|||||||
if (e.status === 500 && e.error && e.error.errors) {
|
if (e.status === 500 && e.error && e.error.errors) {
|
||||||
if (e.error.errors.length > 0) {
|
if (e.error.errors.length > 0) {
|
||||||
toast = {
|
toast = {
|
||||||
summary: this.t.instant('common.api_error'),
|
summary: this.t.instant("common.api_error"),
|
||||||
detail: e.error.errors[0].message,
|
detail: e.error.errors[0].message,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -86,8 +86,8 @@ export class ErrorHandlingService implements ErrorHandler {
|
|||||||
private handleGraphQlErrors(errors: GraphQLError[]) {
|
private handleGraphQlErrors(errors: GraphQLError[]) {
|
||||||
errors.forEach((e: GraphQLError) => {
|
errors.forEach((e: GraphQLError) => {
|
||||||
this.toast.error(
|
this.toast.error(
|
||||||
this.t.instant('common.api_error'),
|
this.t.instant("common.api_error"),
|
||||||
`${e.message}${e.path ? ' ' + e.path.join('.') : ''}`
|
`${e.message}${e.path ? " " + e.path.join(".") : ""}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { BehaviorSubject, filter } from 'rxjs';
|
|||||||
import { Logger } from 'src/app/service/logger.service';
|
import { Logger } from 'src/app/service/logger.service';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
import { SidebarService } from 'src/app/service/sidebar.service';
|
import { SidebarService } from 'src/app/service/sidebar.service';
|
||||||
import { SettingsService } from 'src/app/service/settings.service';
|
|
||||||
|
|
||||||
const logger = new Logger('GuiService');
|
const logger = new Logger('GuiService');
|
||||||
|
|
||||||
@ -15,14 +14,10 @@ export class GuiService {
|
|||||||
isTablet$ = new BehaviorSubject<boolean>(this.isTabletByWindowWith());
|
isTablet$ = new BehaviorSubject<boolean>(this.isTabletByWindowWith());
|
||||||
|
|
||||||
hideGui$ = new BehaviorSubject<boolean>(false);
|
hideGui$ = new BehaviorSubject<boolean>(false);
|
||||||
theme$ = new BehaviorSubject<string>(
|
|
||||||
this.settingsService.settings.themes[0].name
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private sidebarService: SidebarService,
|
private sidebarService: SidebarService
|
||||||
private settingsService: SettingsService
|
|
||||||
) {
|
) {
|
||||||
this.router.events
|
this.router.events
|
||||||
.pipe(filter(event => event instanceof NavigationEnd))
|
.pipe(filter(event => event instanceof NavigationEnd))
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { throwError } from 'rxjs';
|
import { throwError } from "rxjs";
|
||||||
import { catchError } from 'rxjs/operators';
|
import { catchError } from "rxjs/operators";
|
||||||
import { AppSettings } from 'src/app/model/config/app-settings';
|
import { AppSettings } from "src/app/model/config/app-settings";
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from "src/environments/environment";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class SettingsService {
|
export class SettingsService {
|
||||||
settings!: AppSettings;
|
settings!: AppSettings;
|
||||||
@ -18,19 +18,13 @@ export class SettingsService {
|
|||||||
this.http
|
this.http
|
||||||
.get<AppSettings>(`/assets/config/${environment.config}`)
|
.get<AppSettings>(`/assets/config/${environment.config}`)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(error => {
|
catchError((error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
return throwError(() => error);
|
return throwError(() => error);
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.subscribe(settings => {
|
.subscribe((settings) => {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
if (this.settings.themes.length === 0) {
|
|
||||||
this.settings.themes.push({
|
|
||||||
label: 'Open-redirect',
|
|
||||||
name: 'open-redirect',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,14 +30,6 @@ export class SidebarService {
|
|||||||
// trust me, you'll need this async
|
// trust me, you'll need this async
|
||||||
async setElements() {
|
async setElements() {
|
||||||
const elements: MenuElement[] = [
|
const elements: MenuElement[] = [
|
||||||
{
|
|
||||||
label: 'common.domains',
|
|
||||||
icon: 'pi pi-sitemap',
|
|
||||||
routerLink: ['/admin/domains'],
|
|
||||||
visible: await this.auth.hasAnyPermissionLazy([
|
|
||||||
PermissionsEnum.domains,
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'common.groups',
|
label: 'common.groups',
|
||||||
icon: 'pi pi-tags',
|
icon: 'pi pi-tags',
|
||||||
@ -50,7 +42,6 @@ export class SidebarService {
|
|||||||
routerLink: ['/admin/urls'],
|
routerLink: ['/admin/urls'],
|
||||||
visible: await this.auth.hasAnyPermissionLazy([
|
visible: await this.auth.hasAnyPermissionLazy([
|
||||||
PermissionsEnum.shortUrls,
|
PermissionsEnum.shortUrls,
|
||||||
PermissionsEnum.shortUrlsByAssignment,
|
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
await this.groupAdministration(),
|
await this.groupAdministration(),
|
||||||
|
@ -5,11 +5,7 @@
|
|||||||
"themes": [
|
"themes": [
|
||||||
{
|
{
|
||||||
"label": "Open-redirect",
|
"label": "Open-redirect",
|
||||||
"name": "open-redirect"
|
"name": "Open-redirect"
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Maxlan",
|
|
||||||
"name": "maxlan"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"api": {
|
"api": {
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
"created": "Erstellt",
|
"created": "Erstellt",
|
||||||
"deleted": "Gelöscht",
|
"deleted": "Gelöscht",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"domain": "Domain",
|
|
||||||
"domains": "Domains",
|
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"edited_at": "Bearbeitet am",
|
"edited_at": "Bearbeitet am",
|
||||||
"editor": "Bearbeiter",
|
"editor": "Bearbeiter",
|
||||||
@ -40,7 +38,6 @@
|
|||||||
"news": "News",
|
"news": "News",
|
||||||
"role": "Rolle",
|
"role": "Rolle",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"short_url": "Url",
|
|
||||||
"updated": "Bearbeitet",
|
"updated": "Bearbeitet",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"urls": "URLs",
|
"urls": "URLs",
|
||||||
@ -61,9 +58,6 @@
|
|||||||
},
|
},
|
||||||
"save": "Speichern"
|
"save": "Speichern"
|
||||||
},
|
},
|
||||||
"domain": {
|
|
||||||
"count_header": "Domain(s)"
|
|
||||||
},
|
|
||||||
"error": {
|
"error": {
|
||||||
"404": "404 - Nicht gefunden",
|
"404": "404 - Nicht gefunden",
|
||||||
"create_failed": "Erstellung fehlgeschlagen",
|
"create_failed": "Erstellung fehlgeschlagen",
|
||||||
@ -95,14 +89,6 @@
|
|||||||
"api_keys.create": "Erstellen",
|
"api_keys.create": "Erstellen",
|
||||||
"api_keys.delete": "Löschen",
|
"api_keys.delete": "Löschen",
|
||||||
"api_keys.update": "Bearbeiten",
|
"api_keys.update": "Bearbeiten",
|
||||||
"domains": "Domains",
|
|
||||||
"domains.create": "Erstellen",
|
|
||||||
"domains.delete": "Löschen",
|
|
||||||
"domains.update": "Bearbeiten",
|
|
||||||
"groups": "Gruppen",
|
|
||||||
"groups.create": "Erstellen",
|
|
||||||
"groups.delete": "Löschen",
|
|
||||||
"groups.update": "Bearbeiten",
|
|
||||||
"ip_list": "IP Liste",
|
"ip_list": "IP Liste",
|
||||||
"ip_list.create": "Erstellen",
|
"ip_list.create": "Erstellen",
|
||||||
"ip_list.delete": "Löschen",
|
"ip_list.delete": "Löschen",
|
||||||
@ -115,11 +101,6 @@
|
|||||||
"roles.create": "Erstellen",
|
"roles.create": "Erstellen",
|
||||||
"roles.delete": "Löschen",
|
"roles.delete": "Löschen",
|
||||||
"roles.update": "Bearbeiten",
|
"roles.update": "Bearbeiten",
|
||||||
"short_urls": "Kurz-URLs",
|
|
||||||
"short_urls.by_assignment": "Zuweisung",
|
|
||||||
"short_urls.create": "Erstellen",
|
|
||||||
"short_urls.delete": "Löschen",
|
|
||||||
"short_urls.update": "Bearbeiten",
|
|
||||||
"users": "Benutzer",
|
"users": "Benutzer",
|
||||||
"users.create": "Erstellen",
|
"users.create": "Erstellen",
|
||||||
"users.delete": "Löschen",
|
"users.delete": "Löschen",
|
||||||
@ -128,11 +109,6 @@
|
|||||||
"qr": {
|
"qr": {
|
||||||
"width": "Breite"
|
"width": "Breite"
|
||||||
},
|
},
|
||||||
"permission_descriptions": {
|
|
||||||
"users.update": "Benutzer inkl. der Rollen ändern",
|
|
||||||
"short_urls": "Alle URLs sehen",
|
|
||||||
"short_urls.by_assignment": "Alle Kurz-URLs anzeigen, die einer Gruppe nach Rolle zugewiesen sind"
|
|
||||||
},
|
|
||||||
"role": {
|
"role": {
|
||||||
"count_header": "Rolle(n)"
|
"count_header": "Rolle(n)"
|
||||||
},
|
},
|
||||||
@ -168,7 +144,6 @@
|
|||||||
"update": "Bearbeiten"
|
"update": "Bearbeiten"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"assign_roles": "Rollen zuweisen",
|
|
||||||
"count_header": "Benutzer",
|
"count_header": "Benutzer",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
"user": "Benutzer",
|
"user": "Benutzer",
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
"created": "Created",
|
"created": "Created",
|
||||||
"deleted": "Deleted",
|
"deleted": "Deleted",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"domain": "Domain",
|
|
||||||
"domains": "Domains",
|
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"edited_at": "Edited at",
|
"edited_at": "Edited at",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
@ -40,7 +38,6 @@
|
|||||||
"news": "News",
|
"news": "News",
|
||||||
"role": "Role",
|
"role": "Role",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"short_url": "Url",
|
|
||||||
"updated": "Updated",
|
"updated": "Updated",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"urls": "URLs",
|
"urls": "URLs",
|
||||||
@ -61,9 +58,6 @@
|
|||||||
},
|
},
|
||||||
"save": "Save"
|
"save": "Save"
|
||||||
},
|
},
|
||||||
"domain": {
|
|
||||||
"count_header": "Domain(s)"
|
|
||||||
},
|
|
||||||
"error": {
|
"error": {
|
||||||
"404": "404 - Not found",
|
"404": "404 - Not found",
|
||||||
"create_failed": "Create failed",
|
"create_failed": "Create failed",
|
||||||
@ -95,14 +89,6 @@
|
|||||||
"api_keys.create": "Create",
|
"api_keys.create": "Create",
|
||||||
"api_keys.delete": "Delete",
|
"api_keys.delete": "Delete",
|
||||||
"api_keys.update": "Update",
|
"api_keys.update": "Update",
|
||||||
"domains": "Domains",
|
|
||||||
"domains.create": "Create",
|
|
||||||
"domains.delete": "Delete",
|
|
||||||
"domains.update": "Update",
|
|
||||||
"groups": "Groups",
|
|
||||||
"groups.create": "Create",
|
|
||||||
"groups.delete": "Delete",
|
|
||||||
"groups.update": "Update",
|
|
||||||
"ip_list": "IP list",
|
"ip_list": "IP list",
|
||||||
"ip_list.create": "Create",
|
"ip_list.create": "Create",
|
||||||
"ip_list.delete": "Delete",
|
"ip_list.delete": "Delete",
|
||||||
@ -115,21 +101,11 @@
|
|||||||
"roles.create": "Create",
|
"roles.create": "Create",
|
||||||
"roles.delete": "Delete",
|
"roles.delete": "Delete",
|
||||||
"roles.update": "Update",
|
"roles.update": "Update",
|
||||||
"short_urls": "Short URLs",
|
|
||||||
"short_urls.by_assignment": "By assignment",
|
|
||||||
"short_urls.create": "Create",
|
|
||||||
"short_urls.delete": "Delete",
|
|
||||||
"short_urls.update": "Update",
|
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
"users.create": "Create",
|
"users.create": "Create",
|
||||||
"users.delete": "Delete",
|
"users.delete": "Delete",
|
||||||
"users.update": "Update"
|
"users.update": "Update"
|
||||||
},
|
},
|
||||||
"permission_descriptions": {
|
|
||||||
"users.update": "Change users including their roles",
|
|
||||||
"short_urls": "See all URLs",
|
|
||||||
"short_urls.by_assignment": "See all short urls assigned to a group by role"
|
|
||||||
},
|
|
||||||
"qr": {
|
"qr": {
|
||||||
"width": "Width"
|
"width": "Width"
|
||||||
},
|
},
|
||||||
@ -168,7 +144,6 @@
|
|||||||
"update": "Update"
|
"update": "Update"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"assign_roles": "Assign roles",
|
|
||||||
"count_header": "User(s)",
|
"count_header": "User(s)",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
@import 'tailwindcss/utilities';
|
@import 'tailwindcss/utilities';
|
||||||
|
|
||||||
@import 'styles/theme';
|
@import 'styles/theme';
|
||||||
@import 'styles/theme_maxlan';
|
|
||||||
@import 'styles/tablet';
|
@import 'styles/tablet';
|
||||||
@import 'styles/mobile';
|
@import 'styles/mobile';
|
||||||
|
|
||||||
@ -59,6 +58,7 @@ body {
|
|||||||
input,
|
input,
|
||||||
.p-checkbox-box,
|
.p-checkbox-box,
|
||||||
.p-dropdown {
|
.p-dropdown {
|
||||||
|
border: 1px solid $accentColor;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +70,16 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.divider {
|
||||||
|
border-bottom: 1px solid $accentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-divider {
|
||||||
|
border-left: 1px solid $accentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
height: $headerHeight;
|
height: $headerHeight;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -119,6 +129,7 @@ main {
|
|||||||
.component {
|
.component {
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
background-color: $backgroundColor;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
@ -130,6 +141,7 @@ footer {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
color: $textColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
@ -1,414 +1,406 @@
|
|||||||
.open-redirect {
|
$headerColor: #a2271f;
|
||||||
$headerColor: #ef9d0d;
|
|
||||||
|
|
||||||
// generated with https://colorffy.com/dark-theme-generator?colors=314390-121212
|
// generated with https://colorffy.com/dark-theme-generator?colors=314390-121212
|
||||||
$textColor: #fff;
|
$textColor: #fff;
|
||||||
$textColorHighlight: #ef9d0d;
|
$textColorHighlight: #314390;
|
||||||
$textColorHighlight2: #b76f00;
|
$textColorHighlight2: #a2271f;
|
||||||
|
|
||||||
// https://tailwindcss.com/docs/customizing-colors
|
// https://tailwindcss.com/docs/customizing-colors
|
||||||
$backgroundColor: #1e293b; // slate-800
|
$backgroundColor: #1e293b; // slate-800
|
||||||
$backgroundColor2: #0f172a; // slate-900
|
$backgroundColor2: #0f172a; // slate-900
|
||||||
$backgroundColor3: #334155; // slate-700
|
$backgroundColor3: #334155; // slate-700
|
||||||
|
|
||||||
$accentColor: #475569; // slate-600
|
$accentColor: #475569; // slate-600
|
||||||
$infoColor: #1ea97c;
|
$infoColor: #1ea97c;
|
||||||
$warningColor: #ffc107;
|
$warningColor: #ffc107;
|
||||||
$errorColor: #ff5252;
|
$errorColor: #ff5252;
|
||||||
|
|
||||||
|
body {
|
||||||
background-color: $backgroundColor2;
|
background-color: $backgroundColor2;
|
||||||
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.highlight {
|
.highlight {
|
||||||
color: $textColorHighlight;
|
color: $textColorHighlight;
|
||||||
}
|
|
||||||
|
|
||||||
.highlight2 {
|
|
||||||
color: $textColorHighlight2 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg2 {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg3 {
|
|
||||||
background-color: $backgroundColor3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accent {
|
|
||||||
color: $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
color: $infoColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
color: $warningColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: $errorColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deleted {
|
|
||||||
color: $accentColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
border-bottom: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
.highlight2 {
|
||||||
h2,
|
color: $textColorHighlight2 !important;
|
||||||
h3 {
|
|
||||||
color: $headerColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input, .p-checkbox-box, .p-dropdown {
|
.bg {
|
||||||
border: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app {
|
|
||||||
.component {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-base {
|
|
||||||
color: $textColor;
|
|
||||||
background: $textColorHighlight !important;
|
|
||||||
border: 1px solid $textColorHighlight;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
.p-button {
|
|
||||||
@extend .btn-base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
border: none !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn-without-hover {
|
|
||||||
&:hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn {
|
|
||||||
&:hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-btn,
|
|
||||||
.danger-icon-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $errorColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-columns-select {
|
|
||||||
.active-while-panel-open {
|
|
||||||
.p-button {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-spinner {
|
|
||||||
.p-progress-spinner-circle {
|
|
||||||
stroke: $textColorHighlight !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-paginator {
|
|
||||||
.p-paginator-element {
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-highlight {
|
|
||||||
color: $textColorHighlight2;
|
|
||||||
box-shadow: none !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight2 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-multiselect,
|
|
||||||
.p-paginator,
|
|
||||||
.p-dropdown,
|
|
||||||
input {
|
|
||||||
background-color: $backgroundColor;
|
background-color: $backgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputtext:enabled:focus,
|
.bg2 {
|
||||||
.p-inputtext:enabled:hover,
|
background-color: $backgroundColor2;
|
||||||
.p-multiselect:not(.p-disabled):hover,
|
}
|
||||||
.p-multiselect:not(.p-disabled).p-focus,
|
|
||||||
.p-dropdown:not(.p-disabled):hover,
|
.bg3 {
|
||||||
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover,
|
background-color: $backgroundColor3;
|
||||||
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus {
|
}
|
||||||
border-color: $textColorHighlight;
|
|
||||||
|
.accent {
|
||||||
|
color: $accentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: $infoColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: $warningColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: $errorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleted {
|
||||||
|
color: $accentColor !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
color: $headerColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
.component {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-base {
|
||||||
|
color: $textColor;
|
||||||
|
background: $textColorHighlight !important;
|
||||||
|
border: 1px solid $textColorHighlight;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-checkbox .p-checkbox-box.p-highlight {
|
&:hover {
|
||||||
border-color: $textColorHighlight;
|
background: transparent !important;
|
||||||
background: $textColorHighlight;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p-checkbox {
|
.btn {
|
||||||
.p-checkbox {
|
.p-button {
|
||||||
.p-checkbox-box {
|
@extend .btn-base;
|
||||||
background-color: $backgroundColor;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-checkbox-box.p-highlight {
|
.icon-btn {
|
||||||
border-color: $textColorHighlight;
|
background-color: transparent !important;
|
||||||
background-color: $textColorHighlight;
|
border: none;
|
||||||
}
|
|
||||||
|
.p-button {
|
||||||
|
color: $textColor;
|
||||||
|
background: transparent !important;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $textColorHighlight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p-inputSwitch {
|
.text-btn {
|
||||||
.p-inputswitch {
|
background-color: transparent !important;
|
||||||
&.p-focus .p-inputswitch-slider {
|
|
||||||
|
.p-button {
|
||||||
|
color: $textColor;
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn-without-hover {
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger-btn,
|
||||||
|
.danger-icon-btn {
|
||||||
|
background-color: transparent !important;
|
||||||
|
|
||||||
|
.p-button {
|
||||||
|
color: $textColor;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $errorColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-columns-select {
|
||||||
|
.active-while-panel-open {
|
||||||
|
.p-button {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-spinner {
|
||||||
|
.p-progress-spinner-circle {
|
||||||
|
stroke: $textColorHighlight !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-paginator {
|
||||||
|
.p-paginator-element {
|
||||||
|
&:hover {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-highlight {
|
||||||
|
color: $textColorHighlight2;
|
||||||
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $textColorHighlight2 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-multiselect,
|
||||||
|
.p-paginator,
|
||||||
|
.p-dropdown,
|
||||||
|
input {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-inputtext:enabled:focus,
|
||||||
|
.p-inputtext:enabled:hover,
|
||||||
|
.p-multiselect:not(.p-disabled):hover,
|
||||||
|
.p-multiselect:not(.p-disabled).p-focus,
|
||||||
|
.p-dropdown:not(.p-disabled):hover,
|
||||||
|
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover,
|
||||||
|
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus {
|
||||||
|
border-color: $textColorHighlight;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-checkbox .p-checkbox-box.p-highlight {
|
||||||
|
border-color: $textColorHighlight;
|
||||||
|
background: $textColorHighlight;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p-checkbox {
|
||||||
|
.p-checkbox {
|
||||||
|
.p-checkbox-box {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-checkbox-box.p-highlight {
|
||||||
|
border-color: $textColorHighlight;
|
||||||
|
background-color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-inputSwitch {
|
||||||
|
.p-inputswitch {
|
||||||
|
&.p-focus .p-inputswitch-slider {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-inputswitch-slider {
|
||||||
|
background-color: $headerColor;
|
||||||
|
|
||||||
|
&.p-highlight {
|
||||||
|
border-color: $textColorHighlight;
|
||||||
|
background: $textColorHighlight;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.p-inputswitch-checked {
|
||||||
.p-inputswitch-slider {
|
.p-inputswitch-slider {
|
||||||
background-color: $headerColor;
|
background-color: $headerColor;
|
||||||
|
|
||||||
&.p-highlight {
|
&:before {
|
||||||
border-color: $textColorHighlight;
|
background-color: $textColor;
|
||||||
background: $textColorHighlight;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.p-inputswitch-checked {
|
|
||||||
.p-inputswitch-slider {
|
|
||||||
background-color: $headerColor;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
background-color: $textColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-panel-menu {
|
|
||||||
.p-panelmenu {
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-menuitem-text,
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-menuitem-icon,
|
|
||||||
.p-panelmenu
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-submenu-icon {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-panelmenu-header-content {
|
|
||||||
&:hover {
|
|
||||||
.p-panelmenu-header-action {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-menubar {
|
|
||||||
.p-menubar {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-menubar-root-list {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-menuitem {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.p-menuitem-content .p-menuitem-link .p-menuitem-text {
|
|
||||||
color: $textColorHighlight2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-focus {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
|
|
||||||
.p-menuitem-content {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-dropdown {
|
|
||||||
.p-dropdown:not(.p-disabled).p-focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
border-color: $headerColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dropdown-panel {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-multiselect {
|
|
||||||
.p-multiselect-panel,
|
|
||||||
.p-multiselect-panel .p-multiselect-header {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-table {
|
|
||||||
.p-datatable {
|
|
||||||
.p-datatable-header,
|
|
||||||
.p-datatable-footer,
|
|
||||||
.p-datatable-thead > tr > th,
|
|
||||||
.p-datatable-tbody > tr {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
border-bottom: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-sortable-column.p-highlight,
|
|
||||||
.p-sortable-column:not(.p-highlight):hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-sortable-column:not(.p-highlight):hover,
|
|
||||||
.p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-sidebar {
|
|
||||||
.p-sidebar {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-steps .p-steps-item.p-highlight .p-steps-number {
|
|
||||||
background-color: $headerColor;
|
|
||||||
color: $textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog {
|
|
||||||
.p-dialog-header {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog-content {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog-footer {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-badge {
|
|
||||||
background: $headerColor;
|
|
||||||
color: $textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
p-password {
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.p-password-panel {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-slider {
|
|
||||||
.p-slider {
|
|
||||||
.p-slider-range {
|
|
||||||
background: $headerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-slider-handle {
|
|
||||||
border-color: $headerColor;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $headerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p-panel-menu {
|
||||||
|
.p-panelmenu {
|
||||||
|
.p-panelmenu-content
|
||||||
|
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
||||||
|
> .p-menuitem-content:hover
|
||||||
|
.p-menuitem-link
|
||||||
|
.p-menuitem-text,
|
||||||
|
.p-panelmenu-content
|
||||||
|
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
||||||
|
> .p-menuitem-content:hover
|
||||||
|
.p-menuitem-link
|
||||||
|
.p-menuitem-icon,
|
||||||
|
.p-panelmenu
|
||||||
|
.p-panelmenu-content
|
||||||
|
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
||||||
|
> .p-menuitem-content:hover
|
||||||
|
.p-menuitem-link
|
||||||
|
.p-submenu-icon {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-panelmenu-header-content {
|
||||||
|
&:hover {
|
||||||
|
.p-panelmenu-header-action {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-menubar {
|
||||||
|
.p-menubar {
|
||||||
|
background-color: $backgroundColor2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-menubar-root-list {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-menuitem {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.p-menuitem-content .p-menuitem-link .p-menuitem-text {
|
||||||
|
color: $textColorHighlight2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-focus {
|
||||||
|
background-color: $backgroundColor2;
|
||||||
|
|
||||||
|
.p-menuitem-content {
|
||||||
|
background-color: $backgroundColor2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-dropdown {
|
||||||
|
.p-dropdown:not(.p-disabled).p-focus {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-color: $headerColor !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dropdown-panel {
|
||||||
|
background-color: $backgroundColor2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-multiselect {
|
||||||
|
.p-multiselect-panel,
|
||||||
|
.p-multiselect-panel .p-multiselect-header {
|
||||||
|
background-color: $backgroundColor2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-table {
|
||||||
|
.p-datatable {
|
||||||
|
.p-datatable-header,
|
||||||
|
.p-datatable-footer,
|
||||||
|
.p-datatable-thead > tr > th,
|
||||||
|
.p-datatable-tbody > tr {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
border-bottom: 1px solid $accentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-sortable-column.p-highlight,
|
||||||
|
.p-sortable-column:not(.p-highlight):hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-sortable-column:not(.p-highlight):hover,
|
||||||
|
.p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon {
|
||||||
|
color: $textColorHighlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-sidebar {
|
||||||
|
.p-sidebar {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-steps .p-steps-item.p-highlight .p-steps-number {
|
||||||
|
background-color: $headerColor;
|
||||||
|
color: $textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dialog {
|
||||||
|
.p-dialog-header {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dialog-content {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dialog-footer {
|
||||||
|
background-color: $backgroundColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-badge {
|
||||||
|
background: $headerColor;
|
||||||
|
color: $textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
p-password {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.p-password-panel {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p-slider {
|
||||||
|
.p-slider {
|
||||||
|
.p-slider-range {
|
||||||
|
background: $headerColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-slider-handle {
|
||||||
|
border-color: $headerColor;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $headerColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,414 +0,0 @@
|
|||||||
.maxlan {
|
|
||||||
$headerColor: #a2271f;
|
|
||||||
|
|
||||||
// generated with https://colorffy.com/dark-theme-generator?colors=314390-121212
|
|
||||||
$textColor: #fff;
|
|
||||||
$textColorHighlight: #314390;
|
|
||||||
$textColorHighlight2: #a2271f;
|
|
||||||
|
|
||||||
// https://tailwindcss.com/docs/customizing-colors
|
|
||||||
$backgroundColor: #1e293b; // slate-800
|
|
||||||
$backgroundColor2: #0f172a; // slate-900
|
|
||||||
$backgroundColor3: #334155; // slate-700
|
|
||||||
|
|
||||||
$accentColor: #475569; // slate-600
|
|
||||||
$infoColor: #1ea97c;
|
|
||||||
$warningColor: #ffc107;
|
|
||||||
$errorColor: #ff5252;
|
|
||||||
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
|
|
||||||
@layer utilities {
|
|
||||||
.highlight {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight2 {
|
|
||||||
color: $textColorHighlight2 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg2 {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg3 {
|
|
||||||
background-color: $backgroundColor3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accent {
|
|
||||||
color: $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
color: $infoColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
color: $warningColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: $errorColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deleted {
|
|
||||||
color: $accentColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
border-bottom: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
color: $headerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, .p-checkbox-box, .p-dropdown {
|
|
||||||
border: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app {
|
|
||||||
.component {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-base {
|
|
||||||
color: $textColor;
|
|
||||||
background: $textColorHighlight !important;
|
|
||||||
border: 1px solid $textColorHighlight;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
.p-button {
|
|
||||||
@extend .btn-base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
border: none !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn-without-hover {
|
|
||||||
&:hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn {
|
|
||||||
&:hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-btn,
|
|
||||||
.danger-icon-btn {
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
.p-button {
|
|
||||||
color: $textColor;
|
|
||||||
background: transparent !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $errorColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-columns-select {
|
|
||||||
.active-while-panel-open {
|
|
||||||
.p-button {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-spinner {
|
|
||||||
.p-progress-spinner-circle {
|
|
||||||
stroke: $textColorHighlight !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-paginator {
|
|
||||||
.p-paginator-element {
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-highlight {
|
|
||||||
color: $textColorHighlight2;
|
|
||||||
box-shadow: none !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $textColorHighlight2 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-multiselect,
|
|
||||||
.p-paginator,
|
|
||||||
.p-dropdown,
|
|
||||||
input {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-inputtext:enabled:focus,
|
|
||||||
.p-inputtext:enabled:hover,
|
|
||||||
.p-multiselect:not(.p-disabled):hover,
|
|
||||||
.p-multiselect:not(.p-disabled).p-focus,
|
|
||||||
.p-dropdown:not(.p-disabled):hover,
|
|
||||||
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover,
|
|
||||||
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus {
|
|
||||||
border-color: $textColorHighlight;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox .p-checkbox-box.p-highlight {
|
|
||||||
border-color: $textColorHighlight;
|
|
||||||
background: $textColorHighlight;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
p-checkbox {
|
|
||||||
.p-checkbox {
|
|
||||||
.p-checkbox-box {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-checkbox-box.p-highlight {
|
|
||||||
border-color: $textColorHighlight;
|
|
||||||
background-color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-inputSwitch {
|
|
||||||
.p-inputswitch {
|
|
||||||
&.p-focus .p-inputswitch-slider {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-inputswitch-slider {
|
|
||||||
background-color: $headerColor;
|
|
||||||
|
|
||||||
&.p-highlight {
|
|
||||||
border-color: $textColorHighlight;
|
|
||||||
background: $textColorHighlight;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.p-inputswitch-checked {
|
|
||||||
.p-inputswitch-slider {
|
|
||||||
background-color: $headerColor;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
background-color: $textColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-panel-menu {
|
|
||||||
.p-panelmenu {
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-menuitem-text,
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-menuitem-icon,
|
|
||||||
.p-panelmenu
|
|
||||||
.p-panelmenu-content
|
|
||||||
.p-menuitem:not(.p-highlight):not(.p-disabled)
|
|
||||||
> .p-menuitem-content:hover
|
|
||||||
.p-menuitem-link
|
|
||||||
.p-submenu-icon {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-panelmenu-header-content {
|
|
||||||
&:hover {
|
|
||||||
.p-panelmenu-header-action {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-menubar {
|
|
||||||
.p-menubar {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-menubar-root-list {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-menuitem {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.p-menuitem-content .p-menuitem-link .p-menuitem-text {
|
|
||||||
color: $textColorHighlight2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-focus {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
|
|
||||||
.p-menuitem-content {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-dropdown {
|
|
||||||
.p-dropdown:not(.p-disabled).p-focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
border-color: $headerColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dropdown-panel {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-multiselect {
|
|
||||||
.p-multiselect-panel,
|
|
||||||
.p-multiselect-panel .p-multiselect-header {
|
|
||||||
background-color: $backgroundColor2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-table {
|
|
||||||
.p-datatable {
|
|
||||||
.p-datatable-header,
|
|
||||||
.p-datatable-footer,
|
|
||||||
.p-datatable-thead > tr > th,
|
|
||||||
.p-datatable-tbody > tr {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
border-bottom: 1px solid $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-sortable-column.p-highlight,
|
|
||||||
.p-sortable-column:not(.p-highlight):hover {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-sortable-column:not(.p-highlight):hover,
|
|
||||||
.p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon {
|
|
||||||
color: $textColorHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-sidebar {
|
|
||||||
.p-sidebar {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-steps .p-steps-item.p-highlight .p-steps-number {
|
|
||||||
background-color: $headerColor;
|
|
||||||
color: $textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog {
|
|
||||||
.p-dialog-header {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog-content {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-dialog-footer {
|
|
||||||
background-color: $backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-badge {
|
|
||||||
background: $headerColor;
|
|
||||||
color: $textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
p-password {
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.p-password-panel {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p-slider {
|
|
||||||
.p-slider {
|
|
||||||
.p-slider-range {
|
|
||||||
background: $headerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-slider-handle {
|
|
||||||
border-color: $headerColor;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $headerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user