parent
dd5769823f
commit
5ffd66d06d
@ -1,6 +1,4 @@
|
||||
import json
|
||||
|
||||
from flask import redirect, jsonify, request
|
||||
from flask import redirect, jsonify, request, Response
|
||||
|
||||
from api.route import Route
|
||||
from core.logger import Logger
|
||||
@ -41,4 +39,11 @@ async def handle_short_url(path: str):
|
||||
except Exception as 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
|
||||
visits: Int
|
||||
group: Group
|
||||
loadingScreen: Boolean
|
||||
|
||||
deleted: Boolean
|
||||
editor: User
|
||||
@ -22,6 +23,7 @@ input ShortUrlSort {
|
||||
id: SortOrder
|
||||
name: SortOrder
|
||||
description: SortOrder
|
||||
loadingScreen: SortOrder
|
||||
|
||||
deleted: SortOrder
|
||||
editorId: SortOrder
|
||||
@ -33,6 +35,7 @@ input ShortUrlFilter {
|
||||
id: IntFilter
|
||||
name: StringFilter
|
||||
description: StringFilter
|
||||
loadingScreen: BooleanFilter
|
||||
|
||||
deleted: BooleanFilter
|
||||
editor: IntFilter
|
||||
@ -52,6 +55,7 @@ input ShortUrlCreateInput {
|
||||
targetUrl: String!
|
||||
description: String
|
||||
groupId: ID
|
||||
loadingScreen: Boolean
|
||||
}
|
||||
|
||||
input ShortUrlUpdateInput {
|
||||
@ -60,4 +64,5 @@ input ShortUrlUpdateInput {
|
||||
targetUrl: String
|
||||
description: String
|
||||
groupId: ID
|
||||
loadingScreen: Boolean
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ class ShortUrlCreateInput(InputABC):
|
||||
self._target_url = self.option("targetUrl", str, required=True)
|
||||
self._description = self.option("description", str)
|
||||
self._group_id = self.option("groupId", int)
|
||||
self._loading_screen = self.option("loadingScreen", str)
|
||||
|
||||
@property
|
||||
def short_url(self) -> str:
|
||||
@ -28,3 +29,7 @@ class ShortUrlCreateInput(InputABC):
|
||||
@property
|
||||
def group_id(self) -> Optional[int]:
|
||||
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._description = self.option("description", str)
|
||||
self._group_id = self.option("groupId", int)
|
||||
self._loading_screen = self.option("loadingScreen", str)
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
@ -33,3 +34,7 @@ class ShortUrlUpdateInput(InputABC):
|
||||
@property
|
||||
def group_id(self) -> Optional[int]:
|
||||
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.description,
|
||||
obj.group_id,
|
||||
obj.loading_screen,
|
||||
)
|
||||
nid = await shortUrlDao.create(short_url)
|
||||
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")
|
||||
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)
|
||||
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("group", lambda x, *_: x.group)
|
||||
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,
|
||||
description: Optional[str],
|
||||
group_id: Optional[SerialId],
|
||||
loading_screen: Optional[str] = None,
|
||||
deleted: bool = False,
|
||||
editor_id: Optional[SerialId] = None,
|
||||
created: Optional[datetime] = None,
|
||||
@ -26,6 +27,7 @@ class ShortUrl(DbModelABC):
|
||||
self._target_url = target_url
|
||||
self._description = description
|
||||
self._group_id = group_id
|
||||
self._loading_screen = loading_screen
|
||||
|
||||
@property
|
||||
def short_url(self) -> str:
|
||||
@ -74,8 +76,17 @@ class ShortUrl(DbModelABC):
|
||||
|
||||
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:
|
||||
return {
|
||||
"id": self.id,
|
||||
"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.description, str)
|
||||
self.attribute(ShortUrl.group_id, int)
|
||||
self.attribute(ShortUrl.loading_screen, bool)
|
||||
|
||||
|
||||
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>
|
||||
|
||||
<div class="app">
|
||||
<aside *ngIf="showSidebar">
|
||||
<aside>
|
||||
<app-sidebar></app-sidebar>
|
||||
</aside>
|
||||
<section class="component">
|
||||
|
@ -22,6 +22,7 @@ export class AppComponent implements OnDestroy {
|
||||
|
||||
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe((user) => {
|
||||
this.isLoggedIn = user !== null && user !== undefined;
|
||||
console.warn("isLoggedIn");
|
||||
});
|
||||
|
||||
this.sidebar.visible$
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { AuthService } from "src/app/service/auth.service";
|
||||
import { KeycloakService } from "keycloak-angular";
|
||||
|
||||
@Component({
|
||||
selector: "app-home",
|
||||
@ -7,11 +7,9 @@ import { AuthService } from "src/app/service/auth.service";
|
||||
styleUrl: "./home.component.scss",
|
||||
})
|
||||
export class HomeComponent {
|
||||
constructor(private auth: AuthService) {
|
||||
if (this.auth.user$.value !== undefined) {
|
||||
return;
|
||||
constructor(private keycloak: KeycloakService) {
|
||||
if (!this.keycloak.isLoggedIn()) {
|
||||
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",
|
||||
})
|
||||
export class RedirectComponent implements OnInit {
|
||||
defaultSecs = 5;
|
||||
secs = -1;
|
||||
|
||||
constructor(
|
||||
private sidebar: SidebarService,
|
||||
private router: Router,
|
||||
@ -41,7 +44,19 @@ export class RedirectComponent implements OnInit {
|
||||
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 {
|
||||
id: number;
|
||||
targetUrl: string;
|
||||
loadingScreen: boolean;
|
||||
}
|
||||
|
||||
export interface ShortUrlCreateInput {
|
||||
|
Loading…
Reference in New Issue
Block a user