diff --git a/.env.example b/.env.example index dc4532b..ce2bb45 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,7 @@ APP_HEALTHCHECK_TIMEOUT=5s APP_HEALTHCHECK_START_PERIOD=10s APP_HEALTHCHECK_RETRIES=3 LINKLOG_APP_NAME=LinkLog +LINKLOG_VERSION=0.1.0 LINKLOG_SECRET_KEY=replace-with-a-long-random-secret LINKLOG_TOKEN_EXPIRY_DAYS=30 # Optional comma-separated override. Leave empty to use the built-in list. diff --git a/.gitignore b/.gitignore index fdc1eef..192c4a8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ __pycache__/ *.log backend/data/avatars/ LinkLog.afdesign~lock~ + diff --git a/Makefile b/Makefile index e88d081..f213b4d 100644 --- a/Makefile +++ b/Makefile @@ -9,13 +9,27 @@ WEB_LOGO := frontend/static/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 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 + 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: @command -v $(MAGICK) >/dev/null 2>&1 || { echo "Error: ImageMagick '$(MAGICK)' is required. Install ImageMagick and retry." >&2; exit 1; } diff --git a/README.md b/README.md index 4b6d0d0..04f7092 100644 --- a/README.md +++ b/README.md @@ -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. +For full transparency: The author used vibe coding to create this software. + ## Project Layout ```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. 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). ## 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. +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. 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. diff --git a/VIBE/CHAT_LOG.md b/VIBE/CHAT_LOG.md index 9fcf775..d1d50a8 100644 --- a/VIBE/CHAT_LOG.md +++ b/VIBE/CHAT_LOG.md @@ -493,3 +493,21 @@ Add a copyright statement to source files refer to GNU license ### 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. + +### 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`. diff --git a/VIBE/PROMPTS.md b/VIBE/PROMPTS.md index 856116c..01fc9c4 100644 --- a/VIBE/PROMPTS.md +++ b/VIBE/PROMPTS.md @@ -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 82. Add a GNU License file 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 diff --git a/VIBE/README.md b/VIBE/README.md index eb628bb..5c9cbbf 100644 --- a/VIBE/README.md +++ b/VIBE/README.md @@ -1,9 +1,11 @@ # 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. -- `CHAT_LOG.md` records user requests and assistant responses or outcomes in chronological order. -- `PROMPTS.md` records user prompts separately for quick reference. +- [`CHAT_LOG.md`](./CHAT_LOG.md) records user requests and assistant responses or outcomes in chronological order. +- [`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. diff --git a/XPI/unsigned/LinkLog-0.1.0.xpi b/XPI/unsigned/LinkLog-0.1.0.xpi new file mode 100644 index 0000000..1ca1c29 Binary files /dev/null and b/XPI/unsigned/LinkLog-0.1.0.xpi differ diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 269ba28..187780f 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -13,6 +13,7 @@ DB_PATH = BASE_DIR / 'data' / 'linklog.db' @dataclass class Settings: 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}') 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')) diff --git a/backend/app/main.py b/backend/app/main.py index b8ecd7e..2ded283 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -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.public import router as public_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.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('/media', StaticFiles(directory=AVATARS_DIR), name='media') app.include_router(auth_router, prefix='/api/auth') diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 1a1c518..0da1620 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -22,6 +22,7 @@ def login_headers(username='alice'): def test_login_returns_token(): + assert app.version == '0.1.0' response = client.post('/api/auth/login', json={ 'username': 'alice', 'password': 'secret123', diff --git a/frontend/static/logo.svg b/frontend/static/logo.svg index ee95617..f87aede 100644 --- a/frontend/static/logo.svg +++ b/frontend/static/logo.svg @@ -1,4 +1,6 @@ + + diff --git a/webextension/icon-16.png b/webextension/icon-16.png index 5f895f2..b86cdec 100644 Binary files a/webextension/icon-16.png and b/webextension/icon-16.png differ diff --git a/webextension/icon-32.png b/webextension/icon-32.png index 3d5f3ef..b9c0e01 100644 Binary files a/webextension/icon-32.png and b/webextension/icon-32.png differ diff --git a/webextension/icon-48.png b/webextension/icon-48.png index 95cdfb4..d16b25e 100644 Binary files a/webextension/icon-48.png and b/webextension/icon-48.png differ diff --git a/webextension/icon-96.png b/webextension/icon-96.png index 65ad0cc..92d7326 100644 Binary files a/webextension/icon-96.png and b/webextension/icon-96.png differ diff --git a/webextension/logo.svg b/webextension/logo.svg index ee95617..f87aede 100644 --- a/webextension/logo.svg +++ b/webextension/logo.svg @@ -1,4 +1,6 @@ + +