Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ A modern, minimalist URL shortener inspired by Google's internal golinks system.

- **Simple URL Shortening**: Create memorable shortcuts for long URLs
- **Variable Substitution**: Use `{*}` placeholders for dynamic content
- **Recursive Aliases**: Keywords can point to other keywords
- **Usage Analytics**: Track popular queries and usage patterns
- **Clean Architecture**: Modular, testable, and maintainable codebase
- **Modern UI**: HTMX-powered interface with Dieter Rams-inspired design
Expand Down Expand Up @@ -203,10 +202,6 @@ export ENVIRONMENT=production
4. Add tests
5. Submit a pull request

## License

MIT License - see LICENSE file for details.

## Acknowledgments

- Inspired by Google's internal golinks system
Expand Down
12 changes: 12 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- [ ] Support Postgres as a DB not just SQLite
- [ ] Add authentication and login, whether propietary or integration with 3rd parties
- [ ] Add authorization, different users can have different roles
- [ ] Support for theme customization
- [ ] Support for user administration
- [ ] Add golang migrations
- [ ] Consider the use of an ORM
- [ ] Statistics of most accessed docs
- [ ] Support to render Markdown docs and MDX docs
- [ ] Deployment in google app engine
- [ ] Add admin panel to manage golinks
- [ ] Add the ability to create a request to an admin for a keyword to be added
37 changes: 30 additions & 7 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"golinks/internal/config"
"golinks/internal/database"
"golinks/internal/handlers"
"golinks/internal/logger"
"golinks/internal/repository"
"golinks/internal/service"

Expand All @@ -26,31 +27,50 @@ func main() {
log.Fatalf("Failed to load configuration: %v", err)
}

// Initialize simple logging
logger.Initialize(cfg.Logging)
appLogger := logger.Default()

appLogger.Info("Starting GoLinks application on port %d (env: %s)", cfg.Port, cfg.Environment)

// Initialize database
appLogger.Info("Initializing database: %s", cfg.DatabasePath)
db, err := database.NewSQLiteDB(cfg.DatabasePath)
if err != nil {
appLogger.Error("Failed to initialize database: %v", err)
log.Fatalf("Failed to initialize database: %v", err)
}
defer db.Close()

// Run migrations
appLogger.Info("Running database migrations")
if err := database.Migrate(db); err != nil {
appLogger.Error("Failed to run migrations: %v", err)
log.Fatalf("Failed to run migrations: %v", err)
}
appLogger.Info("Database migrations completed successfully")

// Initialize repositories
shortcutRepo := repository.NewShortcutRepository(db)
queryRepo := repository.NewQueryRepository(db)
appLogger.Info("Initializing repositories")
shortcutRepo := repository.NewShortcutRepository(db, appLogger)
queryRepo := repository.NewQueryRepository(db, appLogger)

// Initialize services
linkService := service.NewLinkService(shortcutRepo, queryRepo)
appLogger.Info("Initializing services")
linkService := service.NewLinkService(shortcutRepo, queryRepo, appLogger)
docService := service.NewDocumentService("docs", appLogger)

// Initialize handlers
handler := handlers.NewHandler(linkService, cfg)
appLogger.Info("Initializing handlers")
handler := handlers.NewHandler(linkService, cfg, appLogger)
docHandler := handlers.NewDocumentHandler(docService, appLogger)

// Setup router
appLogger.Info("Setting up HTTP router")
router := mux.NewRouter()

handler.RegisterRoutes(router)
docHandler.RegisterRoutes(router)

// Setup server
server := &http.Server{
Expand All @@ -63,8 +83,9 @@ func main() {

// Start server in a goroutine
go func() {
log.Printf("Starting server on port %d", cfg.Port)
appLogger.Info("Starting HTTP server on %s", server.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
appLogger.Error("Server failed to start: %v", err)
log.Fatalf("Server failed to start: %v", err)
}
}()
Expand All @@ -73,15 +94,17 @@ func main() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
appLogger.Info("Received shutdown signal, initiating graceful shutdown")

// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

appLogger.Info("Shutting down HTTP server (timeout: 30s)")
if err := server.Shutdown(ctx); err != nil {
appLogger.Error("Server forced to shutdown: %v", err)
log.Fatalf("Server forced to shutdown: %v", err)
}

log.Println("Server exited")
appLogger.Info("Server shutdown completed successfully")
}
36 changes: 36 additions & 0 deletions docs/sample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Sample Documentation

This is a **sample markdown document** to test the rendering functionality.

## Features

- Markdown rendering with [Goldmark](https://github.com/yuin/goldmark)
- Syntax highlighting for code blocks
- Support for tables, lists, and more

## Code Example

```go
func main() {
fmt.Println("Hello, GoLinks!")
}
```

## Table Example

| Feature | Status |
|---------|--------|
| Markdown | ✅ Working |
| MDX | 🚧 In Progress |
| Syntax Highlighting | ✅ Working |

> This is a blockquote to test styling.

### Links and Images

- [GoLinks Repository](https://github.com/example/golinks)
- ![Sample Image](https://via.placeholder.com/300x200?text=Sample+Image)

---

*Last updated: 2024
58 changes: 58 additions & 0 deletions docs/sample.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Sample MDX Documentation

This is a **sample MDX document** that combines markdown with JSX components.

## Interactive Elements

<div className="alert alert-info">
<strong>Info:</strong> This is an MDX component example.
</div>

## Code with Syntax Highlighting

```javascript
const greeting = (name) => {
return `Hello, ${name}!`;
};

console.log(greeting("GoLinks"));
```

## Custom Components (Future)

In the future, we could add custom React components:

```jsx
<CodeBlock language="go">
func renderMDX() string {
return "Awesome!"
}
</CodeBlock>
```

## Standard Markdown Features

All standard markdown features work:

- **Bold text**
- *Italic text*
- `Inline code`
- [Links](https://example.com)

### Lists

1. First item
2. Second item
3. Third item

### Tables

| Component | Status | Notes |
|-----------|--------|-------|
| Markdown | ✅ | Fully supported |
| MDX | 🚧 | Basic support |
| React Components | ⏳ | Planned |

---

*This MDX file demonstrates the potential for interactive documentation.*
5 changes: 5 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ BASE_URL=http://localhost:8080
# Database Configuration
DATABASE_PATH=golinks.db

# Environment Configuration
ENVIRONMENT=development

# Logging Configuration
# LOG_LEVEL: debug, info, warn, error (default: info)
LOG_LEVEL=debug
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,13 @@ require (
github.com/gorilla/mux v1.8.1
github.com/joho/godotenv v1.5.1
github.com/mattn/go-sqlite3 v1.14.18
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
)

require (
github.com/alecthomas/chroma/v2 v2.2.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)
28 changes: 28 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
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/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15 changes: 11 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import (
"os"
"strconv"

"golinks/internal/logger"

"github.com/joho/godotenv"
)

// Config holds all configuration for the application
type Config struct {
Port int `json:"port"`
DatabasePath string `json:"database_path"`
BaseURL string `json:"base_url"`
Environment string `json:"environment"`
Port int `json:"port"`
DatabasePath string `json:"database_path"`
BaseURL string `json:"base_url"`
Environment string `json:"environment"`
Logging logger.Config `json:"logging"`
}

// Load loads configuration from environment variables and .env file
Expand All @@ -25,6 +28,10 @@ func Load() (*Config, error) {
DatabasePath: getEnv("DATABASE_PATH", "golinks.db"),
BaseURL: getEnv("BASE_URL", "http://localhost:8080"),
Environment: getEnv("ENVIRONMENT", "development"),
Logging: logger.Config{
Level: getEnv("LOG_LEVEL", "info"),
Format: "text", // Not used in simple logger
},
}

return cfg, nil
Expand Down
3 changes: 1 addition & 2 deletions internal/domain/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ type PopularQuery struct {
Link string `json:"link"`
}

// KeywordInfo represents keyword information with aliases
// KeywordInfo represents keyword information
type KeywordInfo struct {
Word string `json:"word"`
Aliases string `json:"aliases"`
Link string `json:"link"`
CreatedAt time.Time `json:"created_at"`
}
Loading
Loading