From 7bf28afdbfc85ea0f97cc087f3a6321bab9617ce Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Sat, 2 May 2026 01:15:22 -0500 Subject: [PATCH 1/6] go update --- go.mod | 45 +++++++++--------- go.sum | 141 ++++++++++++++++++++------------------------------------- 2 files changed, 72 insertions(+), 114 deletions(-) diff --git a/go.mod b/go.mod index bdafa0f..c51f815 100644 --- a/go.mod +++ b/go.mod @@ -7,57 +7,56 @@ replace ( git.smarteching.com/goffee/cup/models => ./models ) -go 1.24.1 +go 1.25.0 require ( - git.smarteching.com/goffee/core v1.9.5 + git.smarteching.com/goffee/core v1.9.6 github.com/google/uuid v1.6.0 - github.com/hibiken/asynq v0.25.1 + github.com/hibiken/asynq v0.26.0 github.com/joho/godotenv v1.5.1 github.com/julienschmidt/httprouter v1.3.0 - gorm.io/gorm v1.30.0 + gorm.io/gorm v1.31.1 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect git.smarteching.com/zeni/go-chart/v2 v2.1.4 // indirect github.com/SparkPost/gosparkpost v0.2.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/go-chi/chi/v5 v5.2.2 // indirect + github.com/go-chi/chi/v5 v5.2.5 // indirect github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect - github.com/go-sql-driver/mysql v1.9.3 // indirect - github.com/golang-jwt/jwt/v5 v5.2.3 // indirect + github.com/go-sql-driver/mysql v1.10.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/harranali/mailing v1.2.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.5 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailgun/errors v0.4.0 // indirect + github.com/mailgun/errors v0.5.0 // indirect github.com/mailgun/mailgun-go/v4 v4.23.0 // indirect - github.com/mattn/go-sqlite3 v1.14.28 // indirect + github.com/mattn/go-sqlite3 v1.14.44 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/redis/go-redis/v9 v9.11.0 // indirect + github.com/redis/go-redis/v9 v9.19.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sendgrid/rest v2.6.9+incompatible // indirect github.com/sendgrid/sendgrid-go v3.16.1+incompatible // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/cast v1.9.2 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/image v0.29.0 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.12.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect + github.com/spf13/cast v1.10.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/image v0.39.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gorm.io/driver/mysql v1.6.0 // indirect gorm.io/driver/postgres v1.6.0 // indirect gorm.io/driver/sqlite v1.6.0 // indirect diff --git a/go.sum b/go.sum index 0ea3916..76d68f0 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -git.smarteching.com/goffee/core v1.9.2 h1:SpLAhsTxssPItgkBYLN3UxMH5s+q8qVtbvmRom3WKh8= -git.smarteching.com/goffee/core v1.9.2/go.mod h1:L9a+kL1RVHRHzp+DzCS1apwVLyZAvGE6B94UlyIMhIg= -git.smarteching.com/goffee/core v1.9.5 h1:rq6vI4WSUMGQNzJvhNWmtY2ycC7UszEvXpQ7uUR8sZY= -git.smarteching.com/goffee/core v1.9.5/go.mod h1:ifiBgTOR4zCMzdGsabNrEO792EHny2o149NGe3TSlms= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +git.smarteching.com/goffee/core v1.9.6 h1:GY1EXqbmBEWZAVrl3q22Izb6aXhQzFVQBv2hWhK/So8= +git.smarteching.com/goffee/core v1.9.6/go.mod h1:ifiBgTOR4zCMzdGsabNrEO792EHny2o149NGe3TSlms= git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q= git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ= github.com/SparkPost/gosparkpost v0.2.0 h1:yzhHQT7cE+rqzd5tANNC74j+2x3lrPznqPJrxC1yR8s= @@ -23,46 +21,35 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= -github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= +github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= -github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= -github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= -github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw= +github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= -github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/harranali/mailing v1.2.0 h1:ihIyJwB8hyRVcdk+v465wk1PHMrSrgJqo/kMd+gZClY= github.com/harranali/mailing v1.2.0/go.mod h1:4a5N3yG98pZKluMpmcYlTtll7bisvOfGQEMIng3VQk4= -github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw= -github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg= +github.com/hibiken/asynq v0.26.0 h1:1Zxr92MlDnb1Zt/QR5g2vSCqUS03i95lUfqx5X7/wrw= +github.com/hibiken/asynq v0.26.0/go.mod h1:Qk4e57bTnWDoyJ67VkchuV6VzSM9IQW2nPvAGuDyw58= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= -github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -77,21 +64,21 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailgun/errors v0.4.0 h1:6LFBvod6VIW83CMIOT9sYNp28TCX0NejFPP4dSX++i8= -github.com/mailgun/errors v0.4.0/go.mod h1:xGBaaKdEdQT0/FhwvoXv4oBaqqmVZz9P1XEnvD/onc0= +github.com/mailgun/errors v0.5.0 h1:pLQo8uhAdORsjN69mGixSr0pGs46z/BW/FQXd8HG1VM= +github.com/mailgun/errors v0.5.0/go.mod h1:+2nrgY77E0vDkG4ErehpcpbSkMLkseJzKbrva89WeSs= github.com/mailgun/mailgun-go/v4 v4.23.0 h1:jPEMJzzin2s7lvehcfv/0UkyBu18GvcURPr2+xtZRbk= github.com/mailgun/mailgun-go/v4 v4.23.0/go.mod h1:imTtizoFtpfZqPqGP8vltVBB6q9yWcv6llBhfFeElZU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8= +github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -103,10 +90,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc= -github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= -github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs= -github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -114,81 +99,55 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= -github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw= -github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1GjJohAA0p6hVEaDtHWWs= github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= -github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= -golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= -golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= -golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= +golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= -gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= -gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= -gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= -gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= -gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= -gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= -gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= -gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= From a7310b24d576803cca0a92e55e4f1d5853335307 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Sat, 2 May 2026 03:03:36 -0500 Subject: [PATCH 2/6] Custom `html/template` FuncMap extensions that bring Liquid-like expressiveness to Go server-side templates. Add template demo. --- controllers/themedemo.go | 131 +++++ go.mod | 2 + go.sum | 4 + routes.go | 2 + .../templates/custom_templates_functions.html | 471 ++++++++++++++++++ 5 files changed, 610 insertions(+) create mode 100644 storage/templates/custom_templates_functions.html diff --git a/controllers/themedemo.go b/controllers/themedemo.go index 03c55c1..85446e6 100644 --- a/controllers/themedemo.go +++ b/controllers/themedemo.go @@ -9,6 +9,7 @@ import ( "math/rand/v2" "os" "strconv" + "time" "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/core/template/components" @@ -861,3 +862,133 @@ func getPaginatedPageViews(values [][]components.ContentTableTD, limit int, offs return values[offset:end] // Slice the array } + +// Custom Templates functions +func TemplatesFunctions(c *core.Context) *core.Response { + + // check if template engine is enabled + TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") + if TemplateEnableStr == "" { + TemplateEnableStr = "false" + } + TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) + + if TemplateEnable { + + tmplData := SamplePageData() + return c.Response.Template("custom_templates_functions.html", tmplData) + + } else { + + message := "{\"message\": \"Error, template not enabled\"}" + return c.Response.Json(message) + + } + +} + +// Author represents the writer of an article to show the custom functions +type Author struct { + Name string + Avatar string + Bio string +} + +// Article represents a blog post or news entry used to test template helpers to show the custom functions +type Article struct { + Title string + Slug string + Excerpt string + Body string + Tags []string + PublishedAt time.Time + UpdatedAt time.Time + Author Author + Views int + Price float64 + Featured bool + Subtitle string // intentionally left empty on some entries to test coalesce/defaultVal +} + +// PageData is the top-level context passed to the HTML template to show the custom functions +type PageData struct { + SiteTitle string + Articles []Article +} + +// SamplePageData returns a populated PageData ready to be rendered by the template. +func SamplePageData() PageData { + return PageData{ + SiteTitle: "go/template lab", + Articles: []Article{ + { + Title: "getting started with go templates", + Slug: "getting-started-go-templates", + Excerpt: "Go's html/template package is both powerful and safe by default. In this article we explore how to extend it with custom FuncMap helpers that bring it closer to the expressiveness of Liquid or Twig, without sacrificing any of the security guarantees.", + Tags: []string{"go", "templates", "web", "backend"}, + PublishedAt: time.Now().Add(-3 * 24 * time.Hour), + UpdatedAt: time.Now().Add(-1 * 24 * time.Hour), + Views: 14200, + Price: 0, + Featured: true, + Subtitle: "", + Author: Author{ + Name: "marina voss", + Avatar: "MV", + Bio: "Senior backend engineer focused on developer tooling and observability.", + }, + }, + { + Title: "building a blog engine in go", + Slug: "blog-engine-go", + Excerpt: "We walk through building a minimal but complete blog engine using only the Go standard library: routing with net/http, persistence with database/sql, and rendering with html/template.", + Tags: []string{"go", "blog", "sqlite"}, + PublishedAt: time.Now().Add(-10 * 24 * time.Hour), + UpdatedAt: time.Now().Add(-10 * 24 * time.Hour), + Views: 8750, + Price: 9.99, + Featured: false, + Subtitle: "A zero-dependency approach", + Author: Author{ + Name: "rafael okonkwo", + Avatar: "RO", + Bio: "Full-stack engineer and open-source contributor. Writes about Go and distributed systems.", + }, + }, + { + Title: "concurrency patterns you should know", + Slug: "concurrency-patterns-go", + Excerpt: "Goroutines are cheap, but misusing them is expensive. This deep-dive covers fan-out/fan-in, pipelines, semaphores, and error group patterns with real production examples.", + Tags: []string{"go", "concurrency", "goroutines", "advanced"}, + PublishedAt: time.Now().Add(-45 * 24 * time.Hour), + UpdatedAt: time.Now().Add(-40 * 24 * time.Hour), + Views: 31400, + Price: 14.99, + Featured: true, + Subtitle: "", + Author: Author{ + Name: "selin çelik", + Avatar: "SÇ", + Bio: "Systems programmer. Previously at Cloudflare. Loves writing about the internals of things.", + }, + }, + { + Title: "understanding go interfaces", + Slug: "understanding-go-interfaces", + Excerpt: "Interfaces in Go are implicit and structural, which makes them both elegant and occasionally surprising. We look at how the runtime dispatches method calls and how to design composable interfaces.", + Tags: []string{"go", "interfaces", "design"}, + PublishedAt: time.Now().Add(-2 * time.Hour), + UpdatedAt: time.Now().Add(-1 * time.Hour), + Views: 420, + Price: 0, + Featured: false, + Subtitle: "Implicit, structural, and powerful", + Author: Author{ + Name: "marina voss", + Avatar: "MV", + Bio: "Senior backend engineer focused on developer tooling and observability.", + }, + }, + }, + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index c51f815..21103fe 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,11 @@ require ( require ( filippo.io/edwards25519 v1.2.0 // indirect git.smarteching.com/zeni/go-chart/v2 v2.1.4 // indirect + git.smarteching.com/zeni/go-charts/v2 v2.6.11 // indirect github.com/SparkPost/gosparkpost v0.2.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-chi/chi/v5 v5.2.5 // indirect github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/go-sql-driver/mysql v1.10.0 // indirect diff --git a/go.sum b/go.sum index 76d68f0..2bf5036 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ git.smarteching.com/goffee/core v1.9.6 h1:GY1EXqbmBEWZAVrl3q22Izb6aXhQzFVQBv2hWh git.smarteching.com/goffee/core v1.9.6/go.mod h1:ifiBgTOR4zCMzdGsabNrEO792EHny2o149NGe3TSlms= git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q= git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ= +git.smarteching.com/zeni/go-charts/v2 v2.6.11 h1:9udzlv3uxGXszpplfkL5IaTUrgkNj++KwhbaN1vVEqI= +git.smarteching.com/zeni/go-charts/v2 v2.6.11/go.mod h1:3OpRPSXg7Qx4zcgsmwsC9ZFB9/wAkGSbnXf1wIbHYCg= github.com/SparkPost/gosparkpost v0.2.0 h1:yzhHQT7cE+rqzd5tANNC74j+2x3lrPznqPJrxC1yR8s= github.com/SparkPost/gosparkpost v0.2.0/go.mod h1:S9WKcGeou7cbPpx0kTIgo8Q69WZvUmVeVzbD+djalJ4= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -21,6 +23,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= diff --git a/routes.go b/routes.go index f8d7fec..dc1799c 100644 --- a/routes.go +++ b/routes.go @@ -60,4 +60,6 @@ func registerRoutes() { controller.Get("/appsession", controllers.AppSession) controller.Post("/appsession", controllers.AppSession) + + controller.Get("/templatesfunc", controllers.TemplatesFunctions) } diff --git a/storage/templates/custom_templates_functions.html b/storage/templates/custom_templates_functions.html new file mode 100644 index 0000000..6e0c357 --- /dev/null +++ b/storage/templates/custom_templates_functions.html @@ -0,0 +1,471 @@ + + + + + + {{.SiteTitle}} — template lab + + + + + + + +
+
+

{{.SiteTitle}}

+

Template helpers
in action

+
+

22 helpers registered
across 6 groups

+
+ + + + + +{{$first := first .Articles}} + +
+ +
+

capitalize capitalize

+

{{capitalize $first.Title}}

+

input: "{{$first.Title}}"

+
+ +
+

truncate truncate

+

{{$first.Excerpt | truncate 80}}

+

capped at 80 chars

+
+ +
+

prepend prepend

+

{{prepend $first.Slug "/articles/"}}

+

prepended "/articles/" to slug

+
+ +
+

strAppend strAppend

+

{{strAppend $first.Slug ".html"}}

+

appended ".html" to slug

+
+ +
+

split → join split join

+ {{$parts := split "go,templates,funcmap" ","}} +

{{join $parts " · "}}

+

split on "," then joined with " · "

+
+ +
+ + + + + +
+ +
+

fmtNumber — int fmtNumber

+

{{fmtNumber $first.Views}}

+

raw value: {{$first.Views}}

+
+ + {{$paid := index .Articles 2}} +
+

fmtNumber — float fmtNumber

+

$ {{fmtNumber $paid.Price}}

+

raw value: {{$paid.Price}}

+
+ +
+

fmtDate "short" fmtDate

+

{{fmtDate $first.PublishedAt "short"}}

+

layout: "02 Jan 2006"

+
+ +
+

fmtDate "long" fmtDate

+

{{fmtDate $first.PublishedAt "long"}}

+

layout: "02 January 2006"

+
+ +
+

fmtDate "iso" fmtDate

+

{{fmtDate $first.PublishedAt "iso"}}

+

layout: "2006-01-02"

+
+ +
+

timeAgo timeAgo

+ {{range .Articles}} +

{{timeAgo .PublishedAt}} — {{fmtDate .PublishedAt "short"}}

+ {{end}} +
+ +
+ + + + + +
+ +
+

first first

+ {{with first .Articles}} +

{{capitalize .Title}}

+

first article in the list

+ {{end}} +
+ +
+

last last

+ {{with last .Articles}} +

{{capitalize .Title}}

+

last article in the list

+ {{end}} +
+ +
+

sliceOf 0–2 sliceOf

+ {{range sliceOf .Articles 0 2}} +

— {{capitalize .Title}}

+ {{end}} +

only first 2 of {{len .Articles}} articles

+
+ +
+

contains — slice contains

+ {{if contains $first.Tags "go"}} +

✓ has tag "go"

+ {{else}} +

✗ missing tag "go"

+ {{end}} +

tags: {{join $first.Tags ", "}}

+
+ +
+

contains — string contains

+ {{if contains $first.Excerpt "FuncMap"}} +

✓ excerpt mentions "FuncMap"

+ {{else}} +

✗ not found

+ {{end}} +

substring search on .Excerpt

+
+ +
+

join join

+

{{join $first.Tags " / "}}

+

tags joined with " / "

+
+ +
+ + + + + + + + + + + + + + + + {{range .Articles}} + + + + + + + + + + + + + + + + + + + {{end}} + +
HelperArticleInputOutput
defaultVal{{.Title | capitalize | truncate 30}}.Subtitle ({{if .Subtitle}}"{{.Subtitle}}"{{else}}empty{{end}}){{defaultVal "No subtitle" .Subtitle}}
ternary{{capitalize .Title | truncate 30}}.Featured = {{.Featured}}{{ternary "⭐ featured" "regular" .Featured}}
coalesce{{capitalize .Title | truncate 30}}.Subtitle → .Title{{coalesce .Subtitle .Title}}
+ + + + + +
+ {{range .Articles}} +
+
+
+ {{if .Featured}}featured{{end}} + {{range .Tags}}{{.}}{{end}} +
+ +

{{capitalize .Title}}

+ + {{with coalesce .Subtitle ""}} +

{{.}}

+ {{end}} + +

{{.Excerpt | truncate 160}}

+ + + {{prepend .Slug "/articles/"}} → + + +
+
{{.Author.Avatar}}
+
+
{{capitalize .Author.Name}}
+ +
+
+
+ +
+
+ {{fmtNumber .Views}} + views +
+ {{if gt .Price 0.0}} + $ {{fmtNumber .Price}} + {{else}} + free + {{end}} +
+
+ {{end}} +
+ + + +
+ {{.SiteTitle}} / template sandbox + {{len .Articles}} articles · {{fmtDate (first .Articles).PublishedAt "iso"}} → {{fmtDate (last .Articles).PublishedAt "iso"}} +
+ + + \ No newline at end of file From f320c1d238678958e0ec571faf621ae13d1d9fe5 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Wed, 6 May 2026 16:07:55 -0500 Subject: [PATCH 3/6] support to new log system --- .env-dev | 2 ++ controllers/themedemo.go | 7 +++++++ main.go | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/.env-dev b/.env-dev index c88f67a..800ca98 100644 --- a/.env-dev +++ b/.env-dev @@ -14,6 +14,8 @@ App_HTTPS_HOSTS=example.com, www.example.com App_REDIRECT_HTTP_TO_HTTPS=false App_CERT_FILE_PATH=tls/server.crt App_KEY_FILE_PATH=tls/server.key +LOG_STDOUT_ENABLE=true +LOG_LEVEL=debug ####################################### ###### TEMPLATES ###### diff --git a/controllers/themedemo.go b/controllers/themedemo.go index 85446e6..a3e22a3 100644 --- a/controllers/themedemo.go +++ b/controllers/themedemo.go @@ -874,6 +874,13 @@ func TemplatesFunctions(c *core.Context) *core.Response { TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) if TemplateEnable { + loggr := c.GetLogger() + + loggr.Debug("D e b u g") + loggr.Info("I n f o") + loggr.Warning("W a r n i n g") + loggr.Error("E R R O R") + tmplData := SamplePageData() return c.Response.Template("custom_templates_functions.html", tmplData) diff --git a/main.go b/main.go index 5fac06e..dc5750b 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,19 @@ func main() { app.SetGormConfig(config.GetGormConfig()) app.SetCacheConfig(config.GetCacheConfig()) app.Bootstrap() + // Set log level from environment variable (debug, info, warning, error) + if levelStr := env.GetVar("LOG_LEVEL"); levelStr != "" { + switch levelStr { + case "debug": + logger.ResolveLogger().SetLevel(logger.DEBUG) + case "info": + logger.ResolveLogger().SetLevel(logger.INFO) + case "warning": + logger.ResolveLogger().SetLevel(logger.WARNING) + case "error": + logger.ResolveLogger().SetLevel(logger.ERROR) + } + } app.RegisterTemplates(resources) registerGlobalHooks() registerRoutes() From d80be2713facdba636a4355901233321c3bd8b32 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Wed, 6 May 2026 21:10:06 -0500 Subject: [PATCH 4/6] empty database --- storage/sqlite/sqlite.db | Bin 90112 -> 4096 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/storage/sqlite/sqlite.db b/storage/sqlite/sqlite.db index 97751beb57aa829a9420e53efc55e7c533b572c2..57a82d2db1a7536fb820719d2dbb8ad2ea724ed1 100644 GIT binary patch delta 36 qcmZoTz}lcNL7J73fq{W>qJjRz2?E@V40_3oyg&g4fz5&n)A#|A!Up{S literal 90112 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 Date: Wed, 6 May 2026 21:21:57 -0500 Subject: [PATCH 5/6] clean samples --- controllers/queuesample.go | 52 - controllers/sample.go | 149 --- controllers/themedemo.go | 1001 ----------------- routes.go | 28 +- storage/templates/app.html | 17 - storage/templates/appsession.html | 12 - .../templates/custom_templates_functions.html | 471 -------- storage/templates/custom_theme_base.html | 10 - .../templates/custom_theme_contentpage.html | 30 - storage/templates/custom_theme_elements.html | 66 -- storage/templates/custom_theme_formpage.html | 23 - 11 files changed, 2 insertions(+), 1857 deletions(-) delete mode 100644 controllers/queuesample.go delete mode 100644 controllers/sample.go delete mode 100644 controllers/themedemo.go delete mode 100644 storage/templates/app.html delete mode 100644 storage/templates/appsession.html delete mode 100644 storage/templates/custom_templates_functions.html delete mode 100644 storage/templates/custom_theme_base.html delete mode 100644 storage/templates/custom_theme_contentpage.html delete mode 100644 storage/templates/custom_theme_elements.html delete mode 100644 storage/templates/custom_theme_formpage.html diff --git a/controllers/queuesample.go b/controllers/queuesample.go deleted file mode 100644 index d289aee..0000000 --- a/controllers/queuesample.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2025 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package controllers - -import ( - "encoding/json" - "log" - "time" - - "git.smarteching.com/goffee/core" - "git.smarteching.com/goffee/cup/workers" - "github.com/hibiken/asynq" -) - -// Make samples queues -func Queuesample(c *core.Context) *core.Response { - - // Get client queue asynq - client := c.GetQueueClient() - - // Create a task with typename and payload. - payload, err := json.Marshal(workers.EmailTaskPayload{UserID: 42}) - if err != nil { - log.Fatal(err) - } - - t1 := asynq.NewTask(workers.TypeWelcomeEmail, payload) - - t2 := asynq.NewTask(workers.TypeReminderEmail, payload) - - // Process the task immediately. - info, err := client.Enqueue(t1) - if err != nil { - log.Fatal(err) - } - log.Printf(" [*] Successfully enqueued task: %+v", info) - - // Process 2 task 1 min later. - for i := 1; i < 3; i++ { - info, err = client.Enqueue(t2, asynq.ProcessIn(1*time.Minute)) - if err != nil { - log.Fatal(err) - } - log.Printf(" [*] Successfully enqueued task: %+v", info) - } - - message := "{\"message\": \"Task queued\"}" - return c.Response.Json(message) - -} diff --git a/controllers/sample.go b/controllers/sample.go deleted file mode 100644 index 9f5687b..0000000 --- a/controllers/sample.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2024 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package controllers - -import ( - "fmt" - - "git.smarteching.com/goffee/core" - "git.smarteching.com/goffee/core/template/components" - "git.smarteching.com/goffee/cup/utils" -) - -// Show basic template -func Sample(c *core.Context) *core.Response { - - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Framework Goffee", - CardBody: "Powered by Golang", - }, - } - - return c.Response.Template("basic.html", tmplData) - -} - -// Show basic app login -func AppLogin(c *core.Context) *core.Response { - - // first, include all compoments - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Card title", - CardBody: "Loerm ipsum at deim", - }, - } - return c.Response.Template("login.html", tmplData) -} - -// Show basic app login -func AppSession(c *core.Context) *core.Response { - - var session = new(utils.SessionUser) - - // true if session is active - hassession := session.Init(c) - - //session.Set("numberdos", 66) - - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{} - - if hassession { - - sesiondata := "" - cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID()) - numberdos, ok := session.Get("numberdos") - - if numberdos != nil { - numberdos = numberdos.(float64) + 10 - } else { - numberdos = 10 - } - - session.Set("numberdos", numberdos) - - if ok { - sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos) - } else { - sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos) - } - - // delete single - //session.Delete("numberdos") - - // delete all data - //session.Flush() - - tmplData = templateData{ - PageCard: components.PageCard{ - CardTitle: cardtitle, - CardBody: sesiondata, - }, - } - - return c.Response.Template("appsession.html", tmplData) - - } else { - - return c.Response.Template("login.html", tmplData) - - } - -} - -// Show basic app sample -func AppSample(c *core.Context) *core.Response { - - // first, include all compoments - type templateData struct { - PageCard components.PageCard - ContentDropdown components.ContentDropdown - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Protected page", - CardBody: "If you can see this page, your are loggedin", - }, - ContentDropdown: components.ContentDropdown{ - Label: "dropdown", - Items: []components.ContentDropdownItem{ - { - Text: "Signout", - Link: "#", - ID: "signout", - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - } - //fmt.Printf("Outside cookie user is: %s", user.Email) - - return c.Response.Template("app.html", tmplData) - -} diff --git a/controllers/themedemo.go b/controllers/themedemo.go deleted file mode 100644 index a3e22a3..0000000 --- a/controllers/themedemo.go +++ /dev/null @@ -1,1001 +0,0 @@ -// Copyright (c) 2024 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package controllers - -import ( - "fmt" - "math/rand/v2" - "os" - "strconv" - "time" - - "git.smarteching.com/goffee/core" - "git.smarteching.com/goffee/core/template/components" -) - -// Show home page -func Themedemo(c *core.Context) *core.Response { - - // check if template engine is enabled - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - - if TemplateEnable { - - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Card title", - CardBody: "Loerm ipsum at deim", - }, - } - - return c.Response.Template("custom_theme_base.html", tmplData) - - } else { - - message := "{\"message\": \"Error, template not enabled\"}" - return c.Response.Json(message) - - } - -} - -// Show form element page -func Themeform(c *core.Context) *core.Response { - - // check if template engine is enabled - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - - if TemplateEnable { - - // first, include all compoments - type templateData struct { - FormText components.FormInput - FormEmail components.FormInput - FormButton components.FormButton - FormSelectCity components.FormSelect - FormTextarea components.FormTextarea - FormRadio components.FormRadio - FormCheckbox components.FormCheckbox - } - - // for select options - var allOptions []components.FormSelectOption - var option components.FormSelectOption - option.Value = "ch" - option.Caption = "China" - allOptions = append(allOptions, option) - option.Value = "ba" - option.Caption = "Buenos Aires" - allOptions = append(allOptions, option) - option.Value = "fr" - option.Caption = "France" - selectedOption := option - allOptions = append(allOptions, option) - - // for radio options - var allOptionsr []components.FormRadioItem - var optionr components.FormRadioItem - optionr.ID = "citysch" - optionr.Name = "citys" - optionr.Value = "china" - optionr.Label = "China" - allOptionsr = append(allOptionsr, optionr) - optionr.ID = "citysba" - optionr.Name = "citys" - optionr.Value = "buenosaires" - optionr.Label = "Buenos Aires" - //optionr.IsDisabled = true - allOptionsr = append(allOptionsr, optionr) - - // for radio options - var allOptionsc []components.FormCheckboxItem - var optionc components.FormCheckboxItem - optionc.ID = "citysch" - optionc.Name = "citys" - optionc.Value = "china" - optionc.Label = "China" - allOptionsc = append(allOptionsc, optionc) - optionc.ID = "citysba" - optionc.Name = "citys" - optionc.Value = "buenosaires" - optionc.Label = "Buenos Aires" - allOptionsc = append(allOptionsc, optionc) - optionc.ID = "london" - optionc.Name = "london" - optionc.Value = "london" - optionc.Label = "London" - //optionc.IsChecked = true - allOptionsc = append(allOptionsc, optionc) - - // now fill data of the components - tmplData := templateData{ - FormText: components.FormInput{ - ID: "text", - Label: "Name", - Type: "text", - Hint: "This is sample hint", - Placeholder: "Enter your name", - }, - FormEmail: components.FormInput{ - ID: "email", - Label: "Email", - Type: "email", - IsRequired: true, - Placeholder: "Enter your email address", - }, - FormButton: components.FormButton{ - Text: "Login", - IsSubmit: true, - TypeClass: "primary", - }, - FormSelectCity: components.FormSelect{ - ID: "city", - Label: "Select city", - AllOptions: allOptions, - SelectedOption: selectedOption, - }, - FormTextarea: components.FormTextarea{ - ID: "text", - Label: "Example textarea", - }, - FormRadio: components.FormRadio{ - Label: "Radio buttons", - AllRadios: allOptionsr, - }, - FormCheckbox: components.FormCheckbox{ - Label: "Checkbox options", - AllCheckbox: allOptionsc, - }, - } - return c.Response.Template("custom_theme_formpage.html", tmplData) - - } else { - - message := "{\"message\": \"Error, template not enabled\"}" - return c.Response.Json(message) - - } - -} - -func ThemeElements(c *core.Context) *core.Response { - // check if template engine is enabled - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - - if TemplateEnable { - type templateData struct { - Buttons []components.FormButton - Hrefs []components.ContentHref - Badges []components.ContentBadge - Dropdowns []components.ContentDropdown - Lists []components.ContentList - Menus []components.PageNav - } - buttons := []components.FormButton{ - { - Text: "primary", - TypeClass: "primary", - }, - { - Text: "secondary", - TypeClass: "secondary", - }, - { - Text: "success", - TypeClass: "success", - }, - { - Text: "danger", - TypeClass: "danger", - }, - { - Text: "warning", - TypeClass: "warning", - }, - { - Text: "info", - TypeClass: "info", - }, - { - Text: "light", - TypeClass: "light", - }, - { - Text: "dark", - TypeClass: "dark", - }, - { - Text: "link", - TypeClass: "link", - }, - { - Text: "disabled", - TypeClass: "primary", - IsDisabled: true, - }, - { - Text: "outline-primary", - TypeClass: "outline-primary", - }, - { - Text: "outline-secondary", - TypeClass: "outline-secondary", - }, - { - Text: "outline-success", - TypeClass: "outline-success", - }, - { - Text: "outline-danger", - TypeClass: "outline-danger", - }, - { - Text: "outline-warning", - TypeClass: "outline-warning", - }, - { - Text: "outline-info", - TypeClass: "outline-info", - }, - { - Text: "outline-light", - TypeClass: "outline-light", - }, - { - Text: "outline-dark", - TypeClass: "outline-dark", - }, - } - hrefs := []components.ContentHref{ - { - Text: "href", - Link: "#", - IsButton: false, - }, - { - Text: "link", - Link: "#", - IsButton: false, - TypeClass: "link", - }, - { - Text: "button", - Link: "#", - IsButton: true, - TypeClass: "primary", - }, - { - Text: "href disabled", - Link: "#", - IsButton: false, - IsDisabled: true, - }, - { - Text: "link disabled", - Link: "#", - TypeClass: "link", - IsDisabled: true, - }, - { - Text: "button disabled", - Link: "#", - IsButton: true, - TypeClass: "primary", - IsDisabled: true, - }, - } - badges := []components.ContentBadge{ - { - Text: "primary", - TypeClass: "primary", - }, - { - Text: "secondary", - TypeClass: "secondary", - }, - { - Text: "success", - TypeClass: "success", - }, - { - Text: "danger", - TypeClass: "danger", - }, - { - Text: "warning", - TypeClass: "warning", - }, - { - Text: "info", - TypeClass: "info", - }, - { - Text: "light", - TypeClass: "light", - }, - { - Text: "dark", - TypeClass: "dark", - }, - { - Text: "outline", - TypeClass: "primary", - IsOutline: true, - }, - { - Text: "outline", - TypeClass: "success", - IsOutline: true, - }, - { - Text: "outline", - TypeClass: "danger", - IsOutline: true, - }, - { - Text: "outline", - TypeClass: "warning", - IsOutline: true, - }, - } - dropdowns := []components.ContentDropdown{ - // dropdown - { - Label: "dropdown", - Items: []components.ContentDropdownItem{ - { - Text: "item ", - Link: "#", - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // dropdown - { - Label: "primary", - TypeClass: "primary", - Items: []components.ContentDropdownItem{ - { - Text: "item ", - Link: "#", - }, - { - Text: "item ", - Link: "#", - IsActive: true, - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // dropdown - { - Label: "outline", - TypeClass: "outline-primary", - Items: []components.ContentDropdownItem{ - { - Text: "item ", - Link: "#", - }, - }, - }, - // dropdown - { - Label: "disabled", - TypeClass: "primary", - IsDisabled: true, - // items - }, - } - list := []components.ContentList{ - // basic list - { - Items: []components.ContentListItem{ - { - Text: "item 1", - }, - { - Text: "item 2", - EndElement: "end text", - }, - { - Text: "item disabled", - IsDisabled: true, - }, - }, - }, - // description list - { - Items: []components.ContentListItem{ - { - Text: "item 1", - Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ", - }, - { - Text: "item 2", - Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - }, - { - Text: "item disabled", - Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - IsDisabled: true, - }, - }, - }, - // list with class - { - Items: []components.ContentListItem{ - { - Text: "class primary", - TypeClass: "primary", - }, - { - Text: "class success", - TypeClass: "success", - }, - { - Text: "class danger", - TypeClass: "danger", - }, - }, - }, - } - menus := []components.PageNav{ - // nav - { - NavClass: "nav-pills", - NavItems: []components.PageNavItem{ - { - Text: "item active", - Link: "#", - IsActive: true, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item", - Link: "#", - IsActive: false, - ChildItems: []components.PageNavItem{ - { - Text: "item ", - Link: "#", - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // nav - { - NavClass: "", - NavItems: []components.PageNavItem{ - { - Text: "item active", - Link: "#", - IsActive: true, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // nav underline - { - NavClass: "nav-underline", - NavItems: []components.PageNavItem{ - { - Text: "item active", - Link: "#", - IsActive: true, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // nav tabs - { - NavClass: "", - IsTab: true, - NavItems: []components.PageNavItem{ - { - Text: "tab active", - Link: "#", - IsActive: true, - }, - { - Text: "tab", - Link: "#", - IsActive: false, - }, - { - Text: "tab", - Link: "#", - IsActive: false, - }, - { - Text: "tab disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - // nav vertical - { - NavClass: "", - IsVertical: true, - NavItems: []components.PageNavItem{ - { - Text: "item active", - Link: "#", - IsActive: true, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item", - Link: "#", - IsActive: false, - }, - { - Text: "item disabled", - Link: "#", - IsDisabled: true, - }, - }, - }, - } - - tmplData := templateData{ - Buttons: buttons, - Hrefs: hrefs, - Badges: badges, - Dropdowns: dropdowns, - Lists: list, - Menus: menus, - } - return c.Response.Template("custom_theme_elements.html", tmplData) - - } else { - - message := "{\"message\": \"Error, template not enabled\"}" - return c.Response.Json(message) - } -} - -// Show form element page -func Themecontent(c *core.Context) *core.Response { - - // check if template engine is enabled - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - - if TemplateEnable { - - // first, include all compoments - type templateData struct { - ContentTable components.ContentTable - ContentTabledetail components.ContentTabledetail - ContentGraph components.ContentGraph - FieldText components.FormInput - FormSelectCityM components.FormSelect - Pagination components.ContentPagination - ShouldShowPagination bool - } - - // for select options - var allOptions []components.FormSelectOption - var option components.FormSelectOption - option.Value = "ch" - option.Caption = "China" - allOptions = append(allOptions, option) - option.Value = "ba" - option.Caption = "Buenos Aires" - allOptions = append(allOptions, option) - option.Value = "fr" - option.Caption = "France" - selectedOption := option - allOptions = append(allOptions, option) - option.Value = "kr" - option.Caption = "Korea" - allOptions = append(allOptions, option) - - // for custom attributes in form - var customAtt []components.CustomAtt - var attribute components.CustomAtt - attribute.AttName = "code" - attribute.AttValue = "five" - customAtt = append(customAtt, attribute) - attribute.AttName = "mytag" - attribute.AttValue = "My value" - customAtt = append(customAtt, attribute) - - // TABLES - // for th head - var allTh []components.ContentTableTH - var th components.ContentTableTH - th.Value = "Column heading 1" - allTh = append(allTh, th) - th.Value = "Column heading 2" - allTh = append(allTh, th) - th.ID = "ba" - th.Value = "Column heading 3" - allTh = append(allTh, th) - th.Value = "Column badge" - th.ValueType = "badge" // column type badge - allTh = append(allTh, th) - th.Value = "Column action" - th.ValueType = "href" // column type href - allTh = append(allTh, th) - - // for td items - var allTd [][]components.ContentTableTD - //var vals []components.ContentTableTD - // rows - for i := 1; i <= 28; i++ { - vals := make([]components.ContentTableTD, len(allTh)) - for b := 0; b < len(allTh)-2; b++ { - vals[b].Value = fmt.Sprintf("%s%d%d", "TD data: ", i, b) - vals[b].ID = fmt.Sprintf("%s%d%d", "idtd_", i, b) - } - // column badge - vals[len(allTh)-2].Value = components.ContentBadge{ - Text: "success", - TypeClass: "success", - } - // last column href - vals[len(allTh)-1].Value = components.ContentHref{ - Text: "edit", - Link: "#", - } - allTd = append(allTd, vals) - } - - // Pagination demo - // start config - limit := 10 - shouldShowPagination := false - pageViewTableOffset := 0 - // Get the length of AllOptions - totalrecords := len(allTd) - // get current table offset - tpage := c.RequestParamExists("tpage") - if tpage { - pageViewTableOffset, _ = strconv.Atoi(c.GetRequestParam("tpage").(string)) - } - - // start default option paginator - var pagination components.ContentPagination - pagination.PageStartRecord = pageViewTableOffset + 1 - pagination.PageEndRecord = 0 - pagination.TotalRecords = 0 - pagination.PrevLink = "" - pagination.NextLink = "" - - // check current page - // fake function to emulate a query offset - newTd := getPaginatedPageViews(allTd, limit, pageViewTableOffset) - - if len(newTd) > 0 { - pagination.TotalRecords = totalrecords - pagination.PageStartRecord = pageViewTableOffset + 1 - pagination.PageEndRecord = pageViewTableOffset + len(newTd) - shouldShowPagination = totalrecords > limit - } - - if shouldShowPagination && pageViewTableOffset != 0 { - pagination.PrevLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset-limit) - } - - if shouldShowPagination && pageViewTableOffset+limit < totalrecords { - pagination.NextLink = fmt.Sprintf("/themecontent?tpage=%d", pageViewTableOffset+limit) - } - - // for td items in table detail - var allTdetail []components.ContentTabledetailTD - // table detail - var thd components.ContentTabledetailTD - thd.Caption = "Continent" - thd.Value = "Asia" - allTdetail = append(allTdetail, thd) - thd.Caption = "Country" - thd.Value = "South Korea" - allTdetail = append(allTdetail, thd) - thd.Caption = "Capital" - thd.Value = "Seoul" - allTdetail = append(allTdetail, thd) - thd.Caption = "Details" - thd.ValueType = "href" // column type href - thd.Value = components.ContentHref{ - Text: "edit", - Link: "#", - } - allTdetail = append(allTdetail, thd) - thd.Caption = "Notifications" - thd.ValueType = "badge" // column type href - thd.Value = components.ContentBadge{ - Text: "success", - TypeClass: "success", - } - allTdetail = append(allTdetail, thd) - - // random values for pie - one := rand.IntN(50) - two := rand.IntN(50) - three := rand.IntN(50) - valuesgraph := fmt.Sprintf("%d|%d|%d", one, two, three) - - // now fill data of the components - tmplData := templateData{ - FormSelectCityM: components.FormSelect{ - ID: "city", - Label: "Select city", - AllOptions: allOptions, - SelectedOption: selectedOption, - IsMultiple: true, - }, - FieldText: components.FormInput{ - ID: "text", - Label: "Name", - Type: "text", - Hint: "This is sample hint", - Placeholder: "Enter your name", - CustomAtt: customAtt, - }, - ContentTable: components.ContentTable{ - ID: "table_demo", - AllTH: allTh, - AllTD: newTd, - }, - ContentTabledetail: components.ContentTabledetail{ - ID: "table_demodetail", - Title: "Sample table detail", - HeadClass: "table-warning", - AllTD: allTdetail, - }, - ContentGraph: components.ContentGraph{ - Graph: "pie", - Labels: "Berlin|Paris|Venecia", - Values: valuesgraph, - }, - Pagination: pagination, - ShouldShowPagination: shouldShowPagination, - } - - return c.Response.Template("custom_theme_contentpage.html", tmplData) - - } else { - - message := "{\"message\": \"Error, template not enabled\"}" - return c.Response.Json(message) - - } - -} - -func getPaginatedPageViews(values [][]components.ContentTableTD, limit int, offset int) [][]components.ContentTableTD { - - // Validate the offset and adjust if necessary - if offset < 0 { - offset = 0 // Ensure offset is not negative - } else if offset >= len(values) { - var emptytd [][]components.ContentTableTD - return emptytd - } - - // Calculate the end index (limit the slice to the size of the array) - end := offset + limit - if end > len(values) { - end = len(values) // Ensure end doesn't exceed the length of the array - } - - return values[offset:end] // Slice the array - -} - -// Custom Templates functions -func TemplatesFunctions(c *core.Context) *core.Response { - - // check if template engine is enabled - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - - if TemplateEnable { - loggr := c.GetLogger() - - loggr.Debug("D e b u g") - loggr.Info("I n f o") - loggr.Warning("W a r n i n g") - loggr.Error("E R R O R") - - - tmplData := SamplePageData() - return c.Response.Template("custom_templates_functions.html", tmplData) - - } else { - - message := "{\"message\": \"Error, template not enabled\"}" - return c.Response.Json(message) - - } - -} - -// Author represents the writer of an article to show the custom functions -type Author struct { - Name string - Avatar string - Bio string -} - -// Article represents a blog post or news entry used to test template helpers to show the custom functions -type Article struct { - Title string - Slug string - Excerpt string - Body string - Tags []string - PublishedAt time.Time - UpdatedAt time.Time - Author Author - Views int - Price float64 - Featured bool - Subtitle string // intentionally left empty on some entries to test coalesce/defaultVal -} - -// PageData is the top-level context passed to the HTML template to show the custom functions -type PageData struct { - SiteTitle string - Articles []Article -} - -// SamplePageData returns a populated PageData ready to be rendered by the template. -func SamplePageData() PageData { - return PageData{ - SiteTitle: "go/template lab", - Articles: []Article{ - { - Title: "getting started with go templates", - Slug: "getting-started-go-templates", - Excerpt: "Go's html/template package is both powerful and safe by default. In this article we explore how to extend it with custom FuncMap helpers that bring it closer to the expressiveness of Liquid or Twig, without sacrificing any of the security guarantees.", - Tags: []string{"go", "templates", "web", "backend"}, - PublishedAt: time.Now().Add(-3 * 24 * time.Hour), - UpdatedAt: time.Now().Add(-1 * 24 * time.Hour), - Views: 14200, - Price: 0, - Featured: true, - Subtitle: "", - Author: Author{ - Name: "marina voss", - Avatar: "MV", - Bio: "Senior backend engineer focused on developer tooling and observability.", - }, - }, - { - Title: "building a blog engine in go", - Slug: "blog-engine-go", - Excerpt: "We walk through building a minimal but complete blog engine using only the Go standard library: routing with net/http, persistence with database/sql, and rendering with html/template.", - Tags: []string{"go", "blog", "sqlite"}, - PublishedAt: time.Now().Add(-10 * 24 * time.Hour), - UpdatedAt: time.Now().Add(-10 * 24 * time.Hour), - Views: 8750, - Price: 9.99, - Featured: false, - Subtitle: "A zero-dependency approach", - Author: Author{ - Name: "rafael okonkwo", - Avatar: "RO", - Bio: "Full-stack engineer and open-source contributor. Writes about Go and distributed systems.", - }, - }, - { - Title: "concurrency patterns you should know", - Slug: "concurrency-patterns-go", - Excerpt: "Goroutines are cheap, but misusing them is expensive. This deep-dive covers fan-out/fan-in, pipelines, semaphores, and error group patterns with real production examples.", - Tags: []string{"go", "concurrency", "goroutines", "advanced"}, - PublishedAt: time.Now().Add(-45 * 24 * time.Hour), - UpdatedAt: time.Now().Add(-40 * 24 * time.Hour), - Views: 31400, - Price: 14.99, - Featured: true, - Subtitle: "", - Author: Author{ - Name: "selin çelik", - Avatar: "SÇ", - Bio: "Systems programmer. Previously at Cloudflare. Loves writing about the internals of things.", - }, - }, - { - Title: "understanding go interfaces", - Slug: "understanding-go-interfaces", - Excerpt: "Interfaces in Go are implicit and structural, which makes them both elegant and occasionally surprising. We look at how the runtime dispatches method calls and how to design composable interfaces.", - Tags: []string{"go", "interfaces", "design"}, - PublishedAt: time.Now().Add(-2 * time.Hour), - UpdatedAt: time.Now().Add(-1 * time.Hour), - Views: 420, - Price: 0, - Featured: false, - Subtitle: "Implicit, structural, and powerful", - Author: Author{ - Name: "marina voss", - Avatar: "MV", - Bio: "Senior backend engineer focused on developer tooling and observability.", - }, - }, - }, - } -} \ No newline at end of file diff --git a/routes.go b/routes.go index dc1799c..193da2d 100644 --- a/routes.go +++ b/routes.go @@ -8,7 +8,6 @@ package main import ( "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/cup/controllers" - "git.smarteching.com/goffee/cup/hooks" ) // Register the app controllers @@ -20,15 +19,8 @@ func registerRoutes() { // Define your routes here... controller.Get("/", controllers.WelcomeHome) - // Uncomment the lines below to enable theme demo - controller.Get("/themebase", controllers.Themedemo) - controller.Get("/themeform", controllers.Themeform) - controller.Get("/themecontent", controllers.Themecontent) - controller.Get("/themepanel", controllers.Themedemo) - controller.Get("/themeelements", controllers.ThemeElements) - controller.Get("/queuesample", controllers.Queuesample) - - // Uncomment the lines below to enable authentication + + // Uncomment the lines below to enable authentication API controller.Post("/signup", controllers.Signup) controller.Post("/signin", controllers.Signin) controller.Post("/signout", controllers.Signout) @@ -44,22 +36,6 @@ func registerRoutes() { controller.Post("/admin/users/edit/:id", controllers.AdminUsersEdit) controller.Post("/admin/users/delete", controllers.AdminUsersDelete) controller.Post("/admin/users/deleteconfirm", controllers.AdminUsersDelConfirm) - //controller.Get("/admin/users/roles", controllers.Signout) - //controller.Get("/admin/users/permissions", controllers.ResetPasswordRequest) - controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck) - // templates demos - controller.Get("/signout", controllers.Signout) - - controller.Get("/appsample", controllers.AppSample, hooks.AuthCheck) - controller.Post("/appsample", controllers.AppSample, hooks.AuthCheck) - - controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) - controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) - - controller.Get("/appsession", controllers.AppSession) - controller.Post("/appsession", controllers.AppSession) - - controller.Get("/templatesfunc", controllers.TemplatesFunctions) } diff --git a/storage/templates/app.html b/storage/templates/app.html deleted file mode 100644 index 2bc5f93..0000000 --- a/storage/templates/app.html +++ /dev/null @@ -1,17 +0,0 @@ - - - {{template "page_head" "Sample page"}} - -
-
- {{template "content_dropdown" .ContentDropdown}} - {{template "page_card" .PageCard}} - {{ define "page_card_content" }} - - - {{ end }} -
-
- {{template "page_footer"}} - - \ No newline at end of file diff --git a/storage/templates/appsession.html b/storage/templates/appsession.html deleted file mode 100644 index 32364f0..0000000 --- a/storage/templates/appsession.html +++ /dev/null @@ -1,12 +0,0 @@ - - - {{template "page_head" "Sample page test session vars"}} - -
-
- {{template "page_card" .PageCard}} -
-
- {{template "page_footer"}} - - \ No newline at end of file diff --git a/storage/templates/custom_templates_functions.html b/storage/templates/custom_templates_functions.html deleted file mode 100644 index 6e0c357..0000000 --- a/storage/templates/custom_templates_functions.html +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - {{.SiteTitle}} — template lab - - - - - - - -
-
-

{{.SiteTitle}}

-

Template helpers
in action

-
-

22 helpers registered
across 6 groups

-
- - - - - -{{$first := first .Articles}} - -
- -
-

capitalize capitalize

-

{{capitalize $first.Title}}

-

input: "{{$first.Title}}"

-
- -
-

truncate truncate

-

{{$first.Excerpt | truncate 80}}

-

capped at 80 chars

-
- -
-

prepend prepend

-

{{prepend $first.Slug "/articles/"}}

-

prepended "/articles/" to slug

-
- -
-

strAppend strAppend

-

{{strAppend $first.Slug ".html"}}

-

appended ".html" to slug

-
- -
-

split → join split join

- {{$parts := split "go,templates,funcmap" ","}} -

{{join $parts " · "}}

-

split on "," then joined with " · "

-
- -
- - - - - -
- -
-

fmtNumber — int fmtNumber

-

{{fmtNumber $first.Views}}

-

raw value: {{$first.Views}}

-
- - {{$paid := index .Articles 2}} -
-

fmtNumber — float fmtNumber

-

$ {{fmtNumber $paid.Price}}

-

raw value: {{$paid.Price}}

-
- -
-

fmtDate "short" fmtDate

-

{{fmtDate $first.PublishedAt "short"}}

-

layout: "02 Jan 2006"

-
- -
-

fmtDate "long" fmtDate

-

{{fmtDate $first.PublishedAt "long"}}

-

layout: "02 January 2006"

-
- -
-

fmtDate "iso" fmtDate

-

{{fmtDate $first.PublishedAt "iso"}}

-

layout: "2006-01-02"

-
- -
-

timeAgo timeAgo

- {{range .Articles}} -

{{timeAgo .PublishedAt}} — {{fmtDate .PublishedAt "short"}}

- {{end}} -
- -
- - - - - -
- -
-

first first

- {{with first .Articles}} -

{{capitalize .Title}}

-

first article in the list

- {{end}} -
- -
-

last last

- {{with last .Articles}} -

{{capitalize .Title}}

-

last article in the list

- {{end}} -
- -
-

sliceOf 0–2 sliceOf

- {{range sliceOf .Articles 0 2}} -

— {{capitalize .Title}}

- {{end}} -

only first 2 of {{len .Articles}} articles

-
- -
-

contains — slice contains

- {{if contains $first.Tags "go"}} -

✓ has tag "go"

- {{else}} -

✗ missing tag "go"

- {{end}} -

tags: {{join $first.Tags ", "}}

-
- -
-

contains — string contains

- {{if contains $first.Excerpt "FuncMap"}} -

✓ excerpt mentions "FuncMap"

- {{else}} -

✗ not found

- {{end}} -

substring search on .Excerpt

-
- -
-

join join

-

{{join $first.Tags " / "}}

-

tags joined with " / "

-
- -
- - - - - - - - - - - - - - - - {{range .Articles}} - - - - - - - - - - - - - - - - - - - {{end}} - -
HelperArticleInputOutput
defaultVal{{.Title | capitalize | truncate 30}}.Subtitle ({{if .Subtitle}}"{{.Subtitle}}"{{else}}empty{{end}}){{defaultVal "No subtitle" .Subtitle}}
ternary{{capitalize .Title | truncate 30}}.Featured = {{.Featured}}{{ternary "⭐ featured" "regular" .Featured}}
coalesce{{capitalize .Title | truncate 30}}.Subtitle → .Title{{coalesce .Subtitle .Title}}
- - - - - -
- {{range .Articles}} -
-
-
- {{if .Featured}}featured{{end}} - {{range .Tags}}{{.}}{{end}} -
- -

{{capitalize .Title}}

- - {{with coalesce .Subtitle ""}} -

{{.}}

- {{end}} - -

{{.Excerpt | truncate 160}}

- - - {{prepend .Slug "/articles/"}} → - - -
-
{{.Author.Avatar}}
-
-
{{capitalize .Author.Name}}
- -
-
-
- -
-
- {{fmtNumber .Views}} - views -
- {{if gt .Price 0.0}} - $ {{fmtNumber .Price}} - {{else}} - free - {{end}} -
-
- {{end}} -
- - - -
- {{.SiteTitle}} / template sandbox - {{len .Articles}} articles · {{fmtDate (first .Articles).PublishedAt "iso"}} → {{fmtDate (last .Articles).PublishedAt "iso"}} -
- - - \ No newline at end of file diff --git a/storage/templates/custom_theme_base.html b/storage/templates/custom_theme_base.html deleted file mode 100644 index 9c28a1e..0000000 --- a/storage/templates/custom_theme_base.html +++ /dev/null @@ -1,10 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
- {{template "page_card" .PageCard}} -
- {{template "page_footer"}} - - \ No newline at end of file diff --git a/storage/templates/custom_theme_contentpage.html b/storage/templates/custom_theme_contentpage.html deleted file mode 100644 index 422c253..0000000 --- a/storage/templates/custom_theme_contentpage.html +++ /dev/null @@ -1,30 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-
- Content demos -
- {{template "content_table" .ContentTable}} - {{if .ShouldShowPagination}} - {{template "content_pagination" .Pagination}} - {{end}} -
-
-
-

Pie chart

- {{template "content_graph" .ContentGraph}} -
- {{template "form_input" .FieldText}} - {{template "form_select" .FormSelectCityM}} -
-
- {{template "content_tabledetail" .ContentTabledetail}} -
-
-
-
- {{template "page_footer"}} - - diff --git a/storage/templates/custom_theme_elements.html b/storage/templates/custom_theme_elements.html deleted file mode 100644 index fbe2d52..0000000 --- a/storage/templates/custom_theme_elements.html +++ /dev/null @@ -1,66 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-
- Demos buttons -
- {{range .Buttons}} - {{template "form_button" .}} - {{end}} -
-
- -
- Demos href -
- {{range .Hrefs}} - {{template "content_href" .}} - {{end}} -
-
- -
- Demos Badges -
- {{range .Badges}} - {{template "content_badge" .}} - {{end}} -
-
- - -
- Demos dropdown -
- {{range .Dropdowns}} - {{template "content_dropdown" .}} - {{end}} -
-
- - -
- Demos List -
- {{range .Lists}} - {{template "content_list" .}} - {{end}} -
-
- -
- Demos nav -
- {{range .Menus}} -
- {{template "page_nav" .}} -
- {{end}} -
-
-
- {{template "page_footer"}} - - diff --git a/storage/templates/custom_theme_formpage.html b/storage/templates/custom_theme_formpage.html deleted file mode 100644 index f45a558..0000000 --- a/storage/templates/custom_theme_formpage.html +++ /dev/null @@ -1,23 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
-
-
- form demos -
- {{template "form_input" .FormText}} - {{template "form_input" .FormEmail}} - {{template "form_select" .FormSelectCity}} - {{template "form_textarea" .FormTextarea}} - {{template "form_radio" .FormRadio}} - {{template "form_checkbox" .FormCheckbox}} -
-
- {{template "form_button" .FormButton}} -
-
- {{template "page_footer"}} - - From 5af4371961a33a936a84c18691d096c12ec4090d Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Thu, 7 May 2026 23:24:02 -0500 Subject: [PATCH 6/6] remove set level --- main.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/main.go b/main.go index dc5750b..5fac06e 100644 --- a/main.go +++ b/main.go @@ -58,19 +58,6 @@ func main() { app.SetGormConfig(config.GetGormConfig()) app.SetCacheConfig(config.GetCacheConfig()) app.Bootstrap() - // Set log level from environment variable (debug, info, warning, error) - if levelStr := env.GetVar("LOG_LEVEL"); levelStr != "" { - switch levelStr { - case "debug": - logger.ResolveLogger().SetLevel(logger.DEBUG) - case "info": - logger.ResolveLogger().SetLevel(logger.INFO) - case "warning": - logger.ResolveLogger().SetLevel(logger.WARNING) - case "error": - logger.ResolveLogger().SetLevel(logger.ERROR) - } - } app.RegisterTemplates(resources) registerGlobalHooks() registerRoutes()