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

@ -289,7 +289,7 @@ V comes with a version of mbedtls, which should work on all systems. If you find
use OpenSSL instead, you will need to make sure that it is installed on your system, then
use the `-d use_openssl` switch when you compile.
Note: Mbed-TLS is smaller and easier to install on windows too (V comes with it), but if you
Note: Mbed-TLS is smaller and easier to install on windows too (V comes with it), but if you
write programs, that do lots of http requests to HTTPS/SSL servers, in most cases, it is *best*
to compile with `-d use_openssl`, and do so on a system, where you do have OpenSSL installed
(see below). Mbed-TLS is slower, and can have more issues, especially when you are doing parallel
@ -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)