golinks 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ eggs/
11
+ .eggs/
12
+ *.manifest
13
+ *.spec
14
+
15
+ # Virtual environments
16
+ venv/
17
+ ENV/
18
+ env/
19
+ .venv
20
+
21
+ # IDEs
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+ *~
27
+ .DS_Store
28
+
29
+ # Testing
30
+ .pytest_cache/
31
+ .coverage
32
+ htmlcov/
33
+ .tox/
34
+ .mypy_cache/
35
+ .ruff_cache/
36
+
37
+ # Package managers
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Distribution / packaging
42
+ .Python
43
+ develop-eggs/
44
+ downloads/
45
+ lib/
46
+ lib64/
47
+ parts/
48
+ sdist/
49
+ var/
50
+ wheels/
51
+ *.whl
52
+ .env
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,82 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Development Commands
6
+
7
+ Use uv to run Python code:
8
+ ```bash
9
+ # Run the server
10
+ uv run golinks
11
+
12
+ # Run with custom config
13
+ uv run golinks --config /path/to/config.json
14
+
15
+ # Run with custom port
16
+ uv run golinks --port 9000
17
+
18
+ # Run any Python command
19
+ uv run python <script.py>
20
+ ```
21
+
22
+ ## Linting
23
+
24
+ ```bash
25
+ # Run linter
26
+ uv run ruff check .
27
+
28
+ # Fix linting issues
29
+ uv run ruff check --fix .
30
+
31
+ # Format code
32
+ uv run ruff format .
33
+ ```
34
+
35
+ ## Architecture
36
+
37
+ ### Core Components
38
+
39
+ **HTTP Server** (`src/server.py`):
40
+ - Uses Python's built-in `http.server` module with custom `GoLinksHandler`
41
+ - Handles redirects based on path matching against configured shortcuts
42
+ - Hot-reloads configuration on each request (no restart needed)
43
+ - Serves web interface at root path showing all available links
44
+
45
+ **Configuration** (`src/models.py`):
46
+ - Pydantic models for type-safe configuration handling
47
+ - Supports simple string URLs or template objects with Jinja2 templating
48
+ - `LinkTemplate` allows for parameterized URLs with defaults
49
+
50
+ **Templates** (`src/templates/`):
51
+ - Jinja2 templates for the web interface
52
+ - `base.html`: Base template with common styles
53
+ - `links.html`: Shows all configured shortcuts
54
+ - `error.html`: Friendly 404 page for unknown shortcuts
55
+
56
+ ### Key Design Patterns
57
+
58
+ 1. **Hot-reload Configuration**: Config is loaded fresh on each request from JSON file
59
+ 2. **Template URLs**: Links can use Jinja2 templates with query parameters as variables
60
+ 3. **Query Parameter Preservation**: Unmatched query params are passed through to destination
61
+ 4. **No External Dependencies**: Uses Python stdlib where possible (except Jinja2, Pydantic)
62
+
63
+ ### Configuration Format
64
+
65
+ Simple redirects:
66
+ ```json
67
+ {
68
+ "github": "https://github.com"
69
+ }
70
+ ```
71
+
72
+ Template redirects with defaults:
73
+ ```json
74
+ {
75
+ "search": {
76
+ "template_url": "https://www.google.com/search?q={{q|default('python')}}",
77
+ "defaults": {
78
+ "q": "python"
79
+ }
80
+ }
81
+ }
82
+ ```
golinks-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
golinks-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: golinks
3
+ Version: 1.0.0
4
+ Summary: A local go links redirect service for URL shortcuts
5
+ Project-URL: Homepage, https://github.com/yourusername/golinks
6
+ Project-URL: Repository, https://github.com/yourusername/golinks.git
7
+ Project-URL: Issues, https://github.com/yourusername/golinks/issues
8
+ Author-email: Haran Rajkumar <haranrajkumar97@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: golinks,redirect,shortcuts,url-shortener
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Internet :: WWW/HTTP
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: jinja2>=3.1.6
23
+ Requires-Dist: pydantic>=2.11.9
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Golinks
27
+
28
+ A simple local HTTP redirect service that turns short URLs like `go/github` into full URLs.
29
+
30
+ ## Installation
31
+
32
+ ### Install manually
33
+
34
+ ```bash
35
+ # Clone the repository
36
+ git clone https://github.com/yourusername/golinks.git
37
+ cd golinks
38
+
39
+ # Run the install script
40
+ ./install
41
+
42
+ # Edit the config
43
+ vim ~/.config/golinks/config.json
44
+ ```
45
+
46
+ ## How it works
47
+
48
+ The `./install` script automates the entire setup:
49
+ 1. Installs the Python package using uv
50
+ 2. Adds `127.0.0.1 go` to your `/etc/hosts` file (with sudo permission)
51
+ 3. Sets up port forwarding from port 80 to 8888 using pfctl (macOS) or iptables (Linux), so you can use `go/shortcut` instead of `go:8888/shortcut`
52
+ 4. Sets up a LaunchAgent (macOS) or systemd service (Linux) to run the server at startup
53
+ 5. Starts the golinks server immediately on port 8888
54
+
55
+ Once installed, golinks runs a lightweight HTTP server that reads shortcuts from a JSON config file at `~/.golinks/config.json` and redirects `http://go/shortcut` to the configured destination URL. The config file is hot-reloaded, so you can add new shortcuts without restarting the server.
@@ -0,0 +1,30 @@
1
+ # Golinks
2
+
3
+ A simple local HTTP redirect service that turns short URLs like `go/github` into full URLs.
4
+
5
+ ## Installation
6
+
7
+ ### Install manually
8
+
9
+ ```bash
10
+ # Clone the repository
11
+ git clone https://github.com/yourusername/golinks.git
12
+ cd golinks
13
+
14
+ # Run the install script
15
+ ./install
16
+
17
+ # Edit the config
18
+ vim ~/.config/golinks/config.json
19
+ ```
20
+
21
+ ## How it works
22
+
23
+ The `./install` script automates the entire setup:
24
+ 1. Installs the Python package using uv
25
+ 2. Adds `127.0.0.1 go` to your `/etc/hosts` file (with sudo permission)
26
+ 3. Sets up port forwarding from port 80 to 8888 using pfctl (macOS) or iptables (Linux), so you can use `go/shortcut` instead of `go:8888/shortcut`
27
+ 4. Sets up a LaunchAgent (macOS) or systemd service (Linux) to run the server at startup
28
+ 5. Starts the golinks server immediately on port 8888
29
+
30
+ Once installed, golinks runs a lightweight HTTP server that reads shortcuts from a JSON config file at `~/.golinks/config.json` and redirects `http://go/shortcut` to the configured destination URL. The config file is hot-reloaded, so you can add new shortcuts without restarting the server.
@@ -0,0 +1,7 @@
1
+ {
2
+ "github": "https://github.com",
3
+ "mail": "https://gmail.com",
4
+ "calendar": "https://calendar.google.com",
5
+ "docs": "https://docs.google.com",
6
+ "drive": "https://drive.google.com"
7
+ }
@@ -0,0 +1,295 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ RED='\033[0;31m'
6
+ GREEN='\033[0;32m'
7
+ YELLOW='\033[1;33m'
8
+ BLUE='\033[0;34m'
9
+ NC='\033[0m' # No Color
10
+
11
+ echo_info() {
12
+ echo -e "${BLUE}[INFO]${NC} $1"
13
+ }
14
+
15
+ echo_success() {
16
+ echo -e "${GREEN}[SUCCESS]${NC} $1"
17
+ }
18
+
19
+ echo_warning() {
20
+ echo -e "${YELLOW}[WARNING]${NC} $1"
21
+ }
22
+
23
+ echo_error() {
24
+ echo -e "${RED}[ERROR]${NC} $1"
25
+ }
26
+
27
+ # Check if already installed
28
+ check_installation() {
29
+ if command -v golinks &> /dev/null; then
30
+ if uv tool list 2>/dev/null | grep -q golinks; then
31
+ echo_warning "golinks is already installed"
32
+ return 0
33
+ fi
34
+ fi
35
+ return 1
36
+ }
37
+
38
+ # Install uv if missing
39
+ install_uv() {
40
+ if ! command -v uv &> /dev/null; then
41
+ echo_info "Installing uv package manager..."
42
+ curl -LsSf https://astral.sh/uv/install.sh | sh
43
+
44
+ # Source shell config to get uv in PATH
45
+ if [ -f "$HOME/.bashrc" ]; then
46
+ source "$HOME/.bashrc"
47
+ elif [ -f "$HOME/.zshrc" ]; then
48
+ source "$HOME/.zshrc"
49
+ fi
50
+
51
+ # Add to current shell session
52
+ export PATH="$HOME/.cargo/bin:$PATH"
53
+
54
+ if ! command -v uv &> /dev/null; then
55
+ echo_error "Failed to install uv. Please install manually."
56
+ exit 1
57
+ fi
58
+ echo_success "uv installed successfully"
59
+ else
60
+ echo_info "uv is already installed"
61
+ fi
62
+ }
63
+
64
+ # Install golinks tool
65
+ install_golinks() {
66
+ echo_info "Installing golinks..."
67
+
68
+ # Install from the current directory if we're in the repo
69
+ if [ -f "pyproject.toml" ]; then
70
+ uv tool install .
71
+ else
72
+ # Install from PyPI or git
73
+ uv tool install golinks
74
+ fi
75
+
76
+ if [ $? -eq 0 ]; then
77
+ echo_success "golinks installed successfully"
78
+ else
79
+ echo_error "Failed to install golinks"
80
+ exit 1
81
+ fi
82
+ }
83
+
84
+ # Create configuration directory and file
85
+ setup_config() {
86
+ CONFIG_DIR="$HOME/.config/golinks"
87
+ CONFIG_FILE="$CONFIG_DIR/config.json"
88
+
89
+ echo_info "Setting up configuration..."
90
+
91
+ # Create config directory
92
+ mkdir -p "$CONFIG_DIR"
93
+
94
+ # Create config file if it doesn't exist
95
+ if [ ! -f "$CONFIG_FILE" ]; then
96
+ cat > "$CONFIG_FILE" <<EOF
97
+ {
98
+ "github": "https://github.com",
99
+ "mail": "https://gmail.com",
100
+ "calendar": "https://calendar.google.com",
101
+ "prs": {
102
+ "template_url": "https://github.com/MithraAI/{1}/pull/{2}",
103
+ "defaults": {
104
+ "1": "istari"
105
+ }
106
+ },
107
+ "issue": {
108
+ "template_url": "https://github.com/{1}/{2}/issues/{3}",
109
+ "defaults": {
110
+ "1": "anthropics",
111
+ "2": "claude-code"
112
+ }
113
+ }
114
+ }
115
+ EOF
116
+ echo_success "Configuration file created at $CONFIG_FILE"
117
+ else
118
+ echo_info "Configuration file already exists"
119
+ fi
120
+ }
121
+
122
+ # Add entry to /etc/hosts
123
+ setup_hosts() {
124
+ echo_info "Configuring /etc/hosts..."
125
+
126
+ # Check if entry already exists
127
+ if grep -q "127.0.0.1[[:space:]]*go$" /etc/hosts 2>/dev/null; then
128
+ echo_info "/etc/hosts already configured"
129
+ else
130
+ echo_info "Adding 'go' to /etc/hosts (requires sudo)..."
131
+ echo "127.0.0.1 go" | sudo tee -a /etc/hosts > /dev/null
132
+ echo_success "Added 'go' to /etc/hosts"
133
+ fi
134
+ }
135
+
136
+ # Setup LaunchAgent
137
+ setup_launchagent() {
138
+ PLIST_DIR="$HOME/Library/LaunchAgents"
139
+ PLIST_FILE="$PLIST_DIR/com.user.golinks.plist"
140
+
141
+ echo_info "Setting up LaunchAgent..."
142
+
143
+ # Create LaunchAgents directory if it doesn't exist
144
+ mkdir -p "$PLIST_DIR"
145
+
146
+ # Find golinks executable path
147
+ GOLINKS_PATH=$(which golinks)
148
+ if [ -z "$GOLINKS_PATH" ]; then
149
+ echo_error "golinks command not found in PATH"
150
+ exit 1
151
+ fi
152
+
153
+ # Create plist file
154
+ cat > "$PLIST_FILE" <<EOF
155
+ <?xml version="1.0" encoding="UTF-8"?>
156
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
157
+ <plist version="1.0">
158
+ <dict>
159
+ <key>Label</key>
160
+ <string>com.user.golinks</string>
161
+ <key>ProgramArguments</key>
162
+ <array>
163
+ <string>$GOLINKS_PATH</string>
164
+ <string>--port</string>
165
+ <string>8080</string>
166
+ <string>--config</string>
167
+ <string>$HOME/.config/golinks/config.json</string>
168
+ </array>
169
+ <key>KeepAlive</key>
170
+ <true/>
171
+ <key>RunAtLoad</key>
172
+ <true/>
173
+ <key>StandardOutPath</key>
174
+ <string>$HOME/.config/golinks/stdout.log</string>
175
+ <key>StandardErrorPath</key>
176
+ <string>$HOME/.config/golinks/stderr.log</string>
177
+ </dict>
178
+ </plist>
179
+ EOF
180
+
181
+ echo_success "LaunchAgent plist created"
182
+ }
183
+
184
+ # Setup port forwarding (port 80 -> 8080)
185
+ setup_port_forwarding() {
186
+ echo_info "Setting up port 80 forwarding to 8080..."
187
+
188
+ # Check if pf anchor file already exists
189
+ if [ -f "/etc/pf.anchors/golinks" ]; then
190
+ echo_warning "Port forwarding anchor already exists"
191
+ else
192
+ echo_info "Creating port forwarding rule (requires sudo)..."
193
+
194
+ # Create the anchor file
195
+ echo "rdr pass on lo0 inet proto tcp from any to 127.0.0.1 port 80 -> 127.0.0.1 port 8080" | sudo tee /etc/pf.anchors/golinks > /dev/null
196
+
197
+ echo_success "Port forwarding anchor created"
198
+ fi
199
+
200
+ # Check if pf.conf already includes our anchor
201
+ if sudo grep -q "golinks" /etc/pf.conf 2>/dev/null; then
202
+ echo_info "pf.conf already configured for golinks"
203
+ else
204
+ echo_info "Updating pf.conf (requires sudo)..."
205
+
206
+ # Backup pf.conf
207
+ sudo cp /etc/pf.conf /etc/pf.conf.backup.$(date +%Y%m%d%H%M%S)
208
+
209
+ # Add rdr-anchor after other rdr-anchor lines (with proper newline)
210
+ sudo sed -i '' '/^rdr-anchor "com.apple\/\*"/a\
211
+ rdr-anchor "golinks"
212
+ ' /etc/pf.conf
213
+
214
+ # Add load anchor after the com.apple load anchor (with proper newline)
215
+ sudo sed -i '' '/^load anchor "com.apple" from "\/etc\/pf.anchors\/com.apple"/a\
216
+ load anchor "golinks" from "/etc/pf.anchors/golinks"
217
+ ' /etc/pf.conf
218
+
219
+ echo_success "pf.conf updated"
220
+ fi
221
+
222
+ # Enable pfctl
223
+ echo_info "Enabling packet filtering..."
224
+ sudo pfctl -e 2>/dev/null || true
225
+ sudo pfctl -f /etc/pf.conf 2>/dev/null || {
226
+ echo_warning "Could not reload pf.conf. You may need to restart or manually run: sudo pfctl -f /etc/pf.conf"
227
+ }
228
+
229
+ echo_success "Port forwarding setup complete (80 -> 8080)"
230
+ }
231
+
232
+ # Start the service
233
+ start_service() {
234
+ PLIST_FILE="$HOME/Library/LaunchAgents/com.user.golinks.plist"
235
+
236
+ echo_info "Starting golinks service..."
237
+
238
+ # Unload if already loaded
239
+ launchctl unload "$PLIST_FILE" 2>/dev/null || true
240
+
241
+ # Load the service
242
+ launchctl load "$PLIST_FILE"
243
+
244
+ # Wait for service to start
245
+ echo_info "Waiting for service to start..."
246
+ sleep 3
247
+
248
+ # Verify service is running
249
+ if curl -s http://localhost:8080/ | grep -q "Go Links" 2>/dev/null; then
250
+ echo_success "Service started successfully at http://localhost:8080"
251
+ else
252
+ echo_warning "Service may not be running. Check logs at ~/.config/golinks/"
253
+ fi
254
+ }
255
+
256
+ # Main installation flow
257
+ main() {
258
+ echo -e "${GREEN}╔══════════════════════════════════════╗${NC}"
259
+ echo -e "${GREEN}║ GoLinks Installation Script ║${NC}"
260
+ echo -e "${GREEN}╚══════════════════════════════════════╝${NC}"
261
+ echo ""
262
+
263
+ # Check if already installed
264
+ if check_installation; then
265
+ read -p "golinks is already installed. Reinstall? (y/N): " -n 1 -r
266
+ echo
267
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
268
+ echo_info "Installation cancelled"
269
+ exit 0
270
+ fi
271
+ fi
272
+
273
+ # Run installation steps
274
+ install_uv
275
+ install_golinks
276
+ setup_config
277
+ setup_hosts
278
+ setup_launchagent
279
+ setup_port_forwarding
280
+ start_service
281
+
282
+ echo ""
283
+ echo -e "${GREEN}╔══════════════════════════════════════╗${NC}"
284
+ echo -e "${GREEN}║ Installation Complete! 🎉 ║${NC}"
285
+ echo -e "${GREEN}╚══════════════════════════════════════╝${NC}"
286
+ echo ""
287
+ echo "Usage examples:"
288
+ echo " • Visit http://go to see the configured links"
289
+ echo " • Use a link: open http://go/<slug>"
290
+ echo ""
291
+ echo "Logs are available at: ~/.config/golinks/"
292
+ }
293
+
294
+ # Run main function
295
+ main
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [tool.hatch.build.targets.wheel]
6
+ packages = ["src"]
7
+
8
+ [project]
9
+ name = "golinks"
10
+ version = "1.0.0"
11
+ description = "A local go links redirect service for URL shortcuts"
12
+ readme = "README.md"
13
+ requires-python = ">=3.11"
14
+ license = { text = "MIT" } # Add your preferred license
15
+ authors = [
16
+ { name = "Haran Rajkumar", email = "haranrajkumar97@gmail.com" },
17
+ ]
18
+ keywords = ["golinks", "url-shortener", "redirect", "shortcuts"]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Operating System :: OS Independent",
27
+ "Topic :: Internet :: WWW/HTTP",
28
+ "Topic :: Utilities",
29
+ ]
30
+ dependencies = [
31
+ "jinja2>=3.1.6",
32
+ "pydantic>=2.11.9",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/yourusername/golinks"
37
+ Repository = "https://github.com/yourusername/golinks.git"
38
+ Issues = "https://github.com/yourusername/golinks/issues"
39
+
40
+ [project.scripts]
41
+ golinks = "src.server:main"
42
+
43
+ [dependency-groups]
44
+ dev = [
45
+ "ruff>=0.13.0",
46
+ ]
47
+
48
+
49
+
50
+ [tool.pyright]
51
+ reportUnusedCallResult = false
52
+ typeCheckingMode = "standard"
File without changes
@@ -0,0 +1,14 @@
1
+ from pydantic import BaseModel, Field, RootModel
2
+ from typing import Union, Dict
3
+
4
+
5
+ class LinkTemplate(BaseModel):
6
+ template_url: str
7
+ defaults: Dict[str, str] = Field(default_factory=dict)
8
+
9
+
10
+ LinkConfig = Union[str, LinkTemplate]
11
+
12
+
13
+ class GoLinksConfig(RootModel):
14
+ root: Dict[str, LinkConfig]