mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2025-06-28 04:45:36 -04:00
rename the app
This commit is contained in:
parent
ce35924a54
commit
f65bf959b1
27 changed files with 1832 additions and 0 deletions
53
messages/src/app.js
Normal file
53
messages/src/app.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
const fs = require("node:fs");
|
||||
const { logging } = require("./middlewares/logging");
|
||||
const path = require("node:path");
|
||||
const express = require("express");
|
||||
const passport = require("passport");
|
||||
const session = require("express-session");
|
||||
const SQLiteStore = require("connect-sqlite3")(session);
|
||||
|
||||
const { indexRouter } = require("./routes/indexRouter");
|
||||
const { authRouter } = require("./routes/authRouter");
|
||||
|
||||
const app = express();
|
||||
|
||||
const assetsPath = path.join(path.dirname(__dirname), "public");
|
||||
app.use(express.static(assetsPath));
|
||||
|
||||
app.set("views", path.join(__dirname, "views"));
|
||||
app.set("view engine", "ejs");
|
||||
|
||||
app.use(
|
||||
session({
|
||||
store: new SQLiteStore({ dir: "./src/db/", db: "keynotes.db" }),
|
||||
secret: "keynotes.app",
|
||||
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
|
||||
saveUninitialized: false,
|
||||
resave: false,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(passport.session());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(logging);
|
||||
app.use((req, res, next) => {
|
||||
res.locals.currentUser = req.user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.use("/", indexRouter);
|
||||
app.use("/auth", authRouter);
|
||||
|
||||
const server = app.listen(5173, () => console.log(`App running on port: 5173`));
|
||||
|
||||
const gracefulShutdownHandler = (signal) => {
|
||||
console.log(`\n${signal} received.`);
|
||||
|
||||
server.close(() => {
|
||||
console.log("Shutting down the server process...");
|
||||
process.exit();
|
||||
});
|
||||
};
|
||||
|
||||
process.on("SIGINT", gracefulShutdownHandler);
|
||||
process.on("SIGTERM", gracefulShutdownHandler);
|
0
messages/src/config.js
Normal file
0
messages/src/config.js
Normal file
46
messages/src/controllers/authController.js
Normal file
46
messages/src/controllers/authController.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
const bcryptjs = require("bcryptjs");
|
||||
const db = require("../models/query");
|
||||
const { validationResult } = require("express-validator");
|
||||
|
||||
function loginGet(req, res, next) {
|
||||
res.render("login", {
|
||||
pageTitle: "InspiredCliches | Login",
|
||||
errors: req.session.messages,
|
||||
});
|
||||
}
|
||||
|
||||
function logOut(req, res, next) {
|
||||
req.logout((err) => {
|
||||
if (err) return next(err);
|
||||
});
|
||||
res.redirect("/");
|
||||
}
|
||||
|
||||
function signUpGet(req, res, next) {
|
||||
res.render("register", {
|
||||
pageTitle: "InspiredCliches | Register",
|
||||
errors: null,
|
||||
});
|
||||
}
|
||||
|
||||
async function signUpPost(req, res, next) {
|
||||
const result = validationResult(req);
|
||||
if (result.isEmpty()) {
|
||||
const { username, password } = req.body;
|
||||
const hashedPassword = await bcryptjs.hash(password, 10);
|
||||
db.insertUser(username, hashedPassword);
|
||||
res.redirect("/auth/login");
|
||||
} else {
|
||||
res.status(400).render("register", {
|
||||
errors: result.array(),
|
||||
pageTitle: "InspiredCliches | Register",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loginGet,
|
||||
logOut,
|
||||
signUpPost,
|
||||
signUpGet,
|
||||
};
|
90
messages/src/controllers/indexController.js
Normal file
90
messages/src/controllers/indexController.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
const db = require("../models/query");
|
||||
|
||||
function indexGet(req, res, next) {
|
||||
if (res.locals.currentUser) {
|
||||
const keynotes = db.getEveryNote();
|
||||
const userLikedPosts = db.getLikesByUser(res.locals.currentUser.user_id);
|
||||
|
||||
res.render("feed", {
|
||||
pageTitle: "InspiredCliches | Feed",
|
||||
keynotes: keynotes,
|
||||
userLikedPosts: userLikedPosts,
|
||||
});
|
||||
}
|
||||
res.render("home", { pageTitle: "InspiredCliches" });
|
||||
}
|
||||
|
||||
function addLike(req, res, next) {
|
||||
const { noteId } = req.query;
|
||||
|
||||
const noteExits = noteId ? db.getNoteById(noteId) : null;
|
||||
|
||||
if (res.locals.currentUser && noteExits) {
|
||||
const userId = res.locals.currentUser.user_id;
|
||||
if (db.checkIfNotedLiked(noteId, userId)) {
|
||||
db.deleteLike(noteId, userId);
|
||||
} else {
|
||||
db.insertLike(Number(noteId), userId);
|
||||
}
|
||||
}
|
||||
res.redirect("/");
|
||||
}
|
||||
|
||||
function addNotePost(req, res, next) {
|
||||
const { message, media } = req.body;
|
||||
|
||||
if (res.locals.currentUser) {
|
||||
const userId = res.locals.currentUser.user_id;
|
||||
|
||||
db.putNewNote({ message: message, media: null, userId: userId });
|
||||
}
|
||||
|
||||
res.redirect("/");
|
||||
}
|
||||
|
||||
function deleteNote(req, res) {
|
||||
const { messageId, userId } = req.query;
|
||||
|
||||
if (res.locals.currentUser) {
|
||||
const message = db.getNoteById(messageId);
|
||||
|
||||
if (message.user_id === Number(userId)) {
|
||||
db.deleteNote(Number(messageId));
|
||||
}
|
||||
res.redirect("/");
|
||||
}
|
||||
}
|
||||
|
||||
function addNoteGet(req, res, next) {
|
||||
res.render("note-form", { pageTitle: "InspiredCliches | New Note" });
|
||||
}
|
||||
|
||||
function getProfile(req, res, next) {
|
||||
const { userId } = req.query;
|
||||
|
||||
const userExists = userId ? db.getUserById(userId) : null;
|
||||
|
||||
if (res.locals.currentUser) {
|
||||
if (userExists) {
|
||||
const sumUserPosts = db.getSumNotesByUserId(userId);
|
||||
const allPosts = db.getAllNotesByUserId(userId);
|
||||
res.render("view-profile", {
|
||||
pageTitle: "InspiredCliches | Edit Profile",
|
||||
user: userExists,
|
||||
totalPosts: sumUserPosts,
|
||||
allPosts: allPosts,
|
||||
});
|
||||
} else {
|
||||
res.redirect("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
indexGet,
|
||||
addNoteGet,
|
||||
addNotePost,
|
||||
deleteNote,
|
||||
addLike,
|
||||
getProfile,
|
||||
};
|
50
messages/src/middlewares/auth.js
Normal file
50
messages/src/middlewares/auth.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
const bcryptjs = require("bcryptjs");
|
||||
const db = require("../models/db");
|
||||
const LocalStrategy = require("passport-local");
|
||||
const passport = require("passport");
|
||||
|
||||
passport.use(
|
||||
new LocalStrategy(
|
||||
{ passReqToCallback: true },
|
||||
async (req, username, password, done) => {
|
||||
try {
|
||||
req.session.messages = [];
|
||||
const query = db.query(`SELECT * FROM users WHERE username = $1`);
|
||||
const user = query.get({ $1: username });
|
||||
|
||||
if (!user) {
|
||||
return done(null, false, {
|
||||
message: "Incorrect username or password.",
|
||||
});
|
||||
}
|
||||
|
||||
const match = await bcryptjs.compare(password, user.password);
|
||||
|
||||
if (!match) {
|
||||
return done(null, false, {
|
||||
message: "Incorrect username or password.",
|
||||
});
|
||||
}
|
||||
return done(null, user);
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user.user_id);
|
||||
});
|
||||
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
try {
|
||||
const query = db.query("SELECT * FROM users WHERE user_id = $userId");
|
||||
const results = query.get({ $userId: id });
|
||||
done(null, results);
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = passport;
|
8
messages/src/middlewares/logging.js
Normal file
8
messages/src/middlewares/logging.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
function logging(req, res, next) {
|
||||
req.time = new Date(Date.now()).toISOString();
|
||||
const clientIp = req.header("cf-connecting-ip") || req.socket.remoteAddress;
|
||||
console.log(req.time, req.method, req.hostname, req.path, clientIp);
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = { logging };
|
35
messages/src/models/db.js
Normal file
35
messages/src/models/db.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
const { Database } = require("bun:sqlite");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { mkdir } = require("node:fs/promises");
|
||||
const { stat } = require("node:fs");
|
||||
|
||||
const dbDirPath = path.join(path.dirname(__dirname), "/db");
|
||||
const dbPath = path.join(dbDirPath, "/keynotes.db");
|
||||
|
||||
async function makeDirectory(path) {
|
||||
const dirCreation = await mkdir(path);
|
||||
return dirCreation;
|
||||
}
|
||||
|
||||
// Make sure DB path exists
|
||||
stat(dbDirPath, (err, stats) => {
|
||||
if (err !== null) {
|
||||
makeDirectory(dbDirPath);
|
||||
}
|
||||
});
|
||||
|
||||
const db = new Database(dbPath);
|
||||
// Enable WAL Mode
|
||||
db.exec("PRAGMA journal_mode = WAL;");
|
||||
|
||||
const initSQL = path.join(__dirname, "./init.sql");
|
||||
fs.readFile(initSQL, "utf8", (err, data) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
db.exec(data);
|
||||
});
|
||||
|
||||
module.exports = db;
|
73
messages/src/models/init.sql
Normal file
73
messages/src/models/init.sql
Normal file
|
@ -0,0 +1,73 @@
|
|||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id INTEGER PRIMARY KEY ASC,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_profile_info (
|
||||
user_info_id INTEGER PRIMARY KEY ASC,
|
||||
email VARCHAR(50),
|
||||
user_id INTEGER REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
message_id INTEGER PRIMARY KEY ASC,
|
||||
message TEXT,
|
||||
media VARCHAR(100),
|
||||
date NUMBER,
|
||||
user_id INTEGER REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS likes (
|
||||
likes_id INTEGER PRIMARY KEY ASC,
|
||||
message_id INTEGER REFERENCES messages(message_id),
|
||||
user_id INTEGER REFERENCES users(user_id),
|
||||
liked_at NUMBER
|
||||
);
|
||||
|
||||
|
||||
|
||||
INSERT OR IGNORE INTO users (username, password)
|
||||
VALUES ("jim", "$2a$10$Bmjre5WSpSSAi.nWBfLZFOlhQhbIAoY/MM7ikJz3Ho9tqeXCExaB6"),
|
||||
("demo", "$2a$10$Vo.XmsVQVx9gGojRIdewpOap5SnhrgZ21/Im5IxhH3PC4FycM5uwC");
|
||||
|
||||
INSERT OR IGNORE INTO messages (message_id, message, media, date, user_id)
|
||||
VALUES
|
||||
(1, "When life gives you lemons, make lemonade.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(2, "Rome wasn’t built in a day – take your time.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(3, "The grass is always greener on the other side.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(4, "If it ain’t broke, don’t fix it.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(5, "You can’t have your cake and eat it too.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(6, "Better late than never.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(7, "What doesn’t kill you makes you stronger.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(8, "Every cloud has a silver lining.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(9, "Don’t bite the hand that feeds you.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(10, "Absence makes the heart grow fonder.", NULL, strftime('%s', 'now')*1000, 1),
|
||||
(11, "Always trust your gut feeling – it’s usually right.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(12, "Money can’t buy happiness, but it sure helps.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(13, "Everything happens for a reason.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(14, "Don’t go to bed angry – resolve conflicts before sleeping.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(15, "The early bird catches the worm.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(16, "You only live once – make the most of it.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(17, "Hard work pays off in the end.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(18, "Family is everything – cherish them.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(19, "Don’t put off until tomorrow what you can do today.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(20, "A smile costs nothing but gives so much.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(21, "You can’t please everyone – focus on yourself.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(22, "Health is wealth – take care of your body.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(23, "Patience is a virtue – good things take time.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(24, "Don’t judge a book by its cover.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(25, "Actions speak louder than words.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(26, "Learn from your mistakes – they make you wiser.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(27, "Kindness costs nothing but means everything.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(28, "Life is short – eat the cake.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(29, "Comparison is the thief of joy – focus on your journey.", NULL, strftime('%s', 'now')*1000, 2),
|
||||
(30, "The best things in life are free.", NULL, strftime('%s', 'now')*1000, 2);
|
||||
|
||||
-- ALTER TABLE likes
|
||||
-- ADD COLUMN user_id INTEGER REFERENCES users(user_id);
|
||||
|
||||
-- ALTER TABLE likes
|
||||
-- ADD COLUMN liked_at NUMBER;
|
||||
|
||||
-- ALTER TABLE likes DROP COLUMN sum_likes;
|
138
messages/src/models/query.js
Normal file
138
messages/src/models/query.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
const db = require("./db");
|
||||
|
||||
function getEveryNote() {
|
||||
try {
|
||||
const query = db.query(
|
||||
`SELECT messages.message_id, message, media, date, users.username AS user, messages.user_id, COUNT(likes.likes_id) AS likes_count
|
||||
FROM messages LEFT JOIN users ON messages.user_id = users.user_id
|
||||
LEFT JOIN likes ON messages.message_id = likes.message_id
|
||||
GROUP BY messages.message_id, message, media, date, users.username
|
||||
ORDER BY date DESC
|
||||
`,
|
||||
);
|
||||
|
||||
const data = query.all();
|
||||
return data;
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
function putNewNote(note) {
|
||||
const { message, media, userId } = note;
|
||||
const date = Date.now();
|
||||
const query = db.query(
|
||||
`INSERT INTO messages (message, media, user_id, date) VALUES ($1, $2, $3, $4)`,
|
||||
);
|
||||
query.run({ $1: message, $2: media, $3: userId, $4: date });
|
||||
}
|
||||
|
||||
function deleteNote(messageId) {
|
||||
try {
|
||||
const query = db.query(`DELETE FROM likes WHERE message_id = $1`);
|
||||
query.run({ $1: messageId });
|
||||
const queryTwo = db.query(`DELETE FROM messages WHERE message_id = $1`);
|
||||
queryTwo.run({ $1: messageId });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function getLikesByUser(userId) {
|
||||
const query = db.query(`SELECT message_id FROM likes WHERE user_id = $1`);
|
||||
|
||||
return query.all({ $1: userId });
|
||||
}
|
||||
|
||||
function getTotalLikesByMessageId(messageId) {
|
||||
const query = db.query(`SELECT COUNT(1) FROM likes WHERE message_id = $1`);
|
||||
return query.get({ $1: messageId });
|
||||
}
|
||||
|
||||
function getNoteById(noteId) {
|
||||
const query = db.query(
|
||||
`SELECT message, user_id from messages WHERE message_id = $1`,
|
||||
);
|
||||
return query.get({ $1: noteId });
|
||||
}
|
||||
|
||||
function checkIfNotedLiked(messageId, userId) {
|
||||
const query = db.query(
|
||||
`SELECT * FROM likes WHERE message_id = $1 and user_id = $2`,
|
||||
);
|
||||
|
||||
const res = query.get({ $1: messageId, $2: userId });
|
||||
return res;
|
||||
}
|
||||
|
||||
function insertLike(messageId, userId) {
|
||||
try {
|
||||
const query = db.query(
|
||||
`INSERT INTO likes (message_id, user_id, liked_at) VALUES ($1, $2, $3)`,
|
||||
);
|
||||
query.run({ $1: messageId, $2: userId, $3: Date.now() });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteLike(messageId, userId) {
|
||||
try {
|
||||
const query = db.query(
|
||||
`DELETE FROM likes WHERE message_id = $1 and user_id = $2`,
|
||||
);
|
||||
query.run({ $1: messageId, $2: userId });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
function insertUser(username, password) {
|
||||
try {
|
||||
const query = db.query(
|
||||
"INSERT INTO users (username, password) VALUES ($username, $password)",
|
||||
);
|
||||
query.run({ $username: username, $password: password });
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
function getUserById(userId) {
|
||||
const query = db.query(
|
||||
`SELECT username, user_id FROM users WHERE user_id = $1`,
|
||||
);
|
||||
const res = query.get({ $1: userId });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function getSumNotesByUserId(userId) {
|
||||
const query = db.query(`SELECT COUNT(1) FROM messages WHERE user_id = $1`);
|
||||
const res = query.get({ $1: userId });
|
||||
|
||||
return res["COUNT(1)"];
|
||||
}
|
||||
function getAllNotesByUserId(userId) {
|
||||
const query = db.query(`SELECT * FROM messages WHERE user_id = $1`);
|
||||
const res = query.all({ $1: userId });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getEveryNote,
|
||||
getNoteById,
|
||||
putNewNote,
|
||||
insertLike,
|
||||
deleteLike,
|
||||
deleteNote,
|
||||
checkIfNotedLiked,
|
||||
insertUser,
|
||||
getLikesByUser,
|
||||
getTotalLikesByMessageId,
|
||||
getUserById,
|
||||
getSumNotesByUserId,
|
||||
getAllNotesByUserId,
|
||||
};
|
32
messages/src/routes/authRouter.js
Normal file
32
messages/src/routes/authRouter.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
const { Router } = require("express");
|
||||
const authController = require("../controllers/authController");
|
||||
const passport = require("../middlewares/auth");
|
||||
const { body } = require("express-validator");
|
||||
|
||||
const authRouter = Router();
|
||||
|
||||
//authRouter.get("/", (req, res) => res.redirect("/auth/login"));
|
||||
authRouter.get("/login", authController.loginGet);
|
||||
authRouter.post(
|
||||
"/login",
|
||||
passport.authenticate("local", {
|
||||
successRedirect: "/",
|
||||
failureRedirect: "/auth/login",
|
||||
failureMessage: true,
|
||||
}),
|
||||
);
|
||||
authRouter.get("/logout", authController.logOut);
|
||||
authRouter.get("/register", authController.signUpGet);
|
||||
authRouter.post(
|
||||
"/register",
|
||||
body("password").isLength({ min: 5 }).withMessage("Password is too short"),
|
||||
body("password-confirmation").custom((value, { req }) => {
|
||||
if (value !== req.body.password) {
|
||||
throw new Error("Passwords do not match!");
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
authController.signUpPost,
|
||||
);
|
||||
|
||||
module.exports = { authRouter };
|
13
messages/src/routes/indexRouter.js
Normal file
13
messages/src/routes/indexRouter.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const { Router } = require("express");
|
||||
const indexController = require("../controllers/indexController");
|
||||
|
||||
const indexRouter = Router();
|
||||
|
||||
indexRouter.get("/", indexController.indexGet);
|
||||
indexRouter.get("/like", indexController.addLike);
|
||||
indexRouter.get("/new", indexController.addNoteGet);
|
||||
indexRouter.post("/new", indexController.addNotePost);
|
||||
indexRouter.get("/profile", indexController.getProfile);
|
||||
indexRouter.get("/delete", indexController.deleteNote);
|
||||
|
||||
module.exports = { indexRouter };
|
0
messages/src/views/content.ejs
Normal file
0
messages/src/views/content.ejs
Normal file
31
messages/src/views/feed.ejs
Normal file
31
messages/src/views/feed.ejs
Normal file
|
@ -0,0 +1,31 @@
|
|||
<%- include('header') %>
|
||||
<div class="grid">
|
||||
<% keynotes.forEach((note) => { %>
|
||||
<% const likedPost = userLikedPosts.find((post) => post.message_id == note.message_id) %>
|
||||
<% let extraMessageClass = "" %>
|
||||
<% if (note.message.length > 250) { %>
|
||||
<% extraMessageClass = " grid__span_two"; %>
|
||||
<% }; %>
|
||||
<div class="grid-item card<%= extraMessageClass %>">
|
||||
<p class="card__message"><%= note.message %></p>
|
||||
<div class="container grid__card__metadata">
|
||||
<div class="flex author__meta">
|
||||
<p class="card__author"><a href="/profile?userId=<%= note.user_id %>"><%= note.user %></a></p>
|
||||
<p class="card__likes">•</p>
|
||||
<p class="card__likes timestamp"><%= new Date(note.date).toLocaleString() %></p>
|
||||
</div>
|
||||
<% if (currentUser.user_id === note.user_id) { %>
|
||||
<a href="/delete?userId=<%= currentUser.user_id %>&messageId=<%= note.message_id %>">delete post</a>
|
||||
<% }; %>
|
||||
<div class="likes">
|
||||
|
||||
<a href="/like?noteId=<%= note.message_id %>">
|
||||
<% if (likedPost) { %><i class="fa-solid fa-heart red"></i><% } else { %><i class="fa-regular fa-heart red"></i><% }; %>
|
||||
</a>
|
||||
<p class="card__likes"><%= note.likes_count %></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
<%- include('footer') %>
|
12
messages/src/views/footer.ejs
Normal file
12
messages/src/views/footer.ejs
Normal file
|
@ -0,0 +1,12 @@
|
|||
<footer class="container">
|
||||
<h2 class="foooter-heading">Inspired Cliches</h2>
|
||||
<p>Making unsolicited advice inspiring.</p>
|
||||
<div class="social-icons flex">
|
||||
<a href="https://github.com/smiggiddy/odin-codeprojects" target="_blank"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="https://smig.tech" target="_blank"><i class="fa-solid fa-link"></i></a>
|
||||
</div>
|
||||
<p> © Copyright <%= new Date(Date.now()).getFullYear() %></p>
|
||||
</footer>
|
||||
<script src="/js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
36
messages/src/views/header.ejs
Normal file
36
messages/src/views/header.ejs
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<title><%= pageTitle %></title>
|
||||
<link href="/css/style.css" rel="stylesheet">
|
||||
<link rel="icon" href="/images/favicon.png">
|
||||
<!--<script src="https://kit.fontawesome.com/24f16b96cf.js" crossorigin="anonymous"></script>-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul class="container flex">
|
||||
<li><a class="hover__underline" href="/">Home</a></li>
|
||||
<% if (currentUser) { %>
|
||||
<li class="nav__profile"><%= currentUser.username %>
|
||||
<div class="hidden nav__hover">
|
||||
<ul class="container flex col">
|
||||
<li><a class="hover__underline" href="/profile?userId=<%= currentUser.user_id %>">view profile</a></li>
|
||||
<li><a class="hover__underline" href="/new" class="new-link">new cliche</a></li>
|
||||
<li><a class="hover__underline" href="/auth/logout">logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li><a class="hover__underline" href="/auth/login">Login</a></li>
|
||||
<% }; %>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="divider"></div>
|
||||
|
49
messages/src/views/home.ejs
Normal file
49
messages/src/views/home.ejs
Normal file
|
@ -0,0 +1,49 @@
|
|||
<%- include('header') %>
|
||||
<% if (currentUser) { %>
|
||||
|
||||
<h1> Hi <%= currentUser.username %> </h1>
|
||||
|
||||
<% } %>
|
||||
<div class="container flex hero">
|
||||
|
||||
<div class="container hero">
|
||||
<h1 class="hero__heading">Unlock Your Potential, One cliche at a time.</h1>
|
||||
<p class="hero__subheading">Read a cliche a day, to keep the brain at bay</p>
|
||||
<a href="/auth/login" type="button" class="btn btn__link">Start Today</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<section id="main">
|
||||
<div class="container flex">
|
||||
<div class="card">
|
||||
<h2>Lorem</h2>
|
||||
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
|
||||
</div>
|
||||
<div class="img">
|
||||
<p>This is an image</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container flex">
|
||||
<div class="img">
|
||||
<p>This is an image</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Lorem</h2>
|
||||
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container flex">
|
||||
<div class="card">
|
||||
<h2>Lorem</h2>
|
||||
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p>
|
||||
</div>
|
||||
<div class="img">
|
||||
<p>This is an image</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%- include('footer') %>
|
30
messages/src/views/login.ejs
Normal file
30
messages/src/views/login.ejs
Normal file
|
@ -0,0 +1,30 @@
|
|||
<%- include('header') %>
|
||||
<% if (locals.errors) {%>
|
||||
<div class="center-horizontal">
|
||||
<ul>
|
||||
<% errors.forEach(function(error) { %>
|
||||
<li><%= error %></li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</div>
|
||||
<% }; %>
|
||||
<main class="container full-page">
|
||||
<form class="center-horizontal" method="POST" action="/auth/login">
|
||||
<div class="form-item flex">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" required/>
|
||||
</div>
|
||||
<div class="form-item flex">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" required/>
|
||||
</div>
|
||||
<div class="form-item-row flex">
|
||||
<button type="submit" class="btn btn__form">Login</button>
|
||||
</div>
|
||||
<p>Need an account?
|
||||
<a href="/auth/register">Click here!</a>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
</main>
|
||||
<%- include('footer') %>
|
19
messages/src/views/note-form.ejs
Normal file
19
messages/src/views/note-form.ejs
Normal file
|
@ -0,0 +1,19 @@
|
|||
<%- include("header") %>
|
||||
<form action="/new" method="POST" class="center-horizontal">
|
||||
<div class="form-item flex">
|
||||
<label for="message">Message</label>
|
||||
<textarea type="text" name="message" required></textarea>
|
||||
</div>
|
||||
<div class="form-item flex">
|
||||
<label for="media">Media</label>
|
||||
<input type="text" name="media" />
|
||||
</div>
|
||||
<div class="form-item flex col">
|
||||
<button type="submit" class="btn">Post</button>
|
||||
<button type="button" class="btn" onClick="document.location.href='/'">Back</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<%- include("footer") %>
|
35
messages/src/views/register.ejs
Normal file
35
messages/src/views/register.ejs
Normal file
|
@ -0,0 +1,35 @@
|
|||
<%- include('header') %>
|
||||
<!-- views/partials/errors.ejs -->
|
||||
<% if (locals.errors) {%>
|
||||
<div class="center-horizontal">
|
||||
<ul>
|
||||
<% errors.forEach(function(error) { %>
|
||||
<li><%= error.msg %></li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</div>
|
||||
<% }; %>
|
||||
<main class="container full-page">
|
||||
<form class="register-form center-horizontal" method="POST" action="/auth/register">
|
||||
<div class="form-item flex">
|
||||
<label for="username">username</label>
|
||||
<input type="text" name="username" required/>
|
||||
<div class="error"></div>
|
||||
</div>
|
||||
<div class="form-item flex">
|
||||
<label for="password">password</label>
|
||||
<input type="password" name="password" required/>
|
||||
<div class="error flex col"></div>
|
||||
</div>
|
||||
<div class="form-item flex">
|
||||
<label for="password-confirmation">confirm password</label>
|
||||
<input type="password" name="password-confirmation" required/>
|
||||
<div class="error"></div>
|
||||
</div>
|
||||
<div class="form-item flex">
|
||||
<button type="submit" class="btn btn__form">register</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</main>
|
||||
<%- include('footer') %>
|
20
messages/src/views/view-profile.ejs
Normal file
20
messages/src/views/view-profile.ejs
Normal file
|
@ -0,0 +1,20 @@
|
|||
<%- include("header") %>
|
||||
|
||||
<div class="full-page container center-horizontal">
|
||||
<div class="container">
|
||||
<h1 class="username"><%= user.username %></h1>
|
||||
<% if(currentUser.user_id === user.user_id ) { %>
|
||||
<p>This is your profile page.</p>
|
||||
<% }; %>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2><%= user.username %>'s <%= totalPosts %> post(s): </h2>
|
||||
<% allPosts.forEach((post) => { %>
|
||||
<div class="container">
|
||||
<p>"<%= post.message %>"</p>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<%- include("footer") %>
|
Loading…
Add table
Add a link
Reference in a new issue