parent
dd5769823f
commit
5ffd66d06d
@ -1,6 +1,4 @@
|
|||||||
import json
|
from flask import redirect, jsonify, request, Response
|
||||||
|
|
||||||
from flask import redirect, jsonify, request
|
|
||||||
|
|
||||||
from api.route import Route
|
from api.route import Route
|
||||||
from core.logger import Logger
|
from core.logger import Logger
|
||||||
@ -41,4 +39,11 @@ async def handle_short_url(path: str):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to update short url {from_db.short_url} with error", e)
|
logger.error(f"Failed to update short url {from_db.short_url} with error", e)
|
||||||
|
|
||||||
return redirect(from_db.target_url)
|
return _do_redirect(from_db.target_url)
|
||||||
|
|
||||||
|
def _do_redirect(url: str) -> Response:
|
||||||
|
# todo: multiple protocols like ts3://
|
||||||
|
if not url.startswith("http://") and not url.startswith("https://"):
|
||||||
|
url = f"http://{url}"
|
||||||
|
|
||||||
|
return redirect(url)
|
||||||
|
@ -11,6 +11,7 @@ type ShortUrl implements DbModel {
|
|||||||
description: String
|
description: String
|
||||||
visits: Int
|
visits: Int
|
||||||
group: Group
|
group: Group
|
||||||
|
loadingScreen: Boolean
|
||||||
|
|
||||||
deleted: Boolean
|
deleted: Boolean
|
||||||
editor: User
|
editor: User
|
||||||
@ -22,6 +23,7 @@ input ShortUrlSort {
|
|||||||
id: SortOrder
|
id: SortOrder
|
||||||
name: SortOrder
|
name: SortOrder
|
||||||
description: SortOrder
|
description: SortOrder
|
||||||
|
loadingScreen: SortOrder
|
||||||
|
|
||||||
deleted: SortOrder
|
deleted: SortOrder
|
||||||
editorId: SortOrder
|
editorId: SortOrder
|
||||||
@ -33,6 +35,7 @@ input ShortUrlFilter {
|
|||||||
id: IntFilter
|
id: IntFilter
|
||||||
name: StringFilter
|
name: StringFilter
|
||||||
description: StringFilter
|
description: StringFilter
|
||||||
|
loadingScreen: BooleanFilter
|
||||||
|
|
||||||
deleted: BooleanFilter
|
deleted: BooleanFilter
|
||||||
editor: IntFilter
|
editor: IntFilter
|
||||||
@ -52,6 +55,7 @@ input ShortUrlCreateInput {
|
|||||||
targetUrl: String!
|
targetUrl: String!
|
||||||
description: String
|
description: String
|
||||||
groupId: ID
|
groupId: ID
|
||||||
|
loadingScreen: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input ShortUrlUpdateInput {
|
input ShortUrlUpdateInput {
|
||||||
@ -60,4 +64,5 @@ input ShortUrlUpdateInput {
|
|||||||
targetUrl: String
|
targetUrl: String
|
||||||
description: String
|
description: String
|
||||||
groupId: ID
|
groupId: ID
|
||||||
|
loadingScreen: Boolean
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ 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._loading_screen = self.option("loadingScreen", str)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_url(self) -> str:
|
def short_url(self) -> str:
|
||||||
@ -28,3 +29,7 @@ class ShortUrlCreateInput(InputABC):
|
|||||||
@property
|
@property
|
||||||
def group_id(self) -> Optional[int]:
|
def group_id(self) -> Optional[int]:
|
||||||
return self._group_id
|
return self._group_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loading_screen(self) -> Optional[str]:
|
||||||
|
return self._loading_screen
|
||||||
|
@ -13,6 +13,7 @@ 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._loading_screen = self.option("loadingScreen", str)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> int:
|
def id(self) -> int:
|
||||||
@ -33,3 +34,7 @@ class ShortUrlUpdateInput(InputABC):
|
|||||||
@property
|
@property
|
||||||
def group_id(self) -> Optional[int]:
|
def group_id(self) -> Optional[int]:
|
||||||
return self._group_id
|
return self._group_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loading_screen(self) -> Optional[str]:
|
||||||
|
return self._loading_screen
|
||||||
|
@ -49,6 +49,7 @@ class ShortUrlMutation(MutationABC):
|
|||||||
obj.target_url,
|
obj.target_url,
|
||||||
obj.description,
|
obj.description,
|
||||||
obj.group_id,
|
obj.group_id,
|
||||||
|
obj.loading_screen,
|
||||||
)
|
)
|
||||||
nid = await shortUrlDao.create(short_url)
|
nid = await shortUrlDao.create(short_url)
|
||||||
return await shortUrlDao.get_by_id(nid)
|
return await shortUrlDao.get_by_id(nid)
|
||||||
@ -74,6 +75,9 @@ class ShortUrlMutation(MutationABC):
|
|||||||
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
|
||||||
|
|
||||||
|
if obj.loading_screen is not None:
|
||||||
|
short_url.loading_screen = obj.loading_screen
|
||||||
|
|
||||||
await shortUrlDao.update(short_url)
|
await shortUrlDao.update(short_url)
|
||||||
return await shortUrlDao.get_by_id(obj.id)
|
return await shortUrlDao.get_by_id(obj.id)
|
||||||
|
|
||||||
|
@ -10,3 +10,4 @@ class ShortUrlQuery(DbModelQueryABC):
|
|||||||
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("visits", lambda x, *_: x.visit_count)
|
self.set_field("visits", lambda x, *_: x.visit_count)
|
||||||
|
self.set_field("loadingScreen", lambda x, *_: x.loading_screen)
|
||||||
|
@ -16,6 +16,7 @@ class ShortUrl(DbModelABC):
|
|||||||
target_url: str,
|
target_url: str,
|
||||||
description: Optional[str],
|
description: Optional[str],
|
||||||
group_id: Optional[SerialId],
|
group_id: Optional[SerialId],
|
||||||
|
loading_screen: Optional[str] = None,
|
||||||
deleted: bool = False,
|
deleted: bool = False,
|
||||||
editor_id: Optional[SerialId] = None,
|
editor_id: Optional[SerialId] = None,
|
||||||
created: Optional[datetime] = None,
|
created: Optional[datetime] = None,
|
||||||
@ -26,6 +27,7 @@ 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._loading_screen = loading_screen
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_url(self) -> str:
|
def short_url(self) -> str:
|
||||||
@ -74,8 +76,17 @@ class ShortUrl(DbModelABC):
|
|||||||
|
|
||||||
return await shortUrlVisitDao.count_by_id(self.id)
|
return await shortUrlVisitDao.count_by_id(self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loading_screen(self) -> Optional[str]:
|
||||||
|
return self._loading_screen
|
||||||
|
|
||||||
|
@loading_screen.setter
|
||||||
|
def loading_screen(self, value: Optional[str]):
|
||||||
|
self._loading_screen = value
|
||||||
|
|
||||||
def to_dto(self) -> dict:
|
def to_dto(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"target_url": self.target_url,
|
"target_url": self.target_url,
|
||||||
|
"loadingScreen": self.loading_screen,
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ 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.loading_screen, bool)
|
||||||
|
|
||||||
|
|
||||||
shortUrlDao = ShortUrlDao()
|
shortUrlDao = ShortUrlDao()
|
||||||
|
6
api/src/data/scripts/2025-01-02-14-15-loading-screen.sql
Normal file
6
api/src/data/scripts/2025-01-02-14-15-loading-screen.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE public.short_urls
|
||||||
|
ADD COLUMN IF NOT EXISTS loadingScreen boolean DEFAULT true;
|
||||||
|
|
||||||
|
ALTER TABLE public.short_urls_history
|
||||||
|
ADD COLUMN IF NOT EXISTS loadingScreen boolean NULL;
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
<main *ngIf="isLoggedIn; else home">
|
<main *ngIf="isLoggedIn && showSidebar; else home">
|
||||||
<app-header></app-header>
|
<app-header></app-header>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<aside *ngIf="showSidebar">
|
<aside>
|
||||||
<app-sidebar></app-sidebar>
|
<app-sidebar></app-sidebar>
|
||||||
</aside>
|
</aside>
|
||||||
<section class="component">
|
<section class="component">
|
||||||
|
@ -22,6 +22,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
|
|
||||||
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;
|
||||||
|
console.warn("isLoggedIn");
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sidebar.visible$
|
this.sidebar.visible$
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { AuthService } from "src/app/service/auth.service";
|
import { KeycloakService } from "keycloak-angular";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-home",
|
selector: "app-home",
|
||||||
@ -7,11 +7,9 @@ import { AuthService } from "src/app/service/auth.service";
|
|||||||
styleUrl: "./home.component.scss",
|
styleUrl: "./home.component.scss",
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent {
|
||||||
constructor(private auth: AuthService) {
|
constructor(private keycloak: KeycloakService) {
|
||||||
if (this.auth.user$.value !== undefined) {
|
if (!this.keycloak.isLoggedIn()) {
|
||||||
return;
|
this.keycloak.login().then(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.auth.login().then(() => {});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,16 @@
|
|||||||
<p>redirect works!</p>
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="relative w-screen h-screen bg-cover bg-center"
|
||||||
|
style="background-image: url(https://www.sh-edraft.de/wp-content/uploads/2020/03/IMG_20170731_213039-scaled.jpg)"></div>
|
||||||
|
|
||||||
|
<div class="absolute w-1/3 h-2/5 rounded-xl p-5 flex flex-col gap-5 justify-center items-center">
|
||||||
|
<div class="absolute inset-0 bg-black opacity-70 rounded-xl"></div>
|
||||||
|
<div class="relative logo flex justify-center items-center">
|
||||||
|
<img class="h-48"
|
||||||
|
src="https://www.sh-edraft.de/wp-content/uploads/2020/04/klein_transparent-e1735827600932.png"
|
||||||
|
alt="logo">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="relative text-3xl text-white">Redirecting...</h1>
|
||||||
|
<p class="relative text-white" *ngIf="secs > -1">You will be redirected in {{ secs }} seconds.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,5 @@
|
|||||||
|
.bg-image {
|
||||||
|
background-position: top center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
@ -11,6 +11,9 @@ import { ShortUrlDto } from "src/app/model/entities/short-url";
|
|||||||
styleUrl: "./redirect.component.scss",
|
styleUrl: "./redirect.component.scss",
|
||||||
})
|
})
|
||||||
export class RedirectComponent implements OnInit {
|
export class RedirectComponent implements OnInit {
|
||||||
|
defaultSecs = 5;
|
||||||
|
secs = -1;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sidebar: SidebarService,
|
private sidebar: SidebarService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -41,7 +44,19 @@ export class RedirectComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = `${environment.api.url}/redirect/${shortUrl}`;
|
if (!response.loadingScreen) {
|
||||||
|
window.location.href = `${environment.api.url}/redirect/${shortUrl}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.secs = this.defaultSecs;
|
||||||
|
setInterval(() => {
|
||||||
|
this.secs--;
|
||||||
|
|
||||||
|
if (this.secs === 0) {
|
||||||
|
window.location.href = `${environment.api.url}/redirect/${shortUrl}`;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ export interface ShortUrl extends DbModel {
|
|||||||
export interface ShortUrlDto {
|
export interface ShortUrlDto {
|
||||||
id: number;
|
id: number;
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
|
loadingScreen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShortUrlCreateInput {
|
export interface ShortUrlCreateInput {
|
||||||
|
Loading…
Reference in New Issue
Block a user