This commit is contained in:
yubintw
2024-03-25 21:39:05 +08:00
commit 9df48062d4
40 changed files with 15864 additions and 0 deletions

21
backend/src/index.ts Normal file
View File

@@ -0,0 +1,21 @@
import * as dotenv from 'dotenv'
import { AppConfig } from './types/config'
import { serverOf, serverStart } from './server'
dotenv.config()
const server = serverOf()
const appConfig: AppConfig = {
host: process.env.HOST || 'localhost',
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
mongoConnectionString: process.env.MONGO_CONNECTION_STRING || ''
}
serverStart(appConfig)(server)
.then(() => {
console.log(`Server listening on ${appConfig.host}:${appConfig.port}`)
})
.catch((err) => {
console.error(err)
})

View File

@@ -0,0 +1,30 @@
import mongoose from 'mongoose'
import { Todo } from '../types/todo'
const todoSchema: mongoose.Schema = new mongoose.Schema(
{
name: {
type: String,
required: true
},
description: {
type: String,
default: ''
},
status: {
type: Boolean,
required: true
}
},
{
timestamps: true
}
)
todoSchema.set('toJSON', {
virtuals: true,
versionKey: false
})
export default mongoose.models.Todo || mongoose.model<Todo>('Todo', todoSchema)

View File

@@ -0,0 +1,3 @@
import mongoose from 'mongoose'
export const establishConnection = (connectionString: string) => mongoose.connect(connectionString)

14
backend/src/repo/todo.ts Normal file
View File

@@ -0,0 +1,14 @@
import { ModifyResult, UpdateQuery } from 'mongoose'
import TodoModel from '../models/todo'
import { Todo, TodoBody } from '../types/todo'
export const findAllTodos: () => Promise<Array<Todo>> = () => TodoModel.find().exec()
export const createTodo: (todoBody: TodoBody) => Promise<Todo> = (todoBody) => TodoModel.create(todoBody)
export const updateTodoById: (id: string, update: UpdateQuery<TodoBody>) => Promise<Todo | null> = (id, update) =>
TodoModel.findByIdAndUpdate(id, update, { new: true })
export const deleteTodoById: (id: string) => Promise<ModifyResult<Todo>> = (id) =>
TodoModel.findByIdAndDelete(id).exec()

View File

@@ -0,0 +1,67 @@
import { FastifyInstance, RouteShorthandOptions } from 'fastify'
import { addTodo, deleteTodo, getTodos, updateTodoStatus } from '../services/todo'
import { TodoBody } from '../types/todo'
export const TodoRouter = (server: FastifyInstance, opts: RouteShorthandOptions, done: (error?: Error) => void) => {
interface IdParam {
id: string
}
interface StatusBody {
status: boolean
}
server.get('/v1/todos', async (request, reply) => {
try {
const todos = await getTodos()
return reply.status(200).send({ todos })
} catch (error) {
server.log.error(`GET /v1/todos Error: ${error}`)
return reply.status(500).send(`[Server Error]: ${error}`)
}
})
server.post<{ Body: TodoBody }>('/v1/todos', async (request, reply) => {
try {
const todoBody = request.body
const todo = await addTodo(todoBody)
return reply.status(201).send({ todo })
} catch (error) {
server.log.error(`POST /v1/todos Error: ${error}`)
return reply.status(500).send(`[Server Error]: ${error}`)
}
})
server.put<{ Params: IdParam; Body: StatusBody }>('/v1/todos/:id', opts, async (request, reply) => {
try {
const id = request.params.id
const status = request.body.status
const todo = await updateTodoStatus(id, status)
if (todo) {
return reply.status(200).send({ todo })
} else {
return reply.status(404).send({ msg: `Not Found Todo:${id}` })
}
} catch (error) {
server.log.error(`PUT /v1/todos/${request.params.id} Error: ${error}`)
return reply.status(500).send(`[Server Error]: ${error}`)
}
})
server.delete<{ Params: IdParam }>('/v1/todos/:id', opts, async (request, reply) => {
try {
const id = request.params.id
const todo = await deleteTodo(id)
if (todo) {
return reply.status(204).send()
} else {
return reply.status(404).send({ msg: `Not Found Todo:${id}` })
}
} catch (error) {
server.log.error(`DELETE /v1/todos/${request.params.id} Error: ${error}`)
return reply.status(500).send(`[Server Error]: ${error}`)
}
})
done()
}

27
backend/src/server.ts Normal file
View File

@@ -0,0 +1,27 @@
import fastify, { FastifyInstance, FastifyListenOptions } from 'fastify'
import { AppConfig } from './types/config'
import { establishConnection } from './plugins/mongodb'
import { TodoRouter } from './routes/todo'
export const serverOf: () => FastifyInstance = () => {
const server = fastify()
server.get('/ping', async (request, reply) => {
return reply.status(200).send({ msg: 'pong!' })
})
server.register(TodoRouter, { prefix: '/api' })
return server
}
export const serverStart: (appConfig: AppConfig) => (server: FastifyInstance) => Promise<FastifyInstance> =
(appConfig) => async (server) => {
await establishConnection(appConfig.mongoConnectionString)
const listenOptions: FastifyListenOptions = {
port: appConfig.port,
host: appConfig.host
}
await server.listen(listenOptions)
return server
}

View File

@@ -0,0 +1,25 @@
import { ModifyResult } from 'mongoose'
import * as repo from '../repo/todo'
import { Todo, TodoBody } from '../types/todo'
export const getTodos: () => Promise<Array<Todo>> = async () => {
const todos = await repo.findAllTodos()
return todos
}
export const addTodo: (todoBody: TodoBody) => Promise<Todo> = async (todoBody) => {
const newTodo = await repo.createTodo({
...todoBody
})
return newTodo
}
export const updateTodoStatus: (id: string, newStatus: boolean) => Promise<Todo | null> = async (id, newStatus) => {
const todo = await repo.updateTodoById(id, { status: newStatus })
return todo
}
export const deleteTodo: (id: string) => Promise<ModifyResult<Todo>> = async (id) => {
const result = await repo.deleteTodoById(id)
return result
}

View File

@@ -0,0 +1,5 @@
export type AppConfig = {
port: number
host: string
mongoConnectionString: string
}

View File

@@ -0,0 +1,8 @@
export type Todo = {
id: string
name: string
description: string
status: boolean
}
export type TodoBody = Omit<Todo, 'id'>