migrating to svelte
This commit is contained in:
parent
96a12939c8
commit
a819d952d3
|
@ -7,7 +7,7 @@ from generateur.generateur_main import generate_from_data, generate_from_path
|
|||
from database.auth.models import User
|
||||
from database.db import get_session
|
||||
from fastapi import Depends
|
||||
from database.exercices.models import ExampleEnum, ExerciceCreate, Exercice, ExerciceEdit, ExerciceRead, ExercicesTagLink, Tag, TagCreate, Supports
|
||||
from database.exercices.models import ExampleEnum, ExerciceCreate, Exercice, ExerciceEdit, ExerciceRead, ExercicesTagLink, Tag, TagCreate, Supports, ExerciceReadFull
|
||||
from services.auth import get_current_user, get_current_user_optional
|
||||
from services.database import generate_unique_code
|
||||
from services.io import add_fast_api_root, get_ancestor, get_or_create_dir
|
||||
|
@ -185,9 +185,9 @@ def get_tags_dependency(tags: List[str] | None = Query(None), db: Session = Depe
|
|||
def check_author(exo: Exercice, user_id:int):
|
||||
return exo.author_id == user_id
|
||||
|
||||
def serialize_exo(*, exo: Exercice, user_id: User = None, db: Session):
|
||||
def serialize_exo(*, exo: ExerciceRead, user_id: User = None, db: Session):
|
||||
tags = parse_exo_tags(exo_id=exo.id, user_id=user_id,
|
||||
db=db) if user_id is not None else []
|
||||
is_author = user_id is not None and check_author(exo=exo, user_id=user_id)
|
||||
return ExerciceRead(**exo.dict(), author=exo.author, original=exo.original, tags=tags, is_author=is_author, supports={**exo.dict()})
|
||||
return ExerciceReadFull(**exo.dict(), author=exo.author, original=exo.original, tags=tags, is_author=is_author, supports={**exo.dict()})
|
||||
|
||||
|
|
|
@ -144,10 +144,8 @@ class Author(SQLModel):
|
|||
def get_source_path_from_name_and_id(name: str, id_code: str):
|
||||
return f'/uploads/{id_code}/{name}'
|
||||
|
||||
|
||||
|
||||
|
||||
class ExerciceRead(ExerciceBase):
|
||||
class ExerciceReadBase(ExerciceBase):
|
||||
id_code: str
|
||||
author: Author
|
||||
original: ExerciceOrigin | None
|
||||
|
@ -158,14 +156,23 @@ class ExerciceRead(ExerciceBase):
|
|||
examples: Example = None
|
||||
|
||||
is_author: bool = None
|
||||
supports: Supports
|
||||
|
||||
|
||||
|
||||
|
||||
@validator('exo_source')
|
||||
def get_exo_source_name(cls, value, values):
|
||||
if value is not None:
|
||||
return get_filename_from_path(value)
|
||||
return value
|
||||
|
||||
class ExerciceRead(ExerciceBase):
|
||||
id: int
|
||||
author_id:int
|
||||
author: User
|
||||
original: Optional[Exercice]
|
||||
tags: List[Tag]
|
||||
|
||||
class ExerciceReadFull(ExerciceReadBase):
|
||||
supports: Supports
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from database.auth.crud import create_user_db
|
|||
from services.auth import get_current_user_optional, jwt_required
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from database.auth.models import User, UserRead
|
||||
from database.exercices.models import Exercice, ExerciceRead
|
||||
from database.exercices.models import Exercice, ExerciceReadFull
|
||||
from fastapi_pagination import add_pagination
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from fastapi.exceptions import RequestValidationError, ValidationError
|
||||
|
@ -161,9 +161,9 @@ def refresh_revoke(Authorize: AuthJWT = Depends()):
|
|||
|
||||
|
||||
class user(UserRead):
|
||||
exercices: List[ExerciceRead] = None
|
||||
exercices: List[ExerciceReadFull] = None
|
||||
|
||||
@app.post('/test', response_model=List[ExerciceRead] )
|
||||
@app.post('/test', response_model=List[ExerciceReadFull] )
|
||||
def test(db:Session= Depends(get_session)):
|
||||
#create_user_db('lilian', get_password_hash('Pomme937342'), db)
|
||||
create_user_db('lilian2', get_password_hash('Pomme937342'), db)
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import List
|
|||
from fastapi import APIRouter, Depends, Path, Query, UploadFile, HTTPException, status
|
||||
from database.auth.models import User
|
||||
from database.db import get_session
|
||||
from database.exercices.models import Exercice, ExerciceCreate, ExerciceEdit, ExerciceRead, ExercicesTagLink, Tag, TagCreate, TagRead
|
||||
from database.exercices.models import Exercice, ExerciceCreate, ExerciceEdit, ExerciceReadFull, ExercicesTagLink, Tag, TagCreate, TagRead, ExerciceRead
|
||||
from services.auth import get_current_user, get_current_user_optional
|
||||
from sqlmodel import Session, select, col
|
||||
from database.exercices.crud import add_tags_db, check_exercice_author, check_private, check_tag_author, create_exo_db, delete_exo_db, get_exo_dependency, clone_exo_db, parse_exo_tags, remove_tag_db, serialize_exo, update_exo_db, get_tags_dependency
|
||||
|
@ -11,7 +11,8 @@ from services.exoValidation import validate_file, validate_file_optionnal
|
|||
from services.io import add_fast_api_root, get_filename_from_path
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlmodel import func
|
||||
|
||||
from fastapi_pagination import Page, paginate
|
||||
from fastapi_pagination.ext.sqlalchemy_future import paginate as p
|
||||
router = APIRouter(tags=['exercices'])
|
||||
|
||||
class ExoType(str, Enum):
|
||||
|
@ -30,7 +31,7 @@ def queryFilters_dependency(search: str = "", tags: List[int] | None = Depends(g
|
|||
return search, tags, type
|
||||
|
||||
|
||||
@router.post('/exercices', response_model=ExerciceRead, status_code=status.HTTP_201_CREATED)
|
||||
@router.post('/exercices', response_model=ExerciceReadFull, status_code=status.HTTP_201_CREATED)
|
||||
def create_exo(exercice: ExerciceCreate = Depends(ExerciceCreate.as_form), file: UploadFile = Depends(validate_file), user: User = Depends(get_current_user), db: Session = Depends(get_session)):
|
||||
file_obj = file['file'].file._file
|
||||
file_obj.name = file['file'].filename
|
||||
|
@ -39,7 +40,7 @@ def create_exo(exercice: ExerciceCreate = Depends(ExerciceCreate.as_form), file:
|
|||
return serialize_exo(exo=exo_obj, user_id=user.id, db=db)
|
||||
|
||||
|
||||
@router.post('/clone/{id_code}', response_model=ExerciceRead)
|
||||
@router.post('/clone/{id_code}', response_model=ExerciceReadFull)
|
||||
def clone_exo(exercice: Exercice | None = Depends(check_private), user: User = Depends(get_current_user), db: Session = Depends(get_session)):
|
||||
if not exercice:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail={
|
||||
|
@ -51,11 +52,10 @@ def clone_exo(exercice: Exercice | None = Depends(check_private), user: User = D
|
|||
return serialize_exo(exo=exo_obj, user_id=user.id, db=db)
|
||||
|
||||
|
||||
|
||||
@router.get('/exercices/user', response_model=List[ExerciceRead])
|
||||
@router.get('/exercices/user', response_model=Page[ExerciceRead|ExerciceReadFull])
|
||||
def get_user_exercices(user: User = Depends(get_current_user), queryFilters: tuple[str, List[int] | None] = Depends(queryFilters_dependency), db: Session = Depends(get_session)):
|
||||
search, tags, type = queryFilters
|
||||
|
||||
|
||||
if tags is not None and len(tags) != 0:
|
||||
statement = select(Exercice, func.group_concat(
|
||||
ExercicesTagLink.tag_id))
|
||||
|
@ -76,16 +76,18 @@ def get_user_exercices(user: User = Depends(get_current_user), queryFilters: tup
|
|||
statement = statement.join(ExercicesTagLink).where(
|
||||
col(ExercicesTagLink.tag_id).in_(tags)).group_by(ExercicesTagLink.exercice_id)
|
||||
|
||||
exercices = db.exec(statement).all()
|
||||
#exercices = db.exec(statement).all()
|
||||
page = p(db, statement)
|
||||
exercices = page.items
|
||||
if tags is not None and len(tags) != 0:
|
||||
exercices = filter_exo_by_tags(exercices, tags)
|
||||
|
||||
exercices = [
|
||||
serialize_exo(exo=e, user_id=user.id, db= db) for e in exercices]
|
||||
return exercices
|
||||
page.items = [
|
||||
serialize_exo(exo=e, user_id=user.id, db=db) for e in exercices]
|
||||
|
||||
return page
|
||||
|
||||
|
||||
@router.get('/exercices/public', response_model=List[ExerciceRead])
|
||||
@router.get('/exercices/public', response_model=Page[ExerciceRead|ExerciceReadFull])
|
||||
def get_public_exercices(user: User | None = Depends(get_current_user_optional), queryFilters: tuple[str, List[int] | None] = Depends(queryFilters_dependency), db: Session = Depends(get_session)):
|
||||
search, tags, type = queryFilters
|
||||
|
||||
|
@ -130,15 +132,15 @@ def get_public_exercices(user: User | None = Depends(get_current_user_optional),
|
|||
if type == ExoType.web:
|
||||
statement = statement.where(Exercice.web == True)
|
||||
exercices = db.exec(statement).all()
|
||||
return [serialize_exo(exo=e, user_id=None, db=db) for e in exercices]
|
||||
return paginate([serialize_exo(exo=e, user_id=None, db=db) for e in exercices])
|
||||
|
||||
|
||||
@router.get('/exercice/{id_code}', response_model=ExerciceRead)
|
||||
@router.get('/exercice/{id_code}', response_model=ExerciceReadFull)
|
||||
def get_exercice(exo: Exercice = Depends(check_private), user: User | None = Depends(get_current_user_optional), db: Session = Depends(get_session)):
|
||||
return serialize_exo(exo=exo, user_id=getattr(user, 'id', None), db=db)
|
||||
|
||||
|
||||
@router.put('/exercice/{id_code}', response_model=ExerciceRead)
|
||||
@router.put('/exercice/{id_code}', response_model=ExerciceReadFull)
|
||||
def update_exo(file: UploadFile = Depends(validate_file_optionnal), exo: Exercice = Depends(check_exercice_author), exercice: ExerciceEdit = Depends(ExerciceEdit.as_form), db: Session = Depends(get_session)):
|
||||
if exo is None:
|
||||
raise HTTPException(
|
||||
|
@ -167,7 +169,7 @@ def delete_exercice(exercice: Exercice | bool | None = Depends(check_exercice_au
|
|||
return {'detail': 'Exercice supprimé avec succès'}
|
||||
|
||||
|
||||
@router.post('/exercice/{id_code}/tags', response_model=ExerciceRead, tags=['tags'])
|
||||
@router.post('/exercice/{id_code}/tags', response_model=ExerciceReadFull, tags=['tags'])
|
||||
def add_tags(tags: List[TagCreate], exo: Exercice | None = Depends(get_exo_dependency), db: Session = Depends(get_session), user: User = Depends(get_current_user)):
|
||||
if exo is None:
|
||||
raise HTTPException(
|
||||
|
@ -176,7 +178,7 @@ def add_tags(tags: List[TagCreate], exo: Exercice | None = Depends(get_exo_depen
|
|||
return serialize_exo(exo=exo_obj, user_id=user.id, db=db)
|
||||
|
||||
|
||||
@router.delete('/exercice/{id_code}/tags/{tag_id}', response_model=ExerciceRead, tags=['tags'])
|
||||
@router.delete('/exercice/{id_code}/tags/{tag_id}', response_model=ExerciceReadFull, tags=['tags'])
|
||||
def remove_tag(exo: Exercice | None = Depends(get_exo_dependency), tag: Tag | None | bool = Depends(check_tag_author), db: Session = Depends(get_session)):
|
||||
if exo is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
|
||||
|
|
|
@ -337,7 +337,60 @@ def test_get_users_exos(client: TestClient):
|
|||
r = client.get('/exercices/user',
|
||||
headers={'Authorization': 'Bearer ' + token1})
|
||||
print(r.json())
|
||||
assert r.json() == [prv]
|
||||
assert r.json()['page'] == 1
|
||||
assert r.json()['size'] == 50
|
||||
assert r.json()["items"] == [prv]
|
||||
|
||||
|
||||
def test_get_users_exos_page(client: TestClient):
|
||||
token1 = test_register(client, username="lilian")['access']
|
||||
token2 = test_register(client, username="lilian2")['access']
|
||||
|
||||
prv = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv2 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv3= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv4= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv5= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv6= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv7= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv8= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv9= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv10= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv11 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv12 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
|
||||
|
||||
r = client.get('/exercices/user',
|
||||
headers={'Authorization': 'Bearer ' + token1}, params={"page": 2, "size": 10})
|
||||
print(r.json())
|
||||
assert r.json()['page'] == 2
|
||||
assert r.json()['size'] == 10
|
||||
assert r.json()["items"] == [prv11, prv12]
|
||||
|
||||
def test_get_users_exos_page_up(client: TestClient):
|
||||
token1 = test_register(client, username="lilian")['access']
|
||||
token2 = test_register(client, username="lilian2")['access']
|
||||
|
||||
prv = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv2 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv3= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv4= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv5= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv6= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv7= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv8= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv9= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv10= test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv11 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
prv12 = test_create(client, private=True, user={'token': token1, 'username': "lilian"})
|
||||
|
||||
|
||||
r = client.get('/exercices/user',
|
||||
headers={'Authorization': 'Bearer ' + token1}, params={"page": 2, "size": 10})
|
||||
print(r.json())
|
||||
assert r.json()['page'] == 2
|
||||
assert r.json()['size'] == 10
|
||||
assert r.json()["items"] == []
|
||||
|
||||
|
||||
def test_get_user_with_search(client: TestClient):
|
||||
|
@ -361,7 +414,7 @@ def test_get_user_with_search(client: TestClient):
|
|||
r = client.get('/exercices/user', params={"search": "test"},
|
||||
headers={'Authorization': 'Bearer ' + token1})
|
||||
print(r.json())
|
||||
assert r.json() == [exo1, exo2]
|
||||
assert r.json()['items'] == [exo1, exo2]
|
||||
|
||||
|
||||
def test_get_user_with_search(client: TestClient):
|
||||
|
@ -385,7 +438,7 @@ def test_get_user_with_search(client: TestClient):
|
|||
r = client.get('/exercices/user', params={"search": "test"},
|
||||
headers={'Authorization': 'Bearer ' + token1})
|
||||
print(r.json())
|
||||
assert r.json() == [exo1, exo2]
|
||||
assert r.json()['items'] == [exo1, exo2]
|
||||
|
||||
|
||||
def test_get_user_with_tags(client: TestClient):
|
||||
|
@ -415,7 +468,7 @@ def test_get_user_with_tags(client: TestClient):
|
|||
r = client.get('/exercices/user', params={'tags': [*[t['id_code'] for t in tags2], 'notexist']},
|
||||
headers={'Authorization': 'Bearer ' + token1})
|
||||
print(r.json())
|
||||
assert r.json() == [exo2, exo3]
|
||||
assert r.json()['items'] == [exo2, exo3]
|
||||
|
||||
|
||||
def test_get_user_with_tags_and_search(client: TestClient):
|
||||
|
@ -446,7 +499,7 @@ def test_get_user_with_tags_and_search(client: TestClient):
|
|||
r = client.get('/exercices/user', params={"search": "yes", 'tags': [t['id_code'] for t in tags2]},
|
||||
headers={'Authorization': 'Bearer ' + token1})
|
||||
print(r.json())
|
||||
assert r.json() == [exo3]
|
||||
assert r.json()['items'] == [exo3]
|
||||
|
||||
|
||||
def test_get_public_auth(client: TestClient):
|
||||
|
@ -463,7 +516,7 @@ def test_get_public_auth(client: TestClient):
|
|||
r = client.get('/exercices/public',
|
||||
headers={'Authorization': 'Bearer ' + token2})
|
||||
print(r.json())
|
||||
assert r.json() == [{**public2, 'is_author': False}]
|
||||
assert r.json()['items'] == [{**public2, 'is_author': False}]
|
||||
|
||||
|
||||
def test_get_public_auth_with_search(client: TestClient):
|
||||
|
@ -483,7 +536,7 @@ def test_get_public_auth_with_search(client: TestClient):
|
|||
r = client.get('/exercices/public',
|
||||
params={'search': "yes"}, headers={'Authorization': 'Bearer ' + token2})
|
||||
print(r.json())
|
||||
assert r.json() == [{**public2, 'is_author': False}]
|
||||
assert r.json()['items'] == [{**public2, 'is_author': False}]
|
||||
|
||||
|
||||
def test_get_public_no_auth(client: TestClient):
|
||||
|
@ -499,7 +552,7 @@ def test_get_public_no_auth(client: TestClient):
|
|||
|
||||
r = client.get('/exercices/public')
|
||||
print(r.json())
|
||||
assert r.json() == [{**public1, 'is_author': False},
|
||||
assert r.json()['items'] == [{**public1, 'is_author': False},
|
||||
{**public2, 'is_author': False}]
|
||||
|
||||
|
||||
|
@ -508,7 +561,7 @@ def test_get_exo_no_auth(client: TestClient):
|
|||
exo = test_add_tags(client, user={'token': token, 'username': "lilian"})
|
||||
|
||||
r = client.get('/exercice/' + exo['id_code'])
|
||||
assert r.json() == {**exo, "tags": [], 'is_author': False}
|
||||
assert r.json()['items'] == {**exo, "tags": [], 'is_author': False}
|
||||
|
||||
|
||||
def test_get_exo_no_auth_private(client: TestClient):
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
plugins: ['svelte3', '@typescript-eslint'],
|
||||
ignorePatterns: ['*.cjs'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
}
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
|
@ -0,0 +1 @@
|
|||
engine-strict=true
|
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
|
@ -1,34 +1,38 @@
|
|||
## Usage
|
||||
# create-svelte
|
||||
|
||||
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
|
||||
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
$ npm install # or pnpm install or yarn install
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
|
||||
## Developing
|
||||
|
||||
## Available Scripts
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
In the project directory, you can run:
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
### `npm dev` or `npm start`
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
Runs the app in the development mode.<br>
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
## Building
|
||||
|
||||
The page will reload if you make edits.<br>
|
||||
To create a production version of your app:
|
||||
|
||||
### `npm run build`
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Builds the app for production to the `dist` folder.<br>
|
||||
It correctly bundles Solid in production mode and optimizes the build for the best performance.
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br>
|
||||
Your app is ready to be deployed!
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
{
|
||||
"name": "vite-template-solid",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"sass": "^1.54.3",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.0",
|
||||
"vite-plugin-solid": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@solidjs/meta": "^0.28.0",
|
||||
"@solidjs/router": "^0.4.2",
|
||||
"axios": "^0.27.2",
|
||||
"chroma-js": "^2.4.2",
|
||||
"emotion-solid": "^1.1.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"solid-forms": "^0.4.5",
|
||||
"solid-icons": "^1.0.1",
|
||||
"solid-js": "^1.4.7",
|
||||
"solid-styled-components": "^0.28.4",
|
||||
"solid-styled-jsx": "^0.27.1",
|
||||
"solid-toast": "^0.3.4",
|
||||
"styled-jsx": "^3.4.4"
|
||||
}
|
||||
"name": "frontend",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"test:unit": "vitest",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@sveltejs/adapter-auto": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^2.9.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.0",
|
||||
"vitest": "^0.25.3"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173
|
||||
},
|
||||
testDir: 'tests'
|
||||
};
|
||||
|
||||
export default config;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,73 +0,0 @@
|
|||
import { Component, createSignal } from "solid-js";
|
||||
import Section from "./section";
|
||||
import { FaSolidLock } from "solid-icons/fa";
|
||||
import TextField from "../forms/TextField";
|
||||
import { createStore } from "solid-js/store";
|
||||
import TextField2 from "../forms/TextField2";
|
||||
import styles from "../../styles/auth/changePassword.module.scss";
|
||||
import { Modal } from "../Modal";
|
||||
import { DeleteConfirm } from "./DeleteConfirm";
|
||||
import { update_password } from "../../requests/auth.requests.js";
|
||||
import { useLoginPopup } from "../../context/loginPopUp.context.jsx";
|
||||
import { useForm } from "../../hooks/useForm.js";
|
||||
export const ChangePasswordForm: Component = () => {
|
||||
const [fields, setFields] = createStore({
|
||||
password: "",
|
||||
password_confirm: "",
|
||||
});
|
||||
const { form, setFieldsErrors, getFieldController } = useForm({
|
||||
password: "",
|
||||
password_confirm: "",
|
||||
});
|
||||
const loginPopup = useLoginPopup();
|
||||
return (
|
||||
<div>
|
||||
<Section name="Sécurité" icon={<FaSolidLock />}>
|
||||
<form class={styles.form}>
|
||||
<TextField2
|
||||
placeholder="Mot de passe..."
|
||||
control={getFieldController("password")}
|
||||
type="password"
|
||||
/>
|
||||
<TextField2
|
||||
placeholder="Confirmation"
|
||||
control={getFieldController("password_confirm")}
|
||||
type="password"
|
||||
/>
|
||||
</form>
|
||||
</Section>
|
||||
<button
|
||||
class={`exo-btn ${styles.submit}`}
|
||||
onclick={() => {
|
||||
var data = new URLSearchParams({
|
||||
password: form.password,
|
||||
password_confirm: form.password_confirm,
|
||||
});
|
||||
update_password(data)
|
||||
.then((r) => {
|
||||
console.log(r);
|
||||
setFieldsErrors({});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("r", err);
|
||||
var errs = err.response.data.detail;
|
||||
if (errs == "Fresh token required") {
|
||||
loginPopup.popup(() => {
|
||||
update_password(data).catch(() => {
|
||||
var errs = err.response.data.detail;
|
||||
if (err.response.status == 422) {
|
||||
setFieldsErrors(errs);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (err.response.status == 422) {
|
||||
setFieldsErrors(errs);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Modifier mon mot de passe
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,53 +0,0 @@
|
|||
import { Component, createSignal } from "solid-js";
|
||||
import styles from "../../styles/auth/deleteConfirm.module.scss";
|
||||
import TextField2 from "../forms/TextField2";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import {
|
||||
delete_user, revoke_all
|
||||
} from "../../requests/auth.requests.js";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
export const DeleteConfirm: Component = () => {
|
||||
const [password, setPassword] = createSignal("");
|
||||
const [error, setError] = createSignal("");
|
||||
|
||||
const navigate = useNavigate()
|
||||
return (
|
||||
<div class={styles.main}>
|
||||
<h1 class={styles.title}>Confirmer la suppression</h1>
|
||||
<p>Vous êtes sur le point de supprimer votre compte.</p>
|
||||
<p>Toutes vos salles seront supprimées.</p>
|
||||
<p>Cette action est irréversible ! </p>
|
||||
<TextField2
|
||||
control={{
|
||||
error: error(),
|
||||
value: password(),
|
||||
setValue: (v) => {
|
||||
setPassword(v);
|
||||
},
|
||||
}}
|
||||
placeholder="Entrez votre mot de passe pour confirmer"
|
||||
type="password"
|
||||
/>
|
||||
<button
|
||||
class={`exo-btn ${styles.delete}`}
|
||||
onclick={() => {
|
||||
var token = localStorage.getItem("token");
|
||||
if (token != null) {
|
||||
var decoded = jwtDecode<{sub:string}>(token);
|
||||
var username = decoded.sub
|
||||
|
||||
var data = new URLSearchParams({ 'username': username, password: password() })
|
||||
delete_user(data).then(() => {
|
||||
revoke_all()
|
||||
navigate('/login')
|
||||
}).catch(() => {
|
||||
setError("Erreur lors de l'authentification")
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
Supprimer mon compte
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,100 +0,0 @@
|
|||
import {
|
||||
withControl,
|
||||
createFormGroup,
|
||||
createFormControl,
|
||||
IFormGroup,
|
||||
IFormControl,
|
||||
} from "solid-forms";
|
||||
import TextField from "../forms/TextField";
|
||||
import styles from "../../styles/auth/editUser.module.scss";
|
||||
import { useAuth } from "../../context/auth.context.jsx";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import TextField2 from "../forms/TextField2";
|
||||
import { Component, createEffect } from "solid-js";
|
||||
import Section from "./section";
|
||||
|
||||
import { FaSolidUser } from "solid-icons/fa";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { update_user, revoke_all } from "../../requests/auth.requests.js";
|
||||
import { useForm } from "../../hooks/useForm.js";
|
||||
import { User } from "../../types/auth.type";
|
||||
|
||||
|
||||
const EditUserForm: Component<{ user: User | undefined }> = (props) => {
|
||||
const { form, setFieldsErrors, getFieldController, setFieldValue } = useForm({
|
||||
email: "",
|
||||
username: "",
|
||||
firstname: "",
|
||||
name: "",
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
setFieldValue("username", props.user?.username);
|
||||
setFieldValue("email", props.user?.email);
|
||||
setFieldValue("firstname", props.user?.firstname);
|
||||
setFieldValue("name", props.user?.name);
|
||||
});
|
||||
return (
|
||||
<div class={styles.main}>
|
||||
<div>
|
||||
<Section name="Infos" icon={<FaSolidUser size={24} />}>
|
||||
<form class={styles.form}>
|
||||
<TextField2
|
||||
label="Username"
|
||||
placeholder="Username..."
|
||||
control={getFieldController("username")}
|
||||
type="text"
|
||||
/>
|
||||
<TextField2
|
||||
label="Email"
|
||||
placeholder="Email..."
|
||||
control={getFieldController("email")}
|
||||
type="text"
|
||||
/>
|
||||
<TextField2
|
||||
label="Prénom"
|
||||
placeholder="Prénom..."
|
||||
control={getFieldController("firstname")}
|
||||
type="text"
|
||||
/>
|
||||
<TextField2
|
||||
label="Nom"
|
||||
placeholder="Nom..."
|
||||
control={getFieldController("name")}
|
||||
type="text"
|
||||
/>
|
||||
</form>
|
||||
</Section>
|
||||
</div>
|
||||
<button
|
||||
onclick={() => {
|
||||
var data = new URLSearchParams({
|
||||
email: form.email == null ? "" : form.email,
|
||||
username: form.username,
|
||||
firstname: form.firstname == null ? "" : form.firstname,
|
||||
name: form.name == null ? "" : form.name,
|
||||
});
|
||||
update_user(data)
|
||||
.then((r) => {
|
||||
console.log(r);
|
||||
revoke_all();
|
||||
localStorage.setItem("token", r.access_token);
|
||||
localStorage.setItem("refresh token", r.refresh_token);
|
||||
setFieldsErrors({});
|
||||
})
|
||||
.catch((err) => {
|
||||
var errs = err.response.data.detail;
|
||||
if (err.response.status == 422) {
|
||||
setFieldsErrors(errs);
|
||||
}
|
||||
});
|
||||
}}
|
||||
class={`${styles.submit} exo-btn`}
|
||||
>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditUserForm;
|
|
@ -1,50 +0,0 @@
|
|||
import { withControl, createFormGroup, createFormControl } from "solid-forms";
|
||||
import TextField from "../forms/TextField";
|
||||
import styles from "../../styles/auth/login.module.scss";
|
||||
import { dispatchAuth } from "../../context/auth.context.jsx";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { useLoginPopup } from "../../context/loginPopUp.context.jsx";
|
||||
import { useForm } from "../../hooks/useForm.js";
|
||||
import { login_request } from "../../requests/auth.requests.js";
|
||||
import { useAuth } from "../../context/auth.context.jsx";
|
||||
|
||||
export const LoginForm: Component = () => {
|
||||
const auth = useAuth();
|
||||
const { form, setFieldsErrors, getFieldController } = useForm({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
return (
|
||||
<form
|
||||
class={styles["main"]}
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
var data = new URLSearchParams({
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
});
|
||||
auth.login(form.username, form.password)
|
||||
.catch((err) => {
|
||||
var errs = err.response.data.detail;
|
||||
setFieldsErrors(errs);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Username"
|
||||
control={getFieldController("username")}
|
||||
type="text"
|
||||
required={true}
|
||||
/>
|
||||
<TextField
|
||||
label="Password"
|
||||
control={getFieldController("password")}
|
||||
type="password"
|
||||
required={true}
|
||||
/>
|
||||
<button class={`exo-btn ${styles.submit}`}>Se connecter</button>
|
||||
</form>
|
||||
);
|
||||
};
|
|
@ -1,57 +0,0 @@
|
|||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component } from "solid-js";
|
||||
import { useForm } from "../../hooks/useForm.js";
|
||||
import { register_request } from "../../requests/auth.requests.js";
|
||||
import TextField from "../forms/TextField";
|
||||
import styles from '../../styles/auth/login.module.scss';
|
||||
import { useAuth } from "../../context/auth.context.jsx";
|
||||
|
||||
export const RegisterForm: Component = () => {
|
||||
const { form, setFieldValue, setFieldsErrors, getFieldController } = useForm({
|
||||
username: "",
|
||||
password: "",
|
||||
password_confirm: "",
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
const auth = useAuth()
|
||||
return (
|
||||
<form
|
||||
class={styles.main}
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
var data = new URLSearchParams({
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
password_confirm: form.password_confirm,
|
||||
});
|
||||
auth.signup(form.username, form.password, form.password_confirm)
|
||||
.catch((err) => {
|
||||
var errs = err.response.data.detail;
|
||||
if (err.response.status == 422) {
|
||||
setFieldsErrors(errs);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
type="text"
|
||||
control={getFieldController("username")}
|
||||
label="Username"
|
||||
required={true}
|
||||
/>
|
||||
<TextField
|
||||
type="password"
|
||||
control={getFieldController("password")}
|
||||
label="Mot de passe"
|
||||
required={true}
|
||||
/>
|
||||
<TextField
|
||||
type="password"
|
||||
control={getFieldController("password_confirm")}
|
||||
label="Confirmation"
|
||||
required={true}
|
||||
/>
|
||||
<button class={`exo-btn ${styles.submit}`}>S'inscrire</button>
|
||||
</form>
|
||||
);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { Component, JSX } from "solid-js";
|
||||
import styles from '../../styles/auth/section.module.scss';
|
||||
const Section: Component<{name:string, icon: JSX.Element, children: JSX.Element}> = (props) => {
|
||||
return <div class={styles['main']}>
|
||||
<h2 class={`marginb-p1 ${styles.title}`}>{props.icon}{' '}{props.name}</h2>
|
||||
<div class={styles["child"]}>{props.children}</div>
|
||||
{/* {validate && validateMsg && <button class={parseclass(['exo-btn', styles['btn']])} onClick={validate}>{validateMsg}</button>} */}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Section
|
|
@ -1,147 +0,0 @@
|
|||
import { useNavigate } from "@solidjs/router";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import { useContext } from "solid-js";
|
||||
import { createSignal } from "solid-js";
|
||||
import { Show } from "solid-js";
|
||||
import { createEffect } from "solid-js";
|
||||
import { createContext } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import toast from "solid-toast";
|
||||
import { setSourceMapRange } from "typescript";
|
||||
import {
|
||||
check_access,
|
||||
get_user,
|
||||
login_request,
|
||||
refresh_request,
|
||||
register_request,
|
||||
revoke_access,
|
||||
revoke_refresh,
|
||||
} from "../requests/auth.requests.js";
|
||||
import { useLoginPopup } from "./loginPopUp.context.jsx";
|
||||
import { AiFillAndroid } from 'solid-icons/ai'
|
||||
import { useNotification } from "./notification.context.jsx";
|
||||
const AuthContext = createContext();
|
||||
|
||||
|
||||
const jwt_expire_check = (token) => {
|
||||
var { exp } = jwtDecode(token);
|
||||
return Date.now() >= exp * 1000;
|
||||
};
|
||||
export function AuthProvider(props) {
|
||||
const [username, setUsername] = createSignal(null);
|
||||
const [isAuthenticated, setAuthenticated] = createSignal(false);
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
const [initialLoading, setInitialLoading] = createSignal(true);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const notification = useNotification()
|
||||
const popup = useLoginPopup();
|
||||
|
||||
createEffect(() => {
|
||||
var token = localStorage.getItem("token");
|
||||
var refresh = localStorage.getItem("refresh_token");
|
||||
if (token != null) {
|
||||
var is_expired = jwt_expire_check(token);
|
||||
if (is_expired && refresh != null) {
|
||||
refresh_request(refresh)
|
||||
.then((r) => {
|
||||
localStorage.setItem("token", r.access_token);
|
||||
return r.access_token;
|
||||
})
|
||||
.then((t) => {
|
||||
setAuthenticated(true);
|
||||
var { sub } = jwtDecode(t);
|
||||
setUsername(sub);
|
||||
}).catch((err) => {
|
||||
if ( !!err.response.status && err.reponse.status == 422) {
|
||||
localStorage.removeItem("refresh_token");
|
||||
localStorage.removeItem("token");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
check_access(token)
|
||||
.then(() => {
|
||||
setAuthenticated(true);
|
||||
var { sub } = jwtDecode(token);
|
||||
setUsername(sub);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.reponse.status == 422) {
|
||||
localStorage.removeItem("refresh_token");
|
||||
localStorage.removeItem("token");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
setInitialLoading(false);
|
||||
});
|
||||
|
||||
const login = (username, password) => {
|
||||
var data = new URLSearchParams({ username, password });
|
||||
return login_request(data).then((r) => {
|
||||
localStorage.setItem("token", r.access_token);
|
||||
localStorage.setItem("refresh_token", r.refresh_token);
|
||||
|
||||
var decoded = jwtDecode(r.access_token);
|
||||
setAuthenticated(true);
|
||||
setUsername(decoded.sub);
|
||||
|
||||
if (popup.active == true) {
|
||||
popup.next();
|
||||
} else {
|
||||
navigate("/dashboard");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const signup = (username, password, password2) => {
|
||||
var data = new URLSearchParams({
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirm: password2,
|
||||
});
|
||||
register_request(data).then((r) => {
|
||||
localStorage.setItem("token", r.access_token);
|
||||
localStorage.setItem("refresh_token", r.refresh_token);
|
||||
|
||||
var decoded = jwtDecode(r.access_token);
|
||||
setAuthenticated(true);
|
||||
setUsername(decoded.sub);
|
||||
|
||||
navigate("/dashboard");
|
||||
});
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
var token = localStorage.getItem("token");
|
||||
var refresh = localStorage.getItem("refresh_token");
|
||||
revoke_access(token)
|
||||
.then(() => {
|
||||
return revoke_refresh(refresh);
|
||||
})
|
||||
.then(() => {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("refresh_token");
|
||||
setUsername(null)
|
||||
setAuthenticated(false)
|
||||
navigate("/login");
|
||||
notification.success('Déconnexion', "Vous n'êtes plus connecté !")
|
||||
});
|
||||
};
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
username: username,
|
||||
isAuthenticated: isAuthenticated,
|
||||
loading: loading,
|
||||
login,
|
||||
signup,
|
||||
logout,
|
||||
}}
|
||||
>
|
||||
<Show when={!initialLoading()}>{props.children}</Show>
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useAuth = () => useContext(AuthContext);
|
|
@ -0,0 +1,7 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('sum test', () => {
|
||||
it('adds 1 + 2 to equal 3', () => {
|
||||
expect(1 + 2).toBe(3);
|
||||
});
|
||||
});
|
|
@ -1,126 +0,0 @@
|
|||
import { authInstance } from "../apis/auth.instance.js"
|
||||
|
||||
export const login_request= (data)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/login",
|
||||
method: "post",
|
||||
data: data,
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
export const register_request= (data)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/register",
|
||||
method: "post",
|
||||
data: data,
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
})
|
||||
.then((r) => r.data).catch((err)=> {throw err});
|
||||
}
|
||||
export const refresh_request= (token)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/refresh",
|
||||
method: "post",
|
||||
headers: { "Authorization": "Bearer " + token },
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
export const revoke_access = (token)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/access-revoke",
|
||||
method: "delete",
|
||||
headers: { "Authorization": "Bearer " + token },
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
export const check_access = (token)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/check-access",
|
||||
method: "post",
|
||||
headers: { "Authorization": "Bearer " + token },
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
export const revoke_refresh = (token)=> {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/refresh-revoke",
|
||||
method: "delete",
|
||||
headers: { "Authorization": "Bearer " + token },
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
export const revoke_all = () => {
|
||||
revoke_access(localStorage.getItem('token'))
|
||||
revoke_refresh(localStorage.getItem('refresh_token'))
|
||||
}
|
||||
|
||||
export const get_user = () => {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/user",
|
||||
method: "get",
|
||||
headers: {
|
||||
...(localStorage.getItem('token') != null && {Authorization: "Bearer " + localStorage.getItem('token')}),
|
||||
},
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
export const update_user = (data) => {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/user",
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
...(localStorage.getItem("token") != null && {
|
||||
Authorization: "Bearer " + localStorage.getItem("token"),
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const update_password = (data) => {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/user/password",
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
...(localStorage.getItem("token") != null && {
|
||||
Authorization: "Bearer " + localStorage.getItem("token"),
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
||||
|
||||
export const delete_user = (data) => {
|
||||
return authInstance
|
||||
.request({
|
||||
url: "/user/",
|
||||
method: "delete",
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
...(localStorage.getItem("token") != null && {
|
||||
Authorization: "Bearer " + localStorage.getItem("token"),
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((r) => r.data);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
|
@ -1,43 +0,0 @@
|
|||
export type Tag = {
|
||||
label: string;
|
||||
color: string;
|
||||
id_code: string;
|
||||
};
|
||||
|
||||
export type ExerciceShort = {
|
||||
id_code: string;
|
||||
name: string;
|
||||
consigne: string;
|
||||
pdfSupport: boolean;
|
||||
csvSupport: boolean;
|
||||
webSupport: boolean;
|
||||
tags: Array<Tag>;
|
||||
|
||||
examples: { type: string; data: Array<{ calcul: string }> };
|
||||
};
|
||||
|
||||
export type Author = {
|
||||
username: string;
|
||||
};
|
||||
|
||||
type supports = {
|
||||
pdf: boolean;
|
||||
csv: boolean;
|
||||
web: boolean;
|
||||
};
|
||||
export type Exercice = {
|
||||
id_code: string;
|
||||
name: string;
|
||||
consigne: string;
|
||||
supports: supports;
|
||||
tags: Array<Tag>;
|
||||
is_author: boolean;
|
||||
examples: {
|
||||
type: string;
|
||||
data: Array<{ calcul: string; correction: string }>;
|
||||
};
|
||||
private: boolean;
|
||||
exo_source_name: string;
|
||||
author: Author;
|
||||
origin: { id_code: string } | null;
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,15 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite' |