mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
examples: v back-end example for vweb (#15141)
This commit is contained in:
parent
1f3be99859
commit
2d7406a8cd
@ -155,6 +155,7 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
|
|||||||
skip_files << 'examples/database/mysql.v'
|
skip_files << 'examples/database/mysql.v'
|
||||||
skip_files << 'examples/database/orm.v'
|
skip_files << 'examples/database/orm.v'
|
||||||
skip_files << 'examples/database/psql/customer.v'
|
skip_files << 'examples/database/psql/customer.v'
|
||||||
|
skip_files << 'examples/vweb_orm_jwt' // requires mysql
|
||||||
}
|
}
|
||||||
$if windows {
|
$if windows {
|
||||||
skip_files << 'examples/database/mysql.v'
|
skip_files << 'examples/database/mysql.v'
|
||||||
@ -163,6 +164,7 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
|
|||||||
skip_files << 'examples/websocket/ping.v' // requires OpenSSL
|
skip_files << 'examples/websocket/ping.v' // requires OpenSSL
|
||||||
skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL
|
skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL
|
||||||
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
|
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
|
||||||
|
skip_files << 'examples/vweb_orm_jwt' // requires mysql
|
||||||
$if tinyc {
|
$if tinyc {
|
||||||
skip_files << 'examples/database/orm.v' // try fix it
|
skip_files << 'examples/database/orm.v' // try fix it
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ const vroot = @VMODROOT
|
|||||||
|
|
||||||
const efolders = [
|
const efolders = [
|
||||||
'examples/viewer',
|
'examples/viewer',
|
||||||
|
'examples/vweb_orm_jwt',
|
||||||
]
|
]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
9
examples/vweb_orm_jwt/.editorconfig
Normal file
9
examples/vweb_orm_jwt/.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.v]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
4
examples/vweb_orm_jwt/.gitattributes
vendored
Normal file
4
examples/vweb_orm_jwt/.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.v linguist-language=V text=auto eol=lf
|
||||||
|
*.vv linguist-language=V text=auto eol=lf
|
||||||
|
*.vsh linguist-language=V text=auto eol=lf
|
||||||
|
**/v.mod linguist-language=V text=auto eol=lf
|
10
examples/vweb_orm_jwt/.gitignore
vendored
Normal file
10
examples/vweb_orm_jwt/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
main
|
||||||
|
v
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
vls.log
|
||||||
|
.env
|
19
examples/vweb_orm_jwt/src/auth_controllers.v
Normal file
19
examples/vweb_orm_jwt/src/auth_controllers.v
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import vweb
|
||||||
|
import json
|
||||||
|
|
||||||
|
['/auth/login'; post]
|
||||||
|
pub fn (mut app App) controller_auth() vweb.Result {
|
||||||
|
body := json.decode(AuthRequestDto, app.req.data) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('Failed to decode json, error: $err')
|
||||||
|
}
|
||||||
|
|
||||||
|
response := app.service_auth(body.username, body.password) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('error: $err')
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.json(response)
|
||||||
|
}
|
8
examples/vweb_orm_jwt/src/auth_dto.v
Normal file
8
examples/vweb_orm_jwt/src/auth_dto.v
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
struct AuthRequestDto {
|
||||||
|
// Adding a [required] attribute will make decoding fail, if that field is not present in the input.
|
||||||
|
// If a field is not [required], but is missing, it will be assumed to have its default value, like 0 for numbers, or '' for strings, and decoding will not fail.
|
||||||
|
username string [required]
|
||||||
|
password string [required]
|
||||||
|
}
|
86
examples/vweb_orm_jwt/src/auth_services.v
Normal file
86
examples/vweb_orm_jwt/src/auth_services.v
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import crypto.hmac
|
||||||
|
import crypto.sha256
|
||||||
|
import crypto.bcrypt
|
||||||
|
import encoding.base64
|
||||||
|
import json
|
||||||
|
import databases
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
struct JwtHeader {
|
||||||
|
alg string
|
||||||
|
typ string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JwtPayload {
|
||||||
|
sub string // (subject) = Entidade à quem o token pertence, normalmente o ID do usuário;
|
||||||
|
iss string // (issuer) = Emissor do token;
|
||||||
|
exp string // (expiration) = Timestamp de quando o token irá expirar;
|
||||||
|
iat time.Time // (issued at) = Timestamp de quando o token foi criado;
|
||||||
|
aud string // (audience) = Destinatário do token, representa a aplicação que irá usá-lo.
|
||||||
|
name string
|
||||||
|
roles string
|
||||||
|
permissions string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) service_auth(username string, password string) ?string {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
eprintln(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := sql db {
|
||||||
|
select from User where username == username limit 1
|
||||||
|
}
|
||||||
|
if user.username != username {
|
||||||
|
return error('user not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.active {
|
||||||
|
return error('user is not active')
|
||||||
|
}
|
||||||
|
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
bcrypt.compare_hash_and_password(password.bytes(), user.password.bytes()) or {
|
||||||
|
return error('Failed to auth user, $err')
|
||||||
|
}
|
||||||
|
|
||||||
|
token := make_token(user)
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_token(user User) string {
|
||||||
|
secret := os.getenv('SECRET_KEY')
|
||||||
|
|
||||||
|
jwt_header := JwtHeader{'HS256', 'JWT'}
|
||||||
|
jwt_payload := JwtPayload{
|
||||||
|
sub: '$user.id'
|
||||||
|
name: '$user.username'
|
||||||
|
iat: time.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
header := base64.url_encode(json.encode(jwt_header).bytes())
|
||||||
|
payload := base64.url_encode(json.encode(jwt_payload).bytes())
|
||||||
|
signature := base64.url_encode(hmac.new(secret.bytes(), '${header}.$payload'.bytes(),
|
||||||
|
sha256.sum, sha256.block_size).bytestr().bytes())
|
||||||
|
|
||||||
|
jwt := '${header}.${payload}.$signature'
|
||||||
|
|
||||||
|
return jwt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_verify(token string) bool {
|
||||||
|
secret := os.getenv('SECRET_KEY')
|
||||||
|
token_split := token.split('.')
|
||||||
|
|
||||||
|
signature_mirror := hmac.new(secret.bytes(), '${token_split[0]}.${token_split[1]}'.bytes(),
|
||||||
|
sha256.sum, sha256.block_size).bytestr().bytes()
|
||||||
|
|
||||||
|
signature_from_token := base64.url_decode(token_split[2])
|
||||||
|
|
||||||
|
return hmac.equal(signature_from_token, signature_mirror)
|
||||||
|
}
|
18
examples/vweb_orm_jwt/src/databases/config_datases_mysql.v
Normal file
18
examples/vweb_orm_jwt/src/databases/config_datases_mysql.v
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module databases
|
||||||
|
|
||||||
|
import mysql
|
||||||
|
import os
|
||||||
|
|
||||||
|
pub fn create_db_connection() ?mysql.Connection {
|
||||||
|
mut db := mysql.Connection{
|
||||||
|
host: os.getenv('DB_HOST')
|
||||||
|
port: os.getenv('DB_PORT').u32()
|
||||||
|
username: os.getenv('DB_USERNAME')
|
||||||
|
password: os.getenv('DB_PASSWORD')
|
||||||
|
dbname: os.getenv('DB_NAME')
|
||||||
|
}
|
||||||
|
|
||||||
|
db.connect() or { println(err) }
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
30
examples/vweb_orm_jwt/src/main.v
Normal file
30
examples/vweb_orm_jwt/src/main.v
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import vweb
|
||||||
|
import databases
|
||||||
|
|
||||||
|
const (
|
||||||
|
http_port = 8081
|
||||||
|
)
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
vweb.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut db := databases.create_db_connection() or { panic(err) }
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
create table User
|
||||||
|
}
|
||||||
|
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
vweb.run(new_app(), http_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_app() &App {
|
||||||
|
mut app := &App{}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
72
examples/vweb_orm_jwt/src/user_controllers.v
Normal file
72
examples/vweb_orm_jwt/src/user_controllers.v
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import vweb
|
||||||
|
import json
|
||||||
|
import databases
|
||||||
|
|
||||||
|
['/user/:id/get'; get]
|
||||||
|
pub fn (mut app App) controller_get_user_by_id(id int) vweb.Result {
|
||||||
|
response := app.service_get_user_by_id(id) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('$err')
|
||||||
|
}
|
||||||
|
return app.json(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
['/user/create'; post]
|
||||||
|
pub fn (mut app App) controller_create_user() vweb.Result {
|
||||||
|
body := json.decode(User, app.req.data) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('Failed to decode json, error: $err')
|
||||||
|
}
|
||||||
|
|
||||||
|
response := app.service_add_user(body.username, body.password) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('error: $err')
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.json(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
['/user/get_all'; get]
|
||||||
|
pub fn (mut app App) controller_get_all_user() vweb.Result {
|
||||||
|
token := app.get_header('token')
|
||||||
|
|
||||||
|
if !auth_verify(token) {
|
||||||
|
app.set_status(401, '')
|
||||||
|
return app.text('Not valid token')
|
||||||
|
}
|
||||||
|
|
||||||
|
response := app.service_get_all_user() or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('$err')
|
||||||
|
}
|
||||||
|
return app.json(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
['/user/get_by_username/:username'; get]
|
||||||
|
pub fn (mut app App) controller_get_by_username(username string) vweb.Result {
|
||||||
|
response := app.service_get_by_username(username) or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('$err')
|
||||||
|
}
|
||||||
|
return app.json(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
['/user/drop'; delete]
|
||||||
|
pub fn (mut app App) delete() vweb.Result {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
app.set_status(400, '')
|
||||||
|
return app.text('$err')
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
drop table User
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.text('Tabela deletada com sucesso')
|
||||||
|
}
|
16
examples/vweb_orm_jwt/src/user_entities.v
Normal file
16
examples/vweb_orm_jwt/src/user_entities.v
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
[table: 'usersxqa']
|
||||||
|
struct User {
|
||||||
|
mut:
|
||||||
|
id int [primary; sql: serial]
|
||||||
|
username string [required; sql_type: 'varchar(191)']
|
||||||
|
password string [required; sql_type: 'longtext']
|
||||||
|
name string [sql_type: 'varchar(191)']
|
||||||
|
created_at time.Time [sql_type: 'datetime(3)']
|
||||||
|
updated_at time.Time [sql_type: 'datetime(3)']
|
||||||
|
deleted_at time.Time [sql_type: 'datetime(3)']
|
||||||
|
active bool
|
||||||
|
}
|
96
examples/vweb_orm_jwt/src/user_services.v
Normal file
96
examples/vweb_orm_jwt/src/user_services.v
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import crypto.bcrypt
|
||||||
|
import databases
|
||||||
|
import time
|
||||||
|
|
||||||
|
fn (mut app App) service_add_user(username string, password string) ?User {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
eprintln(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed_password := bcrypt.generate_from_password(password.bytes(), bcrypt.min_cost) or {
|
||||||
|
eprintln(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user_model := User{
|
||||||
|
username: username
|
||||||
|
name: password
|
||||||
|
password: hashed_password
|
||||||
|
created_at: time.now()
|
||||||
|
updated_at: time.now()
|
||||||
|
deleted_at: time.now()
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
insert user_model into User
|
||||||
|
}
|
||||||
|
|
||||||
|
result := sql db {
|
||||||
|
select from User where username == username limit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) service_get_user_by_id(user_id int) ?User {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := sql db {
|
||||||
|
select from User where id == user_id
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) service_get_all_user() ?[]User {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := sql db {
|
||||||
|
select from User
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) service_get_by_username(username string) ?User {
|
||||||
|
mut db := databases.create_db_connection() or {
|
||||||
|
println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := sql db {
|
||||||
|
select from User where username == username
|
||||||
|
}
|
||||||
|
|
||||||
|
if results.len == 0 {
|
||||||
|
return error('Usuário não encontrado')
|
||||||
|
}
|
||||||
|
|
||||||
|
return results[0]
|
||||||
|
}
|
7
examples/vweb_orm_jwt/src/v.mod
Normal file
7
examples/vweb_orm_jwt/src/v.mod
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Module {
|
||||||
|
name: 'vweb_orm_jwt'
|
||||||
|
description: ''
|
||||||
|
version: ''
|
||||||
|
license: ''
|
||||||
|
dependencies: []
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user