Compare commits

..

1 commit

Author SHA1 Message Date
DarkMax31
7f48f00ba4 modif couleur graphique 2025-08-29 08:53:24 +02:00
17 changed files with 234 additions and 1087 deletions

59
app.js
View file

@ -1,25 +1,24 @@
const DISCORD_CLIENT_ID = "your_client_id";
const DISCORD_CLIENT_SECRET = "your_client_secret";
const DISCORD_CALLBACK_URL = "http://localhost:3000/auth/discord/callback";
const SESSION_SECRET = "your_session_secret"; const SESSION_SECRET = "your_session_secret";
// ...existing code...
const express = require('express'); const express = require('express');
const session = require('express-session'); const session = require('express-session');
const initDb = require('./db_init'); const passport = require('passport');
const DiscordStrategy = require('passport-discord').Strategy;
const SQLite = require('sqlite3').verbose();
const path = require('path'); const path = require('path');
const expressLayouts = require('express-ejs-layouts');
const routes = require('./routes'); const routes = require('./routes');
const app = express(); const app = express();
// DB SQLite // DB SQLite
let db; const db = new SQLite.Database('./database.sqlite', (err) => {
initDb('./database.sqlite') if (err) console.error('Erreur SQLite:', err);
.then((database) => { else db.run('CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, username TEXT, discriminator TEXT, avatar TEXT)');
db = database;
console.log('Base de données initialisée.');
})
.catch((err) => {
console.error('Erreur lors de l\'initialisation de la base de données :', err);
process.exit(1);
}); });
// Sessions // Sessions
@ -29,19 +28,39 @@ app.use(session({
saveUninitialized: false saveUninitialized: false
})); }));
// Passport config
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser((id, done) => {
db.get('SELECT * FROM users WHERE id = ?', [id], (err, row) => {
if (err) return done(err);
done(null, row);
});
});
passport.use(new DiscordStrategy({
clientID: DISCORD_CLIENT_ID,
clientSecret: DISCORD_CLIENT_SECRET,
callbackURL: DISCORD_CALLBACK_URL,
scope: ['identify']
}, (accessToken, refreshToken, profile, done) => {
db.run('INSERT OR REPLACE INTO users (id, username, discriminator, avatar) VALUES (?, ?, ?, ?)',
[profile.id, profile.username, profile.discriminator, profile.avatar],
(err) => {
if (err) return done(err);
done(null, profile);
}
);
}));
app.use(passport.initialize());
app.use(passport.session());
// EJS // EJS
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views')); app.set('views', path.join(__dirname, 'views'));
// Express EJS Layouts
app.use(expressLayouts);
app.set('layout', 'layout'); // nom du fichier layout sans .ejs
// Static files
app.use(express.static(path.join(__dirname, 'public')));
// Routes // Routes
app.use('/', routes); app.use('/', routes);
const PORT = 3000; const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log('Serveur lancé sur http://localhost:' + PORT)); app.listen(PORT, () => console.log('Serveur lancé sur http://localhost:' + PORT));

Binary file not shown.

View file

@ -1,25 +0,0 @@
const SQLite = require('sqlite3').verbose();
function initDb(dbPath = './database.sqlite') {
return new Promise((resolve, reject) => {
const db = new SQLite.Database(dbPath, (err) => {
if (err) {
reject(err);
} else {
db.run(`CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT,
discord_id TEXT DEFAULT NULL,
google_id TEXT DEFAULT NULL,
discriminator TEXT,
avatar TEXT
)`, (err) => {
if (err) reject(err);
else resolve(db);
});
}
});
});
}
module.exports = initDb;

View file

@ -1,4 +0,0 @@
// Singleton pour partager l'instance db partout
const SQLite = require('sqlite3').verbose();
const db = new SQLite.Database('./database.sqlite');
module.exports = db;

View file

@ -1,58 +0,0 @@
const axios = require("axios");
const { getUserByDId } = require('./../users/getUsers');
const { postUser } = require('./../users/postUsers');
const BOT_ID = "1410258710407811082";
const BOT_SECRET = "Bn5FGfrNZCdG1XpCciOcLkzLmrz6fhib";
const REDIRECT_URI = "http://localhost:3000/auth/discord/callback";
exports.handleDiscordAuth = async (req, res) => {
const code = req.query.code;
if (!code) return res.status(400).json({ error: "Code de validation manquant" });
try {
const params = new URLSearchParams();
params.append("client_id", BOT_ID);
params.append("client_secret", BOT_SECRET);
params.append("grant_type", "authorization_code");
params.append("code", code);
params.append("redirect_uri", REDIRECT_URI);
params.append("scope", "identify email");
const tokenData = await axios.post(
"https://discord.com/api/oauth2/token",
params,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
const accessToken = tokenData.data.access_token;
const userResponse = await axios.get("https://discord.com/api/users/@me", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const userData = userResponse.data;
if (!userData || !userData.id) {
console.error('Réponse Discord inattendue:', userResponse.data);
return res.status(500).json({ error: "Impossible de récupérer les infos utilisateur depuis Discord", details: userResponse.data });
}
let savedUser = await getUserByDId(userData.id);
if (!savedUser) {
const newUser = {
username: userData.username,
discord_id: userData.id,
avatar: userData.avatar
? `https://cdn.discordapp.com/avatars/${userData.id}/${userData.avatar}.png`
: null,
};
savedUser = await postUser(newUser);
}
req.session.user = savedUser;
res.redirect('/');
} catch (err) {
console.error(err.response?.data || err.message);
res.status(500).json({ error: "Erreur lors de la connexion à Discord" });
}
};

View file

@ -1,54 +0,0 @@
const axios = require("axios");
const { getUserByGId } = require('../users/getUsers');
const { postUser } = require('../users/postUsers');
const CLIEN_ID = "71229835507-9413gbpdamv2qbcb2ov8oda2oqgcsk8q.apps.googleusercontent.com";
const GOOGLE_SECRET = "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";
const GOOGLE_REDIRECT_URI = "http://localhost:3000/auth/google/callback";
exports.handleGoogleAuth = async (req, res) => {
const code = req.query.code;
if (!code) return res.status(400).json({ error: "Code de validation manquant" });
try {
const params = new URLSearchParams();
params.append("client_id", CLIEN_ID);
params.append("client_secret", GOOGLE_SECRET);
params.append("grant_type", "authorization_code");
params.append("code", code);
params.append("redirect_uri", GOOGLE_REDIRECT_URI);
const tokenData = await axios.post(
"https://oauth2.googleapis.com/token",
params,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
const accessToken = tokenData.data.access_token;
const userResponse = await axios.get("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const userData = userResponse.data;
if (!userData || !userData.id) {
console.error("Réponse Google inattendue:", userResponse.data);
return res.status(500).json({ error: "Impossible de récupérer les infos utilisateur depuis Google", details: userResponse.data });
}
let savedUser = await getUserByGId(userData.id);
if (!savedUser) {
const newUser = {
username: userData.name || userData.email,
google_id: userData.id,
};
savedUser = await postUser(newUser);
}
req.session.user = savedUser;
res.redirect('/');
} catch (err) {
console.error(err.response?.data || err.message);
res.status(500).json({ error: "Erreur lors de la connexion à Google" });
}
};

View file

@ -1,26 +0,0 @@
const db = require('../../db_instance');
module.exports.getUserByDId = async (discordId) => {
return new Promise((resolve, reject) => {
const query = `SELECT * FROM users WHERE discord_id = ?`;
db.get(query, [discordId], (err, row) => {
if (err) {
return reject(err);
}
resolve(row);
});
});
}
module.exports.getUserByGId = async (discordId) => {
return new Promise((resolve, reject) => {
const query = `SELECT * FROM users WHERE google_id = ?`;
db.get(query, [discordId], (err, row) => {
if (err) {
return reject(err);
}
resolve(row);
});
});
}

View file

@ -1,20 +0,0 @@
const db = require('../../db_instance');
module.exports.postUser = (user) => {
return new Promise((resolve, reject) => {
const query = `INSERT INTO users (id, username, discord_id, discriminator, avatar) VALUES (?, ?, ?, ?, ?)`;
const params = [
user.id,
user.username,
user.discord_id,
user.discriminator,
user.avatar
];
db.run(query, params, function(err) {
if (err) {
return reject(err);
}
resolve({ id: this.lastID, ...user });
});
});
}

243
package-lock.json generated
View file

@ -9,11 +9,12 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.11.0", "dotenv": "^17.2.1",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"express": "^5.1.0", "express": "^5.1.0",
"express-ejs-layouts": "^2.5.1",
"express-session": "^1.18.2", "express-session": "^1.18.2",
"passport": "^0.7.0",
"passport-discord": "^0.1.4",
"sqlite3": "^5.1.7" "sqlite3": "^5.1.7"
} }
}, },
@ -158,23 +159,6 @@
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -201,6 +185,15 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/base64url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/bindings": { "node_modules/bindings": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -371,18 +364,6 @@
"color-support": "bin.js" "color-support": "bin.js"
} }
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -477,15 +458,6 @@
"node": ">=4.0.0" "node": ">=4.0.0"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/delegates": { "node_modules/delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -511,6 +483,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/dotenv": {
"version": "17.2.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
"integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@ -628,21 +612,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": { "node_modules/escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -709,11 +678,6 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/express-ejs-layouts": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz",
"integrity": "sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA=="
},
"node_modules/express-session": { "node_modules/express-session": {
"version": "1.18.2", "version": "1.18.2",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz",
@ -786,63 +750,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/form-data/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -1036,21 +943,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-unicode": { "node_modules/has-unicode": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@ -1635,6 +1527,12 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/oauth": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.2.tgz",
"integrity": "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==",
"license": "MIT"
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@ -1702,6 +1600,61 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/passport": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
"license": "MIT",
"dependencies": {
"passport-strategy": "1.x.x",
"pause": "0.0.1",
"utils-merge": "^1.0.1"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-discord": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/passport-discord/-/passport-discord-0.1.4.tgz",
"integrity": "sha512-VJWPYqSOmh7SaCLw/C+k1ZqCzJnn2frrmQRx1YrcPJ3MQ+Oa31XclbbmqFICSvl8xv3Fqd6YWQ4H4p1MpIN9rA==",
"license": "ISC",
"dependencies": {
"passport-oauth2": "^1.5.0"
}
},
"node_modules/passport-oauth2": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz",
"integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==",
"license": "MIT",
"dependencies": {
"base64url": "3.x.x",
"oauth": "0.10.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1721,6 +1674,11 @@
"node": ">=16" "node": ">=16"
} }
}, },
"node_modules/pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1787,12 +1745,6 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
@ -2375,6 +2327,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/uid2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==",
"license": "MIT"
},
"node_modules/unique-filename": { "node_modules/unique-filename": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -2410,6 +2368,15 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View file

@ -15,11 +15,12 @@
"license": "ISC", "license": "ISC",
"type": "commonjs", "type": "commonjs",
"dependencies": { "dependencies": {
"axios": "^1.11.0", "dotenv": "^17.2.1",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"express": "^5.1.0", "express": "^5.1.0",
"express-ejs-layouts": "^2.5.1",
"express-session": "^1.18.2", "express-session": "^1.18.2",
"passport": "^0.7.0",
"passport-discord": "^0.1.4",
"sqlite3": "^5.1.7" "sqlite3": "^5.1.7"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

View file

@ -1,111 +0,0 @@
main {
text-align: left !important;
}
.restaurant-page {
width: 90%;
max-width: 1200px;
margin: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-top: 20px;
}
.restaurant-info h1 {
margin: 0;
font-size: 24px;
}
.heart {
display: inline-flex;
align-items: center;
justify-content: center;
background: #ececec;
border-radius: 50%;
width: 32px;
height: 32px;
font-size: 20px;
margin-left: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.rating {
display: flex;
align-items: center;
margin: 10px 0;
gap: 10px;
}
.btn {
background: #999;
border: none;
color: white;
padding: 5px 10px;
cursor: pointer;
}
.chart-placeholder {
width: 200px;
height: 200px;
background: #eee;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.main-image {
width: 100%;
height: 200px;
background: #ccc;
margin: 20px 0;
}
.description {
background: #f9f9f9;
padding: 15px;
margin-bottom: 20px;
}
.gallery {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
}
.gallery-item {
width: 32%;
height: 150px;
background: #ddd;
}
.reviews-title {
background: #eee;
padding: 10px;
margin: 0;
}
.reviews {
display: flex;
flex-direction: column;
gap: 20px;
margin: 20px 0;
}
.review {
background: #f5f5f5;
padding: 15px;
border: 1px solid #ddd;
}
.review h3 {
margin-top: 0;
}
.review-rating div {
margin-top: 5px;
}

View file

@ -1,294 +0,0 @@
body {
margin: 0;
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #f8f6fc 0%, #fffbe7 100%);
color: #7a5c1e;
}
header {
background: linear-gradient(90deg, #8e68aa 60%, #cb8d37 100%);
color: white;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 20px;
box-shadow: 0 2px 8px rgba(140, 104, 170, 0.08);
}
header h1 {
font-size: 1.2rem;
}
.search-bar {
flex-grow: 1;
margin: 0 20px;
}
.search-bar input {
width: 100%;
padding: 5px;
border: none;
border-radius: 4px;
}
.account {
background: linear-gradient(90deg, #cb8d37 60%, #8e68aa 100%);
padding: 6px 12px;
border-radius: 4px;
color: white;
cursor: pointer;
font-weight: bold;
transition: background 0.2s;
}
.account:hover {
background: #a97b2c;
}
main {
padding: 20px;
text-align: center;
}
main h2 {
font-size: 2rem;
margin: 10px 0;
}
.sections {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-top: 20px;
}
.section h3 {
margin-bottom: 15px;
text-align: left;
}
.cards {
display: flex;
flex-direction: column;
gap: 15px;
}
.card {
background: #ddd;
border-radius: 8px;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.card-info {
text-align: left;
}
.card-info h4 {
margin: 0 0 5px;
font-weight: bold;
}
.card-photo {
background: #8e68aa;
width: 120px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
color: #555;
}
footer {
background: #8e68aa;
text-align: center;
padding: 10px;
margin-top: 30px;
}
footer a {
margin: 0 10px;
text-decoration: none;
color: #333;
}
/* Header layout amélioré */
.main-header {
background: linear-gradient(90deg, #8e68aa 60%, #cb8d37 100%);
color: white;
box-shadow: 0 2px 8px rgba(140, 104, 170, 0.08);
padding: 0;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 20px;
max-width: 1200px;
margin: 0 auto;
width: 100%;
gap: 0;
}
.header-left {
display: flex;
align-items: center;
min-width: 220px;
}
.header-left h1 {
font-size: 1.4rem;
margin: 0 0 0 10px;
white-space: nowrap;
}
.header-center {
flex: 1 1 0;
display: flex;
justify-content: center;
}
.header-right {
display: flex;
align-items: center;
min-width: 260px;
justify-content: flex-end;
}
.search-bar {
flex: 1 1 300px;
max-width: 700px;
margin: 0 18px;
}
.search-bar input {
width: 100%;
padding: 7px 12px;
border: none;
border-radius: 4px;
font-size: 1rem;
}
.account {
display: flex;
align-items: center;
background: linear-gradient(90deg, #cb8d37 60%, #8e68aa 100%);
padding: 6px 12px;
border-radius: 4px;
color: white;
font-weight: bold;
transition: background 0.2s;
}
.account a {
color: white;
text-decoration: none;
margin-left: 8px;
font-weight: bold;
}
.account a:first-child {
margin-left: 0;
}
.account:hover {
background: #a97b2c;
}
.social-buttons {
display: flex;
gap: 12px;
margin: 0;
}
.btn-social {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 18px;
border: none;
border-radius: 30px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
transition: transform 0.1s, box-shadow 0.2s;
outline: none;
text-decoration: none;
}
.btn-social:active {
transform: scale(0.97);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.btn-google {
background: #fff;
color: #444;
border: 1px solid #e0e0e0;
}
.btn-google:hover {
background: #f5f5f5;
}
.btn-discord {
background: #5865f2;
color: #fff;
border: none;
}
.btn-discord:hover {
background: #4752c4;
}
.icon-social {
width: 22px;
height: 22px;
display: inline-block;
vertical-align: middle;
}
.logo {
height: 40px;
width: 40px;
border-radius: 8px;
margin-right: 10px;
object-fit: contain;
}
/* Responsive */
@media (max-width: 900px) {
.header-content {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.header-center {
justify-content: stretch;
margin: 10px 0;
}
.header-right {
justify-content: center;
min-width: 0;
margin-top: 10px;
}
.search-bar {
min-width: 180px;
max-width: 100%;
margin: 10px 0;
}
.account {
margin-top: 10px;
}
.sections {
grid-template-columns: 1fr;
}
.social-buttons {
flex-direction: column;
gap: 12px;
}
}

View file

@ -1,46 +1,26 @@
const express = require('express'); const express = require('express');
const passport = require('passport');
const router = express.Router(); const router = express.Router();
// Page d'accueil // Page d'accueil
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.render('index', { user: req.session.user }); res.render('index', { user: req.user });
});
// Page resto
router.get('/resto/:id', (req, res) => {
res.render('resto', { user: req.session.user });
}); });
// Auth Discord // Auth Discord
router.get('/auth/discord', (req, res) => { router.get('/auth/discord', passport.authenticate('discord'));
const clientId = '1410258710407811082';
const redirectUri = 'http://localhost:3000/auth/discord/callback';
const scope = 'identify email';
const discordAuthUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${scope}`;
res.redirect(discordAuthUrl);
});
router.get('/auth/discord/callback', require('./modules/auth/discord').handleDiscordAuth); router.get('/auth/discord/callback',
passport.authenticate('discord', { failureRedirect: '/' }),
// Auth Google (req, res) => {
router.get('/auth/google', (req, res) => { res.redirect('/');
const clientId = '71229835507-9413gbpdamv2qbcb2ov8oda2oqgcsk8q.apps.googleusercontent.com'; }
const redirectUri = 'http://localhost:3000/auth/google/callback'; );
const scope = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile';
const googleAuthUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}`;
res.redirect(googleAuthUrl);
});
router.get('/auth/google/callback', require('./modules/auth/google').handleGoogleAuth);
// Déconnexion // Déconnexion
router.get('/logout', (req, res) => { router.get('/logout', (req, res) => {
req.session.destroy((err) => { req.logout(() => {
if (err) { res.redirect('/');
console.error(err);
return res.send("Erreur lors de la déconnexion");
}
res.redirect("/"); // Redirige vers la page d'accueil
}); });
}); });

View file

@ -1,78 +1,76 @@
<div style="display:flex; justify-content: center; align-items: center;"> <!DOCTYPE html>
<h2 style="margin-right: 20px;">LEPICURIEN</h2> <html lang="fr">
<img style="height: 70px; width: 70px; object-fit: contain;" alt="Logo de l'entreprise" src="logo.png" onerror="this.onerror=null;this.src='https://via.placeholder.com/70?text=Logo';" /> <head>
</div> <meta charset="UTF-8">
<div class="sections"> <title>Diagramme Radar avec Chart.js</title>
<!-- Restaurants proches --> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="section"> </head>
<h3>Restaurants les plus proches</h3> <body>
<div class="cards"> <div style="width: 35%; margin: 0 auto;">
<a href="/resto/1" style="text-decoration:none; color:inherit;"> <canvas id="radarChart"></canvas>
<div class="card">
<div class="card-info">
<h4>Le Canard Toulousain</h4>
<p>12 Rue du Capitole, 31000 Toulouse</p>
<p>Avis général</p>
</div>
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=1" />
</div>
</a>
<a href="/resto/2" style="text-decoration:none; color:inherit;">
<div class="card">
<div class="card-info">
<h4>Chez Pépé Louis</h4>
<p>8 Avenue de la Garonne, 31000 Toulouse</p>
<p>Avis général</p>
</div>
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=2" />
</div>
</a>
<a href="/resto/3" style="text-decoration:none; color:inherit;">
<div class="card">
<div class="card-info">
<h4>La Table Rose</h4>
<p>25 Rue Saint-Rome, 31000 Toulouse</p>
<p>Avis général</p>
</div>
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=3" />
</div>
</a>
</div>
</div> </div>
<!-- Restaurants populaires --> <script>
<div class="section"> const ctx = document.getElementById('radarChart').getContext('2d');
<h3>Restaurants les plus populaires</h3> const radarChart = new Chart(ctx, {
<div class="cards"> type: 'radar',
<a href="/resto/4" style="text-decoration:none; color:inherit;"> data: {
<div class="card"> labels: ['Qualité des plats', 'Ambiance', 'Accessibilité', 'Service', 'Tradition'],
<div class="card-info"> datasets: [
<h4>Bistro Occitan</h4> {
<p>5 Place Wilson, 31000 Toulouse</p> label: 'Moyenne des notes',
<p>Avis général</p> data: [4.7, 4.6, 4.4, 4.3, 4.8],
</div> fill: true,
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=4" /> backgroundColor: 'rgba(255,215,0,0.2)',
</div> borderColor: 'rgb(255,215,0)',
</a> pointBackgroundColor: 'rgb(255,215,0)',
<a href="/resto/5" style="text-decoration:none; color:inherit;"> pointBorderColor: '#fff',
<div class="card"> pointHoverBackgroundColor: '#fff',
<div class="card-info"> pointHoverBorderColor: 'rgb(255, 215, 0)',
<h4>LAssiette du Sud</h4> pointStyle: 'circle',
<p>17 Allée Jean Jaurès, 31000 Toulouse</p> pointRadius: 5,
<p>Avis général</p> pointRotation: 0,
</div> },
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=5" /> ]
</div> },
</a> options: {
<a href="/resto/6" style="text-decoration:none; color:inherit;"> plugins: {
<div class="card"> legend: {
<div class="card-info"> display: false
<h4>Le Petit Cassoulet</h4> },
<p>3 Rue des Filatiers, 31000 Toulouse</p> tooltip: {
<p>Avis général</p> callbacks: {
</div> label: function(context) {
<img alt="Photo du restaurant" src="https://source.unsplash.com/100x100/?restaurant=6" /> let label = context.dataset.label || '';
</div> if (label) {
</a> label += ': ';
</div> }
</div> label += context.formattedValue;
return label;
}
}
}
},
scales: {
r: {
max: 5,
min: 0,
ticks: {
stepSize: 1,
callback: function(value, index, values) {
return value.toFixed(1);
}
}
}
},
elements: {
line: {
borderWidth: 3
}
}
}
});
</script>
</body>
</html>

View file

@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="L'EPICURIEN - Découvrez et partagez les meilleurs restaurants autour de vous." />
<meta name="author" content="L'EPICURIEN Team" />
<title>L'EPICURIEN</title>
<link rel="stylesheet" href="/style.css" />
<link rel="icon" href="/logo.png" type="image/png" />
</head>
<body>
<header class="main-header">
<div class="header-content">
<div class="header-left">
<a href="/" class="logo-link">
<img src="/logo.png" alt="Logo L'EPICURIEN" class="logo" />
</a>
<a href="/" class="">
<h1 style="text-decoration: none; color: white;">L'EPICURIEN</h1>
</a>
</div>
<div class="header-center">
<div class="search-bar">
<input type="text" placeholder="Rechercher un restaurant" />
</div>
</div>
<div class="header-right">
<div class="account">
<% if (user) { %>
<a href="/logout" style="margin-left:12px;">Déconnexion</a>
<% } else { %>
<div class="social-buttons">
<a href="/auth/google" class="btn-social btn-google">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/google/google-original.svg" alt="Google" class="icon-social"/>
</a>
<a href="/auth/discord" class="btn-social btn-discord">
<img src="https://cdn.jsdelivr.net/gh/edent/SuperTinyIcons/images/svg/discord.svg" alt="Discord" class="icon-social"/>
</a>
</div>
<% } %>
</div>
</div>
</div>
</header>
<main>
<%- body %>
</main>
<footer class="main-footer">
<a href="#">Mentions légales</a> |
<a href="#">Politique de confidentialité</a> |
<a href="#">CGU</a>
</footer>
</body>
</html>

View file

@ -1,166 +0,0 @@
<link rel="stylesheet" href="/resto.css" />
<div class="restaurant-page">
<!-- En-tête restaurant -->
<div class="header">
<div class="restaurant-info">
<h1>
Nom restaurant
<span class="heart">
<% if (user) { %>
❤️
<% } else { %>
🤍
<% } %>
</span>
</h1>
<p class="address">Adresse</p>
<div class="rating">
<span>⭐️⭐️⭐️⭐️⭐️</span>
<a href="#avis" class="btn">Voir tous les avis</a>
</div>
</div>
<div class="chart">
<div class="chart-placeholder">
<canvas id="radarChart"></canvas>
</div>
</div>
</div>
<!-- Image principale -->
<div class="main-image"></div>
<!-- Description -->
<div class="description">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat
</p>
</div>
<!-- Galerie -->
<div class="gallery">
<div class="gallery-item"></div>
<div class="gallery-item"></div>
<div class="gallery-item"></div>
</div>
<!-- Avis -->
<h2 class="reviews-title" id="avis">Avis</h2>
<div class="reviews">
<div class="review">
<h3>Titre avis</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<div class="review-rating">
<div>Qualité des plats ⭐⭐⭐⭐⭐</div>
<div>Service ⭐⭐⭐⭐⭐</div>
<div>Ambiance ⭐⭐⭐⭐⭐</div>
<div>Accessibilité ⭐⭐⭐⭐⭐</div>
<div>Tarif ⭐⭐⭐⭐⭐</div>
</div>
</div>
<div class="review">
<h3>Titre avis</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<div class="review-rating">
<div>Qualité des plats ⭐⭐⭐⭐⭐</div>
<div>Service ⭐⭐⭐⭐⭐</div>
<div>Ambiance ⭐⭐⭐⭐⭐</div>
<div>Accessibilité ⭐⭐⭐⭐⭐</div>
<div>Tarif ⭐⭐⭐⭐⭐</div>
</div>
</div>
<div class="review">
<h3>Titre avis</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<div class="review-rating">
<div>Qualité des plats ⭐⭐⭐⭐⭐</div>
<div>Service ⭐⭐⭐⭐⭐</div>
<div>Ambiance ⭐⭐⭐⭐⭐</div>
<div>Accessibilité ⭐⭐⭐⭐⭐</div>
<div>Tarif ⭐⭐⭐⭐⭐</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('radarChart').getContext('2d');
const radarChart = new Chart(ctx, {
type: 'radar',
data: {
labels: ['Qualité des plats', 'Ambiance', 'Accessibilité', 'Service', 'Tradition'],
datasets: [
{
label: 'Moyenne des notes',
data: [4.7, 4.6, 4.4, 4.3, 4.8],
fill: true,
backgroundColor: 'rgba(255,215, 0, 0.2)',
borderColor: 'rgb(255,215, 0, 1)',
pointBackgroundColor: 'rgb(255,215, 0)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgb(255,215, 0)',
pointStyle: 'circle',
pointRadius: 5,
pointRotation: 0,
},
]
},
options: {
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
label += context.formattedValue;
return label;
}
}
}
},
scales: {
r: {
max: 5,
min: 0,
ticks: {
stepSize: 1,
display: false,
callback: function(value, index, values) {
return value.toFixed(1);
}
}
}
},
elements: {
line: {
borderWidth: 3
}
}
}
});
});
</script>