diff --git a/.gitignore b/.gitignore
index abc591b..d2f262a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
venv
.DS_Store
+
+.vscode
diff --git a/poetry.lock b/poetry.lock
index b21194e..df18141 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1059,13 +1059,13 @@ files = [
[[package]]
name = "pydantic"
-version = "2.10.4"
+version = "2.10.5"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"},
- {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"},
+ {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"},
+ {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"},
]
[package.dependencies]
@@ -1987,4 +1987,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
-content-hash = "54b7af11ca7015e9a96234a672549033d77151acbed7c12675b097ed498a681b"
+content-hash = "bd44ab97afc7310e67c61b6a658180ed4592e01a06f95d645fb42c8e9a60b759"
diff --git a/pyproject.toml b/pyproject.toml
index 12eade5..f25716c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,7 +10,7 @@ packages = [{include = "discord_bot"}]
python = "^3.11"
discord-py = "^2.4.0"
twitchapi = "^4.4.0"
-pydantic = "^2.10.4"
+pydantic = "^2.10.5"
pydantic-settings = "^2.7.1"
httpx = "^0.28.1"
icalendar = "^6.1.0"
diff --git a/src/modules/web_app/frontend/index.css b/src/modules/web_app/frontend/index.css
new file mode 100644
index 0000000..b0cb12c
--- /dev/null
+++ b/src/modules/web_app/frontend/index.css
@@ -0,0 +1,16 @@
+.authorize__container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+}
+
+a.authorize__twitch_btn {
+ display: inline-block;
+ background-color: #6441a5;
+ color: #fff;
+ padding: 10px 20px;
+ border-radius: 5px;
+ text-decoration: none;
+ font-size: 1.5rem;
+}
diff --git a/src/modules/web_app/frontend/index.html b/src/modules/web_app/frontend/index.html
new file mode 100644
index 0000000..219606b
--- /dev/null
+++ b/src/modules/web_app/frontend/index.html
@@ -0,0 +1,20 @@
+
+
+ Web App
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/web_app/frontend/index.js b/src/modules/web_app/frontend/index.js
new file mode 100644
index 0000000..933f4d1
--- /dev/null
+++ b/src/modules/web_app/frontend/index.js
@@ -0,0 +1,97 @@
+import { createApp, ref, onMounted } from 'vue';
+import { createRouter, createWebHistory, RouterView } from 'vue-router';
+
+
+const Authorize = {
+ setup() {
+ const loginLink = ref(null);
+
+ onMounted(() => {
+ fetch('/api/auth/get_authorization_url/twitch/')
+ .then(response => response.json())
+ .then(data => {
+ loginLink.value = data.authorization_url;
+ })
+ });
+
+ return {
+ loginLink
+ }
+ },
+ template: `
+
+ `
+}
+
+
+const Settings = {
+ template: `
+ Settings
+ `
+}
+
+
+const Main = {
+ components: {
+ Authorize,
+ Settings
+ },
+ setup() {
+ const authorized = localStorage.getItem('token') !== null;
+
+ return {
+ authorized
+ };
+ },
+ template: `
+
+ `
+};
+
+
+const AuthCallbackTwitch = {
+ setup() {
+ onMounted(() => {
+ fetch('/api/auth/callback/twitch/' + urlParams.search)
+ .then(response => response.json())
+ .then(data => {
+ localStorage.setItem('token', data.token);
+
+ this.$router.push('/');
+ });
+ });
+ },
+ template: `
+ AuthCallbackTwitch
+ `
+};
+
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes: [
+ { path: '', component: Main },
+ { path: '/auth/callback/twitch/', component: AuthCallbackTwitch },
+ ]
+});
+
+
+const App = {
+ components: {
+ RouterView,
+ },
+ template: `
+
+ `,
+};
+
+
+createApp(App)
+ .use(router)
+ .mount('#app');
diff --git a/src/modules/web_app/views/auth.py b/src/modules/web_app/views/auth.py
index 963140c..c68b3a6 100644
--- a/src/modules/web_app/views/auth.py
+++ b/src/modules/web_app/views/auth.py
@@ -9,7 +9,7 @@ from modules.web_app.auth.authx import auth
from repositories.users import UserRepository
-auth_router = APIRouter(prefix="/auth", tags=["auth"])
+auth_router = APIRouter(prefix="/api/auth", tags=["auth"])
@auth_router.get("/get_authorization_url/{provider}/")
diff --git a/src/modules/web_app/views/streamer.py b/src/modules/web_app/views/streamer.py
index a2c4bb7..349c132 100644
--- a/src/modules/web_app/views/streamer.py
+++ b/src/modules/web_app/views/streamer.py
@@ -8,7 +8,7 @@ from repositories.users import UserRepository
from domain.auth import OAuthProvider
-streamer_router = APIRouter(prefix="/streamers")
+streamer_router = APIRouter(prefix="/api/streamers")
@streamer_router.get("/")