{"id":27520369,"url":"https://github.com/jacobbrewer1/web","last_synced_at":"2025-10-20T03:16:34.626Z","repository":{"id":284236116,"uuid":"954190129","full_name":"Jacobbrewer1/web","owner":"Jacobbrewer1","description":"Golang web module allowing for easy and maintainable project setup","archived":false,"fork":false,"pushed_at":"2025-10-16T18:38:02.000Z","size":12243,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-17T21:12:41.009Z","etag":null,"topics":["go","go-module","golang","web","webapp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jacobbrewer1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-24T17:41:50.000Z","updated_at":"2025-10-16T18:38:05.000Z","dependencies_parsed_at":"2025-06-15T08:33:46.174Z","dependency_job_id":"b9b7e96e-17ce-40c4-b180-cc9aa7041705","html_url":"https://github.com/Jacobbrewer1/web","commit_stats":null,"previous_names":["jacobbrewer1/web"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/Jacobbrewer1/web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jacobbrewer1%2Fweb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jacobbrewer1%2Fweb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jacobbrewer1%2Fweb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jacobbrewer1%2Fweb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jacobbrewer1","download_url":"https://codeload.github.com/Jacobbrewer1/web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jacobbrewer1%2Fweb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279920351,"owners_count":26244337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-19T02:00:07.647Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","go-module","golang","web","webapp"],"created_at":"2025-04-18T06:43:45.848Z","updated_at":"2025-10-20T03:16:34.619Z","avatar_url":"https://github.com/Jacobbrewer1.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Web\n\n## Description\n\nThis repository contains tools that are commonly used between my golang projects. It allows for the projects to be\neasily structured and maintained. The tools are designed to be used in a modular way, allowing for easy integration into\nexisting projects. The repository is easily extensible, allowing for new tools to be added as needed. When creating a\nnew web app, ensure you provide a logger; the logger can be accessed later in through the application.\n\n## Usage\n\n### Config\n\nYou can access the config file using the viper instance from the app. The config file is loaded from the Environment\nVariable `CONFIG_LOCATION`; if no location is provided, the app will default to `config.json` in the present directory.\nThe config file can be in any format that viper supports. Before you can access it you will have to ensure that the app\nloads the config with the `WithViperConfig` option. Once the config is loaded you can access it using the `app.Viper()`\nmethod.\n\n### Example\n\nPlease see a basic example of how to use the tools in a web app. The example is a simple web app that provides a Vault\nclient, database connection from Vault and a Http server.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/jacobbrewer1/web\"\n\t\"github.com/jacobbrewer1/web/logging\"\n)\n\ntype App struct {\n\tbase *web.App\n}\n\nfunc NewApp(l *slog.Logger) (*App, error) {\n\tbase, err := web.NewApp(l)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026App{\n\t\tbase: base,\n\t}, nil\n}\n\nfunc handler(l *slog.Logger) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl.Debug(\"Received request\", \"method\", r.Method, \"url\", r.URL.String())\n\t\tw.WriteHeader(http.StatusOK)\n\t})\n}\n\nfunc (a *App) Start() error {\n\tif err := a.base.Start(\n\t\tweb.WithViperConfig(),\n\t\tweb.WithVaultClient(),\n\t\tweb.WithDatabaseFromVault(),\n\t); err != nil {\n\t\treturn fmt.Errorf(\"start app: %w\", err)\n\t}\n\n\tif err := a.base.StartServer(\"api\", \u0026http.Server{\n\t\tAddr:              \":8080\",\n\t\tHandler:           handler(logging.LoggerWithComponent(a.base.Logger(), \"api-handler\")),\n\t\tReadHeaderTimeout: 5 * time.Second,\n\t}); err != nil {\n\t\treturn fmt.Errorf(\"start server: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (a *App) WaitForEnd() {\n\t// Wait for the application to finish\n\ta.base.WaitForEnd(a.Shutdown)\n}\n\nfunc (a *App) Shutdown() {\n\ta.base.Shutdown()\n}\n\nfunc main() {\n\tl := logging.NewLogger(\n\t\tlogging.WithAppName(\"myapp\"),\n\t)\n\n\tapp, err := NewApp(l)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := app.Start(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp.WaitForEnd()\n}\n\n```\n\nThe config file for the above example is a simple JSON file that contains the following:\n\n```json\n{\n  \"vault\": {\n    \"address\": \"http://vault-active.vault:8200\",\n    \"database\": {\n      \"path\": \"database-mount/creds\",\n      \"role\": \"vault-database-role\"\n    }\n  },\n  \"database\": {\n    \"host\": \"host:port\",\n    \"schema\": \"schema-name\"\n  }\n}\n```\n\n### Metrics\n\nIf you want to expose metrics for your application, these are registered by default. You can use the\n`web.WithMetricsEnabled` option to enable or disable the metrics. The metrics are exposed on the `/metrics` endpoint on\nport `9090`. The metrics are registered with the `prometheus` package and are exposed in the `prometheus` format.\n\n### Dynamic Health Checks\n\nIf you want to add health checks that state whether the application is healthy or not, you can use the\n`web.WithHealthCheck` option. This option will take a list of health checks that will be run whenever the health check\nendpoint is hit. The health checks get setup on the routes `/readyz` and `/livez`; this allows for Kubernetes to check \nthe health of the application and determine if the application is ready to serve traffic or not.\n\n### Options\n\nPlease take a look at the `options.go` file for the available options. The options are used to configure the application\nand are passed to the `app.Start()` method. When the application is started, the options are applied in the order they\nare passed, so the order is important. For example, `WithVaultClient` uses the viper config so it should be passed after\nthe `WithViperConfig` option.\n\n### Async Tasks\n\nIf your application is not using a server, you may be watching some configs for example, or fetching data and placing it\ninto a stream, you can use the `web.WithIndefiniteAsyncTask(\"name\", func(ctx) {})` option to run a task that should not\nexit until the application is stopped. If the task exits, the application will stop. The task will be run in a goroutine\nand will be passed a context that will be cancelled when the application is stopped. The application will wait for the\ntask to finish before stopping.\n\n### Deployment\n\nWhen deploying the application, you will need to ensure that the following environment variables are set for a smooth\noperation, this application makes use of the Downward Kubernetes API to set the environment variables.\n\nThe following environment variables should be set within the deployment yaml:\n\n```yaml\n- name: \"SERVICE_ACCOUNT_NAME\" # This is used to get the service account name; additionally, Vault will fail to initialize if this is not set.\n  valueFrom:\n    fieldRef:\n      fieldPath: spec.serviceAccountName\n- name: \"POD_IP\" # This is used to get the pod IP address\n  valueFrom:\n    fieldRef:\n      fieldPath: status.podIP\n- name: \"NODE_NAME\" # This is used to get the node name\n  valueFrom:\n    fieldRef:\n      fieldPath: spec.nodeName\n```\n\n### Packages\n\nIf you require some Kubernetes helpers, you can use the `k8s` package. It contains a few helpers that are useful for\ninteracting with Kubernetes. Please look at the files within the `k8s` package for more information.\n\n## Local Development\n\nWhile the Web module is primarily designed to be run in Kubernetes, it can also be run locally (or without a cluster, in\na VM depending on your setup).\n\n### Vault Override\n\nIf you want to run the application outside of Kubernetes, you will need to override the `VaultClient()` method that is\ncalled to get the vault client. This method has been kept as a variable so that it can be overridden in this scenario.\n\nPlease see the example below for how to override the `VaultClient()` method. This will allow you to run the application\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\n\t\"github.com/jacobbrewer1/web\"\n    \"github.com/jacobbrewer1/web/vault\"\n    \"github.com/spf13/viper\"\n)\n\nfunc main() {\n\tweb.VaultClient = func(ctx context.Context, l *slog.Logger, v *viper.Viper) (vault.Client, error) {\n\t\tclient, err := vault.NewClient(vault.WithLogger(l)) // Be sure to pass in the authentication method, etc.\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn client, nil\n\t}\n}\n\n```\n\n## Installation\n\n```shell\ngo get github.com/jacobbrewer1/web\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobbrewer1%2Fweb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacobbrewer1%2Fweb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobbrewer1%2Fweb/lists"}