Compare commits

...
Sign in to create a new pull request.

27 commits

Author SHA1 Message Date
1d207b56a6 Merge pull request 'Updated backend' (#15) from back_end into main
Reviewed-on: #15
2024-12-06 06:00:07 +00:00
6228e839d7 Updated backend 2024-12-06 07:00:04 +01:00
iMax
75c29c719a Hot Fix: Debug for build 2024-12-06 06:57:50 +01:00
iMax
2b71492587 Hot Fix: Ajout page chaos + Fix mystère 2024-12-06 06:53:41 +01:00
SwimJéjé
0508ddaa7f Merge branch 'main' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into chaos_page 2024-12-06 06:47:59 +01:00
SwimJéjé
b2cb9ca5c8 Merge branch 'chaos_page' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into chaos_page 2024-12-06 06:47:19 +01:00
SwimJéjé
1e95438b2c Fin Chaos Page 2024-12-06 06:44:06 +01:00
iMax
0fa1a9b6f3 Hot Fix: Reactor routeur 2024-12-06 06:34:45 +01:00
iMax
6405c6db09 Merge branch 'main' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into chaos_page 2024-12-06 06:09:06 +01:00
iMax
f8ffe3e16f HARRRRRRD HOT FIX: Remove all error and warning 2024-12-06 06:08:00 +01:00
iMax
d685e00bc3 Merge branch 'main' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into main 2024-12-06 05:59:59 +01:00
iMax
3f9617549d Hot fix : NavBar ClassName 2024-12-06 05:59:30 +01:00
iMax
0e31e90af9 Création d'un formulaire basique 2024-12-06 05:58:13 +01:00
iMax
6e77233ac0 Merge branch 'chaos_page' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into chaos_page 2024-12-06 05:51:53 +01:00
1ec6b1b0da Merge pull request 'Updated docker-compose.yml' (#13) from back_end into main
Reviewed-on: #13
2024-12-06 04:47:59 +00:00
iMax
4682f8a61e Hot fix - Marker.tsx 2024-12-06 05:46:34 +01:00
b8fa053ca3 Updated docker-compose.yml 2024-12-06 05:43:21 +01:00
SwimJéjé
53853dcce5 Merge branch 'main' of ssh://git.leizour.fr:222/RedCrab/projet-nuitinfo-2024 into chaos_page 2024-12-06 05:40:57 +01:00
SwimJéjé
ea2bb7ac3c Second commit 2024-12-06 05:38:08 +01:00
SwimJéjé
e1bcb4b022 Chaos Commit 2024-12-06 05:35:47 +01:00
iMax
cbae2746fd Merge branch 'main_page' into main 2024-12-06 05:07:10 +01:00
b252122272 Merged nia 2024-12-06 04:44:34 +01:00
467d560b15 Merge pull request 'back_end_docker' (#9) from back_end_docker into back_end
Reviewed-on: #9
2024-12-06 03:36:57 +00:00
ac3fc5c391 Updated ports 2024-12-06 04:34:40 +01:00
d6f2d6cc09 VALIDER PAR OLIVIER : Merge pull request 'better api and yyyy/mm/dd format for dates in the db' (#8) from back_end_id into main
Reviewed-on: #8
2024-12-06 02:58:19 +00:00
e42b81539f Merge branch 'main' into back_end_id 2024-12-06 02:57:42 +00:00
ec2322f04c better api and yyyy/mm/dd format for dates in the db 2024-12-06 03:52:12 +01:00
19 changed files with 289 additions and 42 deletions

View file

@ -12,7 +12,9 @@ pub fn init() -> sqlite::Result<()> {
"CREATE TABLE IF NOT EXISTS articles (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
subTitle TEXT,
auteur TEXT,
edited_at DATE_FORMAT('now', '%YYYY-%mm-%dd'),
published_at DATE_FORMAT('now', '%YYYY-%mm-%dd'),
content TEXT NOT NULL
)",
)?;

View file

@ -1,7 +1,7 @@
mod create_db;
use create_db::init;
use actix_web::{App, HttpServer, get, Responder, HttpResponse, http::header::ContentType};
use actix_web::{App, web, HttpServer, get, Responder, HttpResponse, http::header::ContentType};
use actix_files::Files;
use serde_json::json;
use sqlite::{Connection, State};
@ -11,9 +11,9 @@ use serde::{Serialize, Deserialize};
struct Article {
id: i64,
title: String,
auteur: Option<String>,
edited_at: Option<String>,
published_at: Option<String>,
auteur: String,
edited_at: String,
published_at: String,
content: String,
}
@ -31,13 +31,13 @@ async fn get_articles() -> impl Responder {
while let State::Row = stmt.next().unwrap() {
let id = stmt.read::<i64, _>(0).unwrap();
let title = stmt.read::<String, _>(1).unwrap();
let content = stmt.read::<String, _>(3).unwrap();
let content = stmt.read::<String, _>(5).unwrap();
articles.push(Article {
id,
title,
auteur: None,
edited_at: None,
published_at: None,
auteur: "".to_string(),
edited_at: "".to_string(),
published_at: "".to_string(),
content
});
}
@ -45,6 +45,54 @@ async fn get_articles() -> impl Responder {
HttpResponse::Ok().json(articles)
}
#[get("/api/articles/{id}")]
async fn get_article(path: web::Path<i64>) -> impl Responder {
let id = path.into_inner();
// Open the database connection
let conn = match Connection::open("./data/data.db") {
Ok(conn) => conn,
Err(err) => {
eprintln!("Failed to connect to database: {}", err);
return HttpResponse::InternalServerError().body("Failed to connect to database");
}
};
// Fetch the article from the database
match fetch_article_by_id(&conn, id) {
Ok(Some(article)) => HttpResponse::Ok().json(article),
Ok(None) => HttpResponse::NotFound().body(format!("Article with ID {} not found", id)),
Err(err) => {
eprintln!("Database query error: {}", err);
HttpResponse::InternalServerError().body("Database query failed")
}
}
}
/// Fetches an article by its ID from the database.
fn fetch_article_by_id(conn: &Connection, id: i64) -> Result<Option<Article>, sqlite::Error> {
let mut stmt = conn.prepare(
"SELECT id, title, auteur, edited_at, published_at, content
FROM articles WHERE id = ?1"
)?;
stmt.bind((1, id))?;
let mut article = None;
while let State::Row = stmt.next()? {
article = Some(Article {
id: stmt.read::<i64, _>(0)?,
title: stmt.read::<String, _>(1)?,
auteur: stmt.read::<String, _>(2)?,
edited_at: stmt.read::<String, _>(3)?,
published_at: stmt.read::<String, _>(4)?,
content: stmt.read::<String, _>(5)?,
});
}
Ok(article)
}
#[get("/api")]
async fn api() -> impl Responder {
let value = json!({
@ -72,7 +120,10 @@ async fn main() -> Result<(), std::io::Error> {
.service(hello)
.service(get_articles)
.service(api)
.service(get_article)
.service(Files::new("/", "public").index_file("index.html"))
.service(Files::new("/game", "public").index_file("index.html"))
.service(Files::new("/chaos", "public").index_file("index.html"))
})
.bind(("0.0.0.0", 2486))?
.run()

View file

@ -3,10 +3,22 @@ services:
build:
context: .
dockerfile: dockerfile
container_name: web
network: host
container_name: nuitdelinfo
restart: always
ports:
- 8080:8080
volumes:
- ./back/data:/app/data
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.nuitdelinfo.rule=Host(`nuitdelinfo.leizour.fr`)"
- "traefik.http.routers.nuitdelinfo.entrypoints=websecure"
- "traefik.http.routers.nuitdelinfo.tls=true"
- "traefik.http.routers.nuitdelinfo.tls.certresolver=myresolver"
- "traefik.http.services.nuitdelinfo.loadbalancer.server.port=2486"
networks:
traefik:
external: true

View file

@ -10,9 +10,9 @@ RUN cargo build --release
FROM debian:bookworm-slim
WORKDIR /app
RUN apt-get update & apt-get install -y extra-runtime-dependencies & rm -rf /var/lib/apt/lists/*
RUN apt update && apt install -y libsqlite3-0
COPY --from=front /app/dist /app/public
COPY --from=back /app/target/release/back /app/back
EXPOSE 8080
EXPOSE 2486
CMD ["/app/back"]

View file

@ -16,9 +16,9 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^7.0.2",
"react-router-dom": "^6.2.1",
"three": "^0.171.0",
"three-stdlib": "^2.34.0"
"three-stdlib": "^2.34.0",
"react-router-dom": "^6.2.1"
},
"devDependencies": {
"@eslint/js": "^9.15.0",

View file

@ -1,6 +1,4 @@
import React from 'react'
import { LineBasicMaterial, Line, Group } from 'three'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
export default function Axes() {

View file

@ -1,11 +1,10 @@
import { Group, Mesh, MeshStandardMaterial, BufferGeometry } from 'three'
import { useGLTF } from '@react-three/drei'
export default function Character() {
// import glb file
// load the glb file in "/models/BASEmodel.glb"
const { nodes, materials, scene } = useGLTF('/models/man.glb')
const { scene } = useGLTF('/models/man.glb')
// rotate the character
scene.rotation.x = -Math.PI / 2

View file

@ -1,5 +1,3 @@
import React from 'react'
import { Group } from 'three'
import * as THREE from 'three'

View file

@ -1,21 +1,20 @@
import React from 'react'
import { Group } from 'three'
import * as THREE from 'three'
interface MarkerProps {
position: [number, number, number],
position: number[],
color: string,
onClick?: () => void
}
export default function Marker({ position, color, onClick }: MarkerProps) {
const [positionState, setPositionState] = React.useState(position)
const [positionState, setPositionState] = React.useState(new THREE.Vector3(...position))
// Return the marker object
// return <primitive object={marker} />
return (
<mesh position={positionState} rotation={[Math.PI,0,0]} onClick={onClick} onPointerOver={(e) => setPositionState([positionState[0], positionState[1], positionState[2] + 0.1])} onPointerOut={(e) => setPositionState(position)}>
<mesh position={positionState} rotation={[Math.PI,0,0]} onClick={onClick} onPointerOver={() => setPositionState(positionState.clone().setZ(positionState.z + 0.1))} onPointerOut={() => setPositionState(new THREE.Vector3(...position))}>
<coneGeometry args={[0.15, 0.6, 6]} />
<meshStandardMaterial color={color} side={THREE.DoubleSide} />
</mesh>

View file

@ -2,7 +2,6 @@
import React, { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { Water, WaterOptions } from 'three/examples/jsm/objects/Water.js';
import { WaterMesh, WaterMeshOptions } from 'three/examples/jsm/objects/Water2Mesh.js';
const Ocean: React.FC = () => {

View file

@ -1,4 +1,4 @@
import { ReactNode, MouseEventHandler } from 'react';
import { ReactNode } from 'react';
interface ButtonProps {
color: 'primary' | 'secondary';

View file

@ -1,6 +1,6 @@
import Button from "./Button";
import ButtonLink from "./ButtonLink";
import NavBar from "./NavBar";
import { Link } from "react-router";
export default function FstSection () {
return (
@ -32,8 +32,55 @@ export default function FstSection () {
>Help us to save our oceans</h1><br />
<div style={{display:"flex", justifyContent: "center", gap: "20px"}}>
<Button color="primary" children={"Aller au jeu"} url="/game" />
<Button color="secondary" children={"Lire les articles"} url="/" />
{/* <Button color="primary" children={"Aller au jeu"} url="/game" />
<Button color="secondary" children={"Lire les articles"} url="/" /> */}
<Link to="/game" style={{
backgroundColor: 'var(--color-yellow)',
borderRadius: '8px',
borderWidth: '0',
color: 'var(--color-black)',
cursor: 'pointer',
display: 'inline-block',
fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif',
fontSize: '20px',
fontWeight: '500',
lineHeight: '20px',
listStyle: 'none',
margin: '0',
padding: '10px 12px',
textAlign: 'center',
transition: 'all 200ms',
verticalAlign: 'baseline',
whiteSpace: 'nowrap',
userSelect: 'none',
WebkitUserSelect: 'none',
touchAction: 'manipulation',
textDecoration: 'none',
}}>Aller au jeu</Link>
<Link to="/articles" style={{
backgroundColor: 'var(--color-darkblue)',
borderRadius: '8px',
borderWidth: '0',
color: 'var(--color-white)',
cursor: 'pointer',
display: 'inline-block',
fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif',
fontSize: '20px',
fontWeight: '500',
lineHeight: '20px',
listStyle: 'none',
margin: '0',
padding: '10px 12px',
textAlign: 'center',
transition: 'all 200ms',
verticalAlign: 'baseline',
whiteSpace: 'nowrap',
userSelect: 'none',
WebkitUserSelect: 'none',
touchAction: 'manipulation',
textDecoration: 'none',
}}>Lire les articles</Link>
</div>
</div></div>
)

View file

@ -1,5 +1,4 @@
import LogoButton from '../components/LogoButton.tsx'
import ClickableLink from './ClickableLink.tsx';
import { Link } from 'react-router';
import RoundButton from './RoundButton.tsx';
export default function NavBar(){
@ -7,13 +6,15 @@ export default function NavBar(){
<nav style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "0.5em 1em"}}>
<div style={{ display : "flex", alignItems: "center", flexDirection: "row"}}>
{/* <LogoButton url="/" logo = "https://archlinux.org/static/hetzner_logo.41114a37d25f.png"/> */}
<span class="material-symbols-outlined"
<span className="material-symbols-outlined"
style={{fontSize: "4em", color: "white", margin: "0.5em"}}>sailing</span>
<ClickableLink url="/" text = "Accueil" />
<ClickableLink url="/game" text = "Jeu" />
<Link to="/" style={{ textDecoration: "none", color: "white", fontSize: "1.5em", margin: "0.5em", fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif'}}>Home</Link>
<Link to="/game" style={{ textDecoration: "none", color: "white", fontSize: "1.5em", margin: "0.5em", fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif'}}>Game</Link>
<Link to="/articles" style={{ textDecoration: "none", color: "white", fontSize: "1.5em", margin: "0.5em", fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif'}}>Articles</Link>
<Link to="/chaos" style={{ textDecoration: "none", color: "white", fontSize: "1.5em", margin: "0.5em", fontFamily: '"Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif'}}>Le Chaos</Link>
</div>
<div style={{ display : "flex", alignItems: "center", flexDirection: "row"}}>
<RoundButton url="https://archlinux.org" bgcolor="var(--color-white)" text="?"/>
<RoundButton url="https://www.apple.com/macos/macos-sequoia/" bgcolor="var(--color-white)" text="?"/>
</div>
</nav>);
}

View file

@ -0,0 +1,16 @@
//import { useState } from "react";
interface MonInputProps {
text: string;
new_focus: () => void;
police: string;
}
export default function MonInput({text, new_focus, police}: MonInputProps) {
return (
<div>
<input readOnly value={text} onFocus={new_focus} style={{fontFamily: police,width:1000}}></input>
</div>
)
}

View file

@ -0,0 +1,26 @@
interface MonButtonProps {
letter: string;
changetext: (arg0: string) => void;
sizeFrontw: number;
sizeFronth: number;
rdmFront: () => void;
color: string;
}
export default function MonButton({letter,changetext,sizeFrontw,sizeFronth,rdmFront,color}: MonButtonProps) {
function clicked() {rdmFront();
changetext(letter)}
return (
<div>
<button className="key" onClick={clicked} style={{width:sizeFrontw,height:sizeFronth,background:color}}>{letter}</button>
</div>
)
}

View file

@ -0,0 +1,12 @@
#keys {
display: grid;
grid-template-columns: auto auto 1fr;
}
.key {
font-size: 10px;
border: solid black;
}

View file

@ -2,6 +2,8 @@ import { BrowserRouter, Route, Routes } from "react-router";
import { createRoot } from 'react-dom/client'
import MainPage from "./pages/MainPage.tsx";
import GamePage from "./pages/GamePage.tsx";
import ChaosPage from "./pages/ChaosPage.tsx";
import ArticlePage from "./pages/ArticlePage.tsx";
import './index.css'
@ -12,6 +14,7 @@ createRoot(document.getElementById('root')!).render(
<Route path="/" element={<MainPage />} />
// Game page
<Route path="/game" element={<GamePage />} />
<Route path="/chaos" element={<ChaosPage />} />
// Article page (dynamic route)
<Route path="/article/:id" element={<ArticlePage />} />
// Not found

View file

@ -0,0 +1,86 @@
import MonButton from "../components/chaos/monButton";
import MonInput from "../components/chaos/MonInput";
import "../components/chaos/style.css"
import { useState } from "react";
export default function ChaosPage(){
const [array_letter,setArray_letter]=useState(["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"," ","!"]);
const [sizeFrontw,setSizeFrontw] = useState(16)
const [sizeFronth,setSizeFronth] = useState(20)
const [color,setColor] = useState("#ffffff")
function randomFront(){setSizeFronth(Math.floor(Math.random() * (1000)));
setSizeFrontw(Math.floor(Math.random() * (1000)));
setColor(`#${Math.floor(Math.random() * 16777215).toString(16)}`)}
function shuffleArray(arr: string[]) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); // Choisir un index aléatoire
[arr[i], arr[j]] = [arr[j], arr[i]]; // Échanger les éléments
}
return arr;
}
// console.log(shuffledArray); // Affiche un tableau mélangé
const [entry1,setEntry1] = useState("")
function E1(ent:string) {
setEntry1(entry1+ent);
}
const [connarddefocus,setFocus] = useState(()=>E1)
const [entry2,setEntry2] = useState("")
function E2(ent:string) {
setEntry2(entry2+ent);
}
function changeFocus(E: (ent:string)=>void) {
setArray_letter(shuffleArray(array_letter));
setFocus(()=>E);
}
const [entry3,setEntry3] = useState("")
function E3(ent:string) {
setEntry3(entry3+ent);
}
const [tel,setTel] = useState(0)
return(
<div>
<h1>Chaos Page</h1>
<p>Quel est votre nom ?</p>
<MonInput text={entry1} new_focus={()=>changeFocus(E1)} police={""}/>
<p>Quel adjectif désigne le mieux Xi Junpin ?</p>
<MonInput text={entry2} new_focus={()=>changeFocus(E2)} police={""}/>
<p>Combien font 1+1 ?</p>
<MonInput text={entry3} new_focus={()=>changeFocus(E3)} police={"Wingdings"}/>
<div id = "keys">
{array_letter.map((letter) => {return <MonButton
letter={letter}
changetext={connarddefocus}
sizeFrontw={sizeFrontw}
sizeFronth={sizeFronth}
rdmFront={randomFront}
color={color}/> })}
</div>
<fieldset>
<legend>Formulaire super mega bien</legend>
<input type="text" placeholder="nom"/>
<input type="text" placeholder="prenom"/>
<input type="text" placeholder="email"/>
<input type="text" placeholder="mdp"/>
<input type="text" placeholder="mdp2"/>
<label htmlFor="phone">phone : {tel}</label>
<input type="range" min="0" max="9999999999" step="1" name="phone" id="phone" value={tel} onChange={(e)=>setTel(parseInt(e.target.value))}/>
<input type="submit" value="envoyer"/>
</fieldset>
</div>
)
}

View file

@ -1,9 +1,7 @@
import { Canvas } from "@react-three/fiber";
import { OrbitControls, PerspectiveCamera, Sky } from "@react-three/drei";
import * as THREE from "three";
import React, { useEffect, useRef, useState } from "react";
import { useEffect, useState } from "react";
import Ocean from "../components/3d/Ocean";
import Axes from "../components/3d/Axes";
import Character from "../components/3d/Character";
import Floor from "../components/3d/Floor";
import Marker from "../components/3d/Marker";