veb: deprecate vweb (part 2); move all vweb examples to veb

This commit is contained in:
Alexander Medvednikov 2025-03-02 14:37:00 +03:00
parent 17fc31b746
commit 8683e634ee
36 changed files with 101 additions and 192 deletions

View File

@ -363,7 +363,7 @@ With V's `vab` tool, building V UI and graphical apps for Android can become as
## Developing web applications
Check out the
[Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md)
[Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/README.md)
tutorial and Gitly, a light and fast alternative to GitHub/GitLab:
https://github.com/vlang/gitly

View File

@ -115,7 +115,7 @@ fn run_vsh_script_tcc() {
}
fn test_v_tutorials_tcc() {
exec('v tutorials/building_a_simple_web_blog_with_vweb/code/blog')
exec('v tutorials/building_a_simple_web_blog_with_veb/code/blog')
}
fn build_fast_tcc() {
@ -228,7 +228,7 @@ fn build_tetris_autofree_gcc() {
}
fn build_blog_autofree_gcc() {
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog')
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_veb/code/blog')
}
fn build_option_test_autofree_gcc() {

View File

@ -76,7 +76,7 @@ fn build_tetris_autofree() {
}
fn build_blog_autofree() {
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog')
exec('v -autofree -o blog tutorials/building_a_simple_web_blog_with_veb/code/blog')
}
fn build_examples_prod() {

View File

@ -0,0 +1,40 @@
module main
import veb
const port = 8082
struct App {
}
struct Context {
veb.Context
}
fn main() {
mut app := &App{}
veb.run[App, Context](mut app, port)
}
pub fn (mut app App) index() veb.Result {
return $veb.html()
}
@['/upload'; post]
pub fn (mut app App) upload() veb.Result {
dump(ctx.form)
dump(ctx.files)
fdata := ctx.files['upfile']
mut files := []veb.RawHtml{}
for d in fdata {
files << d.data.replace_each(['\n', '<br>', '\n\r', '<br>', '\t', ' ', ' ', '&nbsp;'])
}
return $veb.html()
}
@['/submit'; post]
pub fn (mut app App) submit() veb.Result {
dump(ctx.form)
form_data := ctx.form.clone()
return $veb.html()
}

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,44 @@
module main
import os
import rand
import time
import veb
import veb.sse
struct App {
veb.StaticHandler
}
struct Context {
veb.Context
}
fn main() {
mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico')!
app.mount_static_folder_at(os.resource_abs_path('.'), '/')!
veb.run[App, Context](mut app, 8081)
}
// XTODO template broken (@)
pub fn (mut app App) index() veb.Result {
title := 'SSE Example'
return $veb.html()
}
fn (mut app App) sse() veb.Result {
mut session := sse.start_connection(mut ctx.Context)
// Note: you can setup session.write_timeout and session.headers here
// session.start() or { return app.server_error(501) }
session.send_message(data: 'ok') or { return ctx.server_error_with_status(.not_implemented) }
for {
data := '{"time": "${time.now().str()}", "random_id": "${rand.ulid()}"}'
session.send_message(event: 'ping', data: data) or {
return ctx.server_error_with_status(.not_implemented)
}
println('> sent event: ${data}')
time.sleep(1 * time.second)
}
return ctx.server_error_with_status(.not_implemented)
}

View File

@ -1,37 +0,0 @@
module main
import vweb
const port = 8082
struct App {
vweb.Context
}
fn main() {
vweb.run(&App{}, port)
}
pub fn (mut app App) index() vweb.Result {
return $vweb.html()
}
@['/upload'; post]
pub fn (mut app App) upload() vweb.Result {
dump(app.form)
dump(app.files)
fdata := app.files['upfile']
mut files := []vweb.RawHtml{}
for d in fdata {
files << d.data.replace_each(['\n', '<br>', '\n\r', '<br>', '\t', ' ', ' ', '&nbsp;'])
}
return $vweb.html()
}
@['/submit'; post]
pub fn (mut app App) submit() vweb.Result {
dump(app.form)
form_data := app.form.clone()
return $vweb.html()
}

View File

@ -1,37 +0,0 @@
module main
import os
import rand
import time
import vweb
import vweb.sse
struct App {
vweb.Context
}
fn main() {
mut app := &App{}
app.serve_static('/favicon.ico', 'favicon.ico')
app.mount_static_folder_at(os.resource_abs_path('.'), '/')
vweb.run(app, 8081)
}
pub fn (mut app App) index() vweb.Result {
title := 'SSE Example'
return $vweb.html()
}
fn (mut app App) sse() vweb.Result {
mut session := sse.new_connection(app.conn)
// Note: you can setup session.write_timeout and session.headers here
session.start() or { return app.server_error(501) }
session.send_message(data: 'ok') or { return app.server_error(501) }
for {
data := '{"time": "${time.now().str()}", "random_id": "${rand.ulid()}"}'
session.send_message(event: 'ping', data: data) or { return app.server_error(501) }
println('> sent event: ${data}')
time.sleep(1 * time.second)
}
return app.server_error(501)
}

View File

@ -1,22 +0,0 @@
const messageList = document.getElementById('message-list');
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
const socket = new WebSocket(`${protocol}://${location.host}/ws`);
let i = 0;
function send(message) {
messageList.innerHTML += `<li>&gt; ${message}</li>`;
socket.send(message);
}
socket.addEventListener("open", (event) => {
console.log('Connected to WS server');
send('Hey everyone !');
});
socket.addEventListener("message", (event) => {
const { data } = event;
messageList.innerHTML += `<li>&lt; ${data}</li>`;
setTimeout(() => {
send(`Roger ${i++}`);
}, 3000);
});

View File

@ -1,11 +0,0 @@
<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>vweb websocket example page</title>
</head>
<body>
<ol id="message-list"></ol>
<script type="text/javascript" src="websocket_client.js"></script>
</body>
</html>

View File

@ -1,75 +0,0 @@
module main
import log
import net.http
import net.websocket
import term
import vweb
const http_port = 8080
struct App {
vweb.Context
mut:
wss &websocket.Server @[vweb_global]
}
fn slog(message string) {
eprintln(term.colorize(term.bright_yellow, message))
}
fn clog(message string) {
eprintln(term.colorize(term.cyan, message))
}
fn wlog(message string) {
eprintln(term.colorize(term.bright_blue, message))
}
fn main() {
mut app := new_app() or { panic(err) }
vweb.run(app, http_port)
}
fn new_app() !&App {
mut app := &App{
wss: new_websocker_server()!
}
app.handle_static('assets', true)
return app
}
fn new_websocker_server() !&websocket.Server {
mut logger := &log.Log{}
logger.set_level(.debug)
mut wss := websocket.new_server(.ip, 8080, '', logger: logger)
wss.on_connect(fn (mut server_client websocket.ServerClient) !bool {
slog('ws.on_connect, server_client.client_key: ${server_client.client_key}')
return true
})!
wss.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ! {
slog('s.on_message msg.opcode: ${msg.opcode} | msg.payload: ${msg.payload}')
ws.write(msg.payload, msg.opcode) or {
eprintln('ws.write err: ${err}')
return err
}
})
wss.on_close(fn (mut ws websocket.Client, code int, reason string) ! {
slog('s.on_close code: ${code}, reason: ${reason}')
})
slog('Websocket Server initialized')
return wss
}
pub fn (mut app App) index() vweb.Result {
return $vweb.html()
}
pub fn (mut app App) ws() !vweb.Result {
key := app.req.header.get(http.CommonHeader.sec_websocket_key)!
app.wss.handle_handshake(mut app.conn, key) or {
wlog('handle_handshake error: ${err.msg()}')
return err
}
return app.text('')
}

View File

@ -83,7 +83,7 @@ Running a Veb app on http://localhost:8081 ...
Veb helpfully provided a link, open http://localhost:8081/ in your browser:
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png?raw=true">
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/hello.png?raw=true">
The App struct holds shared application data, while Context handles per-request data and embeds
`veb.Context` for response methods like `.text()`.
@ -109,7 +109,7 @@ fn (mut app App) time() veb.Result {
Custom routes can be defined using attributes like @['/index'].
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/time.png?raw=true">
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/time.png?raw=true">
> TIP: run the following command to live-reload the server: `v watch run blog.v`
@ -143,7 +143,7 @@ pub fn (mut app App) index() veb.Result {
}
```
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png?raw=true">
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/hello_html.png?raw=true">
Good, now we have an actual HTML page.
@ -278,7 +278,7 @@ Finally, let's update our view:
v run .
```
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png?raw=true">
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/articles1.png?raw=true">
That was very simple, wasn't it?
@ -398,7 +398,7 @@ pub fn (mut app App) articles() veb.Result {
}
```
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png?raw=true">
<img width=662 src="https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_veb/img/articles_json.png?raw=true">
### Persistent data

View File

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -117,6 +117,7 @@ pub const mime_types = {
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
'.rar': 'application/vnd.rar'
'.rtf': 'application/rtf'
'.scss': 'text/css'
'.sh': 'application/x-sh'
'.svg': 'image/svg+xml'
'.swf': 'application/x-shockwave-flash'

View File

@ -227,6 +227,12 @@ pub fn (mut ctx Context) server_error(msg string) Result {
return ctx.send_response_to_client('text/plain', msg)
}
// send an error with a custom status
pub fn (mut ctx Context) server_error_with_status(s http.Status) Result {
ctx.res.set_status(s)
return ctx.send_response_to_client('text/plain', 'Server error')
}
// send a 204 No Content response without body and content-type
pub fn (mut ctx Context) no_content() Result {
ctx.res.set_status(.no_content)