add initial user and post APIs for login/posting/adding users
This commit is contained in:
parent
5f4f8ed144
commit
cb02089224
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
||||
BLOG_NAME=Blog
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -31,7 +31,7 @@ yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
.env
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
@ -39,3 +39,11 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# bun
|
||||
bun.lockb
|
||||
|
||||
# server
|
||||
server/db.sqlite
|
||||
server/package-lock.json
|
||||
server/node_modules
|
11
app/admin/login/page.tsx
Normal file
11
app/admin/login/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { LoginForm } from "@/components/login-form"
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
|
||||
<div className="w-full max-w-sm">
|
||||
<LoginForm />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -30,10 +30,28 @@ export default function CreatePost() {
|
||||
setContent(value || "")
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
const date = Math.floor(Date.now() / 1000);
|
||||
e.preventDefault()
|
||||
// TODO: handle form submission here!
|
||||
console.log({ title, description, category, slug, content })
|
||||
try {
|
||||
const response = await fetch('http://localhost:3001/api/posts/new', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ title, description, category, slug, content, date }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create post');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Success:', data);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
console.log({ title, description, category, slug, content, date })
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -8,14 +8,31 @@ import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
|
||||
export default function CreateUser() {
|
||||
const [name, setName] = useState("")
|
||||
const [username, setUsername] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
// TODO: handle form submission here!
|
||||
console.log({ name, email, password })
|
||||
try {
|
||||
const response = await fetch('http://localhost:3001/api/users/add', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, email, password }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create user');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Success:', data);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
console.log({ username, email, password })
|
||||
}
|
||||
|
||||
return (
|
||||
@ -28,11 +45,11 @@ export default function CreateUser() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">{strings.newUserNameFieldLabel}</Label>
|
||||
<Label htmlFor="username">{strings.newUserNameFieldLabel}</Label>
|
||||
<Input
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
id="username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder={strings.newUserNameFieldPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
|
11
app/login/page.tsx
Normal file
11
app/login/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { LoginForm } from "@/components/login-form"
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
|
||||
<div className="w-full max-w-sm">
|
||||
<LoginForm />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
88
components/login-form.tsx
Normal file
88
components/login-form.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
|
||||
export function LoginForm({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<"div">) {
|
||||
const [username, setUsername] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const response = await fetch('http://localhost:3001/api/admin/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('Failed to login');
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log('Success:', data)
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Administration Panel</CardTitle>
|
||||
<CardDescription>
|
||||
Please authenticate with your credentials.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input
|
||||
id="username"
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full">
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
77
server/index.js
Normal file
77
server/index.js
Normal file
@ -0,0 +1,77 @@
|
||||
import express from 'express';
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { open } from 'sqlite';
|
||||
import cors from 'cors';
|
||||
|
||||
const app = express();
|
||||
const port = 3001;
|
||||
|
||||
app.use(cors(
|
||||
{
|
||||
origin: 'http://localhost:3000',
|
||||
credentials: true
|
||||
}
|
||||
));
|
||||
|
||||
let db;
|
||||
|
||||
(async () => {
|
||||
db = await open({
|
||||
filename: './db.sqlite',
|
||||
driver: sqlite3.Database
|
||||
});
|
||||
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT,
|
||||
email TEXT,
|
||||
password TEXT
|
||||
);
|
||||
`);
|
||||
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
description TEXT,
|
||||
category TEXT,
|
||||
slug TEXT,
|
||||
content TEXT,
|
||||
date TEXT
|
||||
);
|
||||
`);
|
||||
})();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.post('/api/admin/login', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
try {
|
||||
const user = await db.get('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
|
||||
if (user) {
|
||||
res.json({ success: true, user });
|
||||
} else {
|
||||
res.status(401).json({ success: false, message: 'Invalid credentials' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error logging in:', error);
|
||||
res.status(500).json({ success: false, message: 'Server error' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/users/add', async (req, res) => {
|
||||
const { username, email, password } = req.body;
|
||||
const result = await db.run('INSERT INTO users (username, email, password) VALUES (?, ?, ?)', [username, email, password]);
|
||||
res.json({ id: result.lastID });
|
||||
});
|
||||
|
||||
app.post('/api/posts/new', async (req, res) => {
|
||||
const { title, description, category, slug, content, date } = req.body;
|
||||
const result = await db.run('INSERT INTO posts (title, description, category, slug, content, date) VALUES (?, ?, ?, ?, ?, ?)', [title, description, category, slug, content, date]);
|
||||
res.json({ id: result.lastID });
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running on http://localhost:${port}`);
|
||||
});
|
30
server/package.json
Normal file
30
server/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ihatenodejs/blogpop.git"
|
||||
},
|
||||
"author": "ihatenodejs",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ihatenodejs/blogpop/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ihatenodejs/blogpop#readme",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.21.2",
|
||||
"sqlite": "^5.1.1",
|
||||
"sqlite3": "^5.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user