init
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# 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, docker compose .env file convention
|
||||||
|
#POSTGRES_HOST=localhost
|
||||||
|
#POSTGRES_PORT=5432
|
||||||
|
#POSTGRES_DB=changeme
|
||||||
|
#POSTGRES_USER=changeme
|
||||||
|
#POSTGRES_PASSWORD=changeme
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.idea/
|
||||||
|
|
||||||
|
db.sqlite3
|
||||||
|
.env
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
## First run
|
||||||
|
* install https://nodejs.org/en (tested with version 20)
|
||||||
|
* install https://www.python.org/ (tested with version 3.12)
|
||||||
|
* `cp .env.sample .env`
|
||||||
|
* modify .env
|
||||||
|
* follow requirements sections
|
||||||
|
|
||||||
|
## Requirements python
|
||||||
|
* ### for dev : `pip install -r requirements/dev.txt`
|
||||||
|
* ### for prod : `pip install -r requirements/prod.txt`
|
||||||
|
|
||||||
|
## Requirements nodejs
|
||||||
|
* ### Linux `cd frontend && npm install && cd -`
|
||||||
|
* ### Windows `pushd .; cd frontend; npm install; popd`
|
||||||
|
|
||||||
|
|
||||||
|
## Run project
|
||||||
|
* ### for dev :
|
||||||
|
* in base dir `python manage.py runserver`
|
||||||
|
* in frontend dir `npm run dev`
|
||||||
|
* ### for prod : in frontend dir `npm run build`
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 751 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="600px" height="400px" viewBox="0.5 0.5 600 400" enable-background="new 0.5 0.5 600 400" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#454545" d="M43.261,165.3V76.771h12.478l28.372,44.79c6.567,10.376,11.69,19.702,15.893,28.765l0.263-0.132 c-1.051-11.821-1.314-22.592-1.314-36.384V76.77h10.77v88.53H98.165l-28.108-44.921c-6.173-9.851-12.084-19.965-16.55-29.553 l-0.394,0.131c0.657,11.165,0.92,21.804,0.92,36.515V165.3H43.261z"/>
|
||||||
|
<path fill="#454545" d="M186.822,132.988c0,23.511-16.287,33.757-31.655,33.757c-17.207,0-30.473-12.61-30.473-32.707 c0-21.278,13.923-33.756,31.524-33.756C174.475,100.282,186.822,113.549,186.822,132.988z M136.384,133.645 c0,13.923,8.012,24.431,19.308,24.431c11.033,0,19.308-10.377,19.308-24.694c0-10.77-5.385-24.43-19.045-24.43 S136.384,121.561,136.384,133.645z"/>
|
||||||
|
<path fill="#454545" d="M242.116,83.864c0.131,3.94-2.758,7.093-7.355,7.093c-4.072,0-6.962-3.153-6.962-7.093 c0-4.072,3.021-7.225,7.224-7.225C239.358,76.64,242.116,79.792,242.116,83.864z M229.244,165.3v-63.573h11.558V165.3H229.244z"/>
|
||||||
|
<path fill="#454545" d="M259.978,118.934c0-6.568-0.132-11.953-0.526-17.207h10.114l0.526,10.245h0.394 c3.546-6.042,9.457-11.69,19.965-11.69c8.67,0,15.237,5.254,17.996,12.741h0.262c1.97-3.546,4.466-6.305,7.093-8.274 c3.809-2.89,8.012-4.466,14.055-4.466c8.406,0,20.884,5.517,20.884,27.584V165.3h-11.296v-35.989 c0-12.216-4.466-19.571-13.791-19.571c-6.568,0-11.69,4.86-13.661,10.508c-0.525,1.576-0.919,3.678-0.919,5.779V165.3h-11.296 v-38.091c0-10.114-4.467-17.469-13.267-17.469c-7.225,0-12.478,5.779-14.317,11.558c-0.657,1.708-0.919,3.678-0.919,5.648V165.3 h-11.296v-46.366C259.979,118.934,259.978,118.934,259.978,118.934z"/>
|
||||||
|
<path fill="#454545" d="M404.722,165.3l-0.919-8.012h-0.393c-3.548,4.991-10.377,9.457-19.44,9.457 c-12.873,0-19.44-9.064-19.44-18.258c0-15.368,13.66-23.774,38.223-23.643v-1.314c0-5.254-1.445-14.711-14.449-14.711 c-5.91,0-12.083,1.839-16.55,4.729l-2.626-7.618c5.253-3.415,12.872-5.648,20.884-5.648c19.44,0,24.168,13.266,24.168,26.007 v23.774c0,5.517,0.262,10.902,1.051,15.237L404.722,165.3L404.722,165.3z M403.015,132.857 c-12.609-0.263-26.927,1.97-26.927,14.317c0,7.487,4.992,11.033,10.902,11.033c8.276,0,13.529-5.254,15.368-10.639 c0.394-1.182,0.657-2.496,0.657-3.678L403.015,132.857L403.015,132.857z"/>
|
||||||
|
<path fill="#454545" d="M487.602,101.727c-0.262,4.597-0.525,9.72-0.525,17.469v36.909c0,14.579-2.891,23.512-9.063,29.028 c-6.173,5.779-15.106,7.618-23.118,7.618c-7.618,0-16.024-1.839-21.148-5.254l2.891-8.799c4.203,2.626,10.77,4.991,18.651,4.991 c11.821,0,20.49-6.173,20.49-22.197v-7.093h-0.262c-3.546,5.911-10.377,10.64-20.227,10.64c-15.762,0-27.057-13.398-27.057-30.999 c0-21.541,14.054-33.756,28.633-33.756c11.033,0,17.075,5.779,19.834,11.033h0.262l0.525-9.589L487.602,101.727L487.602,101.727z M475.649,126.815c0-1.97-0.132-3.678-0.657-5.254c-2.102-6.699-7.75-12.216-16.156-12.216c-11.033,0-18.915,9.326-18.915,24.037 c0,12.478,6.305,22.855,18.783,22.855c7.093,0,13.529-4.466,16.025-11.821c0.657-1.97,0.919-4.203,0.919-6.173v-11.428 C475.648,126.815,475.649,126.815,475.649,126.815z"/>
|
||||||
|
<path fill="#454545" d="M512.686,135.615c0.264,15.63,10.246,22.066,21.804,22.066c8.274,0,13.266-1.445,17.601-3.284l1.969,8.275 c-4.071,1.839-11.032,3.941-21.146,3.941c-19.572,0-31.261-12.872-31.261-32.049s11.296-34.282,29.815-34.282 c20.753,0,26.27,18.257,26.27,29.948c0,2.364-0.263,4.203-0.394,5.385L512.686,135.615L512.686,135.615z M546.575,127.34 c0.131-7.356-3.021-18.783-16.025-18.783c-11.69,0-16.813,10.77-17.732,18.783H546.575z"/>
|
||||||
|
<path fill="#454545" d="M100.266,322.915l-0.919-8.012h-0.394c-3.546,4.991-10.377,9.457-19.44,9.457 c-12.872,0-19.439-9.063-19.439-18.258c0-15.367,13.66-23.773,38.222-23.643v-1.314c0-5.254-1.445-14.71-14.449-14.71 c-5.91,0-12.084,1.839-16.55,4.728l-2.627-7.618c5.254-3.416,12.873-5.649,20.884-5.649c19.44,0,24.168,13.266,24.168,26.008 v23.773c0,5.517,0.263,10.902,1.051,15.237L100.266,322.915L100.266,322.915z M98.559,290.472 c-12.609-0.262-26.927,1.97-26.927,14.317c0,7.487,4.991,11.033,10.902,11.033c8.274,0,13.529-5.254,15.368-10.639 c0.394-1.183,0.657-2.495,0.657-3.678C98.559,301.505,98.559,290.472,98.559,290.472z"/>
|
||||||
|
<path fill="#454545" d="M131.789,259.343l12.478,35.726c2.102,5.779,3.809,11.033,5.122,16.288h0.394 c1.445-5.254,3.283-10.508,5.385-16.288l12.347-35.726h12.084l-24.956,63.572H143.61l-24.168-63.572H131.789z"/>
|
||||||
|
<path fill="#454545" d="M225.175,322.915l-0.919-8.012h-0.394c-3.546,4.991-10.377,9.457-19.44,9.457 c-12.872,0-19.439-9.063-19.439-18.258c0-15.367,13.66-23.773,38.222-23.643v-1.314c0-5.254-1.445-14.71-14.449-14.71 c-5.91,0-12.084,1.839-16.55,4.728l-2.627-7.618c5.254-3.416,12.873-5.649,20.884-5.649c19.44,0,24.168,13.266,24.168,26.008 v23.773c0,5.517,0.263,10.902,1.051,15.237L225.175,322.915L225.175,322.915z M223.468,290.472 c-12.609-0.262-26.927,1.97-26.927,14.317c0,7.487,4.991,11.033,10.902,11.033c8.274,0,13.529-5.254,15.368-10.639 c0.394-1.183,0.657-2.495,0.657-3.678L223.468,290.472L223.468,290.472z"/>
|
||||||
|
<path fill="#454545" d="M266.154,241.479c0.131,3.941-2.758,7.093-7.355,7.093c-4.072,0-6.961-3.152-6.961-7.093 c0-4.072,3.021-7.225,7.224-7.225C263.396,234.255,266.154,237.407,266.154,241.479z M253.282,322.915v-63.572h11.558v63.572 H253.282z"/>
|
||||||
|
<path fill="#454545" d="M284.016,229.657h11.558v93.258h-11.558V229.657z"/>
|
||||||
|
<path fill="#454545" d="M350.214,322.915l-0.919-8.012h-0.393c-3.546,4.991-10.377,9.457-19.44,9.457 c-12.873,0-19.44-9.063-19.44-18.258c0-15.367,13.661-23.773,38.223-23.643v-1.314c0-5.254-1.445-14.71-14.449-14.71 c-5.91,0-12.083,1.839-16.55,4.728l-2.626-7.618c5.253-3.416,12.872-5.649,20.884-5.649c19.44,0,24.168,13.266,24.168,26.008 v23.773c0,5.517,0.262,10.902,1.051,15.237L350.214,322.915L350.214,322.915z M348.507,290.472 c-12.609-0.262-26.927,1.97-26.927,14.317c0,7.487,4.992,11.033,10.902,11.033c8.276,0,13.529-5.254,15.368-10.639 c0.394-1.183,0.657-2.495,0.657-3.678L348.507,290.472L348.507,290.472z"/>
|
||||||
|
<path fill="#454545" d="M377.796,322.915c0.262-4.335,0.525-10.771,0.525-16.418v-76.84h11.428v39.93h0.262 c4.073-7.093,11.428-11.69,21.673-11.69c15.762,0,26.927,13.134,26.795,32.443c0,22.722-14.316,34.019-28.503,34.019 c-9.194,0-16.549-3.548-21.278-11.954h-0.394l-0.525,10.508h-9.983V322.915z M389.749,297.434c0,1.444,0.262,2.89,0.525,4.203 c2.233,8.012,8.932,13.529,17.339,13.529c12.083,0,19.308-9.852,19.308-24.43c0-12.741-6.568-23.643-18.914-23.643 c-7.881,0-15.237,5.385-17.601,14.185c-0.262,1.314-0.657,2.89-0.657,4.728L389.749,297.434L389.749,297.434z"/>
|
||||||
|
<path fill="#454545" d="M453.055,229.657h11.56v93.258h-11.56V229.657z"/>
|
||||||
|
<path fill="#454545" d="M490.488,293.23c0.264,15.63,10.246,22.067,21.804,22.067c8.274,0,13.266-1.445,17.601-3.284l1.969,8.274 c-4.071,1.839-11.032,3.941-21.146,3.941c-19.572,0-31.261-12.872-31.261-32.049s11.296-34.282,29.815-34.282 c20.753,0,26.27,18.258,26.27,29.947c0,2.365-0.262,4.203-0.394,5.386L490.488,293.23L490.488,293.23z M524.377,284.955 c0.131-7.355-3.021-18.782-16.025-18.782c-11.69,0-16.813,10.77-17.732,18.782H524.377z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.2 KiB |
+16
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
ASGI config for app project.
|
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
"""
|
||||||
|
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 dotenv import load_dotenv
|
||||||
|
from os import getenv
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
load_dotenv(BASE_DIR / ".env")
|
||||||
|
|
||||||
|
# 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]
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
|
"corsheaders",
|
||||||
|
"django_vite",
|
||||||
|
|
||||||
|
"user",
|
||||||
|
"home",
|
||||||
|
"blog"
|
||||||
|
]
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
|
||||||
|
# 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 = 'fr-fr'
|
||||||
|
|
||||||
|
TIME_ZONE = 'Europe/Paris'
|
||||||
|
|
||||||
|
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",
|
||||||
|
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
|
||||||
|
path("", include("home.urls", namespace="home")),
|
||||||
|
path("blog/", include("blog.urls", namespace="blog")),
|
||||||
|
]
|
||||||
+16
@@ -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,0 +1,26 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from ordered_model.admin import OrderedModelAdmin
|
||||||
|
|
||||||
|
from .models import Article, Tag, Category
|
||||||
|
from .forms import ArticleForm
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Article)
|
||||||
|
class ArticleAdmin(admin.ModelAdmin):
|
||||||
|
form = ArticleForm
|
||||||
|
list_display = ('title', 'description', 'author')
|
||||||
|
exclude = ["author"]
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.author = request.user
|
||||||
|
return super().save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Category)
|
||||||
|
class CategoryAdmin(OrderedModelAdmin):
|
||||||
|
list_display = ('name', 'move_up_down_links')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Tag)
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class BlogConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'blog'
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from django import forms
|
||||||
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
|
from blog.models import Article
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
exclude = "__all__"
|
||||||
|
widgets = {
|
||||||
|
"content": TinyMCE()
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{% extends "blog/layout.html" %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/toolbar/prism-toolbar.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/show-language/prism-show-language.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h2>
|
||||||
|
{{ article.title }}
|
||||||
|
</h2>
|
||||||
|
</header>
|
||||||
|
<p>{{ article.description.replace("\n", "<br />")|safe }}</p>
|
||||||
|
<div class="blog_content line-numbers">
|
||||||
|
{{ article.content|safe }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{% extends "blog/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="article_list">
|
||||||
|
{% for article in articles %}
|
||||||
|
<a href="{{ url("blog:article_detail", args=[article.pk, slugify(article.title)]) }}">
|
||||||
|
<article>
|
||||||
|
{% set article_image %}
|
||||||
|
<section class="article_image">
|
||||||
|
{# <img src="{{ static("images/no_mage_600_x_400.svg") }}">#}
|
||||||
|
<img src="{{ static("images/no_image.png") }}" alt="No image"/>
|
||||||
|
</section>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{% set article_text %}
|
||||||
|
<section class="article_text">
|
||||||
|
<header>
|
||||||
|
<h2>{{ article.title }}</h2>
|
||||||
|
</header>
|
||||||
|
<p>{{ article.description.replace("\n", "<br/>")|safe }}</p>
|
||||||
|
</section>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{{ article_image }}
|
||||||
|
{{ article_text }}
|
||||||
|
</article>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<p>Aucun article</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ super() }}
|
||||||
|
{{ stylesheet_pack("blog") }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ super() }}
|
||||||
|
{{ javascript_pack("blog") }}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Generated by Django 5.0.7 on 2024-07-19 08:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Article',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=255)),
|
||||||
|
('description', models.TextField()),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('date_modified', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Category',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('order', models.PositiveIntegerField(db_index=True, editable=False, verbose_name='order')),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name_plural': 'Categories',
|
||||||
|
'ordering': ('order',),
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Comment',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('date_modified', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Tag',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Generated by Django 5.0.7 on 2024-07-19 08:31
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('blog', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='author',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='article',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='author',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='response_to',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.comment'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='tags',
|
||||||
|
field=models.ManyToManyField(blank=True, to='blog.tag'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from ordered_model.models import OrderedModel
|
||||||
|
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
description = models.TextField()
|
||||||
|
content = models.TextField()
|
||||||
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
date_modified = models.DateTimeField(auto_now=True)
|
||||||
|
category = models.ForeignKey("Category", on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
tags = models.ManyToManyField("Tag", blank=True)
|
||||||
|
author = models.ForeignKey("user.User", on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
|
class Category(OrderedModel):
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class Meta(OrderedModel.Meta):
|
||||||
|
verbose_name_plural = "Categories"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(models.Model):
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(models.Model):
|
||||||
|
author = models.ForeignKey("user.User", on_delete=models.CASCADE)
|
||||||
|
article = models.ForeignKey("Article", on_delete=models.CASCADE)
|
||||||
|
content = models.TextField()
|
||||||
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
date_modified = models.DateTimeField(auto_now=True)
|
||||||
|
response_to = models.ForeignKey("self", on_delete=models.CASCADE, null=True, blank=True)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{% extends "blog/layout.html" %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/toolbar/prism-toolbar.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/show-language/prism-show-language.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h2>
|
||||||
|
{{ article.title }}
|
||||||
|
</h2>
|
||||||
|
</header>
|
||||||
|
<p>{{ article.description.replace("\n", "<br />")|safe }}</p>
|
||||||
|
<div class="blog_content line-numbers">
|
||||||
|
{{ article.content|safe }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{% extends "blog/layout.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="article_list">
|
||||||
|
{% for article in articles %}
|
||||||
|
<a href="{% url "blog:article_detail" article.pk article.title|slugify %}">
|
||||||
|
<article>
|
||||||
|
{# {% set article_image %}#}
|
||||||
|
{# <section class="article_image">#}
|
||||||
|
{# <img src="{{ static("images/no_mage_600_x_400.svg") }}">#}
|
||||||
|
{# <img src="{% static "images/no_image.png" %}" alt="No image"/>#}
|
||||||
|
{# </section>#}
|
||||||
|
{# {% endset %}#}
|
||||||
|
{##}
|
||||||
|
{# {% set article_text %}#}
|
||||||
|
{# <section class="article_text">#}
|
||||||
|
{# <header>#}
|
||||||
|
{# <h2>{{ article.title }}</h2>#}
|
||||||
|
{# </header>#}
|
||||||
|
{# <p>{{ article.description|safe|linebreaks }}</p>#}
|
||||||
|
{# </section>#}
|
||||||
|
{# {% endset %}#}
|
||||||
|
|
||||||
|
{# {{ article_image }}#}
|
||||||
|
{# {{ article_text }}#}
|
||||||
|
</article>
|
||||||
|
</a>
|
||||||
|
{% empty %}
|
||||||
|
<p>Aucun article</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ super }}
|
||||||
|
{# {{ stylesheet_pack("blog") }}#}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ super }}
|
||||||
|
{# {{ javascript_pack("blog") }}#}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'blog'
|
||||||
|
urlpatterns = [
|
||||||
|
path("", views.ArticleListView.as_view(), name="article_list"),
|
||||||
|
path("<int:pk>-<str:slug>/", views.ArticleDetailView.as_view(), name="article_detail"),
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from django.views.generic import ListView, DetailView
|
||||||
|
|
||||||
|
from blog.models import Article
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleListView(ListView):
|
||||||
|
model = Article
|
||||||
|
template_name = 'blog/article_list.html'
|
||||||
|
context_object_name = 'articles'
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleDetailView(DetailView):
|
||||||
|
model = Article
|
||||||
|
template_name = 'blog/article_detail.html'
|
||||||
|
context_object_name = 'article'
|
||||||
@@ -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?
|
||||||
Generated
+1098
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
"vite": "^5.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = (ctx) => ({
|
||||||
|
plugins: [
|
||||||
|
require('postcss-import')(),
|
||||||
|
require('postcss-simple-vars')(),
|
||||||
|
require("autoprefixer")(),
|
||||||
|
]
|
||||||
|
})
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import 'vite/modulepreload-polyfill'
|
||||||
|
// import "../styles/app.css"
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== '') {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i].trim();
|
||||||
|
// Does this cookie string begin with the name we want?
|
||||||
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
// DOM is ready.
|
||||||
|
setTimeout(async () => {
|
||||||
|
const response = await fetch("/hit/", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
'X-CSRFToken': getCookie("csrftoken")
|
||||||
|
},
|
||||||
|
mode: 'same-origin',
|
||||||
|
body: JSON.stringify({
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}, 1000)
|
||||||
|
})();
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import "../components/sidebar";
|
||||||
|
|
||||||
|
window.document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
window.console.log("dom ready 2");
|
||||||
|
});
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import "../styles/blog.css"
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// import "../styles/cv.css"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
window.console.log("sidebar is loaded");
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
@import "normalize.css";
|
||||||
|
|
||||||
|
html::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
body{
|
||||||
|
background-color: rgba(43, 43, 42, 0.8);
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul{
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_header{
|
||||||
|
background-color: white;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_header nav{
|
||||||
|
max-width: 90%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_header ul{
|
||||||
|
margin-top: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_header li {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main_app{
|
||||||
|
max-width: 90%;
|
||||||
|
min-height: 50rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/* hello */
|
||||||
|
|
||||||
|
#article_list{
|
||||||
|
/*background-color: grey;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#article_list article{
|
||||||
|
/*background-color: green;*/
|
||||||
|
display: flex;
|
||||||
|
border: 0.1rem solid green;
|
||||||
|
margin: 1rem;
|
||||||
|
/*height: 10rem;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#article_list a{
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article_image{
|
||||||
|
width: 30%;
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article_image img{
|
||||||
|
/*image-;*/
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article_text{
|
||||||
|
width: 70%;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article_text h2{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#cv_main{
|
||||||
|
display: flex;
|
||||||
|
justify-content: normal;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cv_main > #cv_left{
|
||||||
|
background-color: beige;
|
||||||
|
max-width: 30%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cv_main > #cv_right{
|
||||||
|
background-color: cadetblue;
|
||||||
|
min-width: 70%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
article header{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
article header>div:first-child{
|
||||||
|
margin-left: 1rem;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv_header_title{
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv_header_title>*{
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv_content_competence{
|
||||||
|
max-width: 30%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 2fr);
|
||||||
|
column-gap: 1rem;
|
||||||
|
row-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*article header{*/
|
||||||
|
/* display: flex;*/
|
||||||
|
/* flex-direction: row;*/
|
||||||
|
/* align-items: center;*/
|
||||||
|
/* background-color: #e0e0e0;*/
|
||||||
|
/* justify-content: center;*/
|
||||||
|
/* position: relative;*/
|
||||||
|
/*}*/
|
||||||
|
/*article header>div:first-child{*/
|
||||||
|
/* margin-left: 1rem;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.cv_header_title{*/
|
||||||
|
/* margin: auto;*/
|
||||||
|
/* display: flex;*/
|
||||||
|
/* flex-direction: column;*/
|
||||||
|
/* align-items: center;*/
|
||||||
|
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.cv_header_title>*{*/
|
||||||
|
/* padding: 0;*/
|
||||||
|
/* margin: 0.1rem;*/
|
||||||
|
/*}*/
|
||||||
|
/*.g_pas_idee {*/
|
||||||
|
/* position: absolute;*/
|
||||||
|
/* left: 0;*/
|
||||||
|
/* background-color: red;*/
|
||||||
|
/* text-align: center;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*@media screen and (max-width: 700px) {*/
|
||||||
|
/* .g_pas_idee {*/
|
||||||
|
/* position: inherit;*/
|
||||||
|
/* width: 100%;*/
|
||||||
|
/* }*/
|
||||||
|
/* article header{*/
|
||||||
|
/* flex-direction: column;*/
|
||||||
|
/* }*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*article header{*/
|
||||||
|
/* display: grid;*/
|
||||||
|
/* grid-template-columns: 3fr 5fr;*/
|
||||||
|
/* align-items: center;*/
|
||||||
|
/* background-color: #e0e0e0;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*article header>div:first-child{*/
|
||||||
|
/* margin-left: 1rem;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.cv_header_title{*/
|
||||||
|
/* text-align: center;*/
|
||||||
|
/* margin-right: auto;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.cv_header_title>h2,h3{*/
|
||||||
|
/* margin: .5rem;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*article header>div{*/
|
||||||
|
/* max-width: 60%;*/
|
||||||
|
/* text-align: center;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.cv_header_title{*/
|
||||||
|
/* display: flex;*/
|
||||||
|
/* flex-direction: column;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
.cv_content h4 {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
#cv_main{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cv_main > #cv_left{
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cv_main > #cv_right{
|
||||||
|
min-width: 90%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
@import "tailwindcss/base";
|
||||||
|
@import "tailwindcss/components";
|
||||||
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
|
//.jumbotron {
|
||||||
|
// // should be relative path of the entry scss file
|
||||||
|
// background-image: url("../../vendors/images/sample.jpg");
|
||||||
|
// background-size: cover;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//.btn-blue {
|
||||||
|
// @apply inline-block px-4 py-2;
|
||||||
|
// @apply font-semibold rounded-lg shadow-md;
|
||||||
|
// @apply bg-blue-500 text-white;
|
||||||
|
// @apply hover:bg-blue-700 focus:outline-none;
|
||||||
|
// @apply focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
|
||||||
|
//}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import {defineConfig, loadEnv} from "vite";
|
||||||
|
import {resolve, join} from "path";
|
||||||
|
|
||||||
|
// 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: [
|
||||||
|
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": resolve(SRC_DIR)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
root: SRC_DIR,
|
||||||
|
base: "/static/",
|
||||||
|
// css: {
|
||||||
|
// postcss: postcssConfig
|
||||||
|
// },
|
||||||
|
server: {
|
||||||
|
host: env.DEV_SERVER_HOST,
|
||||||
|
port: env.DEV_SERVER_PORT
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
manifest: "manifest.json",
|
||||||
|
emptyOutDir: true,
|
||||||
|
outDir: OUT_DIR,
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
app: join(SRC_DIR, "application/app.js"),
|
||||||
|
app2: join(SRC_DIR, "application/app2.js"),
|
||||||
|
blog: join(SRC_DIR, "application/blog.js"),
|
||||||
|
cv: join(SRC_DIR, "application/cv.js")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.20.2":
|
||||||
|
version "0.20.2"
|
||||||
|
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz"
|
||||||
|
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.17.2":
|
||||||
|
version "4.17.2"
|
||||||
|
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz"
|
||||||
|
integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==
|
||||||
|
|
||||||
|
"@types/estree@1.0.5":
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
|
||||||
|
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||||
|
|
||||||
|
autoprefixer@^10.4.19:
|
||||||
|
version "10.4.19"
|
||||||
|
resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz"
|
||||||
|
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, "browserslist@>= 4.21.0":
|
||||||
|
version "4.23.0"
|
||||||
|
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz"
|
||||||
|
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.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz"
|
||||||
|
integrity sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==
|
||||||
|
|
||||||
|
electron-to-chromium@^1.4.668:
|
||||||
|
version "1.4.762"
|
||||||
|
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz"
|
||||||
|
integrity sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ==
|
||||||
|
|
||||||
|
esbuild@^0.20.1:
|
||||||
|
version "0.20.2"
|
||||||
|
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz"
|
||||||
|
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.npmjs.org/escalade/-/escalade-3.1.2.tgz"
|
||||||
|
integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
|
||||||
|
|
||||||
|
fraction.js@^4.3.7:
|
||||||
|
version "4.3.7"
|
||||||
|
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz"
|
||||||
|
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
|
||||||
|
|
||||||
|
function-bind@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
|
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||||
|
|
||||||
|
hasown@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz"
|
||||||
|
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
|
is-core-module@^2.13.0:
|
||||||
|
version "2.13.1"
|
||||||
|
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz"
|
||||||
|
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
|
||||||
|
dependencies:
|
||||||
|
hasown "^2.0.0"
|
||||||
|
|
||||||
|
nanoid@^3.3.7:
|
||||||
|
version "3.3.7"
|
||||||
|
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz"
|
||||||
|
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||||
|
|
||||||
|
node-releases@^2.0.14:
|
||||||
|
version "2.0.14"
|
||||||
|
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz"
|
||||||
|
integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
|
||||||
|
|
||||||
|
normalize-range@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz"
|
||||||
|
integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
|
||||||
|
|
||||||
|
normalize.css@^8.0.1:
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz"
|
||||||
|
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
|
||||||
|
|
||||||
|
path-parse@^1.0.7:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
||||||
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
|
picocolors@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
|
||||||
|
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||||
|
|
||||||
|
pify@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
|
||||||
|
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
|
||||||
|
|
||||||
|
postcss-import@^16.1.0:
|
||||||
|
version "16.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.0.tgz"
|
||||||
|
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.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz"
|
||||||
|
integrity sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==
|
||||||
|
|
||||||
|
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
||||||
|
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||||
|
|
||||||
|
postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.1, postcss@^8.4.38:
|
||||||
|
version "8.4.38"
|
||||||
|
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz"
|
||||||
|
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.npmjs.org/read-cache/-/read-cache-1.0.0.tgz"
|
||||||
|
integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
|
||||||
|
dependencies:
|
||||||
|
pify "^2.3.0"
|
||||||
|
|
||||||
|
resolve@^1.1.7:
|
||||||
|
version "1.22.8"
|
||||||
|
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
|
||||||
|
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.npmjs.org/rollup/-/rollup-4.17.2.tgz"
|
||||||
|
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.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"
|
||||||
|
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||||
|
|
||||||
|
supports-preserve-symlinks-flag@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
|
||||||
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
update-browserslist-db@^1.0.13:
|
||||||
|
version "1.0.15"
|
||||||
|
resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz"
|
||||||
|
integrity sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==
|
||||||
|
dependencies:
|
||||||
|
escalade "^3.1.2"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
|
||||||
|
vite@^5.2.0:
|
||||||
|
version "5.2.11"
|
||||||
|
resolved "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz"
|
||||||
|
integrity sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.20.1"
|
||||||
|
postcss "^8.4.38"
|
||||||
|
rollup "^4.13.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class HomeConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'home'
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class ContactForm(forms.Form):
|
||||||
|
full_name = forms.CharField(max_length=255, required=True, label="Nom/Prénom ou entreprise")
|
||||||
|
email = forms.EmailField(required=True)
|
||||||
|
subject = forms.CharField(max_length=255, required=True, label="Sujet")
|
||||||
|
message = forms.CharField(widget=forms.Textarea, required=True)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from django.middleware.csrf import rotate_token
|
||||||
|
|
||||||
|
|
||||||
|
class SessionOnHitMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
if not request.user.is_authenticated and not request.session.session_key:
|
||||||
|
request.session.save()
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class CsrfOnHitMiddleWare:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
if 'CSRF_COOKIE' not in request.META:
|
||||||
|
rotate_token(request)
|
||||||
|
response = self.get_response(request)
|
||||||
|
|
||||||
|
return response
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 5.0.7 on 2024-07-19 08:31
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Hit',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('path', models.CharField(max_length=255)),
|
||||||
|
('parameters', models.JSONField(default=dict)),
|
||||||
|
('session_id', models.CharField(max_length=255)),
|
||||||
|
('ip_address', models.GenericIPAddressField()),
|
||||||
|
('user_agent', models.CharField(max_length=255)),
|
||||||
|
('date_created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Hit(models.Model):
|
||||||
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
path = models.CharField(max_length=255)
|
||||||
|
parameters = models.JSONField(default=dict)
|
||||||
|
session_id = models.CharField(max_length=255)
|
||||||
|
ip_address = models.GenericIPAddressField(max_length=255)
|
||||||
|
user_agent = models.CharField(max_length=255)
|
||||||
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends "home/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post" action="{% url "home:contact" %}">
|
||||||
|
{{ csrf_input }}
|
||||||
|
<div id="form_{{ form.full_name.id_for_label }}">
|
||||||
|
{{ form.full_name.errors }}
|
||||||
|
{{ form.full_name.label_tag }}
|
||||||
|
{{ form.full_name }}
|
||||||
|
</div>
|
||||||
|
<div id="form_{{ form.email.id_for_label }}">
|
||||||
|
{{ form.email.errors }}
|
||||||
|
{{ form.email.label_tag }}
|
||||||
|
{{ form.email }}
|
||||||
|
</div>
|
||||||
|
<div id="form_{{ form.subject.id_for_label }}">
|
||||||
|
{{ form.subject.errors }}
|
||||||
|
{{ form.subject.label_tag }}
|
||||||
|
{{ form.subject }}
|
||||||
|
</div>
|
||||||
|
<div id="form_{{ form.message.id_for_label }}">
|
||||||
|
{{ form.message.errors }}
|
||||||
|
{{ form.message.label_tag }}
|
||||||
|
{{ form.message }}
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Envoyer">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "home/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
{% extends "home/layout.html" %}
|
||||||
|
{% load django_vite %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ super }}
|
||||||
|
<link rel="stylesheet" href="{% vite_asset_url "styles/cv.css" %}">
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% vite_asset "application/cv.js" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="cv_main">
|
||||||
|
<div id="cv_left">
|
||||||
|
<div id="cv_presentation">
|
||||||
|
<h2>Fabien ROUSSEAU</h2>
|
||||||
|
<h3>Développeur Python/Django</h3>
|
||||||
|
<p>
|
||||||
|
Passionné d'informatique. J'ai décidé d'en faire mon métier en apprenant par moi-même l'électronique, le réseau puis par la suite le développement
|
||||||
|
Aujourd'hui je souhaiterais poursuivre ce parcours au sein d'une entreprise dont je partage les valeurs qui m'accompagnera dans un processus de validation et certification de mon expérience et de mes compétences
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="cv_contact">
|
||||||
|
<h3>Coodonnées</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://www.linkedin.com/in/44-fabien-rousseau/">LinkedIN</a></li>
|
||||||
|
<li><a href="{% url "home:contact" %}">Me contacter</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cv_competence">
|
||||||
|
<h3>Compétences</h3>
|
||||||
|
<h4>Language et framework</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Python</li>
|
||||||
|
<li>Django</li>
|
||||||
|
<li>Django Rest Framework</li>
|
||||||
|
<li>Django Channels</li>
|
||||||
|
<li>Jinja</li>
|
||||||
|
<li>Celery</li>
|
||||||
|
<li>VueJS</li>
|
||||||
|
<li>NuxtJS</li>
|
||||||
|
<li>Vuetify</li>
|
||||||
|
<li>OCPP</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Système d'exploitation</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Linux</li>
|
||||||
|
<li>Windows</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Outils</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Docker</li>
|
||||||
|
<li>Git</li>
|
||||||
|
<li>Kanban</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Base de données</h4>
|
||||||
|
<ul>
|
||||||
|
<li>PostgreSQL</li>
|
||||||
|
<li>MySQL</li>
|
||||||
|
<li>Redis</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cv_strong">
|
||||||
|
<h3>Points forts</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Passionné de développement</li>
|
||||||
|
<li>Polyvalent et adaptabilité</li>
|
||||||
|
<li>Travaille efficacement en équipe</li>
|
||||||
|
<li>Consciencieux</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="cv_right">
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<p>Avril 2017 à Maintenant</p>
|
||||||
|
</div>
|
||||||
|
<div class="cv_header_title">
|
||||||
|
<h2>Développeur</h2>
|
||||||
|
<h3>Auto-entrepreneur - Nantes - Full remote</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="cv_content">
|
||||||
|
<h4>Services : </h4>
|
||||||
|
<ul>
|
||||||
|
<li>Développement applicatif</li>
|
||||||
|
<li>Conseil en informatique</li>
|
||||||
|
<li>Hébergement et maintenance web</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Missions : </h4>
|
||||||
|
<ul>
|
||||||
|
<li>Site web de suivi d’actualité footballistique</li>
|
||||||
|
<li>Site de suivi des résultats et statistique footballistique</li>
|
||||||
|
<li>Site d’hébergement de CV pour sportifs amateurs</li>
|
||||||
|
<li>Site de gestion de formation dans le domaine des sports aquatiques</li>
|
||||||
|
<li>Conseil divers en informatique</li>
|
||||||
|
<li>Intervention en soutien pour maintenance d’infrastructure</li>
|
||||||
|
<li>intervention pour migration de sites</li>
|
||||||
|
<li>Site vitrine d’entreprise d’enfouissement électrique et gaz</li>
|
||||||
|
<li>Mise en place de scrapping pour entreprise</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Compétences technique : </h4>
|
||||||
|
<ul class="cv_content_competence">
|
||||||
|
<li>Python</li>
|
||||||
|
<li>Django</li>
|
||||||
|
<li>Django REST FRAMEWORK</li>
|
||||||
|
<li>Django Channels</li>
|
||||||
|
<li>Celery</li>
|
||||||
|
<li>PostgreSQL</li>
|
||||||
|
<li>Redis</li>
|
||||||
|
<li>VueJS</li>
|
||||||
|
<li>NuxtJS</li>
|
||||||
|
<li>Vuetify</li>
|
||||||
|
<li>Docker</li>
|
||||||
|
<li>Docker compose</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<p>Octobre 2021 à Novembre 2023</p>
|
||||||
|
</div>
|
||||||
|
<div class="cv_header_title">
|
||||||
|
<h2>Lead Développeur</h2>
|
||||||
|
<h3>PredicVision - Bordeaux - Full remote</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="cv_content">
|
||||||
|
<h4>Objectif : </h4>
|
||||||
|
<p>
|
||||||
|
Création d’une plateforme afin d’analyser et maintenir un parc de bornes de rechargement pour véhicules électriques.
|
||||||
|
</p>
|
||||||
|
<h4>Compétences Techniques : </h4>
|
||||||
|
<ul class="cv_content_competence">
|
||||||
|
<li>Python</li>
|
||||||
|
<li>Django</li>
|
||||||
|
<li>Django REST FRAMEWORK</li>
|
||||||
|
<li>Django Channels</li>
|
||||||
|
<li>Celery</li>
|
||||||
|
<li>OCPP</li>
|
||||||
|
<li>PostgreSQL</li>
|
||||||
|
<li>Redis</li>
|
||||||
|
<li>VueJS</li>
|
||||||
|
<li>NuxtJS</li>
|
||||||
|
<li>Vuetify</li>
|
||||||
|
<li>Docker</li>
|
||||||
|
<li>Docker compose</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Compétences supplémentaires : </h4>
|
||||||
|
<ul>
|
||||||
|
<li>Gitlab</li>
|
||||||
|
<li>Kanban</li>
|
||||||
|
<li>conseil</li>
|
||||||
|
<li>réunion</li>
|
||||||
|
<li>veille technique</li>
|
||||||
|
<li>étude de faisabilité</li>
|
||||||
|
<li>présentation</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<p>Septembre 2020 à Octobre 2021</p>
|
||||||
|
</div>
|
||||||
|
<div class="cv_header_title">
|
||||||
|
<h2>Développeur Django</h2>
|
||||||
|
<h3>FMC Production - Nantes - Hybride</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="cv_content">
|
||||||
|
<h4>Objectif :</h4>
|
||||||
|
<p>
|
||||||
|
Maintient d’une plateforme e-learning et création d’une plateforme de conférence live et VOD pour le monde médical
|
||||||
|
</p>
|
||||||
|
<h4>Compétences Techniques : </h4>
|
||||||
|
<ul class="cv_content_competence">
|
||||||
|
<li>Python</li>
|
||||||
|
<li>Django</li>
|
||||||
|
<li>Django REST FRAMEWORK</li>
|
||||||
|
<li>Django Channels</li>
|
||||||
|
<li>Celery</li>
|
||||||
|
<li>PostgreSQL</li>
|
||||||
|
<li>Redis</li>
|
||||||
|
<li>Docker</li>
|
||||||
|
<li>Docker compose</li>
|
||||||
|
</ul>
|
||||||
|
<h4>Compétences supplémentaires : </h4>
|
||||||
|
<ul>
|
||||||
|
<li>Gitlab</li>
|
||||||
|
<li>Daily</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{% extends "home/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Présentation</h2>
|
||||||
|
<p>
|
||||||
|
Développeur passionné d'informatique depuis le plus jeune âge.
|
||||||
|
Cette page sera un condenser d'une vitrine de moi même,
|
||||||
|
des prestations que je peux offrir
|
||||||
|
ainsi qu'une multitude de partage de connaissance à travers mon blog
|
||||||
|
</p>
|
||||||
|
<h2>En quête d'un projet sur la DATA</h2>
|
||||||
|
<p>
|
||||||
|
Ayant une expérience dans la data,
|
||||||
|
je suis en recherche d'un projet qui approfondira mes connaissances sur le sujet
|
||||||
|
avec pourquoi pas un cumul de connaissances sur le web
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "home/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
Prestation de service ou de maintenance Python/Django/Flask/FastAPI :
|
||||||
|
facturation à la journée à raison de 450€ par jour en moyenne (ce prix peut varier si c'est un projet qui dur dans le temps)
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Création de script</li>
|
||||||
|
<li>Création de site simple comme complex</li>
|
||||||
|
<li>Création de graphique (chart) sur des grands comme des petits volumes de donnée</li>
|
||||||
|
<li>Mise à jour de sécurité</li>
|
||||||
|
<li>Correction de bug</li>
|
||||||
|
<li>Modification de votre projet existant</li>
|
||||||
|
<li>Migration</li>
|
||||||
|
<li>Optimisation</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
|
app_name = 'home'
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.IndexView.as_view(), name='index'),
|
||||||
|
path("cv/", views.CVView.as_view(), name='cv'),
|
||||||
|
path("service/", views.ServiceView.as_view(), name='service'),
|
||||||
|
path("contact/", views.ContactView.as_view(), name='contact'),
|
||||||
|
path("contact_valid/", views.ContactValidView.as_view(), name='contact_valid'),
|
||||||
|
path("hit/", views.hit, name="hit")
|
||||||
|
]
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
from django.views.generic import TemplateView, FormView
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
from ipware import get_client_ip
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
|
from .forms import ContactForm
|
||||||
|
from .models import Hit
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(TemplateView):
|
||||||
|
template_name = 'home/index.html'
|
||||||
|
|
||||||
|
|
||||||
|
class CVView(TemplateView):
|
||||||
|
template_name = 'home/cv.html'
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceView(TemplateView):
|
||||||
|
template_name = "home/service.html"
|
||||||
|
|
||||||
|
|
||||||
|
class ContactView(FormView):
|
||||||
|
template_name = 'home/contact.html'
|
||||||
|
form_class = ContactForm
|
||||||
|
|
||||||
|
|
||||||
|
class ContactValidView(TemplateView):
|
||||||
|
template_name = "home/contact_success.html"
|
||||||
|
|
||||||
|
|
||||||
|
def hit(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
ip_address, _ = get_client_ip(request)
|
||||||
|
ip_address = '.'.join(ip_address.split('.')[:-1]+["0"])
|
||||||
|
path_parser = parse.urlsplit(request.headers["referer"])
|
||||||
|
path = path_parser.path
|
||||||
|
parameters = dict(parse.parse_qsl(path_parser.query))
|
||||||
|
user_agent = request.headers["user-agent"]
|
||||||
|
session_id = request.session.session_key
|
||||||
|
|
||||||
|
Hit.objects.create(
|
||||||
|
path=path_parser.path,
|
||||||
|
parameters=parameters,
|
||||||
|
session_id=session_id,
|
||||||
|
ip_address=ip_address,
|
||||||
|
user_agent=user_agent,
|
||||||
|
)
|
||||||
|
return HttpResponse("")
|
||||||
|
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE = app.settings
|
||||||
|
python_files = test_*
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
asgiref==3.8.*
|
||||||
|
Django==5.0.*
|
||||||
|
django-cors-headers==4.4.*
|
||||||
|
sqlparse==0.5.*
|
||||||
|
tzdata==2024.1
|
||||||
|
django-vite==3.0.*
|
||||||
|
python-dotenv==1.0.*
|
||||||
|
django-ordered-model==3.7.*
|
||||||
|
django-tinymce==4.1.*
|
||||||
|
django-ipware==7.0.*
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-r common.txt
|
||||||
|
pytest-django==4.8.*
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-r common.txt
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{% load django_vite %}
|
||||||
|
{% load static %}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="fr">
|
||||||
|
<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>{% block base_title %}devpanel - {% block title %}Accueil{% endblock %}{% endblock %}</title>
|
||||||
|
{% block base_css %}
|
||||||
|
<link rel="stylesheet" href="{% vite_asset_url "styles/app.css" %}">
|
||||||
|
{% block css %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="main_header">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{% url "home:index" %}">Présentation</a></li>
|
||||||
|
<li><a href="{% url "home:cv" %}">CV</a></li>
|
||||||
|
<li><a href="{% url "home:service" %}">Services</a></li>
|
||||||
|
<li><a href="{% url "blog:article_list" %}">Blog (WIP)</a></li>
|
||||||
|
<li><a href="{% url "home:contact" %}">Contact</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main id="main_app">
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer id="main_footer">
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
{% block base_js %}
|
||||||
|
{% vite_asset "application/app.js" %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
from django.test import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
import pytest
|
||||||
|
from pytest_django import asserts
|
||||||
|
|
||||||
|
|
||||||
|
def test_index():
|
||||||
|
c = Client()
|
||||||
|
response = c.get(reverse('home:index'))
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
asserts.assertTemplateUsed(response, 'home/index.html')
|
||||||
|
|
||||||
|
|
||||||
|
def test_cv():
|
||||||
|
c = Client()
|
||||||
|
response = c.get(reverse('home:cv'))
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
asserts.assertTemplateUsed(response, 'home/cv.html')
|
||||||
|
|
||||||
|
|
||||||
|
def test_service():
|
||||||
|
c = Client()
|
||||||
|
response = c.get(reverse('home:service'))
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
asserts.assertTemplateUsed(response, 'home/service.html')
|
||||||
|
|
||||||
|
|
||||||
|
def test_contact():
|
||||||
|
c = Client()
|
||||||
|
response = c.get(reverse('home:contact'))
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
asserts.assertTemplateUsed(response, 'home/contact.html')
|
||||||
|
|
||||||
|
|
||||||
|
def test_contact_submit_valid():
|
||||||
|
c = Client()
|
||||||
|
data = {"full_name": "foo bar", "email": "foo@bar.com", "subject": "test", "message": "test"}
|
||||||
|
response = c.post(reverse('home:contact'), data=data)
|
||||||
|
asserts.assertRedirects(response, reverse('home:contact_valid'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_contact_submit_invalid():
|
||||||
|
c = Client()
|
||||||
|
data = {"bad": "form"}
|
||||||
|
response = c.post(reverse('home:contact'), data=data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
@@ -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"]
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UserConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'user'
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Generated by Django 5.0.7 on 2024-07-19 08:31
|
||||||
|
|
||||||
|
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.UsernameUserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
REQUIRED_FIELDS = []
|
||||||
|
objects = UsernameUserManager()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
Reference in New Issue
Block a user