1
0
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:
Hitalo de Jesus do Rosário Souza 2022-07-24 07:02:57 -03:00 committed by GitHub
parent 1f3be99859
commit 2d7406a8cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 378 additions and 0 deletions

View File

@ -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
} }

View File

@ -7,6 +7,7 @@ const vroot = @VMODROOT
const efolders = [ const efolders = [
'examples/viewer', 'examples/viewer',
'examples/vweb_orm_jwt',
] ]
fn main() { fn main() {

View 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
View 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
View File

@ -0,0 +1,10 @@
# Binaries for programs and plugins
main
v
*.exe
*.exe~
*.so
*.dylib
*.dll
vls.log
.env

View 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)
}

View 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]
}

View 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)
}

View 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
}

View 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
}

View 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')
}

View 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
}

View 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]
}

View File

@ -0,0 +1,7 @@
Module {
name: 'vweb_orm_jwt'
description: ''
version: ''
license: ''
dependencies: []
}