This commit is contained in:
2024-06-04 22:08:15 +02:00
commit e899252f31
74 changed files with 1969 additions and 0 deletions

5
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
db.sqlite3
celerybeat-schedule
db/credentials/*
!db/credentials/.gitkeep

46
app/Dockerfile Normal file
View File

@@ -0,0 +1,46 @@
FROM python:3.12-slim
ARG PUID
ARG PGID
ARG ENV
ENV PYTHONUNBUFFERED 1
#RUN groupadd -g ${PGID} -o custom_user
#RUN useradd -m -u ${PUID} -g ${PGID} -o -s /bin/bash custom_user
# install requirements debian dependencies
RUN apt update && apt install -y curl inotify-tools mariadb-client ca-certificates gnupg wget lsb-release
# install lastest version of postgresql-client
RUN install -d /usr/share/postgresql-common/pgdg
RUN curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
RUN echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
RUN apt update && apt install -y postgresql-client-16
# install node 20
RUN mkdir -p /etc/apt/keyrings
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
RUN apt update && apt install nodejs -y
RUN npm install -g yarn
# clean apt cache
RUN find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete
# Setup python requirements
WORKDIR /app/requirements
COPY ./requirements/* ./
RUN python -m pip install --upgrade pip
RUN pip install -r ${ENV}.txt
#USER custom_user
# Setup node dependencies
WORKDIR /app/frontend
COPY ./frontend/package.json ./package.json
RUN yarn install
WORKDIR /app

3
app/app/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from .celery import app as celery_app
__all__ = ("celery_app",)

16
app/app/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
import os
from django.core.asgi import get_asgi_application
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
django_asgi_app = get_asgi_application()
from .urls import websocket_urlpatterns
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AuthMiddlewareStack(websocket_urlpatterns)
})

9
app/app/celery.py Normal file
View File

@@ -0,0 +1,9 @@
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
app = Celery("app")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

216
app/app/settings.py Normal file
View File

@@ -0,0 +1,216 @@
"""
Django settings for app project.
Generated by 'django-admin startproject' using Django 5.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from pathlib import Path
from os import getenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = getenv("SECRET", "not_secure")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = getenv("ENV", "prod") == "dev"
ALLOWED_HOSTS = [host.strip() for host in getenv("ALLOWED_HOSTS").split(",") if host]
CSRF_TRUSTED_ORIGINS = [host.strip() for host in getenv("CSRF_TRUSTED_ORIGINS").split(",") if host]
CORS_ALLOWED_ORIGINS = CSRF_TRUSTED_ORIGINS
# Application definition
INSTALLED_APPS = []
if DEBUG:
INSTALLED_APPS += ["daphne"]
INSTALLED_APPS += [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"corsheaders",
"django_vite",
"rest_framework",
"django_celery_beat",
"user",
"app_site",
"db"
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
"corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'app.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'app.wsgi.application'
ASGI_APPLICATION = "app.asgi.application"
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
match getenv("DB_ENGINE"):
case "pgsql":
DB = {
"ENGINE": "django.db.backends.postgresql",
"NAME": getenv("DB_NAME"),
"USER": getenv("DB_USER"),
"PASSWORD": getenv("DB_PASSWORD"),
"HOST": getenv("DB_HOST", "localhost"),
"PORT": int(getenv("DB_PORT", 5432)),
}
case _:
DB = {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / getenv("SQLITE_REL_PATH", "db.sqlite3"),
}
DATABASES = {
'default': DB
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS = []
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = "user.User"
LOGIN_REDIRECT_URL = "/"
LOGIN_URL = "/user/login/"
LOGOUT_REDIRECT_URL = "/user/login/"
SESSION_COOKIE_AGE = 60 * 60 * 24 * 30
DJANGO_VITE = {
"default": {
"dev_mode": DEBUG,
"manifest_path": BASE_DIR / "frontend/dist/manifest.json",
"dev_server_host": getenv("DEV_SERVER_HOST", "localhost"),
"dev_server_port": int(getenv("DEV_SERVER_PORT", 8080)),
}
}
STATICFILES_DIRS += BASE_DIR / "frontend/dist",
BACKUPS_PATH = Path("/backups")
CELERY_BROKER_URL = "redis://redis:6379/1"
CELERY_TIMEZONE = "Europe/Paris"
CELERY_TASK_TRACK_STARTED = True
# CELERY_TASK_TIME_LIMIT = 30 * 60
CELERY_TASK_SERIALIZER = "json"
CELERY_ACCEPT_CONTENT = ['json']
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
CELERY_BEAT_SCHEDULE = {
"db_job_check_daily_backup": {
"task": "db.tasks.job_check_daily_backup",
"schedule": 1*60
}
}
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
"CONFIG": {
"hosts": ["redis://redis:6379/2"],
},
},
}
# REST_FRAMEWORK = {
# 'DEFAULT_FILTER_BACKENDS': [
# 'rest_framework.filters.OrderingFilter'
# ],
# # 'DEFAULT_METADATA_CLASS': 'api.metadatas.CustomMetaData',
# 'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication',
# # "api.authentication.CsrfExemptSessionAuthentication",
# # 'rest_framework.authentication.BasicAuthentication',
# # 'rest_framework_simplejwt.authentication.JWTAuthentication',
# ],
# 'DEFAULT_PERMISSION_CLASSES': [
# # 'api.permissions.IsSuperUser'
# ],
# 'DEFAULT_RENDERER_CLASSES': [
# 'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.BrowsableAPIRenderer',
# # 'rest_framework_csv.renderers.CSVRenderer',
# ],
# }

31
app/app/urls.py Normal file
View File

@@ -0,0 +1,31 @@
"""
URL configuration for app project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.views import static
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("app_site.urls", namespace="app_site")),
path("db/", include("db.urls", namespace="db")),
]
websocket_urlpatterns = [
# path("ws/torrent/", TorrentConsumer.as_asgi()),
]

16
app/app/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for app project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
application = get_wsgi_application()

0
app/app_site/__init__.py Normal file
View File

3
app/app_site/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
app/app_site/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AppSiteConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app_site'

View File

3
app/app_site/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,2 @@
{% extends "app_site/layout.html" %}

View File

@@ -0,0 +1,2 @@
{% extends "base.html" %}

3
app/app_site/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

8
app/app_site/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import path
from .views import HomeView
app_name = "app_site"
urlpatterns = [
path('', HomeView.as_view(), name='home'),
]

5
app/app_site/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "app_site/home.html"

0
app/db/__init__.py Normal file
View File

18
app/db/admin.py Normal file
View File

@@ -0,0 +1,18 @@
from django.contrib import admin
from .models import DB, DBBackup, DBCredential
@admin.register(DB)
class DBAdmin(admin.ModelAdmin):
pass
@admin.register(DBCredential)
class DBCredentialAdmin(admin.ModelAdmin):
pass
@admin.register(DBBackup)
class BackupDBAdmin(admin.ModelAdmin):
readonly_fields = ("task_id", "status", "rel_path",)

13
app/db/apps.py Normal file
View File

@@ -0,0 +1,13 @@
from django.apps import AppConfig
from django.db.models import signals as db_signals
class DbConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'db'
def ready(self):
from . import signals, models
db_signals.post_save.connect(signals.on_backupdb_save, sender=models.DBBackup)
db_signals.post_delete.connect(signals.on_backupdb_delete, sender=models.DBBackup)

98
app/db/backup_engine.py Normal file
View File

@@ -0,0 +1,98 @@
import time
from abc import ABC, abstractmethod
import gzip
import subprocess
import uuid
from typing import Any
from .models import DB, DBBackup
# https://medium.com/poka-techblog/5-different-ways-to-backup-your-postgresql-database-using-python-3f06cea4f51
class BackupEngineBase(ABC):
def __init__(self, backupdb: DBBackup):
self.backup_instance = backupdb
self.db_instance = self.backup_instance.db
# self.db_instance = db_instance
# self.backup_instance = BackupDB.objects.create(
# db=self.db_instance,
# rel_path="{db_id}-{timestamp}-{rand_gen}.{db_type}.{ext}".format(
# db_id=self.db_instance.id,
# timestamp=int(time.time()),
# rand_gen=uuid.uuid4().hex[:5],
# db_type=self.db_instance.db_type,
# ext='sql.gz' if self.gzip else 'sql'
# ),
# )
self.credentials = {"database": self.db_instance.db_name, **self.db_instance.credential.credentials}
@property
@abstractmethod
def cmd_kwargs(self) -> dict[str, Any]:
...
@property
def format_cmd_kwargs(self):
return " ".join([
key if isinstance(value, bool) else f"{key} {value}"
for key, value in self.cmd_kwargs.items()
if value
])
@property
@abstractmethod
def cmd(self):
...
@property
def cmd_format(self):
return self.cmd.format(cmd_kwargs=self.format_cmd_kwargs, **self.credentials)
def popen(self):
return subprocess.Popen(self.cmd_format,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
universal_newlines=True)
def file_obj(self, path):
return gzip.open(path, "wb")
def run(self, task_id=None):
self.backup_instance.update_task_id(task_id)
try:
with self.file_obj(self.backup_instance.abs_path) as f:
with self.popen() as proc:
self.backup_instance.update_status("running")
for line in iter(proc.stdout.readline, ""):
f.write(line.encode())
except Exception as e:
self.backup_instance.update_status("finished")
raise e
class MySQLBackupEngine(BackupEngineBase):
@property
def cmd_kwargs(self) -> dict[str, Any]:
return {
"--no-tablespaces": True
}
@property
def cmd(self):
return "mysqldump {cmd_kwargs} -h {host} -P {port} -u {user} -p{password} {database}"
class PostgreSQLBackupEngine(BackupEngineBase):
@property
def cmd_kwargs(self) -> dict[str, Any]:
return {
"--no-owner": True,
"--no-privileges": True,
"--clean": False
}
@property
def cmd(self):
return "pg_dump {cmd_kwargs} --dbname=postgresql://{user}:{password}@{host}:{port}/{database}"

View File

View File

@@ -0,0 +1,55 @@
# Generated by Django 5.0.6 on 2024-05-28 09:06
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='DB',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_modified', models.DateTimeField(auto_now=True)),
('db_type', models.CharField(max_length=100)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='DBBackup',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_modified', models.DateTimeField(auto_now=True)),
('status', models.CharField(choices=[('waiting', 'waiting'), ('running', 'running'), ('ok', 'Ok'), ('error', 'Error'), ('expired', 'Expired')], default='waiting', max_length=20)),
('task_id', models.CharField(max_length=100, null=True)),
('rel_path', models.CharField(max_length=100)),
('db', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.db')),
],
),
migrations.CreateModel(
name='DBCredential',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('credentials', models.JSONField(default=dict)),
('create_db_perm', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='db',
name='credential',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.dbcredential'),
),
]

View File

@@ -0,0 +1,30 @@
# Generated by Django 5.0.6 on 2024-06-04 10:27
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('db', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='db',
name='db_name',
field=models.CharField(default='test', max_length=255),
preserve_default=False,
),
migrations.AlterField(
model_name='db',
name='db_type',
field=models.CharField(choices=[('mysql', 'MySQL/MariaDB'), ('postgres', 'PostgreSQL')], max_length=100),
),
migrations.AlterField(
model_name='dbbackup',
name='db',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='backups', to='db.db'),
),
]

View File

79
app/db/models.py Normal file
View File

@@ -0,0 +1,79 @@
from django.db import models
from django.core.files.storage import FileSystemStorage
from django.conf import settings
import pathlib
import uuid
import time
credentials_storage = FileSystemStorage(pathlib.Path(__file__).parent / 'credentials')
class DBCredential(models.Model):
# credentials = models.FileField(storage=credentials_storage)
credentials = models.JSONField(default=dict)
create_db_perm = models.BooleanField(default=False)
user = models.ForeignKey("user.User", on_delete=models.CASCADE)
class DB(models.Model):
name = models.CharField(max_length=100)
db_name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
credential = models.ForeignKey("DBCredential", on_delete=models.CASCADE)
user = models.ForeignKey("user.User", on_delete=models.CASCADE)
db_types = (
("mysql", "MySQL/MariaDB"),
("postgres", "PostgreSQL"),
)
db_type = models.CharField(max_length=100, choices=db_types)
class DBBackup(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
db = models.ForeignKey("DB", on_delete=models.CASCADE, related_name="backups")
status_choices = [
("waiting", "waiting"),
("running", "running"),
("ok", "Ok"),
("error", "Error"),
("expired", "Expired")
]
status = models.CharField(max_length=20, default="waiting", choices=status_choices)
task_id = models.CharField(max_length=100, null=True)
rel_path = models.CharField(max_length=100)
def save(self, *args, **kwargs):
if not self.rel_path:
self.rel_path = "{db_id}-{timestamp}-{rand_gen}.{db_type}.{ext}".format(
db_id=self.db.id,
timestamp=int(time.time()),
rand_gen=uuid.uuid4().hex[:5],
db_type=self.db.db_type,
ext='sql.gz'
)
return super().save(*args, **kwargs)
def update_status(self, status):
self.status = status
self.save()
def update_task_id(self, task_id):
if task_id != self.task_id:
self.task_id = task_id
self.save()
def set_expired(self):
self.status = "expired"
self.abs_path.unlink(True)
self.save()
@property
def abs_path(self):
return settings.BACKUPS_PATH / self.rel_path

0
app/db/serializers.py Normal file
View File

12
app/db/signals.py Normal file
View File

@@ -0,0 +1,12 @@
from .models import DBBackup
from .tasks import start_backup
def on_backupdb_save(instance: DBBackup, created, **kwargs):
if created:
start_backup.delay(instance.id)
def on_backupdb_delete(instance: DBBackup, **kwargs):
instance.abs_path.unlink(missing_ok=True)

53
app/db/tasks.py Normal file
View File

@@ -0,0 +1,53 @@
from django.db.models import Prefetch, Q
from django.utils import timezone
import celery
from datetime import timedelta
from .models import DBBackup, DB
from .backup_engine import MySQLBackupEngine, PostgreSQLBackupEngine
@celery.shared_task
def start_backup(backup_id):
backupdb = DBBackup.objects.get(id=backup_id)
# task_id can be none if it's not a celery task (for ex : if running the function without apply it)
task_id = start_backup.request.id
klass = None
match backupdb.db.db_type:
case "mysql":
klass = MySQLBackupEngine
case "postgres":
klass = PostgreSQLBackupEngine
case _:
pass
if klass:
be = klass(backupdb=backupdb)
be.run(task_id)
# if start_backup.request.id:
# # running from celery task
# pass
# else:
# # running from standard function
# pass
@celery.shared_task
def job_check_daily_backup():
now = timezone.now()
for db in DB.objects.all():
# check if need backup
last_backup: DBBackup = (db.backups.order_by("-date_created")
.filter(Q(status="ok") | Q(status="running") | Q(status="waiting"))
.first())
if not last_backup or last_backup.date_created < (now - timedelta(hours=24)):
DBBackup.objects.create(db=db)
for backup in db.backups.filter(date_created__lt=now - timedelta(days=30)):
backup.set_expired()

View File

@@ -0,0 +1,6 @@
{% extends "db/layout.html" %}
{% load django_vite %}
{% block js %}
{% vite_asset "entrypoint/db.js" %}
{% endblock %}

View File

@@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block js %}
{% endblock %}

3
app/db/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

8
app/db/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import path
from .views import IndexView
app_name = 'db'
urlpatterns = [
path("", IndexView.as_view(), name='index'),
]

5
app/db/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.views.generic import TemplateView
class IndexView(TemplateView):
template_name = "db/index.html"

6
app/dev_run.sh Normal file
View File

@@ -0,0 +1,6 @@
cd /app/frontend && yarn dev &
cd /app && python manage.py runserver 0.0.0.0:8000 &
wait -n
exit $?

24
app/frontend/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

25
app/frontend/package.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@mdi/font": "^7.4.47",
"@vitejs/plugin-vue": "^5.0.4",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"postcss-import": "^16.1.0",
"postcss-simple-vars": "^7.0.1",
"vite": "^5.2.0"
},
"dependencies": {
"vite-plugin-vuetify": "^2.0.3",
"vue": "^3.4.27",
"vuetify": "^3.6.7"
}
}

View File

@@ -0,0 +1,7 @@
module.exports = (ctx) => ({
plugins: [
require('postcss-import')(),
require('postcss-simple-vars')(),
require("autoprefixer")(),
]
})

View File

@@ -0,0 +1,29 @@
<template>
<v-app id="inspire">
<v-navigation-drawer>
<v-list-item
v-for="(item, i) in menu_items"
:key="i"
:href="item.link"
:active="item.link === current_path"
:title="item.title"
/>
</v-navigation-drawer>
<v-app-bar>
<v-app-bar-title>WebPanel</v-app-bar-title>
</v-app-bar>
<v-main>
<slot></slot>
</v-main>
</v-app>
</template>
<script setup>
const current_path = window.location.pathname
const menu_items = [
{title: "Home", link: "/"},
{title: "DB", link: "/db/"},
]
</script>

View File

@@ -0,0 +1,27 @@
<template>
</template>
<script setup>
import {ref, onMounted} from "vue";
const loading = ref(false);
const db_backups = ref(null);
onMounted(() => {
fetchDBBackups();
})
async function fetchDBBackups(){
loading.value = true;
try{
let response = await fetch("/api/db/db_backups/");
db_backups.value = await response.json();
}catch(err){
console.log(err);
}finally {
loading.value = false;
}
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
</template>
<script setup>
import {ref, onMounted} from "vue";
const loading = ref(false);
const dbs = ref(null);
onMounted(() => {
fetchDBs();
})
async function fetchDBs(){
loading.value = true;
try{
let response = await fetch("/api/db/dbs/");
dbs.value = await response.json();
}catch(err){
console.log(err);
}finally {
loading.value = false;
}
}
</script>

View File

@@ -0,0 +1,13 @@
<template>
<Base>
<v-container>
<v-btn icon="mdi-home"/>
</v-container>
</Base>
</template>
<script setup>
import {defineAsyncComponent} from "vue";
const Base = defineAsyncComponent(() => import("../Base.vue"))
</script>

View File

@@ -0,0 +1,3 @@
// add the beginning of your app entry
import 'vite/modulepreload-polyfill'

View File

@@ -0,0 +1,17 @@
// add the beginning of your app entry
import 'vite/modulepreload-polyfill'
// import {createVue} from "../utils.js"
import App from "@/components/db/Index.vue"
import("@/plugins/vue-loader.js").then(utils => {
utils.createVue(App, "#app");
})
// (async function(){
// const a = () => import("../utils.js");
// const b = await a();
// console.log(b)
// b.default.createVue(App, "#app")
// // b.createVue(App, "#app")
// })()

View File

View File

@@ -0,0 +1,6 @@
import vuetify from "./vuetify.js";
import { createApp } from 'vue';
export function createVue(component, dom_id){
return createApp(component).use(vuetify).mount(dom_id)
}

View File

@@ -0,0 +1,30 @@
import '@mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import colors from 'vuetify/lib/util/colors.mjs'
// import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
export default createVuetify({
// components,
// directives,
icons: {
defaultSet: 'mdi',
},
// ssr: false,
theme: {
defaultTheme: "dark",
themes: {
dark: {
colors: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
}
});

View File

@@ -0,0 +1,3 @@
/*body {*/
/* background-color: grey;*/
/*}*/

View File

@@ -0,0 +1 @@
import "@/style/main.css"

View File

@@ -0,0 +1,56 @@
import {defineConfig, loadEnv} from "vite";
import {resolve, join} from "path";
import vue from "@vitejs/plugin-vue";
import vuetify from "vite-plugin-vuetify";
// const postcssConfig = {
// plugins: [
// require('postcss-import')(),
// require('postcss-simple-vars')(),
// require('autoprefixer')(),
// ],
// };
export default defineConfig((mode) => {
const env = loadEnv(mode, "..", ""),
SRC_DIR = resolve("./src"),
OUT_DIR = resolve("./dist")
return {
plugins: [
vue(),
vuetify()
],
resolve: {
alias: {
"@": resolve(SRC_DIR),
"vue": "vue/dist/vue.esm-bundler.js"
}
},
root: SRC_DIR,
base: "/static/",
// css: {
// postcss: postcssConfig
// },
server: {
host: "0.0.0.0",
port: env.DEV_SERVER_PORT,
origin: `http://${env.DEV_SERVER_HOST}:${env.DEV_SERVER_PORT}`, // hotfix, webfont was loaded from wrong url
watch: {
usePolling: true
}
},
build: {
manifest: "manifest.json",
emptyOutDir: true,
outDir: OUT_DIR,
rollupOptions: {
input: {
app: join(SRC_DIR, "entrypoint/app.js"),
db: join(SRC_DIR, "entrypoint/db.js"),
style: join(SRC_DIR, "style/main.css.js")
},
}
}
}
})

596
app/frontend/yarn.lock Normal file
View File

@@ -0,0 +1,596 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/parser@^7.24.4":
version "7.24.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790"
integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==
"@esbuild/aix-ppc64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==
"@esbuild/android-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==
"@esbuild/android-arm@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==
"@esbuild/android-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==
"@esbuild/darwin-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==
"@esbuild/darwin-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
"@esbuild/freebsd-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==
"@esbuild/freebsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==
"@esbuild/linux-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==
"@esbuild/linux-arm@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==
"@esbuild/linux-ia32@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==
"@esbuild/linux-loong64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==
"@esbuild/linux-mips64el@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==
"@esbuild/linux-ppc64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==
"@esbuild/linux-riscv64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==
"@esbuild/linux-s390x@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==
"@esbuild/linux-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==
"@esbuild/netbsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==
"@esbuild/openbsd-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==
"@esbuild/sunos-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==
"@esbuild/win32-arm64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==
"@esbuild/win32-ia32@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==
"@esbuild/win32-x64@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
"@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@mdi/font@^7.4.47":
version "7.4.47"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.4.47.tgz#2ae522867da3a5c88b738d54b403eb91471903af"
integrity sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==
"@rollup/rollup-android-arm-eabi@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz#1a32112822660ee104c5dd3a7c595e26100d4c2d"
integrity sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==
"@rollup/rollup-android-arm64@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz#5aeef206d65ff4db423f3a93f71af91b28662c5b"
integrity sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==
"@rollup/rollup-darwin-arm64@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz#6b66aaf003c70454c292cd5f0236ebdc6ffbdf1a"
integrity sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==
"@rollup/rollup-darwin-x64@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz#f64fc51ed12b19f883131ccbcea59fc68cbd6c0b"
integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==
"@rollup/rollup-linux-arm-gnueabihf@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz#1a7641111be67c10111f7122d1e375d1226cbf14"
integrity sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==
"@rollup/rollup-linux-arm-musleabihf@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz#c93fd632923e0fee25aacd2ae414288d0b7455bb"
integrity sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==
"@rollup/rollup-linux-arm64-gnu@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz#fa531425dd21d058a630947527b4612d9d0b4a4a"
integrity sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==
"@rollup/rollup-linux-arm64-musl@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz#8acc16f095ceea5854caf7b07e73f7d1802ac5af"
integrity sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==
"@rollup/rollup-linux-powerpc64le-gnu@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz#94e69a8499b5cf368911b83a44bb230782aeb571"
integrity sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==
"@rollup/rollup-linux-riscv64-gnu@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz#7ef1c781c7e59e85a6ce261cc95d7f1e0b56db0f"
integrity sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==
"@rollup/rollup-linux-s390x-gnu@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz#f15775841c3232fca9b78cd25a7a0512c694b354"
integrity sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==
"@rollup/rollup-linux-x64-gnu@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz#b521d271798d037ad70c9f85dd97d25f8a52e811"
integrity sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==
"@rollup/rollup-linux-x64-musl@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz#9254019cc4baac35800991315d133cc9fd1bf385"
integrity sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==
"@rollup/rollup-win32-arm64-msvc@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz#27f65a89f6f52ee9426ec11e3571038e4671790f"
integrity sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==
"@rollup/rollup-win32-ia32-msvc@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz#a2fbf8246ed0bb014f078ca34ae6b377a90cb411"
integrity sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==
"@rollup/rollup-win32-x64-msvc@4.17.2":
version "4.17.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503"
integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==
"@types/estree@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@vitejs/plugin-vue@^5.0.4":
version "5.0.4"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37"
integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==
"@vue/compiler-core@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz#e69060f4b61429fe57976aa5872cfa21389e4d91"
integrity sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==
dependencies:
"@babel/parser" "^7.24.4"
"@vue/shared" "3.4.27"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.2.0"
"@vue/compiler-dom@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz#d51d35f40d00ce235d7afc6ad8b09dfd92b1cc1c"
integrity sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==
dependencies:
"@vue/compiler-core" "3.4.27"
"@vue/shared" "3.4.27"
"@vue/compiler-sfc@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz#399cac1b75c6737bf5440dc9cf3c385bb2959701"
integrity sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==
dependencies:
"@babel/parser" "^7.24.4"
"@vue/compiler-core" "3.4.27"
"@vue/compiler-dom" "3.4.27"
"@vue/compiler-ssr" "3.4.27"
"@vue/shared" "3.4.27"
estree-walker "^2.0.2"
magic-string "^0.30.10"
postcss "^8.4.38"
source-map-js "^1.2.0"
"@vue/compiler-ssr@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz#2a8ecfef1cf448b09be633901a9c020360472e3d"
integrity sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==
dependencies:
"@vue/compiler-dom" "3.4.27"
"@vue/shared" "3.4.27"
"@vue/reactivity@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.27.tgz#6ece72331bf719953f5eaa95ec60b2b8d49e3791"
integrity sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==
dependencies:
"@vue/shared" "3.4.27"
"@vue/runtime-core@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.27.tgz#1b6e1d71e4604ba7442dd25ed22e4a1fc6adbbda"
integrity sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==
dependencies:
"@vue/reactivity" "3.4.27"
"@vue/shared" "3.4.27"
"@vue/runtime-dom@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz#fe8d1ce9bbe8921d5dd0ad5c10df0e04ef7a5ee7"
integrity sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==
dependencies:
"@vue/runtime-core" "3.4.27"
"@vue/shared" "3.4.27"
csstype "^3.1.3"
"@vue/server-renderer@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.27.tgz#3306176f37e648ba665f97dda3ce705687be63d2"
integrity sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==
dependencies:
"@vue/compiler-ssr" "3.4.27"
"@vue/shared" "3.4.27"
"@vue/shared@3.4.27":
version "3.4.27"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.27.tgz#f05e3cd107d157354bb4ae7a7b5fc9cf73c63b50"
integrity sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==
"@vuetify/loader-shared@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz#11451c717e4a352ec311da52a79c857cd256c92f"
integrity sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==
dependencies:
upath "^2.0.1"
autoprefixer@^10.4.19:
version "10.4.19"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f"
integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==
dependencies:
browserslist "^4.23.0"
caniuse-lite "^1.0.30001599"
fraction.js "^4.3.7"
normalize-range "^0.1.2"
picocolors "^1.0.0"
postcss-value-parser "^4.2.0"
browserslist@^4.23.0:
version "4.23.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==
dependencies:
caniuse-lite "^1.0.30001587"
electron-to-chromium "^1.4.668"
node-releases "^2.0.14"
update-browserslist-db "^1.0.13"
caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599:
version "1.0.30001617"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz#809bc25f3f5027ceb33142a7d6c40759d7a901eb"
integrity sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==
csstype@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
debug@^4.3.3:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
electron-to-chromium@^1.4.668:
version "1.4.762"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz#c29c9d47cf7cc128a9c364baa28adbadde95a47c"
integrity sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ==
entities@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
esbuild@^0.20.1:
version "0.20.2"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1"
integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==
optionalDependencies:
"@esbuild/aix-ppc64" "0.20.2"
"@esbuild/android-arm" "0.20.2"
"@esbuild/android-arm64" "0.20.2"
"@esbuild/android-x64" "0.20.2"
"@esbuild/darwin-arm64" "0.20.2"
"@esbuild/darwin-x64" "0.20.2"
"@esbuild/freebsd-arm64" "0.20.2"
"@esbuild/freebsd-x64" "0.20.2"
"@esbuild/linux-arm" "0.20.2"
"@esbuild/linux-arm64" "0.20.2"
"@esbuild/linux-ia32" "0.20.2"
"@esbuild/linux-loong64" "0.20.2"
"@esbuild/linux-mips64el" "0.20.2"
"@esbuild/linux-ppc64" "0.20.2"
"@esbuild/linux-riscv64" "0.20.2"
"@esbuild/linux-s390x" "0.20.2"
"@esbuild/linux-x64" "0.20.2"
"@esbuild/netbsd-x64" "0.20.2"
"@esbuild/openbsd-x64" "0.20.2"
"@esbuild/sunos-x64" "0.20.2"
"@esbuild/win32-arm64" "0.20.2"
"@esbuild/win32-ia32" "0.20.2"
"@esbuild/win32-x64" "0.20.2"
escalade@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
fraction.js@^4.3.7:
version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
hasown@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
is-core-module@^2.13.0:
version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies:
hasown "^2.0.0"
magic-string@^0.30.10:
version "0.30.10"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e"
integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.3.7:
version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
node-releases@^2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
postcss-import@^16.1.0:
version "16.1.0"
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-16.1.0.tgz#258732175518129667fe1e2e2a05b19b5654b96a"
integrity sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==
dependencies:
postcss-value-parser "^4.0.0"
read-cache "^1.0.0"
resolve "^1.1.7"
postcss-simple-vars@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz#836b3097a54dcd13dbd3c36a5dbdd512fad2954c"
integrity sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.4.38:
version "8.4.38"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
dependencies:
nanoid "^3.3.7"
picocolors "^1.0.0"
source-map-js "^1.2.0"
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
dependencies:
pify "^2.3.0"
resolve@^1.1.7:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
rollup@^4.13.0:
version "4.17.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.17.2.tgz#26d1785d0144122277fdb20ab3a24729ae68301f"
integrity sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==
dependencies:
"@types/estree" "1.0.5"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.17.2"
"@rollup/rollup-android-arm64" "4.17.2"
"@rollup/rollup-darwin-arm64" "4.17.2"
"@rollup/rollup-darwin-x64" "4.17.2"
"@rollup/rollup-linux-arm-gnueabihf" "4.17.2"
"@rollup/rollup-linux-arm-musleabihf" "4.17.2"
"@rollup/rollup-linux-arm64-gnu" "4.17.2"
"@rollup/rollup-linux-arm64-musl" "4.17.2"
"@rollup/rollup-linux-powerpc64le-gnu" "4.17.2"
"@rollup/rollup-linux-riscv64-gnu" "4.17.2"
"@rollup/rollup-linux-s390x-gnu" "4.17.2"
"@rollup/rollup-linux-x64-gnu" "4.17.2"
"@rollup/rollup-linux-x64-musl" "4.17.2"
"@rollup/rollup-win32-arm64-msvc" "4.17.2"
"@rollup/rollup-win32-ia32-msvc" "4.17.2"
"@rollup/rollup-win32-x64-msvc" "4.17.2"
fsevents "~2.3.2"
source-map-js@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
upath@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
update-browserslist-db@^1.0.13:
version "1.0.15"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz#60ed9f8cba4a728b7ecf7356f641a31e3a691d97"
integrity sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==
dependencies:
escalade "^3.1.2"
picocolors "^1.0.0"
vite-plugin-vuetify@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.3.tgz#b65ee4e05cfc6bf2b478a32b6d58b42398519f1e"
integrity sha512-HbYajgGgb/noaVKNRhnnXIiQZrNXfNIeanUGAwXgOxL6h/KULS40Uf51Kyz8hNmdegF+DwjgXXI/8J1PNS83xw==
dependencies:
"@vuetify/loader-shared" "^2.0.3"
debug "^4.3.3"
upath "^2.0.1"
vite@^5.2.0:
version "5.2.11"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.11.tgz#726ec05555431735853417c3c0bfb36003ca0cbd"
integrity sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==
dependencies:
esbuild "^0.20.1"
postcss "^8.4.38"
rollup "^4.13.0"
optionalDependencies:
fsevents "~2.3.3"
vue@^3.4.27:
version "3.4.27"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.27.tgz#40b7d929d3e53f427f7f5945386234d2854cc2a1"
integrity sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==
dependencies:
"@vue/compiler-dom" "3.4.27"
"@vue/compiler-sfc" "3.4.27"
"@vue/runtime-dom" "3.4.27"
"@vue/server-renderer" "3.4.27"
"@vue/shared" "3.4.27"
vuetify@^3.6.7:
version "3.6.7"
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.6.7.tgz#f120799ad35b156c48a877aac4633c2250e79903"
integrity sha512-oj7zrNbjKFlSS38449K9/ad9mc/o25bxuTFT9rs2AtSNnOA+BOLIVJCAPlmLGWoQGslPh80RzckXDedoifs+TQ==

22
app/manage.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,8 @@
Django
django-cors-headers
django-vite
celery
channels_redis
djangorestframework
django-celery-beat

3
app/requirements/dev.txt Normal file
View File

@@ -0,0 +1,3 @@
-r common.txt
channels[daphne]

View File

@@ -0,0 +1,4 @@
-r common.txt
uvicorn
channels

23
app/templates/base.html Normal file
View File

@@ -0,0 +1,23 @@
{% load django_vite %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
{% block base_css %}
{% block css %}{% endblock %}
<link rel="stylesheet" href="{% vite_asset_url "style/main.css" %}">
{% endblock %}
</head>
<body>
{% block content %}<p id="app">hello world</p>{% endblock %}
{% block base_js %}
{% vite_hmr_client %}
{% vite_asset "entrypoint/app.js" %}
{% block js %}{% endblock %}
{% endblock %}
</body>
</html>

0
app/user/__init__.py Normal file
View File

27
app/user/admin.py Normal file
View File

@@ -0,0 +1,27 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
from .forms import UserCreationForm, UserChangeForm
@admin.register(User)
class UserAdmin(BaseUserAdmin):
add_form = UserCreationForm
form = UserChangeForm
model = User
list_display = ["email", "is_staff", "is_superuser", "is_active"]
list_filter = ["email", "is_staff", "is_superuser", "is_active"]
fieldsets = [
[None, {"fields": ["email", "password"]}],
("permissions", {"fields": ["is_staff", "is_active", "is_superuser"]})
]
add_fieldsets = [
[None, {
"classes": ["wide"],
"fields": ["email", "password1", "password2", "is_staff", "is_active", "is_superuser"]
}]
]
search_fields = ["email"]
ordering = ["email"]

6
app/user/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class UserConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'user'

11
app/user/forms.py Normal file
View File

@@ -0,0 +1,11 @@
from django.contrib.auth import forms as BaseAuthForms
from .models import User
class UserCreationForm(BaseAuthForms.UserCreationForm):
pass
class UserChangeForm(BaseAuthForms.UserChangeForm):
pass

View File

@@ -0,0 +1,44 @@
# Generated by Django 5.0.6 on 2024-05-15 14:10
import django.contrib.auth.validators
import django.utils.timezone
import user.models
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('email', models.EmailField(max_length=254, unique=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', user.models.EmailUserManager()),
],
),
]

View File

72
app/user/models.py Normal file
View File

@@ -0,0 +1,72 @@
from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager
class EmailUserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError("Un email doit être défini")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self.db)
return user
def create_user(self, username, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser doit être staff à True")
if extra_fields.get("is_superuser") is not True:
raise ValueError("SuperUser doit être is_superuser à True")
return self._create_user(email, password, **extra_fields)
# class UsernameUserManager(BaseUserManager):
# use_in_migrations = True
#
# def _create_user(self, username, email, password, **extra_fields):
# if not username:
# raise ValueError("Un username doit être défini")
# if not email:
# raise ValueError("Un email doit être défini")
#
# email = self.normalize_email(email)
# user = self.model(username=username, email=email, **extra_fields)
# user.set_password(password)
# user.save(using=self.db)
# return user
#
# def create_user(self, username, email, password=None, **extra_fields):
# extra_fields.setdefault("is_staff", False)
# extra_fields.setdefault("is_superuser", False)
# return self._create_user(username, email, password, **extra_fields)
#
# def create_superuser(self, username, email, password, **extra_fields):
# extra_fields.setdefault("is_staff", True)
# extra_fields.setdefault("is_superuser", True)
#
# if extra_fields.get("is_staff") is not True:
# raise ValueError("Superuser doit être staff à True")
# if extra_fields.get("is_superuser") is not True:
# raise ValueError("SuperUser doit être is_superuser à True")
#
# return self._create_user(username, email, password, **extra_fields)
class User(AbstractUser):
email = models.EmailField(unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = EmailUserManager()

3
app/user/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
app/user/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.