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/orm.v'
|
||||
skip_files << 'examples/database/psql/customer.v'
|
||||
skip_files << 'examples/vweb_orm_jwt' // requires mysql
|
||||
}
|
||||
$if windows {
|
||||
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/client-server/client.v' // requires OpenSSL
|
||||
skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL
|
||||
skip_files << 'examples/vweb_orm_jwt' // requires mysql
|
||||
$if tinyc {
|
||||
skip_files << 'examples/database/orm.v' // try fix it
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ const vroot = @VMODROOT
|
||||
|
||||
const efolders = [
|
||||
'examples/viewer',
|
||||
'examples/vweb_orm_jwt',
|
||||
]
|
||||
|
||||
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