Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,8 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.v]
|
||||
indent_style = tab
|
||||
@@ -0,0 +1,7 @@
|
||||
* text=auto eol=lf
|
||||
*.bat eol=crlf
|
||||
|
||||
**/*.v linguist-language=V
|
||||
**/*.vv linguist-language=V
|
||||
**/*.vsh linguist-language=V
|
||||
**/v.mod linguist-language=V
|
||||
@@ -0,0 +1,18 @@
|
||||
# Binaries for programs and plugins
|
||||
main
|
||||
batata
|
||||
*.exe
|
||||
*.exe~
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.sql
|
||||
|
||||
# Ignore binary output folders
|
||||
bin/
|
||||
|
||||
# Ignore common editor/system specific metadata
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hitalo Souza
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#536b8a" d="M 323.509,471.167 480.202,32.185 C 482.857,24.74 478.671,19.312 470.853,20.071 L 347.629,32.03 c -7.816,0.759 -16.259,7.44 -18.841,14.911 L 183.753,466.465 c -2.583,7.47 1.693,13.535 9.546,13.535 h 119.938 c 3.925,0 8.192,-3.02 9.52,-6.744 l 0.746,-2.089 v 0 0 z"/>
|
||||
<path fill="#5d87be" d="M 30.055,20.07 152.165,32.029 c 7.745,0.76 16.122,7.438 18.692,14.905 l 146.766,426.302 c 1.284,3.735 -0.83,6.764 -4.721,6.764 H 193.294 c -7.781,0 -16.239,-6.044 -18.871,-13.489 L 20.791,32.185 c -2.633,-7.445 1.518,-12.874 9.264,-12.114 v 0 0 z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
@@ -0,0 +1,13 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
|
||||
@['/controller/auth'; post]
|
||||
pub fn (mut app App) controller_auth(mut ctx Context, username string, password string) veb.Result {
|
||||
response := app.service_auth(username, password) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('error: ${err}')
|
||||
}
|
||||
|
||||
return ctx.json(response)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module main
|
||||
|
||||
struct AuthRequestDto {
|
||||
username string @[nonull]
|
||||
password string @[nonull]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
module main
|
||||
|
||||
import crypto.hmac
|
||||
import crypto.sha256
|
||||
import crypto.bcrypt
|
||||
import encoding.base64
|
||||
import json
|
||||
import databases
|
||||
import time
|
||||
|
||||
struct JwtHeader {
|
||||
alg string
|
||||
typ string
|
||||
}
|
||||
|
||||
struct JwtPayload {
|
||||
sub string // (subject) = Entity to whom the token belongs, usually the user ID;
|
||||
iss string // (issuer) = Token issuer;
|
||||
exp string // (expiration) = Timestamp of when the token will expire;
|
||||
iat time.Time // (issued at) = Timestamp of when the token was created;
|
||||
aud string // (audience) = Token recipient, represents the application that will use it.
|
||||
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)
|
||||
}
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
users := sql db {
|
||||
select from User where username == username
|
||||
}!
|
||||
if users.len == 0 {
|
||||
return error('user not found')
|
||||
}
|
||||
user := users.first()
|
||||
if user.username != username {
|
||||
return error('user not found')
|
||||
}
|
||||
|
||||
if !user.active {
|
||||
return error('user is not active')
|
||||
}
|
||||
|
||||
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 := 'SECRET_KEY' // 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 {
|
||||
if token == '' {
|
||||
return false
|
||||
}
|
||||
secret := 'SECRET_KEY' // 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)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
module databases
|
||||
|
||||
import db.sqlite // can change to 'db.mysql', 'db.pg'
|
||||
|
||||
pub fn create_db_connection() !sqlite.DB {
|
||||
mut db := sqlite.connect('app.db')!
|
||||
return db
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<!--Let browser know website is optimized for mobile-->
|
||||
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||
<!-- Compiled and minified JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||
<!-- Material UI icons -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<title>${title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>@include 'templates/header_component.html'</div>
|
||||
<div class="card-panel center-align" style="max-width: 240px; padding: 10px; margin: 10px; border-radius: 5px;">
|
||||
<form id="index_form" method='post' action=''>
|
||||
<div style="display:flex; flex-direction: column;">
|
||||
<input type='text' name='username' placeholder='Username' required autofocus>
|
||||
<input type='password' name='password' placeholder='Password' required>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<input class="waves-effect waves-light btn-small" type='submit' onclick="login()" formaction="javascript:void(0);" value='Login'>
|
||||
<input class="waves-effect waves-light btn-small" type='submit' onclick="addUser()" formaction="javascript:void(0);" value='Register'>
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
async function addUser() {
|
||||
const form = document.querySelector('#index_form');
|
||||
const formData = new FormData(form);
|
||||
await fetch('/controller/user/create', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then( async (response) => {
|
||||
if (response.status != 201) {
|
||||
throw await response.text()
|
||||
}
|
||||
return await response.text()
|
||||
})
|
||||
.then((data) => {
|
||||
alert("User created successfully")
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
async function login() {
|
||||
const form = document.querySelector('#index_form');
|
||||
const formData = new FormData(form);
|
||||
await fetch('/controller/auth', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then( async (response) => {
|
||||
if (response.status != 200) {
|
||||
throw await response.text()
|
||||
}
|
||||
return response.json()
|
||||
})
|
||||
.then((data) => {
|
||||
document.cookie = 'token='+data+';';
|
||||
window.location.href = '/products'
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
import databases
|
||||
import os
|
||||
|
||||
const port = 8082
|
||||
|
||||
pub struct Context {
|
||||
veb.Context
|
||||
}
|
||||
|
||||
struct App {
|
||||
veb.StaticHandler
|
||||
}
|
||||
|
||||
pub fn (app &App) before_request(mut ctx Context) {
|
||||
println('[veb] before_request: ${ctx.req.method} ${ctx.req.url}')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut db := databases.create_db_connection() or { panic(err) }
|
||||
|
||||
sql db {
|
||||
create table User
|
||||
create table Product
|
||||
} or { panic('error on create table: ${err}') }
|
||||
|
||||
db.close() or { panic(err) }
|
||||
|
||||
mut app := &App{}
|
||||
app.serve_static('/favicon.ico', 'assets/favicon.ico') or { panic(err) }
|
||||
app.mount_static_folder_at(os.resource_abs_path('.'), '/') or { panic(err) }
|
||||
|
||||
veb.run[App, Context](mut app, port)
|
||||
}
|
||||
|
||||
pub fn (mut app App) index(mut ctx Context) veb.Result {
|
||||
title := 'veb app'
|
||||
|
||||
return $veb.html()
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
import encoding.base64
|
||||
import json
|
||||
|
||||
@['/controller/products'; get]
|
||||
pub fn (mut app App) controller_get_all_products(mut ctx Context) veb.Result {
|
||||
token := ctx.req.header.get_custom('token') or { '' }
|
||||
|
||||
if !auth_verify(token) {
|
||||
ctx.res.set_status(.unauthorized)
|
||||
return ctx.text('Not valid token')
|
||||
}
|
||||
|
||||
jwt_payload_stringify := base64.url_decode_str(token.split('.')[1])
|
||||
|
||||
jwt_payload := json.decode(JwtPayload, jwt_payload_stringify) or {
|
||||
ctx.res.set_status(.internal_server_error)
|
||||
return ctx.text('jwt decode error')
|
||||
}
|
||||
|
||||
user_id := jwt_payload.sub
|
||||
|
||||
response := app.service_get_all_products_from(user_id.int()) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('${err}')
|
||||
}
|
||||
return ctx.json(response)
|
||||
}
|
||||
|
||||
@['/controller/product/create'; post]
|
||||
pub fn (mut app App) controller_create_product(mut ctx Context, product_name string) veb.Result {
|
||||
if product_name == '' {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('product name cannot be empty')
|
||||
}
|
||||
|
||||
token := ctx.req.header.get_custom('token') or { '' }
|
||||
|
||||
if !auth_verify(token) {
|
||||
ctx.res.set_status(.unauthorized)
|
||||
return ctx.text('Not valid token')
|
||||
}
|
||||
|
||||
jwt_payload_stringify := base64.url_decode_str(token.split('.')[1])
|
||||
|
||||
jwt_payload := json.decode(JwtPayload, jwt_payload_stringify) or {
|
||||
ctx.res.set_status(.internal_server_error)
|
||||
return ctx.text('jwt decode error')
|
||||
}
|
||||
|
||||
user_id := jwt_payload.sub
|
||||
|
||||
app.service_add_product(product_name, user_id.int()) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('error: ${err}')
|
||||
}
|
||||
ctx.res.set_status(.created)
|
||||
return ctx.text('product created successfully')
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
module main
|
||||
|
||||
@[table: 'products']
|
||||
struct Product {
|
||||
id int @[primary; sql: serial]
|
||||
user_id int
|
||||
name string @[sql_type: 'TEXT']
|
||||
created_at string @[default: 'CURRENT_TIMESTAMP']
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
module main
|
||||
|
||||
import databases
|
||||
|
||||
fn (mut app App) service_add_product(product_name string, user_id int) ! {
|
||||
mut db := databases.create_db_connection()!
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
product_model := Product{
|
||||
name: product_name
|
||||
user_id: user_id
|
||||
}
|
||||
|
||||
mut insert_error := ''
|
||||
|
||||
sql db {
|
||||
insert product_model into Product
|
||||
} or { insert_error = err.msg() }
|
||||
|
||||
if insert_error != '' {
|
||||
return error(insert_error)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) service_get_all_products_from(user_id int) ![]Product {
|
||||
mut db := databases.create_db_connection() or {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
results := sql db {
|
||||
select from Product where user_id == user_id
|
||||
}!
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
|
||||
@['/products'; get]
|
||||
pub fn (mut app App) products(mut ctx Context) !veb.Result {
|
||||
token := ctx.get_cookie('token') or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('${err}')
|
||||
}
|
||||
|
||||
user := get_user(token) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('Failed to fetch data from the server. Error: ${err}')
|
||||
}
|
||||
|
||||
return $veb.html()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
module main
|
||||
|
||||
import json
|
||||
import net.http
|
||||
|
||||
pub fn get_products(token string) ![]Product {
|
||||
mut header := http.new_header()
|
||||
header.add_custom('token', token)!
|
||||
url := 'http://localhost:8082/controller/products'
|
||||
|
||||
mut config := http.FetchConfig{
|
||||
header: header
|
||||
}
|
||||
|
||||
resp := http.fetch(http.FetchConfig{ ...config, url: url })!
|
||||
products := json.decode([]Product, resp.body)!
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
pub fn get_user(token string) !User {
|
||||
mut header := http.new_header()
|
||||
header.add_custom('token', token)!
|
||||
|
||||
url := 'http://localhost:8082/controller/user'
|
||||
|
||||
mut config := http.FetchConfig{
|
||||
header: header
|
||||
}
|
||||
|
||||
resp := http.fetch(http.FetchConfig{ ...config, url: url })!
|
||||
user := json.decode(User, resp.body)!
|
||||
|
||||
return user
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||

|
||||
|
||||

|
||||
@@ -0,0 +1,15 @@
|
||||
<nav>
|
||||
<div class="nav-wrapper">
|
||||
<a href="javascript:window.history.back();" class="left">
|
||||
<i class="material-icons">arrow_back_ios_new</i>
|
||||
</a>
|
||||
<a href="/">
|
||||
<img src="src/assets/veasel.png" alt="logo" style="max-height: 100%" />
|
||||
</a>
|
||||
<ul id="nav-mobile" class="right">
|
||||
<li><a href="https://github.com/vlang/v">github</a></li>
|
||||
<li><a href="https://vlang.io/">website</a></li>
|
||||
<li><a href="https://github.com/sponsors/medvednikov">support</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1,11 @@
|
||||
h1.title {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #3b7bbf;
|
||||
}
|
||||
|
||||
div.products-table {
|
||||
border: 1px solid;
|
||||
max-width: 720px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<!--Let browser know website is optimized for mobile-->
|
||||
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||
|
||||
<!-- Compiled and minified JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||
|
||||
<!-- Material UI icons -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
|
||||
<title>Login</title>
|
||||
@css 'src/templates/products.css'
|
||||
</head>
|
||||
<body>
|
||||
<div>@include 'header_component.html'</div>
|
||||
<h1 class="title">Hi, ${user.username}! you are online</h1>
|
||||
<form id="product_form" method='post' action=''>
|
||||
<div class="row">
|
||||
<div class="input-field col s2">
|
||||
<input id="product_name" name='product_name' type="text" class="validate">
|
||||
<label class="active" for="product_name">product name</label>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<input class="waves-effect waves-light btn-small" type='submit' onclick="addProduct()" formaction="javascript:void(0);" value='Register' required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
function getCookie(cookieName) {
|
||||
let cookie = {};
|
||||
document.cookie.split(';').forEach(function(el) {
|
||||
let [key,value] = el.split('=');
|
||||
cookie[key.trim()] = value;
|
||||
})
|
||||
return cookie[cookieName];
|
||||
}
|
||||
async function addProduct() {
|
||||
const form = document.querySelector('#product_form');
|
||||
const formData = new FormData(form);
|
||||
console.log(getCookie("token"));
|
||||
await fetch('/controller/product/create', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers :{
|
||||
token: getCookie("token")
|
||||
}
|
||||
})
|
||||
.then( async (response) => {
|
||||
if (response.status != 201) {
|
||||
throw await response.text()
|
||||
}
|
||||
return await response.text()
|
||||
})
|
||||
.then((data) => {
|
||||
document.location.reload(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div class="products-table card-panel">
|
||||
<table class="highlight striped responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Created date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@for product in user.products
|
||||
<tr>
|
||||
<td>${product.id}</td>
|
||||
<td>${product.name}</td>
|
||||
<td>${product.created_at}</td>
|
||||
</tr>
|
||||
@end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,64 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
import encoding.base64
|
||||
import json
|
||||
|
||||
@['/controller/users'; get]
|
||||
pub fn (mut app App) controller_get_all_user(mut ctx Context) veb.Result {
|
||||
token := ctx.req.header.get_custom('token') or { '' }
|
||||
|
||||
if !auth_verify(token) {
|
||||
ctx.res.set_status(.unauthorized)
|
||||
return ctx.text('Not valid token')
|
||||
}
|
||||
|
||||
response := app.service_get_all_user() or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('${err}')
|
||||
}
|
||||
return ctx.json(response)
|
||||
}
|
||||
|
||||
@['/controller/user'; get]
|
||||
pub fn (mut app App) controller_get_user(mut ctx Context) veb.Result {
|
||||
token := ctx.req.header.get_custom('token') or { '' }
|
||||
|
||||
if !auth_verify(token) {
|
||||
ctx.res.set_status(.unauthorized)
|
||||
return ctx.text('Not valid token')
|
||||
}
|
||||
|
||||
jwt_payload_stringify := base64.url_decode_str(token.split('.')[1])
|
||||
|
||||
jwt_payload := json.decode(JwtPayload, jwt_payload_stringify) or {
|
||||
ctx.res.set_status(.internal_server_error)
|
||||
return ctx.text('jwt decode error')
|
||||
}
|
||||
|
||||
user_id := jwt_payload.sub
|
||||
|
||||
response := app.service_get_user(user_id.int()) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('${err}')
|
||||
}
|
||||
return ctx.json(response)
|
||||
}
|
||||
|
||||
@['/controller/user/create'; post]
|
||||
pub fn (mut app App) controller_create_user(mut ctx Context, username string, password string) veb.Result {
|
||||
if username == '' {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('username cannot be empty')
|
||||
}
|
||||
if password == '' {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('password cannot be empty')
|
||||
}
|
||||
app.service_add_user(username, password) or {
|
||||
ctx.res.set_status(.bad_request)
|
||||
return ctx.text('error: ${err}')
|
||||
}
|
||||
ctx.res.set_status(.created)
|
||||
return ctx.text('User created successfully')
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
module main
|
||||
|
||||
@[table: 'users']
|
||||
pub struct User {
|
||||
mut:
|
||||
id int @[primary; sql: serial]
|
||||
username string @[sql_type: 'TEXT'; unique]
|
||||
password string @[sql_type: 'TEXT']
|
||||
active bool
|
||||
products []Product @[fkey: 'user_id']
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
module main
|
||||
|
||||
import crypto.bcrypt
|
||||
import databases
|
||||
|
||||
fn (mut app App) service_add_user(username string, password string) ! {
|
||||
mut db := databases.create_db_connection()!
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
hashed_password := bcrypt.generate_from_password(password.bytes(), bcrypt.min_cost) or {
|
||||
eprintln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
user_model := User{
|
||||
username: username
|
||||
password: hashed_password
|
||||
active: true
|
||||
}
|
||||
|
||||
mut insert_error := ''
|
||||
sql db {
|
||||
insert user_model into User
|
||||
} or { insert_error = err.msg() }
|
||||
if insert_error != '' {
|
||||
return error(insert_error)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) service_get_all_user() ![]User {
|
||||
mut db := databases.create_db_connection() or {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
results := sql db {
|
||||
select from User
|
||||
}!
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
fn (mut app App) service_get_user(id int) !User {
|
||||
mut db := databases.create_db_connection() or {
|
||||
println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
results := sql db {
|
||||
select from User where id == id
|
||||
}!
|
||||
|
||||
return results.first()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
Module {
|
||||
name: 'veb_fullstack'
|
||||
description: ''
|
||||
version: ''
|
||||
license: ''
|
||||
dependencies: []
|
||||
}
|
||||
Reference in new issue
Block a user