Versioning (0.1.0)
@@ -8,6 +8,7 @@ APP_HEALTHCHECK_TIMEOUT=5s
|
|||||||
APP_HEALTHCHECK_START_PERIOD=10s
|
APP_HEALTHCHECK_START_PERIOD=10s
|
||||||
APP_HEALTHCHECK_RETRIES=3
|
APP_HEALTHCHECK_RETRIES=3
|
||||||
LINKLOG_APP_NAME=LinkLog
|
LINKLOG_APP_NAME=LinkLog
|
||||||
|
LINKLOG_VERSION=0.1.0
|
||||||
LINKLOG_SECRET_KEY=replace-with-a-long-random-secret
|
LINKLOG_SECRET_KEY=replace-with-a-long-random-secret
|
||||||
LINKLOG_TOKEN_EXPIRY_DAYS=30
|
LINKLOG_TOKEN_EXPIRY_DAYS=30
|
||||||
# Optional comma-separated override. Leave empty to use the built-in list.
|
# Optional comma-separated override. Leave empty to use the built-in list.
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ __pycache__/
|
|||||||
*.log
|
*.log
|
||||||
backend/data/avatars/
|
backend/data/avatars/
|
||||||
LinkLog.afdesign~lock~
|
LinkLog.afdesign~lock~
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,27 @@ WEB_LOGO := frontend/static/logo.svg
|
|||||||
EXTENSION_LOGO := webextension/logo.svg
|
EXTENSION_LOGO := webextension/logo.svg
|
||||||
ICON_FILES := webextension/icon-16.png webextension/icon-32.png webextension/icon-48.png webextension/icon-96.png
|
ICON_FILES := webextension/icon-16.png webextension/icon-32.png webextension/icon-48.png webextension/icon-96.png
|
||||||
GENERATED_LOGOS := $(WEB_LOGO) $(EXTENSION_LOGO) $(ICON_FILES)
|
GENERATED_LOGOS := $(WEB_LOGO) $(EXTENSION_LOGO) $(ICON_FILES)
|
||||||
|
EXTENSION_VERSION := $(shell sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' webextension/manifest.json | head -n 1)
|
||||||
|
XPI_FILE := LinkLog-$(EXTENSION_VERSION).xpi
|
||||||
|
XPI_UNSIGNED_DIR := XPI/unsigned
|
||||||
|
XPI_SIGNED_DIR := XPI/signed
|
||||||
|
XPI_OUTPUT := $(XPI_UNSIGNED_DIR)/$(XPI_FILE)
|
||||||
|
EXTENSION_FILES := manifest.json logo.svg icon-16.png icon-32.png icon-48.png icon-96.png options.css options.html options.js popup.css popup.html popup.js
|
||||||
|
|
||||||
.PHONY: all logos check-tools clean-generated
|
.PHONY: all logos xpi check-tools clean-generated
|
||||||
|
|
||||||
all: logos
|
all: logos
|
||||||
|
|
||||||
|
|
||||||
logos: check-tools $(GENERATED_LOGOS)
|
logos: check-tools $(GENERATED_LOGOS)
|
||||||
|
|
||||||
|
xpi: logos
|
||||||
|
@test -n "$(EXTENSION_VERSION)" || { echo "Error: extension version is missing from webextension/manifest.json" >&2; exit 1; }
|
||||||
|
@mkdir -p $(XPI_UNSIGNED_DIR) $(XPI_SIGNED_DIR)
|
||||||
|
@rm -f $(XPI_OUTPUT)
|
||||||
|
@cd webextension && zip -q -9 "../$(XPI_OUTPUT)" $(EXTENSION_FILES)
|
||||||
|
@echo "Created $(XPI_OUTPUT)"
|
||||||
|
|
||||||
check-tools:
|
check-tools:
|
||||||
@command -v $(MAGICK) >/dev/null 2>&1 || { echo "Error: ImageMagick '$(MAGICK)' is required. Install ImageMagick and retry." >&2; exit 1; }
|
@command -v $(MAGICK) >/dev/null 2>&1 || { echo "Error: ImageMagick '$(MAGICK)' is required. Install ImageMagick and retry." >&2; exit 1; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
LinkLog is a Firefox extension and Python web service for saving links with a title, comment, timestamp, and tracking parameters removed. The service stores links in SQLite and can publish them through plugins, including Mastodon.
|
LinkLog is a Firefox extension and Python web service for saving links with a title, comment, timestamp, and tracking parameters removed. The service stores links in SQLite and can publish them through plugins, including Mastodon.
|
||||||
|
|
||||||
|
For full transparency: The author used vibe coding to create this software.
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -25,6 +27,7 @@ For local development:
|
|||||||
|
|
||||||
The backend currently uses FastAPI, uvicorn, SQLite, and Pydantic. `httpx2` is included for the Starlette-compatible test client.
|
The backend currently uses FastAPI, uvicorn, SQLite, and Pydantic. `httpx2` is included for the Starlette-compatible test client.
|
||||||
Jinja2 is included for server-rendered HTML templates.
|
Jinja2 is included for server-rendered HTML templates.
|
||||||
|
The backend version is `0.1.0` and is exposed through the FastAPI/OpenAPI metadata. It can be overridden with `LINKLOG_VERSION`.
|
||||||
LinkLog is licensed under the GNU General Public License, version 3 or any later version. See [LICENSE](LICENSE).
|
LinkLog is licensed under the GNU General Public License, version 3 or any later version. See [LICENSE](LICENSE).
|
||||||
|
|
||||||
## Local Installation
|
## Local Installation
|
||||||
@@ -93,6 +96,14 @@ make logos
|
|||||||
|
|
||||||
The generated files are `frontend/static/logo.svg`, `webextension/logo.svg`, and `webextension/icon-16.png`, `icon-32.png`, `icon-48.png`, and `icon-96.png`. Override the ImageMagick executable with `make MAGICK=magick logos` when needed.
|
The generated files are `frontend/static/logo.svg`, `webextension/logo.svg`, and `webextension/icon-16.png`, `icon-32.png`, `icon-48.png`, and `icon-96.png`. Override the ImageMagick executable with `make MAGICK=magick logos` when needed.
|
||||||
|
|
||||||
|
Create an installable Firefox XPI bundle with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make xpi
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates `XPI/unsigned/LinkLog-0.1.0.xpi` from the `webextension/` package and excludes macOS metadata and minified artifacts. The version is read from `webextension/manifest.json`. The `XPI/signed/` directory is reserved for signed release bundles.
|
||||||
|
|
||||||
Appending a username to the root URL, such as `/alice`, opens that user's public feed and profile information.
|
Appending a username to the root URL, such as `/alice`, opens that user's public feed and profile information.
|
||||||
|
|
||||||
Configuration APIs require a bearer token returned by the login endpoint. User configuration uses the identity in that token. Plugin administration additionally requires an administrator account; the development `alice` account is seeded as an administrator, while `bob` is a standard user.
|
Configuration APIs require a bearer token returned by the login endpoint. User configuration uses the identity in that token. Plugin administration additionally requires an administrator account; the development `alice` account is seeded as an administrator, while `bob` is a standard user.
|
||||||
|
|||||||
@@ -493,3 +493,21 @@ Add a copyright statement to source files refer to GNU license
|
|||||||
|
|
||||||
### Assistant outcome
|
### Assistant outcome
|
||||||
Added `Copyright © 2026 Olaf Kolkman` and `SPDX-License-Identifier: GPL-3.0-or-later` headers to all 47 non-minified application, test, frontend, extension, build, deployment, and source-logo files. Generated/runtime assets were left untouched. Python and JavaScript compilation, Compose validation, whitespace checks, and the strict backend suite passed with 15 tests.
|
Added `Copyright © 2026 Olaf Kolkman` and `SPDX-License-Identifier: GPL-3.0-or-later` headers to all 47 non-minified application, test, frontend, extension, build, deployment, and source-logo files. Generated/runtime assets were left untouched. Python and JavaScript compilation, Compose validation, whitespace checks, and the strict backend suite passed with 15 tests.
|
||||||
|
|
||||||
|
### User
|
||||||
|
Add the version to the name of the resulting xpi
|
||||||
|
|
||||||
|
### Assistant outcome
|
||||||
|
Updated the Makefile to read the extension version from `webextension/manifest.json` and produce a versioned archive such as `LinkLog-0.1.0.xpi`. Updated `.gitignore` and README documentation. The generated XPI contains 12 expected extension files and passes `unzip -tq` integrity validation.
|
||||||
|
|
||||||
|
### User
|
||||||
|
Set the version number of the backend to 0.1.0
|
||||||
|
|
||||||
|
### Assistant outcome
|
||||||
|
Added backend setting `version = 0.1.0`, wired it into FastAPI and OpenAPI metadata, and exposed `LINKLOG_VERSION` as an environment override. Added regression coverage and documented the setting. Compose validation and the strict backend suite passed with 15 tests.
|
||||||
|
|
||||||
|
### User
|
||||||
|
Add the creation of an xpi bundle for the webextention to the Makefile
|
||||||
|
|
||||||
|
### Assistant outcome
|
||||||
|
Added a portable `xpi` Makefile target that regenerates logo assets and packages the Firefox extension into `LinkLog.xpi`, excluding metadata and minified artifacts. Added README instructions, ignored the generated XPI, and verified the archive contents and integrity with `unzip -tq`.
|
||||||
|
|||||||
@@ -85,6 +85,9 @@
|
|||||||
81. in the footer make the link the same color as the mother text, when clicked use a tint from the Catpuccin theme
|
81. in the footer make the link the same color as the mother text, when clicked use a tint from the Catpuccin theme
|
||||||
82. Add a GNU License file
|
82. Add a GNU License file
|
||||||
83. Add a copyright statement to source files refer to GNU license
|
83. Add a copyright statement to source files refer to GNU license
|
||||||
|
84. Add the version to the name of the resulting xpi
|
||||||
|
85. Set the version number of the backend to 0.1.0
|
||||||
|
84. Add the creation of an xpi bundle for the webextention to the Makefile
|
||||||
|
|
||||||
## Future entries
|
## Future entries
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# VIBE
|
# VIBE
|
||||||
|
|
||||||
|
This code has mostly been vibe coded. For full transparency we maintain a log of prompts and results.
|
||||||
|
|
||||||
This folder contains an append-only record of the visible Link Log development conversation.
|
This folder contains an append-only record of the visible Link Log development conversation.
|
||||||
|
|
||||||
- `CHAT_LOG.md` records user requests and assistant responses or outcomes in chronological order.
|
- [`CHAT_LOG.md`](./CHAT_LOG.md) records user requests and assistant responses or outcomes in chronological order.
|
||||||
- `PROMPTS.md` records user prompts separately for quick reference.
|
- [`PROMPTS.md`](PROMPTS.md) records user prompts separately for quick reference.
|
||||||
|
|
||||||
Only the user-visible conversation is recorded. System, developer, environment, and private tool instructions are intentionally excluded.
|
Only the user-visible conversation is recorded. System, developer, environment, and private tool instructions are intentionally excluded.
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ DB_PATH = BASE_DIR / 'data' / 'linklog.db'
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Settings:
|
class Settings:
|
||||||
app_name: str = os.getenv('LINKLOG_APP_NAME', 'LinkLog')
|
app_name: str = os.getenv('LINKLOG_APP_NAME', 'LinkLog')
|
||||||
|
version: str = os.getenv('LINKLOG_VERSION', '0.1.0')
|
||||||
database_url: str = os.getenv('LINKLOG_DATABASE_URL', f'sqlite:///{DB_PATH}')
|
database_url: str = os.getenv('LINKLOG_DATABASE_URL', f'sqlite:///{DB_PATH}')
|
||||||
secret_key: str = os.getenv('LINKLOG_SECRET_KEY', 'dev-secret-key-change-me')
|
secret_key: str = os.getenv('LINKLOG_SECRET_KEY', 'dev-secret-key-change-me')
|
||||||
token_expiry_days: int = int(os.getenv('LINKLOG_TOKEN_EXPIRY_DAYS', '30'))
|
token_expiry_days: int = int(os.getenv('LINKLOG_TOKEN_EXPIRY_DAYS', '30'))
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ from backend.app.api.auth import router as auth_router
|
|||||||
from backend.app.api.links import router as links_router
|
from backend.app.api.links import router as links_router
|
||||||
from backend.app.api.public import router as public_router
|
from backend.app.api.public import router as public_router
|
||||||
from backend.app.api.user_config import router as user_config_router
|
from backend.app.api.user_config import router as user_config_router
|
||||||
|
from backend.app.core.config import settings
|
||||||
from backend.app.database import AVATARS_DIR
|
from backend.app.database import AVATARS_DIR
|
||||||
from backend.app.services.link_service import list_public_links
|
from backend.app.services.link_service import list_public_links
|
||||||
|
|
||||||
app = FastAPI(title='LinkLog API')
|
app = FastAPI(title='LinkLog API', version=settings.version)
|
||||||
app.mount('/static', StaticFiles(directory='frontend/static'), name='static')
|
app.mount('/static', StaticFiles(directory='frontend/static'), name='static')
|
||||||
app.mount('/media', StaticFiles(directory=AVATARS_DIR), name='media')
|
app.mount('/media', StaticFiles(directory=AVATARS_DIR), name='media')
|
||||||
app.include_router(auth_router, prefix='/api/auth')
|
app.include_router(auth_router, prefix='/api/auth')
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ def login_headers(username='alice'):
|
|||||||
|
|
||||||
|
|
||||||
def test_login_returns_token():
|
def test_login_returns_token():
|
||||||
|
assert app.version == '0.1.0'
|
||||||
response = client.post('/api/auth/login', json={
|
response = client.post('/api/auth/login', json={
|
||||||
'username': 'alice',
|
'username': 'alice',
|
||||||
'password': 'secret123',
|
'password': 'secret123',
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||||
|
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 950 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
@@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||||
|
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |