> [!IMPORTANT] > This branch reflects **version 4.0.0**, which is in active development with a complete redesign of the UI. > The live demo at [demo.portkey.page](https://demo.portkey.page) shows this in-development version, **not** the latest stable release. > The screenshots later in this document still show the previous stable version and have not yet been updated to reflect the 4.0.0 redesign.

portkey logo

portkey

⚡🚀🔗
Repository of the portkey application
A simple web portal that can act as startup page and shows a collection of links/urls. It also supports adding small custom pages.
Sonar quality gate GitHub stars Go version Docker image size

Website (GitHub) | Contributing

As magical as in Harry Potter. Built with ☕️ by CodeHat and contributors
## Table of Contents - [Features](#features) - [Screenshots](#screenshots) - [Installation](#installation) - [Usage](#usage) - [Configuration](#configuration) - [Metrics](#metrics) - [Docker](#docker) - [Development](#development) - [See Also](#see-also) - [License](#license) ## Features - 🔗 Shows a collection of links acting as startup page or similar. - 🔎 Includes a search box with configurable keyword support and fuzzy matching. - 📂 Portals can be organised into named groups/sections on the home page. - 📐 Optional multi-column grid layout for portals and groups (configurable column count, mobile-responsive). - 📄 Can be configured easily by modifying only one file. - 🗒️ Also supports adding smaller custom pages. - 🌓 Dark and light mode available. - 🪰 Very lightweight application with Docker images available. ## Screenshots

You can also find a demo here

Click to view screenshots #### Link collection (no groups) ![LinkCollectionNoGroups](docs/images/screenshot_no_groups_full.png "Link collection (no groups)") #### Link collection (with groups) ![LinkCollectionWithGroups](docs/images/screenshot_with_groups_full.png "Link collection (with groups)") #### Search for a link ![SearchForLink](docs/images/screenshot_search.png "Search for a link") #### Custom page ![CustomPage](docs/images/screenshot_custom_page.png "Custom page")
## Installation Download the `portkey` file for your OS. Probably to a location that is in your `PATH`, so you can use it right away. ## Usage 1. Create a `config.yml` or use the [example configuration](https://github.com/kodehat/portkey/blob/main/config.yml) from this repository and configure it as you want. > You can find a detailed explanation of all configuration options [here](#configuration). 2. Start the application with `portkey --config-path=`. Providing the path to the configuration file is optional if it's in the working directory. 3. Open your browser at the defined host and port. Default is ## Configuration `portkey` is configured with a single configuration file called `config.yml`. You can pass its location using the `--config-path` argument. You can also overwrite configuration values from file by using environment variables in uppercase and prefixed with `PORTKEY_`. For instance, the configuration key `host` can be passed as environment variable `PORTKEY_HOST`. The `config.yml` contains the following configuration options: ### Server ```yaml # Can be changed to reduce or increase logs. Values could be "ERROR", "WARN", "INFO" (default) or "DEBUG". logLevel: INFO # If enabled logs are in JSON format. logJson: false # Set the host where the application should bind to. host: localhost # Set the port where the application should bind to. port: 3000 # Set the context path (aka base-url) portkey is hosted under. Must not be specified unless you're using a reverse proxy and are hosting portkey under a directory. If that's the case then you can set this value to e.g. /portkey or whatever the directory is called. Note that the forward slash (/) in the beginning is required! contextPath: "" # Enables the HTTP server that serves metrics that can be scraped by e.g. Prometheus. enableMetrics: false # Set the host where the metrics server should bind to. metricsHost: localhost # Set the port where the metrics server should bind to. metricsPort: 3030 ``` ### Styling ```yaml # Title of the application shown in the browser tab and on the front page. title: "portkey" # Allows to hide the title. hideTitle: false # Allows adding additional scripts/stylesheets etc. to the HTML header. Can be useful for analytics or smaller style modifications. headerAddition: |- # Footer (HTML support) that is shown on every page. # Remember that Tailwind CSS classes used here do only work if already used somewhere else in the application because the bundler couldn't look here! footer: |-

This is a footer!

# Defines whether portkey's application icon should be shown at the top left of the front page. showTopIcon: true # If true keywords of portals are shown as tooltip on hover. showKeywordsAsTooltips: false # If true all links are sorted alphabetically when shown on the front page. Otherwise they are shown in the order they are defined. sortAlphabetically: false # If true search query is also compared to portals and keywords using Levenshtein string metric. searchWithStringSimilarity: false # Minimum required similarity for results when 'searchWithStringSimilarity' is 'true'. Must be between '0.0' (0%) and '1.0' (100%). minimumStringSimilarity: 0.5 # If true, search bar is hidden. Can be useful with a low amount of portals making the search unnecessary. hideSearchBar: false # Number of columns for the grid layout (0 = disabled/vertical). # On mobile (<768px) the layout always falls back to vertical stacking. # When groups exist, each group occupies one grid cell. # When no groups, portals are distributed across N columns. layoutColumns: 0 # On-disk favicon cache directory. Mountable as a Docker volume for persistence across restarts. faviconCacheDir: ./favicon-cache # Set to true to bypass local favicon caching and always discover + download fresh. faviconCacheDisabled: false # Directory for custom icon files (SVG, PNG). Files are served at /_/icons/. # Mountable as a Docker volume. Requires creating the directory and placing icon files. customIconsDir: ./icons ``` ### Portals (Links) ```yaml # Defines a list of portals (links) that have additional attributes defining their appearance. portals: # Name of the link - title: example # (Optional) Icon shown in front of the title. Supports multiple formats: # - Emoji: icon: "🔗" # - Custom SVG/PNG: icon: /_/icons/github.svg (place file in customIconsDir) # - Absolute URL: icon: https://example.com/icon.png # - Data URI: icon: data:image/svg+xml,%3Csvg... # If empty, the global favicon is used for external links (cached automatically) # and a file icon is shown for internal pages. # Link where the portal will lead to (can be relative for custom pages or absolute otherwise) link: https://example.com/ # Additional keywords used by the search feature. keywords: - url - example # (Optional) Group name for organising portals into sections on the home page. # Portals sharing the same group value are rendered together under a labelled heading. # Portals without a group are shown ungrouped at the bottom. Groups appear in the # order the first portal of each group is defined. group: My Group ``` > **Tip:** When a search query is active, portals are still grouped by their group field. Groups with no matching portals are hidden. ### Custom pages ```yaml # Defines a list of custom pages that are made available at the defined paths. # Important: These are not automatically added to the list of portals and have to be added manually! # This may be changed in the future. pages: # Heading for the custom page. Shown in browser tab and as heading on the page. - heading: Custom # If true, shows the (optionally configured) subtitle also on this page. showSubtitle: true # Path where the custom page will be available. path: /custom # Content of the custom page and it supports using HTML. # The same CSS rules apply as for the footer! content: |- This is a custom page
It also supports using HTML! ``` ## Metrics Metrics can be enabled with the `enableMetrics` configuration key and are served on a dedicated HTTP server. By default they are served on `http://localhost:3030/metrics`. Use this address to configure your tool of choice (e.g. [Prometheus](https://prometheus.io/)) to scrape the exported metrics. Besides the default metrics provided by the [Prometheus instrumentation library for Go applications ](https://github.com/prometheus/client_golang), the following additional metrics are provided: ```plain # HELP portkey_portal_handler_requests_total Total number of HTTP requests by portal. # TYPE portkey_portal_handler_requests_total counter portkey_portal_handler_requests_total{portal=""} 0 # HELP portkey_page_handler_requests_total Total number of HTTP requests by page. # TYPE portkey_page_handler_requests_total counter portkey_page_handler_requests_total{path=""} 0 # HELP portkey_search_requests_with_results_total Total number of HTTP requests for search with at least one result. # TYPE portkey_search_requests_with_results_total counter portkey_search_requests_with_results_total 0 # HELP portkey_search_requests_no_results_total Total number of HTTP requests for search with no results. # TYPE portkey_search_requests_no_results_total counter portkey_search_requests_no_results_total 0 # HELP portkey_search_duration_seconds Search query duration in seconds. # TYPE portkey_search_duration_seconds histogram portkey_search_duration_seconds_bucket{le="0.001"} 0 portkey_search_duration_seconds_bucket{le="0.005"} 0 portkey_search_duration_seconds_bucket{le="0.01"} 0 portkey_search_duration_seconds_bucket{le="0.025"} 0 portkey_search_duration_seconds_bucket{le="0.05"} 0 portkey_search_duration_seconds_bucket{le="0.1"} 0 portkey_search_duration_seconds_bucket{le="+Inf"} 0 portkey_search_duration_seconds_sum 0 portkey_search_duration_seconds_count 0 # HELP portkey_http_request_duration_seconds HTTP request duration by handler pattern. # TYPE portkey_http_request_duration_seconds histogram portkey_http_request_duration_seconds_bucket{handler="/",le="0.005"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.01"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.025"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.05"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.1"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.25"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="0.5"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="1"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="2.5"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="5"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="10"} 0 portkey_http_request_duration_seconds_bucket{handler="/",le="+Inf"} 0 portkey_http_request_duration_seconds_sum{handler="/"} 0 portkey_http_request_duration_seconds_count{handler="/"} 0 # HELP portkey_favicon_cache_hits_total Total number of favicon cache hits. # TYPE portkey_favicon_cache_hits_total counter portkey_favicon_cache_hits_total 0 # HELP portkey_favicon_cache_misses_total Total number of favicon cache misses. # TYPE portkey_favicon_cache_misses_total counter portkey_favicon_cache_misses_total 0 # HELP portkey_favicon_fetch_failures_total Total number of failed favicon fetches. # TYPE portkey_favicon_fetch_failures_total counter portkey_favicon_fetch_failures_total 0 # HELP portkey_favicon_cache_size Current number of favicons in the on-disk cache. # TYPE portkey_favicon_cache_size gauge portkey_favicon_cache_size 0 # HELP portkey_portals_total Total number of configured portals. # TYPE portkey_portals_total gauge portkey_portals_total 0 # HELP portkey_groups_total Total number of portal groups. # TYPE portkey_groups_total gauge portkey_groups_total 0 # HELP portkey_version_info Version information about portkey. # TYPE portkey_version_info gauge portkey_version_info{buildTime="2024.10.09_17:29:19",commitHash="4fd1a0f",goVersion="1.23.1",version="dev"} 1 ``` ## Docker There are also Docker images available at Docker hub that you can use. You can start a container with the following command: ```sh # Assumes that there is a config.yml in the current directory. # It is probably better to use a specific version than 'latest'. docker run --rm -it \ -v $(PWD)/config.yml:/opt/config.yml \ -v $(PWD)/favicon-cache:/opt/favicon-cache \ -v $(PWD)/icons:/opt/icons \ -e PORTKEY_FAVICONCACHEDIR=/opt/favicon-cache \ -e PORTKEY_CUSTOMICONSDIR=/opt/icons \ -p 3000:3000 \ codehat/portkey:latest ``` ## Development ### Application Code **portkey** is a *Go* application. You can install its dependencies with `go mod download`. ### Frontend The frontend dependencies (e.g. TailwindCSS, AlpineJS) can be installed with `npm install --include dev`. They can be watched with `npm run watch` and built with `npm run build`. ### Templates A library called [templ](https://templ.guide) is used for the templates. To generate the `.go` files from the templates, it has to be installed. `templ` is installed using go tools and can be invoked with: ```sh go tool templ ``` Afterwards you can generate the compiled templates with `templ generate`. ### Live Reload Live reloading is possible by installing [air](https://github.com/cosmtrek/air) and calling `air`. `air` is installed using go tools and can be invoked with: ```sh go tool air ``` ## License [AGPL-3.0](https://www.tldrlegal.com/license/gnu-affero-general-public-license-v3-agpl-3-0)