Migrate from PHP to independent backend #107

Merged
maxime.batista merged 15 commits from remove-php into master 1 year ago

Due to the second part of the SAE which requires the backend to be a standalone web service, this pull request removes all the php code, and let the front communicate with the server using 100% of API calls.

API standardisation.

The current front uses the dotnet implementation of the web service. You can change the endpoint in the .env file :

VITE_API_ENDPOINT=http://localhost:5254 <-- place your api endpoint here
VITE_BASE=

Error handling

Any errors returned by the backend MUST follow the given simple rule :
One json object is returned, with the keys corresponding to the type of the error, and the value corresponding to its message, for example :

{
    "Email": [
        "The Email field is not a valid e-mail address."
    ],
	// If there is multiple errors of the same type,
	// simply group the errors by types and use an array of messages instead.
    "Username": [
        "Username length must be between 3 and 256",
        "Username should only contain numbers, letters, accents, spaces, _ and - characters."
    ]
}

Authentication

Ask a token

Use /auth/token route to generate a token based on the given credentials :

POST /auth/token:
--- payload
{
    email: string,
    password: string
}
--- response
{
    token: string,
    expirationDate: date
}

If the credentials are invalid, the API must return a 401 (unauthorized) code.

Make a request that needs auth

For requests that needs to be authenticated, the token must be passed within the Authorization Header.

Token prolongation

For each request that needs to be authenticated, they'll return a new token with an elongated expiration date.

The new token is sent by the server and stored in the response header ̀Next-Authorization, its expiration date is stored in the response header named Next-Authorization-Expiration-Date`.

Due to the second part of the SAE which requires the backend to be a standalone web service, this pull request removes all the php code, and let the front communicate with the server using 100% of API calls. # API standardisation. The current front uses the [dotnet implementation of the web service](https://codefirst.iut.uca.fr/git/IQBall/Dotnet-WebAPI). You can change the endpoint in the .env file : ``` VITE_API_ENDPOINT=http://localhost:5254 <-- place your api endpoint here VITE_BASE= ``` ## Error handling Any errors returned by the backend __MUST__ follow the given simple rule : One json object is returned, with the keys corresponding to the type of the error, and the value corresponding to its message, for example : ```json { "Email": [ "The Email field is not a valid e-mail address." ], // If there is multiple errors of the same type, // simply group the errors by types and use an array of messages instead. "Username": [ "Username length must be between 3 and 256", "Username should only contain numbers, letters, accents, spaces, _ and - characters." ] } ``` ## Authentication ### Ask a token Use `/auth/token` route to generate a token based on the given credentials : ```http POST /auth/token: --- payload { email: string, password: string } --- response { token: string, expirationDate: date } ``` If the credentials are invalid, the API must return a 401 (unauthorized) code. ### Make a request that needs auth For requests that needs to be authenticated, the token must be passed within the `Authorization` Header. ### Token prolongation For each request that needs to be authenticated, they'll return a new token with an elongated expiration date. The new token is sent by the server and stored in the response header ̀Next-Authorization`, its expiration date is stored in the response header named `Next-Authorization-Expiration-Date`.
maxime.batista added 8 commits 1 year ago
maxime.batista requested review from clement.freville2 1 year ago
maxime.batista requested review from yanis.dahmane-bounoua 1 year ago
maxime.batista requested review from vivien.dufour 1 year ago
maxime.batista requested review from mael.daim 1 year ago
maxime.batista requested review from samuel.berion 1 year ago
clement.freville2 requested changes 1 year ago
module.exports = {

Choose either a .eslintrc.cjs or a .eslintrc.js file.

Choose either a `.eslintrc.cjs` or a `.eslintrc.js` file.
maxime.batista marked this conversation as resolved
# How to run the project on my local computer
1. Use phpstorm to run a local php server:

Is it still relevant?

Is it still relevant?
maxime.batista marked this conversation as resolved
}
"name": "iqball_web",
"version": "0.1.0",
"private": true,

Add "type": "module" in new applications.

Add [`"type": "module"`](https://vitejs.dev/guide/migration.html#deprecate-cjs-node-api) in new applications.
maxime.batista marked this conversation as resolved
src/App.tsx Outdated
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom"
import loadable from "@loadable/component"

You don't need an external library to lazy load.
Just use the lazy={() => import()} attribute with react-router.

You don't need an external library to lazy load. Just use the [`lazy={() => import()}`](https://reactrouter.com/en/main/route/lazy) attribute with *react-router*.
maxime.batista marked this conversation as resolved
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is passing Details
0a451334d2
apply suggestions
maxime.batista requested review from clement.freville2 1 year ago
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is passing Details
3beff7e972
remove debug logs and format
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build encountered an error Details
a29192c0d4
fix ci
maxime.batista force-pushed remove-php from a29192c0d4 to 423cb03c4b 1 year ago
maxime.batista force-pushed remove-php from 423cb03c4b to 2478b4a403 1 year ago
maxime.batista force-pushed remove-php from 2478b4a403 to d25fe38a7c 1 year ago
maxime.batista force-pushed remove-php from d25fe38a7c to 9eca18dc0b 1 year ago
maxime.batista force-pushed remove-php from 9eca18dc0b to 78f8c35b03 1 year ago
maxime.batista force-pushed remove-php from 78f8c35b03 to 5de0ba76f1 1 year ago
maxime.batista force-pushed remove-php from 5de0ba76f1 to 15ace155e2 1 year ago
maxime.batista force-pushed remove-php from 15ace155e2 to f42303f363 1 year ago
maxime.batista force-pushed remove-php from f42303f363 to 7c3c9340ab 1 year ago
maxime.batista force-pushed remove-php from 7c3c9340ab to 97caca533b 1 year ago
maxime.batista force-pushed remove-php from 97caca533b to 98aef3fd3a 1 year ago
maxime.batista force-pushed remove-php from 98aef3fd3a to 07f89c8ed6 1 year ago
maxime.batista force-pushed remove-php from 07f89c8ed6 to c5b59e5c1f 1 year ago
maxime.batista force-pushed remove-php from c5b59e5c1f to 56bcf3e4b2 1 year ago
maxime.batista force-pushed remove-php from 56bcf3e4b2 to 8fc8aa6917 1 year ago
clement.freville2 requested changes 1 year ago
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",

You are missing rules from the previous file:

        "plugin:react/recommended",
        "plugin:react/jsx-runtime",
You are missing rules from the previous file: ```js "plugin:react/recommended", "plugin:react/jsx-runtime", ```
maxime.batista marked this conversation as resolved
ci/.drone.yml Outdated
-
- BASE="/IQBall/$DRONE_BRANCH/public" OUTPUT=/outputs /root/.local/bin/moshell ci/build_react.msh
- sed -Ei "s/VITE_BASE=/VITE_BASE=\\/${DRONE_BRANCH}/g" .env
- npm run build -- --base=/$DRONE_BRANCH

You're defining the base url two times, just use the --base argument and you are good to go. import.meta.env.BASE_URL will cover your eventual needs to have the current path.

You're defining the base url two times, just use the [`--base` argument](https://vitejs.dev/guide/build#public-base-path) and you are good to go. `import.meta.env.BASE_URL` will cover your eventual needs to have the current path.
maxime.batista marked this conversation as resolved
src/Fetcher.ts Outdated
import { getSession, saveSession, Session } from "./api/session.ts"
export function redirect(url: string) {
location.pathname = BASE + url
-    location.pathname = BASE + url
+    location.href = BASE + url

Note that you are bypassing client-side routing here by using the browser API.

```diff - location.pathname = BASE + url + location.href = BASE + url ``` Note that you are bypassing client-side routing here by using the browser API.
maxime.batista marked this conversation as resolved
}
export default function EditorPage({ guestMode }: EditorPageProps) {
const [tactic, setTactic] = useState<TacticDto>()
-    const [tactic, setTactic] = useState<TacticDto>()
+    const [tactic, setTactic] = useState<TacticDto | null>(() => {
+        if (guestMode) {
+            return {
+                id: -1,
+                courtType: "PLAIN",
+                content: '{"components": []}',
+                name: DEFAULT_TACTIC_NAME,
+            }
+        }
+        return null
+    })
```diff - const [tactic, setTactic] = useState<TacticDto>() + const [tactic, setTactic] = useState<TacticDto | null>(() => { + if (guestMode) { + return { + id: -1, + courtType: "PLAIN", + content: '{"components": []}', + name: DEFAULT_TACTIC_NAME, + } + } + return null + }) ```
maxime.batista marked this conversation as resolved
// import AccountSvg from "../assets/account.svg?react"
import { BASE } from "../Constants"
import { getSession } from "../api/session.ts"
import { fetchAPIGet, redirect } from "../Fetcher.ts"
-import { fetchAPI, redirect } from "../Fetcher.ts"
+import { fetchAPI } from "../Fetcher.ts"
+import { redirect } from "react-router-dom"
```diff -import { fetchAPI, redirect } from "../Fetcher.ts" +import { fetchAPI } from "../Fetcher.ts" +import { redirect } from "react-router-dom" ```
maxime.batista marked this conversation as resolved
import { FormEvent, useRef, useState } from "react"
import { BASE } from "../Constants.ts"
import { fetchAPI, redirect } from "../Fetcher.ts"
-import { fetchAPI, redirect } from "../Fetcher.ts"
+import { fetchAPI } from "../Fetcher.ts"
+import { redirect } from "react-router-dom"
```diff -import { fetchAPI, redirect } from "../Fetcher.ts" +import { fetchAPI } from "../Fetcher.ts" +import { redirect } from "react-router-dom" ```
maxime.batista marked this conversation as resolved
const emailRef = useRef<HTMLInputElement>(null)
const passwordRef = useRef<HTMLInputElement>(null)
-    const emailRef = useRef<HTMLInputElement>(null)
-    const passwordRef = useRef<HTMLInputElement>(null)
-
```diff - const emailRef = useRef<HTMLInputElement>(null) - const passwordRef = useRef<HTMLInputElement>(null) - ```
maxime.batista marked this conversation as resolved
e.preventDefault()
const email = emailRef.current!.value
const password = passwordRef.current!.value
-        const email = emailRef.current!.value
-        const password = passwordRef.current!.value
+        const { email, password } = Object.fromEntries(
+            new FormData(e.target as HTMLFormElement),
+        );
```diff - const email = emailRef.current!.value - const password = passwordRef.current!.value + const { email, password } = Object.fromEntries( + new FormData(e.target as HTMLFormElement), + ); ```
maxime.batista marked this conversation as resolved
{
type: "Non autorisé",
messages: [
"L'adresse email ou le mot de passe sont invalide.",
-                        "L'adresse email ou le mot de passe sont invalide.", 
+                        "L'adresse email ou le mot de passe sont invalides.", 
```diff - "L'adresse email ou le mot de passe sont invalide.", + "L'adresse email ou le mot de passe sont invalides.", ```
maxime.batista marked this conversation as resolved
// import "../style/visualizer.css"
// import Court from "../assets/court/full_court.svg"
//
// // export default function Visualizer({ id, name }: { id: number; name: string }) {

Double comments? Wahh

Double comments? Wahh
maxime.batista marked this conversation as resolved
import AccountSvg from "../../assets/account.svg?react"
import "../../style/template/header.css"
import { useEffect, useState } from "react"
import { fetchAPIGet, redirect } from "../../Fetcher.ts"
-import { fetchAPI, redirect } from "../Fetcher.ts"
+import { fetchAPI } from "../Fetcher.ts"
+import { redirect } from "react-router-dom"
```diff -import { fetchAPI, redirect } from "../Fetcher.ts" +import { fetchAPI } from "../Fetcher.ts" +import { redirect } from "react-router-dom" ```
maxime.batista marked this conversation as resolved
include: "**/*.svg?react"
})
]
include: "**/*.svg?react",

That is already the default.

That is already the default.
maxime.batista marked this conversation as resolved
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is failing Details
6eacfc1ae2
apply suggestions
maxime.batista requested review from clement.freville2 1 year ago
clement.freville2 requested changes 1 year ago
clement.freville2 left a comment

Do not forget to run Prettier.

Do not forget to run Prettier.
"warn",
{ allowConstantExport: true },
],
},
+    settings: {
+        react: {
+            version: 'detect'
+        }
+    }

New plugins add new configuration settings.

```diff + settings: { + react: { + version: 'detect' + } + } ``` New plugins add new configuration settings.
maxime.batista marked this conversation as resolved
return (
<div>
<h1>{target.pathname} NOT FOUND !</h1>
<button onClick={() => (location.pathname = BASE + "/")}>
-            <button onClick={() => (location.pathname = BASE + "/")}>
+            <button onClick={() => redirect("/")}>

To go the extra mile, I would argue that if your button behaves like a link, then it is a a element, not a button. keywboard navigation and accessibility, you know it's a long story... (A link can perfectly have a button style while still being an a element)

```diff - <button onClick={() => (location.pathname = BASE + "/")}> + <button onClick={() => redirect("/")}> ``` To go the extra mile, I would argue that if your button behaves like a link, then it is a `a` element, not a `button`. *keywboard navigation and accessibility, you know it's a long story...* (A link can perfectly have a button style while still being an `a` element)
maxime.batista marked this conversation as resolved
<a href={BASE + "/register"} className="inscr">
Vous n'avez pas de compte ?
</a>
-                    <a href={BASE + "/register"} className="inscr">
+                    <Link to="register" className="inscr">
                         Vous n'avez pas de compte ?
-                    </a>
+                    </Link>

<Link>

```diff - <a href={BASE + "/register"} className="inscr"> + <Link to="register" className="inscr"> Vous n'avez pas de compte ? - </a> + </Link> ``` [`<Link>`](https://reactrouter.com/en/main/components/link)
maxime.batista marked this conversation as resolved
<a href={BASE + "/login"} className="inscr">
Vous avez déjà un compte ?
</a>
-                    <a href={BASE + "/login"} className="inscr">
+                    <Link to={"login"} className="inscr">
                         Vous avez déjà un compte ?
-                    </a>
+                    </Link>
```diff - <a href={BASE + "/login"} className="inscr"> + <Link to={"login"} className="inscr"> Vous avez déjà un compte ? - </a> + </Link> ```
maxime.batista marked this conversation as resolved
maxime.batista force-pushed remove-php from 6eacfc1ae2 to 12daaeaf5c 1 year ago
maxime.batista force-pushed remove-php from 12daaeaf5c to 66ddf4cb00 1 year ago
maxime.batista force-pushed remove-php from 66ddf4cb00 to 5b13fbfbc8 1 year ago
maxime.batista force-pushed remove-php from 5b13fbfbc8 to edfc5368f9 1 year ago
maxime.batista force-pushed remove-php from edfc5368f9 to 1d1ac1e088 1 year ago
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is passing Details
bf5de96871
apply suggestions
maxime.batista force-pushed remove-php from bf5de96871 to b322ffecaa 1 year ago
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is passing Details
30c3e1e7b4
fix redirections
maxime.batista force-pushed remove-php from 30c3e1e7b4 to 19ee432c35 1 year ago
maxime.batista force-pushed remove-php from 19ee432c35 to bc313ae1da 1 year ago
maxime.batista force-pushed remove-php from bc313ae1da to 26127798e4 1 year ago
maxime.batista force-pushed remove-php from 26127798e4 to ee24f57fce 1 year ago
maxime.batista requested review from clement.freville2 1 year ago
maxime.batista force-pushed remove-php from ee24f57fce to defd53c9bc 1 year ago
clement.freville2 requested changes 1 year ago
master

Just default it to master?

Just default it to master?
Poster

No because we could start a new features that would also needs to requires modification of the API, and we won't apply thoses changes directly on the implementation's master branch

No because we could start a new features that would also needs to requires modification of the API, and we won't apply thoses changes directly on the implementation's master branch

Yeah, but the whole reason why this file is here is because of the multi repo architecture. Yes, you will need some way to link a branch between the two repositories, but it seems strange to have a file dedicated to that. I tend to prefer not to have random files outside the CI files. They are not related to the code.

Yeah, but the whole reason why this file is here is because of the multi repo architecture. Yes, you will need some way to link a branch between the two repositories, but it seems strange to have a file dedicated to that. I tend to prefer not to have random files outside the CI files. They are not related to the code.
src/App.tsx Outdated
export default function App() {
return (
<div id="app">
<BrowserRouter basename={BASE}>

This router uses lazy components, without any Suspense element to show when loading. Your current workaround is to wrap all navigation changes in a transition block. It becomes very verbose.
Either:

  • Use small components directly (such as the login, the register and the 404 page)
  • Wrap every lazy component in a <Suspense>
  • Let React Router use transitions directly by using a future option.
  • Use React Router lazy routes implementation (requires a data router).
This router uses lazy components, without any `Suspense` element to show when loading. Your current workaround is to wrap all navigation changes in a [transition](https://react.dev/reference/react/startTransition) block. It becomes very verbose. Either: - Use small components directly (such as the login, the register and the 404 page) - Wrap every lazy component in a [`<Suspense>`](https://react.dev/reference/react/Suspense) - Let React Router use transitions directly by using a [future option](https://reactrouter.com/en/6.22.1/guides/api-development-strategy#react-router-future-flags). - Use React Router [lazy routes](https://reactrouter.com/en/6.22.1/route/lazy) implementation (requires a data router).
maxime.batista marked this conversation as resolved
if (response.status === 401) startTransition(() => {
// if unauthorized
navigate("/login")
return

This return doesn't have any effect since it's the last statement in the transition.

This `return` doesn't have any effect since it's the last statement in the transition.
maxime.batista marked this conversation as resolved
const username = usernameField.current!.value
const password = passwordField.current!.value
const confirmpassword = confirmpasswordField.current!.value
const email = emailField.current!.value
-        const username = usernameField.current!.value
-        const password = passwordField.current!.value
-        const confirmpassword = confirmpasswordField.current!.value
-        const email = emailField.current!.value
+        const { username, password, confirmpassword, email } =
+            Object.fromEntries(new FormData(e.target as HTMLFormElement))
```diff - const username = usernameField.current!.value - const password = passwordField.current!.value - const confirmpassword = confirmpasswordField.current!.value - const email = emailField.current!.value + const { username, password, confirmpassword, email } = + Object.fromEntries(new FormData(e.target as HTMLFormElement)) ```
maxime.batista marked this conversation as resolved
maxime.batista added 1 commit 1 year ago
continuous-integration/drone/push Build is passing Details
73dfa0077d
apply suggestions
samuel.berion approved these changes 1 year ago
clement.freville2 approved these changes 1 year ago
maxime.batista merged commit a1ac1fe72c into master 1 year ago
maxime.batista deleted branch remove-php 1 year ago

Reviewers

mael.daim was requested for review 1 year ago
yanis.dahmane-bounoua was requested for review 1 year ago
vivien.dufour was requested for review 1 year ago
samuel.berion approved these changes 1 year ago
clement.freville2 approved these changes 1 year ago
continuous-integration/drone/push Build is passing
The pull request has been merged as a1ac1fe72c.
Sign in to join this conversation.
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date

No due date set.

Dependencies

No dependencies set.

Reference: IQBall/Application-Web#107
Loading…
There is no content yet.