/
main.v
145 lines (122 loc) · 3.98 KB
/
main.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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 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!') }
// TODO: use $vweb.html()
return ctx.html($tmpl('templates/index.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.len == 0 {
// 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 datbase') }
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('/', .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 == false {
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('/', .see_other)
}
fn main() {
// create a new App instance with a connection to the datbase
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)
}