forked from ccc112a/ws112a
-
Notifications
You must be signed in to change notification settings - Fork 0
Midterm
Rshark edited this page Jan 9, 2024
·
4 revisions
全複製自參考網站 已閱讀完畢且理解 參考資料
*app.js
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import { DB } from "https://deno.land/x/sqlite/mod.ts";
import { Session } from "https://deno.land/x/oak_sessions/mod.ts";
import * as render from './render.js';
const db = new DB("blog.db");
db.query("CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, title TEXT, body TEXT)");
db.query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT, email TEXT)");
const router = new Router();
router.get('/', list)
.get('/signup', signupUi)
.post('/signup', signup)
.get('/login', loginUi)
.post('/login', login)
.get('/logout', logout)
.get('/contact/search', search)
.get('/contact/new', add)
.get('/contact/:id', show)
.post('/contact', create)
.post('/search', find)
.get('/contact/delete/:id', deleteConfirmation)
.post('/contact/delete/:id', deleteContact);
const app = new Application();
app.use(Session.initMiddleware());
app.use(router.routes());
app.use(router.allowedMethods());
function sqlcmd(sql, arg1) {
console.log('sql:', sql)
try {
var results = db.query(sql, arg1)
console.log('sqlcmd: results=', results)
return results
} catch (error) {
console.log('sqlcmd error: ', error)
throw error
}
}
function postQuery(sql) {
let list = []
for (const [id, username, title, body] of sqlcmd(sql)) {
list.push({id, username, title, body})
}
console.log('postQuery: list=', list)
return list
}
function userQuery(sql) {
let list = []
for (const [id, username, password, email] of sqlcmd(sql)) {
list.push({id, username, password, email})
}
console.log('userQuery: list=', list)
return list
}
async function parseFormBody(body) {
const pairs = await body.value
const obj = {}
for (const [key, value] of pairs) {
obj[key] = value
}
return obj
}
async function signupUi(ctx) {
ctx.response.body = await render.signupUi();
}
async function signup(ctx) {
const body = ctx.request.body()
if (body.type === "form") {
var user = await parseFormBody(body)
var dbUsers = userQuery(`SELECT id, username, password, email FROM users WHERE username='${user.username}'`)
if (dbUsers.length === 0) {
sqlcmd("INSERT INTO users (username, password, email) VALUES (?, ?, ?)", [user.username, user.password, user.email]);
ctx.response.body = render.success()
} else
ctx.response.body = render.fail()
}
}
async function loginUi(ctx) {
ctx.response.body = await render.loginUi();
}
async function login(ctx) {
const body = ctx.request.body();
if (body.type === "form") {
const userCredentials = await parseFormBody(body);
const dbUsers = userQuery(`SELECT id, username, password, email FROM users WHERE username='${userCredentials.username}'`);
if (dbUsers.length > 0) {
const dbUser = dbUsers[0];
if (dbUser.password === userCredentials.password) {
await ctx.state.session.set('user', { username: dbUser.username, id: dbUser.id }); // Set the user in the session
console.log('session.user=', await ctx.state.session.get('user'));
ctx.response.redirect('/');
} else {
ctx.response.body = render.fail();
}
} else {
ctx.response.body = render.userNotFound();
}
}
}
async function logout(ctx) {
await ctx.state.session.set('user', null); // Clear the user in the session
ctx.response.redirect('/');
}
async function list(ctx) {
const posts = postQuery("SELECT id, username, title, body FROM posts");
const user = await ctx.state.session.get('user');
console.log('list: user=', user);
console.log('list: posts=', posts);
ctx.response.body = await render.list(posts, user);
}
async function add(ctx) {
var user = await ctx.state.session.get('user')
if (user != null) {
ctx.response.body = await render.newPost();
} else {
ctx.response.body = render.fail()
}
}
async function search(ctx) {
ctx.response.body = await render.search();
}
async function show(ctx) {
const pid = ctx.params.id;
let posts = postQuery(`SELECT id, username, title, body FROM posts WHERE id=${pid}`)
let post = posts[0]
console.log('show:post=', post)
if (!post) ctx.throw(404, 'invalid post id');
ctx.response.body = await render.show(post);
}
async function create(ctx) {
const body = ctx.request.body()
if (body.type === "form") {
var post = await parseFormBody(body)
console.log('create:post=', post)
var user = await ctx.state.session.get('user')
if (user != null) {
console.log('user=', user)
sqlcmd("INSERT INTO posts (username, title, body) VALUES (?, ?, ?)", [user.username, post.title, post.body]);
} else {
ctx.throw(404, 'not login yet!');
}
ctx.response.redirect('/');
}
}
async function find(ctx) {
const body = ctx.request.body();
if (body.type === "form") {
const pairs = await body.value;
const searchTerm = pairs.get('name');
const results = [];
let posts = postQuery("SELECT id, username, title, body FROM posts");
for (const post of posts) {
if (post.title.toLowerCase().includes(searchTerm.toLowerCase())) {
results.push(post);
}
}
console.log('Search Term:', searchTerm);
if (results.length > 0) {
const resultHtml = results.map(post => `<h1>Name:${post.title}</h1><p>Tel:${post.body}</p>`).join('');
ctx.response.body = await render.found(resultHtml);
} else {
ctx.response.body = await render.not_found();
}
}
}
async function deleteConfirmation(ctx) {
const pid = ctx.params.id;
const post = postQuery(`SELECT id, username, title, body FROM posts WHERE id=${pid}`)[0];
if (!post) {
ctx.throw(404, 'Invalid post id');
}
ctx.response.body = await render.deleteConfirmation(post);
}
async function deleteContact(ctx) {
const pid = ctx.params.id;
sqlcmd("DELETE FROM posts WHERE id=?", [pid]);
ctx.response.redirect('/');
}
console.log('Server run at http://127.0.0.1:8000')
await app.listen({ port: 8000 });
*render.js
export function layout(title, content, user) {
return `
<html>
<head>
<title>${title}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-zoF5xaUrnT5Vps1ttX2iCP6B6yA1keRdB9Qo1uWEs21F9vRoLybpBm8xD5NngA6gPLFV/hgtfJC8ed8Cq58wuQ==" crossorigin="anonymous" />
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
}
header {
background-color: #4285f4;
color: #fff;
text-align: center;
padding: 1em 0;
display: flex;
justify-content: space-between;
align-items: center;
}
header a {
text-decoration: none;
color: #fff;
padding: 0.5em 1em;
transition: background-color 0.3s ease;
}
header a:hover {
background-color: #3367d6;
}
main {
max-width: 600px;
margin: 20px auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
form {
text-align: center;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 20px;
}
form input {
width: calc(100% - 20px);
padding: 10px;
margin-bottom: 10px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 5px;
}
form input[type=submit], button {
background-color: #4285f4;
color: #fff;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
}
form input[type=submit]:hover, button:hover {
background-color: #3367d6;
transform: scale(1.05);
}
a {
color: #4285f4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.add-contact-button {
background-color: #4caf50;
color: #fff;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
margin-top: 20px; /* Adjust margin as needed */
}
.add-contact-button:hover {
background-color: #45a049;
transform: scale(1.05);
}
</style>
</head>
<body>
<header>
<div>
<button onclick="window.location.href='/'">Home</button>
</div>
<h1>${title}</h1>
<div>
${user ? `<button onclick="window.location.href='/logout'">Logout</button>` : `<button onclick="window.location.href='/login'">Login</button>`}
</div>
</header>
<main>
<section id="content">
${content}
</section>
</main>
</body>
</html>
`;
}
export function loginUi() {
return layout('Login', `
<form action="/login" method="post">
<input type="text" placeholder="Username" name="username">
<input type="password" placeholder="Password" name="password">
<input type="submit" value="Login">
</form>
<p>New User? <a href="/signup">Create an account</a></p>
`);
}
export function signupUi() {
return layout('Signup', `
<form action="/signup" method="post">
<input type="text" placeholder="Username" name="username">
<input type="password" placeholder="Password" name="password">
<input type="text" placeholder="E-mail" name="email">
<input type="submit" value="Signup">
</form>
`);
}
export function success() {
return layout('Success!', `
You may <a href="/">view all Contacts</a> / <a href="/login">login</a> again!
`);
}
export function fail() {
return layout('Fail!', `
You may <a href="/">view all Contacts</a> or <a href="JavaScript:window.history.back()">go back</a>!
`);
}
export function userNotFound() {
return layout('User Not Found!', `
The provided username does not exist. Please check your username and try again.
<p>You may <a href="/">view all Contacts</a> or <a href="JavaScript:window.history.back()">go back</a>!
`);
}
// ...
export function list(posts, user) {
let list = posts.map(post => {
const deleteLink = (user && post.username === user.username)
? ` | <a href="/contact/delete/${post.id}"><i class="fas fa-trash-alt"></i> Delete</a>`
: '';
return `
<li>
<h2><a href="/contact/${post.id}">${post.title} -- by ${post.username}</a></h2>
<p>
<a href="/contact/${post.id}">View contact</a>
${deleteLink}
</p>
</li>
`;
});
let content = `
<h1>Contacts</h1>
<div id="search-bar">
<form action="/search" method="post">
<input type="text" placeholder="Search Contacts" name="name" required>
<input type="submit" value="Search">
</form>
</div>
<p>${(user == null) ? '<a href="/login">Login</a> to Add a Contact!' : `Welcome ${user.username}!`}</p>
<button onclick="window.location.href='/contact/new'" class="add-contact-button">Add a Contact</button> <!-- Add this line -->
<p>There are <strong>${posts.length}</strong> Contacts!</p>
<ul id="posts">
${list.join('\n')}
</ul>
`;
return layout('Directory', content, user); // Pass user to layout function
}
export function newPost() {
return layout('New Contact', `
<h1>New Contact</h1>
<p>Add a new contact.</p>
<form action="/contact" method="post">
<p><input type="text" placeholder="Name" name="title"></p>
<p><textarea placeholder="Tel" name="body"></textarea></p>
<p><input type="submit" value="Create"></p>
</form>
<button onclick="window.location.href='/contact/new'" class="add-contact-button">Add a Contact</button>
`);
}
export function search() {
return layout('Query Contact person', `
<h1>Search Contacts</h1>
<form action="/search" method="post">
<p><input type="text" placeholder="Name" name="name" required></p>
<p><input type="submit" value="Search"></p>
</form>
`)
}
export function found(resultHtml) {
return layout('Search results', `
<h1>Search Contacts</h1>
<form action="/search" method="post">
<p><input type="text" placeholder="Name" name="name"></p>
<p><input type="submit" value="Search"></p>
</form>
${resultHtml}
`);
}
export function not_found() {
return layout('Search results',
`
<h1>Search Contacts</h1>
<form action="/search" method="post">
<p><input type="text" placeholder="Name" name="name"></p>
<p><input type="submit" value="Search"></p>
</form>
<h1>Not Found</h1>
`,
);
}
export function deleteConfirmation(post) {
return layout('Confirm Deletion', `
<h1>Delete Confirmation</h1>
<p>Are you sure you want to delete the contact?</p>
<p><strong>${post.title}</strong> -- by ${post.username}</p>
<form action="/contact/delete/${post.id}" method="post">
<p><input type="submit" value="Delete"></p>
</form>
`);
}
export function show(post) {
const deleteLink = `<a href="/contact/delete/${post.id}">Delete</a>`;
return layout(post.title, `
<h1>${post.title} -- by ${post.username}</h1>
<p>${post.body}</p>
<p>${deleteLink}</p>
`);
}