From 8683e634eec7625bfe2db10020e729a4eef9ec06 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 2 Mar 2025 14:37:00 +0300 Subject: [PATCH] veb: deprecate vweb (part 2); move all vweb examples to veb --- README.md | 4 +- ci/linux_ci.vsh | 4 +- ci/macos_ci.vsh | 2 +- examples/veb/file_upload/file_uploading.v | 40 ++++++++++ examples/{vweb => veb}/file_upload/index.html | 0 .../{vweb => veb}/file_upload/submit.html | 0 .../{vweb => veb}/file_upload/upload.html | 0 .../middleware/templates/base.html | 0 .../middleware/templates/early.html | 0 .../middleware/templates/index.html | 0 .../middleware/templates/secret.html | 0 .../middleware/using_middleware.v | 0 .../server_sent_events/assets/site.css | 0 .../server_sent_events/assets/v-logo.svg | 0 .../server_sent_events/favicon.ico | Bin .../server_sent_events/index.html | 0 examples/veb/server_sent_events/server.v | 44 ++++++++++ examples/vweb/file_upload/file_uploading.v | 37 --------- examples/vweb/server_sent_events/server.v | 37 --------- .../vweb_websocket/assets/websocket_client.js | 22 ----- examples/vweb/vweb_websocket/index.html | 11 --- examples/vweb/vweb_websocket/vweb_websocket.v | 75 ------------------ .../README.md | 10 +-- .../code/blog/.gitignore | 0 .../code/blog/article.v | 0 .../code/blog/blog.sqlite | 0 .../code/blog/blog.v | 0 .../code/blog/index.html | 0 .../code/blog/new.html | 0 .../img/articles1.png | Bin .../img/articles_json.png | Bin .../img/hello.png | Bin .../img/hello_html.png | Bin .../img/time.png | Bin vlib/veb/consts.v | 1 + vlib/veb/context.v | 6 ++ 36 files changed, 101 insertions(+), 192 deletions(-) create mode 100644 examples/veb/file_upload/file_uploading.v rename examples/{vweb => veb}/file_upload/index.html (100%) rename examples/{vweb => veb}/file_upload/submit.html (100%) rename examples/{vweb => veb}/file_upload/upload.html (100%) rename examples/{vweb => veb}/middleware/templates/base.html (100%) rename examples/{vweb => veb}/middleware/templates/early.html (100%) rename examples/{vweb => veb}/middleware/templates/index.html (100%) rename examples/{vweb => veb}/middleware/templates/secret.html (100%) rename examples/{vweb => veb}/middleware/using_middleware.v (100%) rename examples/{vweb => veb}/server_sent_events/assets/site.css (100%) rename examples/{vweb => veb}/server_sent_events/assets/v-logo.svg (100%) rename examples/{vweb => veb}/server_sent_events/favicon.ico (100%) rename examples/{vweb => veb}/server_sent_events/index.html (100%) create mode 100644 examples/veb/server_sent_events/server.v delete mode 100644 examples/vweb/file_upload/file_uploading.v delete mode 100644 examples/vweb/server_sent_events/server.v delete mode 100644 examples/vweb/vweb_websocket/assets/websocket_client.js delete mode 100644 examples/vweb/vweb_websocket/index.html delete mode 100644 examples/vweb/vweb_websocket/vweb_websocket.v rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/README.md (96%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/.gitignore (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/article.v (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/blog.sqlite (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/blog.v (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/index.html (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/code/blog/new.html (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/img/articles1.png (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/img/articles_json.png (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/img/hello.png (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/img/hello_html.png (100%) rename tutorials/{building_a_simple_web_blog_with_vweb => building_a_simple_web_blog_with_veb}/img/time.png (100%) diff --git a/README.md b/README.md index 2744d769e3..2d4067d9b1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/ci/linux_ci.vsh b/ci/linux_ci.vsh index d7d23554d0..206a1d7b46 100644 --- a/ci/linux_ci.vsh +++ b/ci/linux_ci.vsh @@ -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() { diff --git a/ci/macos_ci.vsh b/ci/macos_ci.vsh index 7d00034e01..3944accc69 100644 --- a/ci/macos_ci.vsh +++ b/ci/macos_ci.vsh @@ -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() { diff --git a/examples/veb/file_upload/file_uploading.v b/examples/veb/file_upload/file_uploading.v new file mode 100644 index 0000000000..38d741fb26 --- /dev/null +++ b/examples/veb/file_upload/file_uploading.v @@ -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', '
', '\n\r', '
', '\t', ' ', ' ', ' ']) + } + return $veb.html() +} + +@['/submit'; post] +pub fn (mut app App) submit() veb.Result { + dump(ctx.form) + form_data := ctx.form.clone() + return $veb.html() +} diff --git a/examples/vweb/file_upload/index.html b/examples/veb/file_upload/index.html similarity index 100% rename from examples/vweb/file_upload/index.html rename to examples/veb/file_upload/index.html diff --git a/examples/vweb/file_upload/submit.html b/examples/veb/file_upload/submit.html similarity index 100% rename from examples/vweb/file_upload/submit.html rename to examples/veb/file_upload/submit.html diff --git a/examples/vweb/file_upload/upload.html b/examples/veb/file_upload/upload.html similarity index 100% rename from examples/vweb/file_upload/upload.html rename to examples/veb/file_upload/upload.html diff --git a/examples/vweb/middleware/templates/base.html b/examples/veb/middleware/templates/base.html similarity index 100% rename from examples/vweb/middleware/templates/base.html rename to examples/veb/middleware/templates/base.html diff --git a/examples/vweb/middleware/templates/early.html b/examples/veb/middleware/templates/early.html similarity index 100% rename from examples/vweb/middleware/templates/early.html rename to examples/veb/middleware/templates/early.html diff --git a/examples/vweb/middleware/templates/index.html b/examples/veb/middleware/templates/index.html similarity index 100% rename from examples/vweb/middleware/templates/index.html rename to examples/veb/middleware/templates/index.html diff --git a/examples/vweb/middleware/templates/secret.html b/examples/veb/middleware/templates/secret.html similarity index 100% rename from examples/vweb/middleware/templates/secret.html rename to examples/veb/middleware/templates/secret.html diff --git a/examples/vweb/middleware/using_middleware.v b/examples/veb/middleware/using_middleware.v similarity index 100% rename from examples/vweb/middleware/using_middleware.v rename to examples/veb/middleware/using_middleware.v diff --git a/examples/vweb/server_sent_events/assets/site.css b/examples/veb/server_sent_events/assets/site.css similarity index 100% rename from examples/vweb/server_sent_events/assets/site.css rename to examples/veb/server_sent_events/assets/site.css diff --git a/examples/vweb/server_sent_events/assets/v-logo.svg b/examples/veb/server_sent_events/assets/v-logo.svg similarity index 100% rename from examples/vweb/server_sent_events/assets/v-logo.svg rename to examples/veb/server_sent_events/assets/v-logo.svg diff --git a/examples/vweb/server_sent_events/favicon.ico b/examples/veb/server_sent_events/favicon.ico similarity index 100% rename from examples/vweb/server_sent_events/favicon.ico rename to examples/veb/server_sent_events/favicon.ico diff --git a/examples/vweb/server_sent_events/index.html b/examples/veb/server_sent_events/index.html similarity index 100% rename from examples/vweb/server_sent_events/index.html rename to examples/veb/server_sent_events/index.html diff --git a/examples/veb/server_sent_events/server.v b/examples/veb/server_sent_events/server.v new file mode 100644 index 0000000000..40988e696f --- /dev/null +++ b/examples/veb/server_sent_events/server.v @@ -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) +} diff --git a/examples/vweb/file_upload/file_uploading.v b/examples/vweb/file_upload/file_uploading.v deleted file mode 100644 index ea1c73a82e..0000000000 --- a/examples/vweb/file_upload/file_uploading.v +++ /dev/null @@ -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', '
', '\n\r', '
', '\t', ' ', ' ', ' ']) - } - - return $vweb.html() -} - -@['/submit'; post] -pub fn (mut app App) submit() vweb.Result { - dump(app.form) - form_data := app.form.clone() - return $vweb.html() -} diff --git a/examples/vweb/server_sent_events/server.v b/examples/vweb/server_sent_events/server.v deleted file mode 100644 index 6585e56778..0000000000 --- a/examples/vweb/server_sent_events/server.v +++ /dev/null @@ -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) -} diff --git a/examples/vweb/vweb_websocket/assets/websocket_client.js b/examples/vweb/vweb_websocket/assets/websocket_client.js deleted file mode 100644 index 629d036ac9..0000000000 --- a/examples/vweb/vweb_websocket/assets/websocket_client.js +++ /dev/null @@ -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 += `
  • > ${message}
  • `; - 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 += `
  • < ${data}
  • `; - setTimeout(() => { - send(`Roger ${i++}`); - }, 3000); -}); \ No newline at end of file diff --git a/examples/vweb/vweb_websocket/index.html b/examples/vweb/vweb_websocket/index.html deleted file mode 100644 index 62b5b6e286..0000000000 --- a/examples/vweb/vweb_websocket/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - -vweb websocket example page - - -
      - - - \ No newline at end of file diff --git a/examples/vweb/vweb_websocket/vweb_websocket.v b/examples/vweb/vweb_websocket/vweb_websocket.v deleted file mode 100644 index 015a6dec75..0000000000 --- a/examples/vweb/vweb_websocket/vweb_websocket.v +++ /dev/null @@ -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('') -} diff --git a/tutorials/building_a_simple_web_blog_with_vweb/README.md b/tutorials/building_a_simple_web_blog_with_veb/README.md similarity index 96% rename from tutorials/building_a_simple_web_blog_with_vweb/README.md rename to tutorials/building_a_simple_web_blog_with_veb/README.md index 5618a8edc2..df85a0edc2 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/README.md +++ b/tutorials/building_a_simple_web_blog_with_veb/README.md @@ -83,7 +83,7 @@ Running a Veb app on http://localhost:8081 ... Veb helpfully provided a link, open http://localhost:8081/ in your browser: - + 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']. - + > 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 { } ``` - + Good, now we have an actual HTML page. @@ -278,7 +278,7 @@ Finally, let's update our view: v run . ``` - + That was very simple, wasn't it? @@ -398,7 +398,7 @@ pub fn (mut app App) articles() veb.Result { } ``` - + ### Persistent data diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore b/tutorials/building_a_simple_web_blog_with_veb/code/blog/.gitignore similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/.gitignore diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v b/tutorials/building_a_simple_web_blog_with_veb/code/blog/article.v similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/article.v diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite b/tutorials/building_a_simple_web_blog_with_veb/code/blog/blog.sqlite similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/blog.sqlite diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v b/tutorials/building_a_simple_web_blog_with_veb/code/blog/blog.v similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/blog.v diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html b/tutorials/building_a_simple_web_blog_with_veb/code/blog/index.html similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/index.html diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html b/tutorials/building_a_simple_web_blog_with_veb/code/blog/new.html similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html rename to tutorials/building_a_simple_web_blog_with_veb/code/blog/new.html diff --git a/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png b/tutorials/building_a_simple_web_blog_with_veb/img/articles1.png similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png rename to tutorials/building_a_simple_web_blog_with_veb/img/articles1.png diff --git a/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png b/tutorials/building_a_simple_web_blog_with_veb/img/articles_json.png similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png rename to tutorials/building_a_simple_web_blog_with_veb/img/articles_json.png diff --git a/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png b/tutorials/building_a_simple_web_blog_with_veb/img/hello.png similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/img/hello.png rename to tutorials/building_a_simple_web_blog_with_veb/img/hello.png diff --git a/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png b/tutorials/building_a_simple_web_blog_with_veb/img/hello_html.png similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png rename to tutorials/building_a_simple_web_blog_with_veb/img/hello_html.png diff --git a/tutorials/building_a_simple_web_blog_with_vweb/img/time.png b/tutorials/building_a_simple_web_blog_with_veb/img/time.png similarity index 100% rename from tutorials/building_a_simple_web_blog_with_vweb/img/time.png rename to tutorials/building_a_simple_web_blog_with_veb/img/time.png diff --git a/vlib/veb/consts.v b/vlib/veb/consts.v index 5b002a53a5..e3887973c4 100644 --- a/vlib/veb/consts.v +++ b/vlib/veb/consts.v @@ -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' diff --git a/vlib/veb/context.v b/vlib/veb/context.v index a14aea5c64..a0d3bbff9f 100644 --- a/vlib/veb/context.v +++ b/vlib/veb/context.v @@ -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)