Added background animation for homepage and similar other pages, started making body validation schemas on backend and removed delay when searching
This commit is contained in:
parent
ca96f57fb8
commit
6d48a10c1d
|
@ -14,6 +14,7 @@
|
||||||
"packageManager": "pnpm@10.10.0",
|
"packageManager": "pnpm@10.10.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
|
"ajv-formats": "^3.0.1",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"better-sqlite3": "^11.10.0",
|
"better-sqlite3": "^11.10.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
|
|
@ -11,6 +11,9 @@ importers:
|
||||||
ajv:
|
ajv:
|
||||||
specifier: ^8.17.1
|
specifier: ^8.17.1
|
||||||
version: 8.17.1
|
version: 8.17.1
|
||||||
|
ajv-formats:
|
||||||
|
specifier: ^3.0.1
|
||||||
|
version: 3.0.1(ajv@8.17.1)
|
||||||
bcrypt:
|
bcrypt:
|
||||||
specifier: ^5.1.1
|
specifier: ^5.1.1
|
||||||
version: 5.1.1
|
version: 5.1.1
|
||||||
|
@ -98,6 +101,14 @@ packages:
|
||||||
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
|
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
ajv-formats@3.0.1:
|
||||||
|
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
|
||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||||
|
|
||||||
|
@ -1000,6 +1011,10 @@ snapshots:
|
||||||
|
|
||||||
agent-base@7.1.3: {}
|
agent-base@7.1.3: {}
|
||||||
|
|
||||||
|
ajv-formats@3.0.1(ajv@8.17.1):
|
||||||
|
optionalDependencies:
|
||||||
|
ajv: 8.17.1
|
||||||
|
|
||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
|
|
|
@ -10,7 +10,7 @@ async function getAllUsers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserByName(name) {
|
async function getUserByName(name) {
|
||||||
return await db.query("SELECT username, display_name, profile_picture, role FROM Users WHERE username = ?;", [name]);
|
return await db.query("SELECT username, display_name, email, profile_picture, role FROM Users WHERE username = ?;", [name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserByEmail(email) {
|
async function getUserByEmail(email) {
|
||||||
|
@ -25,10 +25,15 @@ async function getUserPassword(name) {
|
||||||
return await db.query("SELECT username, password FROM Users WHERE username = ?;", [name]);
|
return await db.query("SELECT username, password FROM Users WHERE username = ?;", [name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO test
|
||||||
async function exists(name) {
|
async function exists(name) {
|
||||||
return await db.exists("Users", "username", name);
|
return await db.exists("Users", "username", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function existsEmail(email) {
|
||||||
|
return await db.exists("Users", "email", email);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Create ---
|
// --- Create ---
|
||||||
|
|
||||||
|
@ -122,4 +127,4 @@ module.exports = { getAllUsers, getUserByName, getUserByEmail, getFullUserInfos,
|
||||||
createUser, addFavoriteMods,
|
createUser, addFavoriteMods,
|
||||||
updateUser,
|
updateUser,
|
||||||
deleteUser, deleteFavoriteMods,
|
deleteUser, deleteFavoriteMods,
|
||||||
exists }
|
exists, existsEmail }
|
|
@ -1,5 +1,6 @@
|
||||||
const Ajv = require("ajv");
|
const Ajv = require("ajv");
|
||||||
const ajv = new Ajv();
|
const addFormats = require("ajv-formats")
|
||||||
|
const ajv = new Ajv({formats: {email: true}});
|
||||||
|
|
||||||
// --- Schemas ---
|
// --- Schemas ---
|
||||||
//TODO
|
//TODO
|
||||||
|
@ -7,14 +8,17 @@ const ajv = new Ajv();
|
||||||
const newUserSchema = {
|
const newUserSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
email: { type: 'string', format: 'email' },
|
username: { type: 'string' }, //TODO
|
||||||
name: { type: 'string' },
|
display_name: { type: 'string'},
|
||||||
password: { type: 'string', minLength: 3, maxLength: 30 },
|
email: { type: 'string', format: 'email' }, //TODO
|
||||||
|
password: { type: 'string', minLength: 6, maxLength: 255 },
|
||||||
|
profile_picture: {type: 'string'} //TODO
|
||||||
},
|
},
|
||||||
required: ['name', 'email', 'password'],
|
required: ['name', 'email', 'password'],
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addFormats(ajv, ['email']);
|
||||||
const validateNewUserData = ajv.compile(newUserSchema);
|
const validateNewUserData = ajv.compile(newUserSchema);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const model = require("../models/user");
|
const model = require("../models/user");
|
||||||
const AppError = require("../utils/appError");
|
const AppError = require("../utils/appError");
|
||||||
const cryptoUtils = require("../utils/crypto");
|
const cryptoUtils = require("../utils/crypto");
|
||||||
const { validateUserData } = require("../utils/validate_legacy");
|
const { validateNewUserData} = require("../schemas/user");
|
||||||
const { sanitizeUserData } = require("../utils/sanitize");
|
const { sanitizeUserData } = require("../utils/sanitize");
|
||||||
|
|
||||||
async function getAllUsers() {
|
async function getAllUsers() {
|
||||||
|
@ -10,22 +10,43 @@ async function getAllUsers() {
|
||||||
|
|
||||||
async function getUserByName(name) {
|
async function getUserByName(name) {
|
||||||
const res = await model.getUserByName(name);
|
const res = await model.getUserByName(name);
|
||||||
|
if (res.length == 0) {
|
||||||
|
throw new AppError(404, "No user with this name", "Not found");
|
||||||
|
}
|
||||||
return res[0];
|
return res[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createUser(user_data) {
|
async function createUser(user_data) {
|
||||||
|
|
||||||
// Check body validity
|
// Check body validity
|
||||||
// TODO
|
try {
|
||||||
|
validateNewUserData(user_data);
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppError(400, "Invalid fields", "Bad request");
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize
|
// Sanitize
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Gather data
|
// Generate data
|
||||||
const { username, email, password, display_name, profile_picture, settings } = user_data
|
|
||||||
const password_hash = await cryptoUtils.hashPassword(password);
|
|
||||||
|
|
||||||
await model.createUser(username, email, password_hash, display_name, null, null);
|
const { username, email, password, display_name, profile_picture, settings } = user_data
|
||||||
|
|
||||||
|
const password_hash = cryptoUtils.hashPassword(password);
|
||||||
|
|
||||||
|
if (await model.exists(username) ) {
|
||||||
|
throw new AppError(403, "A user with this username already exists", "Already exists");
|
||||||
|
}
|
||||||
|
if (await model.existsEmail(email)) {
|
||||||
|
throw new AppError(403, "A user with this email already exists", "Already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!display_name) {
|
||||||
|
display_name = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write changes
|
||||||
|
await model.createUser(username, email, await password_hash, display_name, null, null);
|
||||||
return model.getUserByName(username);
|
return model.getUserByName(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,11 @@ import SearchIcon from '../../assets/search.svg'
|
||||||
import styles from './search.module.css'; // Optional: CSS Modules
|
import styles from './search.module.css'; // Optional: CSS Modules
|
||||||
|
|
||||||
|
|
||||||
function SearchBar({ onSearch }) {
|
function SearchBar({ onSearch, timeout }) {
|
||||||
|
|
||||||
const [search_input, setSearchInput] = useState('');
|
const [search_input, setSearchInput] = useState('');
|
||||||
const timeout_id = useRef(null);
|
const timeout_id = useRef(null);
|
||||||
|
timeout = timeout | 0;
|
||||||
|
|
||||||
const handleInputChange = (event) => {
|
const handleInputChange = (event) => {
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ function SearchBar({ onSearch }) {
|
||||||
|
|
||||||
timeout_id.current = setTimeout(() => {
|
timeout_id.current = setTimeout(() => {
|
||||||
onSearch(new_search_input);
|
onSearch(new_search_input);
|
||||||
}, 500);
|
}, timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,10 +1,35 @@
|
||||||
.halo {
|
.halo {
|
||||||
position: fixed;
|
/* position: fixed; */
|
||||||
|
position: relative;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
background: radial-gradient(circle at 50% 200%,#353be5 0%, #353be5 45%, #00000000 80%);
|
}
|
||||||
|
|
||||||
|
.halo::before,
|
||||||
|
.halo::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: bottom;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.halo::before {
|
||||||
|
background: radial-gradient(circle at 60% 200%,#353be5 0%, #353be5 45%, #00000000 85%);
|
||||||
|
opacity: 1;
|
||||||
|
animation: haloBlue 10s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.halo::after {
|
||||||
|
background: radial-gradient(circle at 0% 210%,#353be5 10%, #4935e5 45%, #00000000 80%);
|
||||||
|
opacity: 0;
|
||||||
|
animation: haloPurple 7s infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
|
@ -87,3 +112,36 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Animations ---------------- */
|
||||||
|
|
||||||
|
@keyframes haloBlue {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
/* background-size: 50% 50%; */
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
/* background-size: 150% 150%; */
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
/* background-size: 50% 50%; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes haloPurple {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
/* background-size: 100% 50%; */
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
/* background-size: 100% 100%; */
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
/* background-size: 100% 50%; */
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue