mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
146 lines
4.0 KiB
V
146 lines
4.0 KiB
V
// Simple TODO app using x.vweb
|
|
// Run from this directory with `v run main.v`
|
|
// You can also enable vwebs livereload feature with
|
|
// `v watch -d vweb_livereload run main.v`
|
|
module main
|
|
|
|
import x.vweb
|
|
import db.sqlite
|
|
import os
|
|
import time
|
|
|
|
struct Todo {
|
|
pub mut:
|
|
// `id` is the primary field. The attribute `sql: serial` acts like AUTO INCREMENT in sql.
|
|
// You can use this attribute if you want a unique id for each row.
|
|
id int @[primary; sql: serial]
|
|
name string
|
|
completed bool
|
|
created time.Time
|
|
updated time.Time
|
|
}
|
|
|
|
pub struct Context {
|
|
vweb.Context
|
|
pub mut:
|
|
// we can use this field to check whether we just created a TODO in our html templates
|
|
created_todo bool
|
|
}
|
|
|
|
pub struct App {
|
|
vweb.StaticHandler
|
|
pub:
|
|
// we can access the SQLITE database directly via `app.db`
|
|
db sqlite.DB
|
|
}
|
|
|
|
// This method will only handle GET requests to the index page
|
|
@[get]
|
|
pub fn (app &App) index(mut ctx Context) vweb.Result {
|
|
todos := sql app.db {
|
|
select from Todo
|
|
} or { return ctx.server_error('could not fetch todos from database!') }
|
|
return $vweb.html()
|
|
}
|
|
|
|
// This method will only handle POST requests to the index page
|
|
@['/'; post]
|
|
pub fn (app &App) create_todo(mut ctx Context, name string) vweb.Result {
|
|
// We can receive form input fields as arguments in a route!
|
|
// we could also access the name field by doing `name := ctx.form['name']`
|
|
|
|
// validate input field
|
|
if name == '' {
|
|
// set a form error
|
|
ctx.form_error = 'You must fill in all the fields!'
|
|
// send a HTTP 400 response code indicating that the form fields are incorrect
|
|
ctx.res.set_status(.bad_request)
|
|
// render the home page
|
|
return app.index(mut ctx)
|
|
}
|
|
|
|
// create a new todo
|
|
todo := Todo{
|
|
name: name
|
|
created: time.now()
|
|
updated: time.now()
|
|
}
|
|
|
|
// insert the todo into our database
|
|
sql app.db {
|
|
insert todo into Todo
|
|
} or { return ctx.server_error('could not insert a new TODO in the database') }
|
|
|
|
ctx.created_todo = true
|
|
|
|
// render the home page
|
|
return app.index(mut ctx)
|
|
}
|
|
|
|
@['/todo/:id/complete'; post]
|
|
pub fn (app &App) complete_todo(mut ctx Context, id int) vweb.Result {
|
|
// first check if there exist a TODO record with `id`
|
|
todos := sql app.db {
|
|
select from Todo where id == id
|
|
} or { return ctx.server_error("could not fetch TODO's") }
|
|
if todos.len == 0 {
|
|
// return HTTP 404 when the TODO does not exist
|
|
ctx.res.set_status(.not_found)
|
|
return ctx.text('There is no TODO item with id=${id}')
|
|
}
|
|
|
|
// update the TODO field
|
|
sql app.db {
|
|
update Todo set completed = true, updated = time.now() where id == id
|
|
} or { return ctx.server_error('could not update TODO') }
|
|
|
|
// redirect client to the home page and tell the browser to sent a GET request
|
|
return ctx.redirect('/', typ: .see_other)
|
|
}
|
|
|
|
@['/todo/:id/delete'; post]
|
|
pub fn (app &App) delete_todo(mut ctx Context, id int) vweb.Result {
|
|
// first check if there exist a TODO record with `id`
|
|
todos := sql app.db {
|
|
select from Todo where id == id
|
|
} or { return ctx.server_error("could not fetch TODO's") }
|
|
if todos.len == 0 {
|
|
// return HTTP 404 when the TODO does not exist
|
|
ctx.res.set_status(.not_found)
|
|
return ctx.text('There is no TODO item with id=${id}')
|
|
}
|
|
|
|
// prevent hackers from deleting TODO's that are not completed ;)
|
|
to_be_deleted := todos[0]
|
|
if !to_be_deleted.completed {
|
|
return ctx.request_error('You must first complete a TODO before you can delete it!')
|
|
}
|
|
|
|
// delete the todo
|
|
sql app.db {
|
|
delete from Todo where id == id
|
|
} or { return ctx.server_error('could not delete TODO') }
|
|
|
|
// redirect client to the home page and tell the browser to sent a GET request
|
|
return ctx.redirect('/', typ: .see_other)
|
|
}
|
|
|
|
fn main() {
|
|
os.chdir(os.dir(@FILE))!
|
|
// create a new App instance with a connection to the database
|
|
mut app := &App{
|
|
db: sqlite.connect('todo.db')!
|
|
}
|
|
|
|
// mount the assets folder at `/assets/`
|
|
app.handle_static('assets', false)!
|
|
|
|
// create the table in our database, if it doesn't exist
|
|
sql app.db {
|
|
create table Todo
|
|
}!
|
|
|
|
// start our app at port 8080
|
|
vweb.run[App, Context](mut app, 8080)
|
|
}
|