Code now passes vet
ci/woodpecker/push/builder unknown status Details
ci/woodpecker/push/publish unknown status Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/build_arm64 Pipeline was successful Details

pull/48/head
Jef Roosens 2022-01-14 22:46:04 +01:00
parent 1f8f16fe8d
commit b86c6b5e16
Signed by: Jef Roosens
GPG Key ID: 955C0660072F691F
6 changed files with 52 additions and 40 deletions

View File

@ -5,7 +5,6 @@ import os
import log import log
import io import io
import pkg import pkg
import archive
import repo import repo
const port = 8000 const port = 8000
@ -110,7 +109,5 @@ fn main() {
return return
} }
// println(info) // println(info)
println(res.info) print(res)
print(res.files)
println(res.info.to_desc())
} }

View File

@ -89,6 +89,7 @@ mut:
checkdepends []string checkdepends []string
} }
// parse_pkg_info_string parses a PkgInfo object from a string
fn parse_pkg_info_string(pkg_info_str &string) ?PkgInfo { fn parse_pkg_info_string(pkg_info_str &string) ?PkgInfo {
mut pkg_info := PkgInfo{} mut pkg_info := PkgInfo{}
@ -140,7 +141,7 @@ fn parse_pkg_info_string(pkg_info_str &string) ?PkgInfo {
return pkg_info return pkg_info
} }
// Extracts the file list & .PKGINFO contents from an archive // read_pkg extracts the file list & .PKGINFO contents from an archive
// NOTE: this command currently only supports zstd-compressed tarballs // NOTE: this command currently only supports zstd-compressed tarballs
pub fn read_pkg(pkg_path string) ?Pkg { pub fn read_pkg(pkg_path string) ?Pkg {
if !os.is_file(pkg_path) { if !os.is_file(pkg_path) {
@ -166,9 +167,9 @@ pub fn read_pkg(pkg_path string) ?Pkg {
return error('Failed to open package.') return error('Failed to open package.')
} }
// We iterate over every header in search of the .PKGINFO one
mut buf := voidptr(0) mut buf := voidptr(0)
mut files := []string{} mut files := []string{}
for C.archive_read_next_header(a, &entry) == C.ARCHIVE_OK { for C.archive_read_next_header(a, &entry) == C.ARCHIVE_OK {
pathname := C.archive_entry_pathname(entry) pathname := C.archive_entry_pathname(entry)
@ -198,7 +199,7 @@ pub fn read_pkg(pkg_path string) ?Pkg {
} }
} }
// Represent a PkgInfo struct as a desc file // to_desc returns a desc file valid string representation
pub fn (p &PkgInfo) to_desc() string { pub fn (p &PkgInfo) to_desc() string {
// TODO calculate md5 & sha256 instead of believing the file // TODO calculate md5 & sha256 instead of believing the file
mut desc := '' mut desc := ''

View File

@ -1,7 +1,6 @@
module repo module repo
import os import os
import archive
const pkgs_subpath = 'pkgs' const pkgs_subpath = 'pkgs'
@ -22,35 +21,37 @@ pub:
pkg_dir string [required] pkg_dir string [required]
} }
// Returns whether the repository contains the given package. // contains returns whether the repository contains the given package.
pub fn (r &Repo) contains(pkg string) bool { pub fn (r &Repo) contains(pkg string) bool {
return os.exists(os.join_path(r.repo_dir, 'files', pkg)) return os.exists(os.join_path(r.repo_dir, 'files', pkg))
} }
// Adds the given package to the repo. If false, the package was already // add adds the given package to the repo. If false, the package was already
// present in the repository. // present in the repository.
pub fn (r &Repo) add(pkg string) ?bool { pub fn (r &Repo) add(pkg string) ?bool {
return false return false
} }
// Re-generate the db & files archives. // generate re-generates the db & files archives.
fn (r &Repo) genenerate() ? { fn (r &Repo) genenerate() ? {
} }
// Returns path to the given package, prepended with the repo's path. // pkg_path returns path to the given package, prepended with the repo's path.
pub fn (r &Repo) pkg_path(pkg string) string { pub fn (r &Repo) pkg_path(pkg string) string {
return os.join_path_single(r.pkg_dir, pkg) return os.join_path_single(r.pkg_dir, pkg)
} }
// exists checks whether a package file exists
pub fn (r &Repo) exists(pkg string) bool { pub fn (r &Repo) exists(pkg string) bool {
return os.exists(r.pkg_path(pkg)) return os.exists(r.pkg_path(pkg))
} }
// Returns the full path to the database file // db_path returns the full path to the database file
pub fn (r &Repo) db_path() string { pub fn (r &Repo) db_path() string {
return os.join_path_single(r.repo_dir, 'repo.tar.gz') return os.join_path_single(r.repo_dir, 'repo.tar.gz')
} }
// add_package adds a package to the repository
pub fn (r &Repo) add_package(pkg_path string) ? { pub fn (r &Repo) add_package(pkg_path string) ? {
mut res := os.Result{} mut res := os.Result{}

View File

@ -7,6 +7,7 @@ import time
const prefixes = ['B', 'KB', 'MB', 'GB'] const prefixes = ['B', 'KB', 'MB', 'GB']
// pretty_bytes converts a byte count to human-readable version
fn pretty_bytes(bytes int) string { fn pretty_bytes(bytes int) string {
mut i := 0 mut i := 0
mut n := f32(bytes) mut n := f32(bytes)
@ -23,6 +24,7 @@ fn is_pkg_name(s string) bool {
return s.contains('.pkg') return s.contains('.pkg')
} }
// get_root handles a GET request for a file on the root
['/:filename'; get] ['/:filename'; get]
fn (mut app App) get_root(filename string) web.Result { fn (mut app App) get_root(filename string) web.Result {
mut full_path := '' mut full_path := ''
@ -88,6 +90,7 @@ fn (mut app App) get_root(filename string) web.Result {
// return app.text('Package added successfully.') // return app.text('Package added successfully.')
// } // }
// add_package PUT a new package to the server
['/add'; put] ['/add'; put]
pub fn (mut app App) add_package() web.Result { pub fn (mut app App) add_package() web.Result {
return app.text('') return app.text('')

View File

@ -2,28 +2,34 @@ module web
import log import log
// log reate a log message with the given level
pub fn (mut ctx Context) log(msg &string, level log.Level) { pub fn (mut ctx Context) log(msg &string, level log.Level) {
lock ctx.logger { lock ctx.logger {
ctx.logger.send_output(msg, level) ctx.logger.send_output(msg, level)
} }
} }
// lfatal create a log message with the fatal level
pub fn (mut ctx Context) lfatal(msg &string) { pub fn (mut ctx Context) lfatal(msg &string) {
ctx.log(msg, log.Level.fatal) ctx.log(msg, log.Level.fatal)
} }
// lerror create a log message with the error level
pub fn (mut ctx Context) lerror(msg &string) { pub fn (mut ctx Context) lerror(msg &string) {
ctx.log(msg, log.Level.error) ctx.log(msg, log.Level.error)
} }
// lwarn create a log message with the warn level
pub fn (mut ctx Context) lwarn(msg &string) { pub fn (mut ctx Context) lwarn(msg &string) {
ctx.log(msg, log.Level.warn) ctx.log(msg, log.Level.warn)
} }
// linfo create a log message with the info level
pub fn (mut ctx Context) linfo(msg &string) { pub fn (mut ctx Context) linfo(msg &string) {
ctx.log(msg, log.Level.info) ctx.log(msg, log.Level.info)
} }
// ldebug create a log message with the debug level
pub fn (mut ctx Context) ldebug(msg &string) { pub fn (mut ctx Context) ldebug(msg &string) {
ctx.log(msg, log.Level.debug) ctx.log(msg, log.Level.debug)
} }

View File

@ -187,14 +187,14 @@ struct Route {
} }
// Defining this method is optional. // Defining this method is optional.
// This method called at server start. // init_server is called at server start.
// You can use it for initializing globals. // You can use it for initializing globals.
pub fn (ctx Context) init_server() { pub fn (ctx Context) init_server() {
eprintln('init_server() has been deprecated, please init your web app in `fn main()`') eprintln('init_server() has been deprecated, please init your web app in `fn main()`')
} }
// Defining this method is optional. // Defining this method is optional.
// This method called before every request (aka middleware). // before_request is called before every request (aka middleware).
// Probably you can use it for check user session cookie or add header. // Probably you can use it for check user session cookie or add header.
pub fn (ctx Context) before_request() {} pub fn (ctx Context) before_request() {}
@ -206,7 +206,7 @@ pub struct Cookie {
http_only bool http_only bool
} }
// web intern function // send_response_to_client sends a response to the client
[manualfree] [manualfree]
pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bool { pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bool {
if ctx.done { if ctx.done {
@ -230,33 +230,33 @@ pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bo
return true return true
} }
// Response HTTP_OK with s as payload with content-type `text/html` // html HTTP_OK with s as payload with content-type `text/html`
pub fn (mut ctx Context) html(s string) Result { pub fn (mut ctx Context) html(s string) Result {
ctx.send_response_to_client('text/html', s) ctx.send_response_to_client('text/html', s)
return Result{} return Result{}
} }
// Response HTTP_OK with s as payload with content-type `text/plain` // text HTTP_OK with s as payload with content-type `text/plain`
pub fn (mut ctx Context) text(s string) Result { pub fn (mut ctx Context) text(s string) Result {
ctx.send_response_to_client('text/plain', s) ctx.send_response_to_client('text/plain', s)
return Result{} return Result{}
} }
// Response HTTP_OK with json_s as payload with content-type `application/json` // json<T> HTTP_OK with json_s as payload with content-type `application/json`
pub fn (mut ctx Context) json<T>(j T) Result { pub fn (mut ctx Context) json<T>(j T) Result {
json_s := json.encode(j) json_s := json.encode(j)
ctx.send_response_to_client('application/json', json_s) ctx.send_response_to_client('application/json', json_s)
return Result{} return Result{}
} }
// Response HTTP_OK with a pretty-printed JSON result // json_pretty<T> Response HTTP_OK with a pretty-printed JSON result
pub fn (mut ctx Context) json_pretty<T>(j T) Result { pub fn (mut ctx Context) json_pretty<T>(j T) Result {
json_s := json.encode_pretty(j) json_s := json.encode_pretty(j)
ctx.send_response_to_client('application/json', json_s) ctx.send_response_to_client('application/json', json_s)
return Result{} return Result{}
} }
// Response HTTP_OK with file as payload // file Response HTTP_OK with file as payload
// This function manually implements responses because it needs to stream the file contents // This function manually implements responses because it needs to stream the file contents
pub fn (mut ctx Context) file(f_path string) Result { pub fn (mut ctx Context) file(f_path string) Result {
if ctx.done { if ctx.done {
@ -329,13 +329,13 @@ pub fn (mut ctx Context) file(f_path string) Result {
return Result{} return Result{}
} }
// Response HTTP_OK with s as payload // ok Response HTTP_OK with s as payload
pub fn (mut ctx Context) ok(s string) Result { pub fn (mut ctx Context) ok(s string) Result {
ctx.send_response_to_client(ctx.content_type, s) ctx.send_response_to_client(ctx.content_type, s)
return Result{} return Result{}
} }
// Response a server error // server_error Response a server error
pub fn (mut ctx Context) server_error(ecode int) Result { pub fn (mut ctx Context) server_error(ecode int) Result {
$if debug { $if debug {
eprintln('> ctx.server_error ecode: $ecode') eprintln('> ctx.server_error ecode: $ecode')
@ -347,7 +347,7 @@ pub fn (mut ctx Context) server_error(ecode int) Result {
return Result{} return Result{}
} }
// Redirect to an url // redirect Redirect to an url
pub fn (mut ctx Context) redirect(url string) Result { pub fn (mut ctx Context) redirect(url string) Result {
if ctx.done { if ctx.done {
return Result{} return Result{}
@ -360,7 +360,7 @@ pub fn (mut ctx Context) redirect(url string) Result {
return Result{} return Result{}
} }
// Send an not_found response // not_found Send an not_found response
pub fn (mut ctx Context) not_found() Result { pub fn (mut ctx Context) not_found() Result {
if ctx.done { if ctx.done {
return Result{} return Result{}
@ -370,7 +370,7 @@ pub fn (mut ctx Context) not_found() Result {
return Result{} return Result{}
} }
// Sets a cookie // set_cookie Sets a cookie
pub fn (mut ctx Context) set_cookie(cookie Cookie) { pub fn (mut ctx Context) set_cookie(cookie Cookie) {
mut cookie_data := []string{} mut cookie_data := []string{}
mut secure := if cookie.secure { 'Secure;' } else { '' } mut secure := if cookie.secure { 'Secure;' } else { '' }
@ -383,17 +383,17 @@ pub fn (mut ctx Context) set_cookie(cookie Cookie) {
ctx.add_header('Set-Cookie', '$cookie.name=$cookie.value; $data') ctx.add_header('Set-Cookie', '$cookie.name=$cookie.value; $data')
} }
// Sets the response content type // set_content_type Sets the response content type
pub fn (mut ctx Context) set_content_type(typ string) { pub fn (mut ctx Context) set_content_type(typ string) {
ctx.content_type = typ ctx.content_type = typ
} }
// Sets a cookie with a `expire_data` // set_cookie_with_expire_date Sets a cookie with a `expire_data`
pub fn (mut ctx Context) set_cookie_with_expire_date(key string, val string, expire_date time.Time) { pub fn (mut ctx Context) set_cookie_with_expire_date(key string, val string, expire_date time.Time) {
ctx.add_header('Set-Cookie', '$key=$val; Secure; HttpOnly; expires=$expire_date.utc_string()') ctx.add_header('Set-Cookie', '$key=$val; Secure; HttpOnly; expires=$expire_date.utc_string()')
} }
// Gets a cookie by a key // get_cookie Gets a cookie by a key
pub fn (ctx &Context) get_cookie(key string) ?string { // TODO refactor pub fn (ctx &Context) get_cookie(key string) ?string { // TODO refactor
mut cookie_header := ctx.get_header('cookie') mut cookie_header := ctx.get_header('cookie')
if cookie_header == '' { if cookie_header == '' {
@ -413,7 +413,7 @@ pub fn (ctx &Context) get_cookie(key string) ?string { // TODO refactor
return error('Cookie not found') return error('Cookie not found')
} }
// Sets the response status // set_status Sets the response status
pub fn (mut ctx Context) set_status(code int, desc string) { pub fn (mut ctx Context) set_status(code int, desc string) {
if code < 100 || code > 599 { if code < 100 || code > 599 {
ctx.status = '500 Internal Server Error' ctx.status = '500 Internal Server Error'
@ -422,12 +422,12 @@ pub fn (mut ctx Context) set_status(code int, desc string) {
} }
} }
// Adds an header to the response with key and val // add_header Adds an header to the response with key and val
pub fn (mut ctx Context) add_header(key string, val string) { pub fn (mut ctx Context) add_header(key string, val string) {
ctx.header.add_custom(key, val) or {} ctx.header.add_custom(key, val) or {}
} }
// Returns the header data from the key // get_header Returns the header data from the key
pub fn (ctx &Context) get_header(key string) string { pub fn (ctx &Context) get_header(key string) string {
return ctx.req.header.get_custom(key) or { '' } return ctx.req.header.get_custom(key) or { '' }
} }
@ -436,7 +436,7 @@ interface DbInterface {
db voidptr db voidptr
} }
// run_app // run runs the app
[manualfree] [manualfree]
pub fn run<T>(global_app &T, port int) { pub fn run<T>(global_app &T, port int) {
mut l := net.listen_tcp(.ip6, ':$port') or { panic('failed to listen $err.code $err') } mut l := net.listen_tcp(.ip6, ':$port') or { panic('failed to listen $err.code $err') }
@ -478,6 +478,7 @@ pub fn run<T>(global_app &T, port int) {
} }
} }
// handle_conn handles a connection
[manualfree] [manualfree]
fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) {
conn.set_read_timeout(30 * time.second) conn.set_read_timeout(30 * time.second)
@ -615,6 +616,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) {
conn.write(web.http_404.bytes()) or {} conn.write(web.http_404.bytes()) or {}
} }
// route_matches returns wether a route matches
fn route_matches(url_words []string, route_words []string) ?[]string { fn route_matches(url_words []string, route_words []string) ?[]string {
// URL path should be at least as long as the route path // URL path should be at least as long as the route path
// except for the catchall route (`/:path...`) // except for the catchall route (`/:path...`)
@ -657,7 +659,7 @@ fn route_matches(url_words []string, route_words []string) ?[]string {
return params return params
} }
// check if request is for a static file and serves it // serve_if_static<T> checks if request is for a static file and serves it
// returns true if we served a static file, false otherwise // returns true if we served a static file, false otherwise
[manualfree] [manualfree]
fn serve_if_static<T>(mut app T, url urllib.URL) bool { fn serve_if_static<T>(mut app T, url urllib.URL) bool {
@ -676,6 +678,7 @@ fn serve_if_static<T>(mut app T, url urllib.URL) bool {
return true return true
} }
// scan_static_directory makes a static route for each file in a directory
fn (mut ctx Context) scan_static_directory(directory_path string, mount_path string) { fn (mut ctx Context) scan_static_directory(directory_path string, mount_path string) {
files := os.ls(directory_path) or { panic(err) } files := os.ls(directory_path) or { panic(err) }
if files.len > 0 { if files.len > 0 {
@ -695,7 +698,7 @@ fn (mut ctx Context) scan_static_directory(directory_path string, mount_path str
} }
} }
// Handles a directory static // handle_static Handles a directory static
// If `root` is set the mount path for the dir will be in '/' // If `root` is set the mount path for the dir will be in '/'
pub fn (mut ctx Context) handle_static(directory_path string, root bool) bool { pub fn (mut ctx Context) handle_static(directory_path string, root bool) bool {
if ctx.done || !os.exists(directory_path) { if ctx.done || !os.exists(directory_path) {
@ -724,7 +727,7 @@ pub fn (mut ctx Context) mount_static_folder_at(directory_path string, mount_pat
return true return true
} }
// Serves a file static // serve_static Serves a file static
// `url` is the access path on the site, `file_path` is the real path to the file, `mime_type` is the file type // `url` is the access path on the site, `file_path` is the real path to the file, `mime_type` is the file type
pub fn (mut ctx Context) serve_static(url string, file_path string) { pub fn (mut ctx Context) serve_static(url string, file_path string) {
ctx.static_files[url] = file_path ctx.static_files[url] = file_path
@ -733,7 +736,7 @@ pub fn (mut ctx Context) serve_static(url string, file_path string) {
ctx.static_mime_types[url] = web.mime_types[ext] ctx.static_mime_types[url] = web.mime_types[ext]
} }
// Returns the ip address from the current user // ip Returns the ip address from the current user
pub fn (ctx &Context) ip() string { pub fn (ctx &Context) ip() string {
mut ip := ctx.req.header.get(.x_forwarded_for) or { '' } mut ip := ctx.req.header.get(.x_forwarded_for) or { '' }
if ip == '' { if ip == '' {
@ -749,22 +752,23 @@ pub fn (ctx &Context) ip() string {
return ip return ip
} }
// Set s to the form error // error Set s to the form error
pub fn (mut ctx Context) error(s string) { pub fn (mut ctx Context) error(s string) {
println('web error: $s') println('web error: $s')
ctx.form_error = s ctx.form_error = s
} }
// Returns an empty result // not_found Returns an empty result
pub fn not_found() Result { pub fn not_found() Result {
return Result{} return Result{}
} }
// send_string
fn send_string(mut conn net.TcpConn, s string) ? { fn send_string(mut conn net.TcpConn, s string) ? {
conn.write(s.bytes()) ? conn.write(s.bytes()) ?
} }
// Do not delete. // filter Do not delete.
// It used by `vlib/v/gen/c/str_intp.v:130` for string interpolation inside web templates // It used by `vlib/v/gen/c/str_intp.v:130` for string interpolation inside web templates
// TODO: move it to template render // TODO: move it to template render
fn filter(s string) string { fn filter(s string) string {