Init
This commit is contained in:
5
app/.gitignore
vendored
Normal file
5
app/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
db.sqlite3
|
||||
celerybeat-schedule
|
||||
|
||||
db/credentials/*
|
||||
!db/credentials/.gitkeep
|
||||
46
app/Dockerfile
Normal file
46
app/Dockerfile
Normal 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
3
app/app/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .celery import app as celery_app
|
||||
|
||||
__all__ = ("celery_app",)
|
||||
16
app/app/asgi.py
Normal file
16
app/app/asgi.py
Normal 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
9
app/app/celery.py
Normal 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
216
app/app/settings.py
Normal 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
31
app/app/urls.py
Normal 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
16
app/app/wsgi.py
Normal 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
0
app/app_site/__init__.py
Normal file
3
app/app_site/admin.py
Normal file
3
app/app_site/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
app/app_site/apps.py
Normal file
6
app/app_site/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AppSiteConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'app_site'
|
||||
0
app/app_site/migrations/__init__.py
Normal file
0
app/app_site/migrations/__init__.py
Normal file
3
app/app_site/models.py
Normal file
3
app/app_site/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
2
app/app_site/templates/app_site/home.html
Normal file
2
app/app_site/templates/app_site/home.html
Normal file
@@ -0,0 +1,2 @@
|
||||
{% extends "app_site/layout.html" %}
|
||||
|
||||
2
app/app_site/templates/app_site/layout.html
Normal file
2
app/app_site/templates/app_site/layout.html
Normal file
@@ -0,0 +1,2 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
3
app/app_site/tests.py
Normal file
3
app/app_site/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
8
app/app_site/urls.py
Normal file
8
app/app_site/urls.py
Normal 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
5
app/app_site/views.py
Normal 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
0
app/db/__init__.py
Normal file
18
app/db/admin.py
Normal file
18
app/db/admin.py
Normal 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
13
app/db/apps.py
Normal 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
98
app/db/backup_engine.py
Normal 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}"
|
||||
0
app/db/credentials/.gitkeep
Normal file
0
app/db/credentials/.gitkeep
Normal file
55
app/db/migrations/0001_initial.py
Normal file
55
app/db/migrations/0001_initial.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
0
app/db/migrations/__init__.py
Normal file
0
app/db/migrations/__init__.py
Normal file
79
app/db/models.py
Normal file
79
app/db/models.py
Normal 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
0
app/db/serializers.py
Normal file
12
app/db/signals.py
Normal file
12
app/db/signals.py
Normal 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
53
app/db/tasks.py
Normal 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()
|
||||
6
app/db/templates/db/index.html
Normal file
6
app/db/templates/db/index.html
Normal file
@@ -0,0 +1,6 @@
|
||||
{% extends "db/layout.html" %}
|
||||
{% load django_vite %}
|
||||
|
||||
{% block js %}
|
||||
{% vite_asset "entrypoint/db.js" %}
|
||||
{% endblock %}
|
||||
5
app/db/templates/db/layout.html
Normal file
5
app/db/templates/db/layout.html
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
{% endblock %}
|
||||
3
app/db/tests.py
Normal file
3
app/db/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
8
app/db/urls.py
Normal file
8
app/db/urls.py
Normal 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
5
app/db/views.py
Normal 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
6
app/dev_run.sh
Normal 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
24
app/frontend/.gitignore
vendored
Normal 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
25
app/frontend/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
7
app/frontend/postcss.config.cjs
Normal file
7
app/frontend/postcss.config.cjs
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = (ctx) => ({
|
||||
plugins: [
|
||||
require('postcss-import')(),
|
||||
require('postcss-simple-vars')(),
|
||||
require("autoprefixer")(),
|
||||
]
|
||||
})
|
||||
29
app/frontend/src/components/Base.vue
Normal file
29
app/frontend/src/components/Base.vue
Normal 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>
|
||||
27
app/frontend/src/components/db/DBBackups.vue
Normal file
27
app/frontend/src/components/db/DBBackups.vue
Normal 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>
|
||||
27
app/frontend/src/components/db/DBs.vue
Normal file
27
app/frontend/src/components/db/DBs.vue
Normal 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>
|
||||
13
app/frontend/src/components/db/Index.vue
Normal file
13
app/frontend/src/components/db/Index.vue
Normal 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>
|
||||
0
app/frontend/src/components/db/forms/DBForm.vue
Normal file
0
app/frontend/src/components/db/forms/DBForm.vue
Normal file
3
app/frontend/src/entrypoint/app.js
Normal file
3
app/frontend/src/entrypoint/app.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// add the beginning of your app entry
|
||||
import 'vite/modulepreload-polyfill'
|
||||
|
||||
17
app/frontend/src/entrypoint/db.js
Normal file
17
app/frontend/src/entrypoint/db.js
Normal 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")
|
||||
// })()
|
||||
0
app/frontend/src/index.js
Normal file
0
app/frontend/src/index.js
Normal file
6
app/frontend/src/plugins/vue-loader.js
Normal file
6
app/frontend/src/plugins/vue-loader.js
Normal 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)
|
||||
}
|
||||
30
app/frontend/src/plugins/vuetify.js
Normal file
30
app/frontend/src/plugins/vuetify.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
3
app/frontend/src/style/main.css
Normal file
3
app/frontend/src/style/main.css
Normal file
@@ -0,0 +1,3 @@
|
||||
/*body {*/
|
||||
/* background-color: grey;*/
|
||||
/*}*/
|
||||
1
app/frontend/src/style/main.css.js
Normal file
1
app/frontend/src/style/main.css.js
Normal file
@@ -0,0 +1 @@
|
||||
import "@/style/main.css"
|
||||
56
app/frontend/vite.config.js
Normal file
56
app/frontend/vite.config.js
Normal 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
596
app/frontend/yarn.lock
Normal 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
22
app/manage.py
Normal 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()
|
||||
8
app/requirements/common.txt
Normal file
8
app/requirements/common.txt
Normal 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
3
app/requirements/dev.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
-r common.txt
|
||||
|
||||
channels[daphne]
|
||||
4
app/requirements/prod.txt
Normal file
4
app/requirements/prod.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
-r common.txt
|
||||
|
||||
uvicorn
|
||||
channels
|
||||
23
app/templates/base.html
Normal file
23
app/templates/base.html
Normal 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
0
app/user/__init__.py
Normal file
27
app/user/admin.py
Normal file
27
app/user/admin.py
Normal 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
6
app/user/apps.py
Normal 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
11
app/user/forms.py
Normal 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
|
||||
44
app/user/migrations/0001_initial.py
Normal file
44
app/user/migrations/0001_initial.py
Normal 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()),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
app/user/migrations/__init__.py
Normal file
0
app/user/migrations/__init__.py
Normal file
72
app/user/models.py
Normal file
72
app/user/models.py
Normal 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
3
app/user/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
app/user/views.py
Normal file
3
app/user/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
Reference in New Issue
Block a user