Init
This commit is contained in:
35
.env.sample
Normal file
35
.env.sample
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
USER_ID=1000
|
||||||
|
GROUP_ID=1000
|
||||||
|
|
||||||
|
LISTEN_PORT=8000
|
||||||
|
|
||||||
|
# should be prod or dev
|
||||||
|
ENV='dev'
|
||||||
|
|
||||||
|
# secret
|
||||||
|
SECRET='CHANGE_ME'
|
||||||
|
|
||||||
|
# Allowed hosts, domain's allowed, comma separated
|
||||||
|
ALLOWED_HOSTS='127.0.0.1, localhost'
|
||||||
|
|
||||||
|
# CSRF Trusted origins
|
||||||
|
CSRF_TRUSTED_ORIGINS='http://127.0.0.1, http://localhost'
|
||||||
|
|
||||||
|
### DEV RELATED
|
||||||
|
# VITE
|
||||||
|
DEV_SERVER_HOST='127.0.0.1'
|
||||||
|
DEV_SERVER_PORT='8080'
|
||||||
|
|
||||||
|
### DB related
|
||||||
|
# db engine, should be 'sqlite', 'pgsql' (psycopg[binary] required)
|
||||||
|
DB_ENGINE='sqlite3'
|
||||||
|
|
||||||
|
# SQLITE
|
||||||
|
SQLITE_REL_PATH='db.sqlite3'
|
||||||
|
|
||||||
|
# PostgreSQL, env logic for postgresql docker image
|
||||||
|
#POSTGRES_HOST=localhost
|
||||||
|
#POSTGRES_PORT=5432
|
||||||
|
#POSTGRES_DB=changeme
|
||||||
|
#POSTGRES_USER=changeme
|
||||||
|
#POSTGRES_PASSWORD=changeme
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.idea/
|
||||||
|
.env
|
||||||
|
|
||||||
|
backups/*
|
||||||
|
!backups/.gitkeep
|
||||||
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.
|
||||||
0
backups/.gitkeep
Normal file
0
backups/.gitkeep
Normal file
18
docker-compose.dev.yml
Normal file
18
docker-compose.dev.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
redis:
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
|
app:
|
||||||
|
restart: "no"
|
||||||
|
command: >
|
||||||
|
bash -c "sleep 2
|
||||||
|
&& python manage.py migrate
|
||||||
|
&& ./dev_run.sh"
|
||||||
|
ports:
|
||||||
|
- "${DEV_SERVER_PORT:-8080}:${DEV_SERVER_PORT:-8080}"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
restart: "no"
|
||||||
56
docker-compose.yml
Normal file
56
docker-compose.yml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
app:
|
||||||
|
image: app
|
||||||
|
build:
|
||||||
|
context: ./app
|
||||||
|
args:
|
||||||
|
ENV: ${ENV}
|
||||||
|
PUID: ${USER_ID}
|
||||||
|
PGID: ${GROUP_ID}
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
# user: "${USER_ID}:${GROUP_ID}"
|
||||||
|
volumes:
|
||||||
|
- ./app:/app
|
||||||
|
- ./backups:/backups
|
||||||
|
- /app/frontend/node_modules
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${LISTEN_PORT:-8000}:8000"
|
||||||
|
command: >
|
||||||
|
bash -c "cd frontend && yarn build && cd ..
|
||||||
|
&& python manage.py collectstatic --noinput
|
||||||
|
&& python manage.py migrate
|
||||||
|
&& uvicorn app.asgi:application --workers 3 --host 0.0.0.0 --port 8000"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
image: app
|
||||||
|
volumes_from:
|
||||||
|
- app
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
# user: "${USER_ID}:${GROUP_ID}"
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
command: >
|
||||||
|
bash -c "sleep 5
|
||||||
|
&& celery -A app worker --loglevel=info"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
image: app
|
||||||
|
volumes_from:
|
||||||
|
- app
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
# user: "${USER_ID}:${GROUP_ID}"
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
command: >
|
||||||
|
bash -c "sleep 5
|
||||||
|
&& celery -A app beat --scheduler django_celery_beat.schedulers:DatabaseScheduler --loglevel=info"
|
||||||
Reference in New Issue
Block a user