You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ratchet/README.md

87 lines
3.9 KiB
Markdown

# Ratchet
An example web application in Golang.
https://lets-go.alexedwards.net/sample/02.09-serving-static-files.html
## Project Structure
Loosely inspired by the organization of [WTFDial](https://github.com/benbjohnson/wtf?tab=readme-ov-file#project-structure),
- Application domain types reside in the project root (User, UserService, etc)
- Implementations of the application domain reside in the subpackages `sqlite`, `http`, etc.
- Everything is tied together in the `cmd` subpackages
### Application Domain
This is project is an Indie reader inspired by [Feedi](https://github.com/facundoolano/feedi).
### Implementation Subpackages
The subpackages function as an adapter between our domain and the technology used to implement the domain. For example `sqlite.FeedService` implements the `ratchet.FeedService` using SQLite.
Subpackages ideally **SHOULD NOT** know about one another and **SHOULD** communicate in terms of the application domain.
A special `mock` packages can be used to create simple mocks for each application domain interface. This will allow each subpackages unit tests to share a common set of mocks so layers can be tested in isolatation.
### Binary Packages
With loosely coupledc subpackages the application is wired together in the `cmd` subpackages to produce a final binary.
The `cmd` packages are ultimately the interface between the application domain and the operator. Configuration of types and any CLI flags *SHOULD* live in these packages.
## Development
You can build `ratchet` locally by cloning the respository, then run
`make`
`go install ./cmd/...`
The `ratchetd` cmd binary uses Oauth so you will need to create a new Oauth App. The vlaue of the authorization callback must be the hostname and IP at which clients can access the `ratchetd` server.
1 week ago
## Additional Resources
- [Content Range Requests](https://web.archive.org/web/20230918195519/https://benramsey.com/blog/2008/05/206-partial-content-and-range-requests/)
- [HTTP 204 and 205 Status Codes](https://web.archive.org/web/20230918193536/https://benramsey.com/blog/2008/05/http-status-204-no-content-and-205-reset-content/)
- [How to Disable FileServer Directory Listings](https://www.alexedwards.net/blog/disable-http-fileserver-directory-listings)
- [Understand Mutexs in Go](https://www.alexedwards.net/blog/understanding-mutexes)
- [Structured Logging in Go with log/slog](https://pkg.go.dev/log/slog)
- [Exit Codes with special meaning](https://tldp.org/LDP/abs/html/exitcodes.html)
- [Organizing Database Access in Go](https://www.alexedwards.net/blog/organising-database-access)
### Go http.FileServer
Supports If-Modified-Since and Last-Modified headers
```
curl -i -H "If-Modified-Since:
Thu, 04 May 2017 13:07:52 GMT" http://localhost:5001/static/img/logo.png
HTTP/1.1 304 Not Modified
Last-Modified: Thu, 04 May 2017 13:07:52 GMT
Date: Sun, 12 Jan 2025 14:26:06 GMT
```
Supports Range Requests and 206 Partial Content responses.
```
curl -i -H "Range: bytes=100-199" --output - http://localhost:5001/static/img/logo.png
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Length: 100
Content-Range: bytes 100-199/1075
Content-Type: image/png
Last-Modified: Thu, 04 May 2017 13:07:52 GMT
Date: Sun, 12 Jan 2025 14:18:32 GMT
```
- The `Content-Type` is automatically set from the file extension using the `mime.TypeByExtension()` function. You can add your own custom extensions and content types using the `mime.AddExtensionType()` function if necessary.
- Sometimes you might want to serve a single file from within a handler. For this theres the `http.ServeFile()` function, which you can use like so:
```go
func downloadHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./ui/static/file.zip")
}
```
Warning: http.ServeFile() does not automatically sanitize the file path. If youre constructing a file path from untrusted user input, to avoid directory traversal attacks you must sanitize the input with filepath.Clean() before using it.