Compare commits

..
Author SHA1 Message Date
angel 77712d5699 gitea workflow: tests: UI test job added
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 12s
Tests / UI tests (push) Successful in 36s
2026-08-22 02:28:35 -06:00
angel b54d3baefa Fix server/ui golden test 500s and commit the golden file
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 17s
ui.Routes' templater resolves templates/pages relative to the process's
working directory on every request, not relative to the package. Under
`go test` that's server/ui itself, not the repo root where ./templates
actually lives, so every request 500'd before rendering anything. Chdir
to the repo root for the test's duration (restoring it via t.Cleanup)
and make the golden-file path absolute before the chdir so it still
resolves. Also generates and commits the home-logged-out golden file so
the test has something to diff against instead of skipping.
2026-08-22 02:15:24 -06:00
Claudeandangel 441d6bb1ee Add golden-file HTML snapshot test for server/ui pages
Issue #33 asked how UI/UX tests should be run, including raw-HTML and
PNG snapshots. This adds the first raw-HTML-snapshot layer from the
proposal posted on that issue: an integration test that exercises
server/ui's real routing and templating path end-to-end (real
templates, real Store types, the actual response middleware) and diffs
the output against a golden file, so unintentional markup changes show
up as a reviewable diff.

Starts with a single case (the logged-out home page) to establish the
pattern; extending to authenticated pages via testdb.SeedOAuthSession
is noted in the test's doc comment. No golden file is committed yet -
it needs to be generated once, with TEST_DATABASE_URL set, by running
the test with its -update flag, then reviewed and committed; until
then the case skips rather than failing, matching this repo's
skip-gracefully convention for missing test infrastructure.
2026-08-22 02:15:24 -06:00
angel 7600e6db21 claude bot: finished TEST_DATABASE_URL
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
2026-08-22 01:41:25 -06:00
angel 736cb4ec0d claude bot: typo
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-22 01:35:31 -06:00
angel a9da8da71a claude bot: give migrate access
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-22 01:27:30 -06:00
angel 5d6009a29e claude bot: db: type
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 14s
2026-08-22 01:17:43 -06:00
angel 00a6cc4ad3 claude bot: db: move env vars to steps
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 13s
2026-08-22 01:14:19 -06:00
angel 5546a513b1 claude bot: db: eliminate redundant vars in psql cmd
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 19s
2026-08-22 01:10:08 -06:00
angel 4d36f7a38c claude bot: db: pull out psql vars
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-22 01:09:28 -06:00
angel f85e7640a4 claude bot: db: revert var changes
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-22 01:07:10 -06:00
angel ed401adc68 claude bot: db: consolidate pg vars
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-22 01:03:02 -06:00
angel 45bea46a88 claude bot: db: test using pgpassword in POSTGRES_PASSWORD
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 11s
2026-08-22 00:59:35 -06:00
angel 1212e6264b claude bot: db: test using pgpassword
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-22 00:57:40 -06:00
angel b8d32c9aea claude bot: db: test psql tweak
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
2026-08-22 00:53:13 -06:00
angel 60b2e5d642 claude bot: db: test psql tweak
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 15s
2026-08-22 00:35:50 -06:00
angel 599098d726 claude bot: db: missing host in psql cmd
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-22 00:22:42 -06:00
angel 648e77952f claude bot: db: create inventory_2 db
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 14s
2026-08-22 00:12:22 -06:00
angel 3c6490f25c claude bot: expose DATABASE_URL to all jobs
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
2026-08-22 00:05:29 -06:00
angel bf8f89ff08 claude bot: install psq: expect ubuntu container
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-21 23:40:47 -06:00
angel 02fb586ae2 claude bot: install psql, go-migrate, migrate schema, and drop node action
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 15s
2026-08-21 23:32:44 -06:00
angel ce3396adcc claude bot: claude env use postgres
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-21 23:31:57 -06:00
angel 6b72532331 claude bot: db user/pwd changes to postgres
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 15s
2026-08-21 23:15:42 -06:00
angel a3e11a6ad1 claude bot: move db env vars to workflow from .env
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 16s
2026-08-21 19:06:49 -06:00
angel 6313817cd4 claude bot: postgres service container basics
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 20s
2026-08-21 18:56:02 -06:00
angel f9fba50093 claude bot: fix npm install allowance
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 13s
2026-08-21 18:38:13 -06:00
angel c50b53cea0 claude bot: allow bot to run npm install
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 22s
2026-08-21 18:32:23 -06:00
angel 7ee6b4969e claude bot: testing NODE_VERSION in claude action
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-21 18:24:26 -06:00
angel 1ef65523d3 claude bot: go command fixes
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-21 18:19:39 -06:00
angel 738b727242 gave claude bot ability to check node version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-21 18:16:26 -06:00
angel 068ac93e40 type in action
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Tests / Go tests (push) Successful in 26s
2026-08-21 18:08:10 -06:00
angel 53d642bbb7 give claude bot latest version of node
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 18s
2026-08-21 18:04:20 -06:00
angel f0ecd16cb0 give claude bot a .env file for development
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Tests / Go tests (push) Successful in 13s
2026-08-21 17:52:59 -06:00
angel 2a4dd82643 give claude tailwind; forgot to add go tools to allow list
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 15s
2026-08-21 17:45:15 -06:00
angel d33cfd6ccc give claude go tools
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 12s
2026-08-21 17:39:02 -06:00
angel d2b1d1e8cc gitea-claude workflow test: add penpot tools
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
2026-08-21 15:43:23 -06:00
angel ccd9cd9c94 gitea-claude workflow fix: allow assignee of @claude to trigger action
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 18s
2026-08-21 02:55:52 -06:00
angel f310eeba9b gitea-claude workflow fix: explicitly set model
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 21s
2026-08-21 02:30:32 -06:00
angel f956aee811 gitea-claude workflow fix: set version to @gitea
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 19s
2026-08-21 02:19:44 -06:00
angel 2146dd3f46 gitea-claude workflow fix: tweak version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 8s
Tests / Go tests (push) Successful in 16s
2026-08-21 01:51:46 -06:00
angel 393cda8785 gitea-claude workflow fix: missing version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 14s
2026-08-21 01:49:56 -06:00
angel 4fbf5c6b53 new gitea-claude workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 19s
2026-08-21 01:44:11 -06:00
angel cdd2202839 docs: document self-hosted Gitea and its MCP tooling for agents
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 13s
Make sure AI agents know this project is hosted on a self-hosted Gitea
instance rather than GitHub, and that mcp__angel__* MCP tools (with no
Projects API) are available for interacting with it.
2026-08-20 23:59:48 -06:00
angelandClaude Sonnet 5 ee97ae20f8 docs: make README's "Where things live" index clickable links
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 21s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 17:40:09 -06:00
angelandClaude Sonnet 5 b9dd4fc0e5 docs: split README into distributed docs, fix stale/broken content
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 16s
README had drifted: an unresolved merge-conflict marker left over from a
past edit, a directory-structure diagram that no longer matches the repo
(/internal/site, /internal/domains/platforms/tiktok - actual layout is
server/, domains/, etc per AGENTS.md), and a duplicated dev-workflow blurb
that AGENTS.md already documents more accurately.

Splits the roadmap checklist out to ROADMAP.md and the Etsy API compliance
checklist to domains/platforms/etsy/COMPLIANCE.md (next to the code it
governs, where someone touching that integration will actually look for
it) instead of burying both in one large README. README itself becomes a
short front door with a "Where things live" index up top, since scattering
docs across files only helps if there's an obvious map to them.

Moves the CQRS/event-sourcing architecture note into AGENTS.md's
Architecture section (with a new domains/raw_events bullet) rather than
leaving it as prose in README, since AGENTS.md is the maintained
engineering reference and that's where a reader would already be looking
for how the domains are structured.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 17:34:58 -06:00
angel ed1c8363fd linked research document to README
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Tests / Go tests (push) Successful in 14s
2026-08-20 17:24:23 -06:00
angel c2f27b3b98 Make README.md badge a link.
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Tests / Go tests (push) Successful in 21s
2026-08-20 17:17:24 -06:00
12 changed files with 603 additions and 120 deletions
+14
View File
@@ -0,0 +1,14 @@
# TODO: delete this file if unused
# This is on the only .env file commited to git.
# It's purpose is to provide an environment from which it can operate the app and do full development.
DATABASE_URL=postgres://app_client:app_password@postgres:5432/inventory_2?sslmode=disable
TEST_DATABASE_URL=postgres://app_client:app_password@postgres:5432/inventory_2_test?sslmode=disable
# AUTH0_DOMAIN / AUTH0_CLIENT_ID / AUTH0_CLIENT_SECRET / AUTH0_CALLBACK_URL
# are intentionally omitted: with DEV_AUTH_ENABLED=true below, real Auth0
# login never runs, so config.Load() doesn't require them.
DEV_AUTH_ENABLED=true
PORT=8090
+115
View File
@@ -0,0 +1,115 @@
name: Claude Assistant for Gitea
on:
# Trigger on issue comments (works on both issues and pull requests in Gitea)
issue_comment:
types: [created]
# Trigger on issues being opened or assigned
issues:
types: [opened, assigned]
# Note: pull_request_review_comment has limited support in Gitea
# Use issue_comment instead which covers PR comments
jobs:
claude-assistant:
# Basic trigger detection - check for @claude in comments or issue body
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || github.event.action == 'assigned'))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
# Note: Gitea Actions may not require id-token: write for basic functionality
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: pgpass
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup golang environment
uses: actions/setup-go@v7
with:
go-version: 'stable'
check-latest: true
token: ${{ gitea.token }}
permissions:
contents: read
- name: Install psql and initialize the db
run: |
sudo apt-get update
sudo apt-get install -y postgresql-client
psql postgres -h postgres -c "CREATE DATABASE inventory_2"
psql postgres -h postgres -c "CREATE DATABASE inventory_2_test"
env:
PGUSER: postgres
PGPASSWORD: pgpass
- name: Install golang-migrate
run: |
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
- name: Migrate schema up
run: |
migrate -path database_migrations -database "$DATABASE_URL" up
migrate -path database_migrations -database "$TEST_DATABASE_URL" up
env:
DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2?sslmode=disable"
TEST_DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2_test?sslmode=disable"
- name: Run Claude Assistant
uses: markwylde/claude-code-gitea-action@gitea
with:
allowed_tools: |
penpot-self-hosted__execute_code
penpot-self-hosted__high_level_overview
penpot-self-hosted__penpot_api_info
penpot-self-hosted__export_shape
penpot_self_hosted__execute_code
penpot_self_hosted__high_level_overview
penpot_self_hosted__penpot_api_info
penpot_self_hosted__export_shape
Bash(go:*)
Bash(gofmt:*)
Bash(./tailwind.sh)
Bash(node -v)
Bash(npm install)
Bash(psql:*)
Bash(migrate:*)
gitea_api_url: "https://gitea.inventory-plus-plus.com/api/v1"
gitea_token: ${{ secrets.GITEA_TOKEN }} # Use standard workflow token
# anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Prefer claude_code_oauth_token over anthropic_api_key (cheaper!)
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
timeout_minutes: "60"
trigger_phrase: "@claude"
assignee_trigger: "@claude"
# Needed due to bug: https://github.com/anthropics/claude-code-action/issues/1416
model: "claude-sonnet-5"
# Optional: Customize for Gitea environment
custom_instructions: |
You are working in a Gitea environment. Be aware that:
- Some GitHub Actions features may behave differently
- Focus on core functionality and avoid advanced GitHub-specific features
- Use standard git operations when possible
env:
GITEA_SERVER_URL: "https://gitea.inventory-plus-plus.com"
NODE_VERSION: 24.x
PGUSER: postgres
PGPASSWORD: pgpass
DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2?sslmode=disable"
TEST_DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2_test?sslmode=disable"
+59 -1
View File
@@ -5,6 +5,7 @@ on: [push]
jobs: jobs:
Go tests: Go tests:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Description - name: Description
run: | run: |
@@ -25,4 +26,61 @@ jobs:
contents: read contents: read
- name: Run go tests - name: Run go tests
run: | run: |
go test ./... go test ./... -v
UI tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: pgpass
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install psql and initialize the db
run: |
sudo apt-get update
sudo apt-get install -y postgresql-client
psql postgres -h postgres -c "CREATE DATABASE inventory_2"
psql postgres -h postgres -c "CREATE DATABASE inventory_2_test"
env:
PGUSER: postgres
PGPASSWORD: pgpass
- name: Setup golang environment
uses: actions/setup-go@v7
with:
go-version: 'stable'
check-latest: true
token: ${{ gitea.token }}
permissions:
contents: read
- name: Install golang-migrate
run: |
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
- name: Migrate schema up
run: |
migrate -path database_migrations -database "$DATABASE_URL" up
migrate -path database_migrations -database "$TEST_DATABASE_URL" up
env:
DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2?sslmode=disable"
TEST_DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2_test?sslmode=disable"
- name: Run UI tests
run: |
go test ./server/ui -v
env:
TEST_DATABASE_URL: "postgres://postgres:pgpass@postgres:5432/inventory_2_test?sslmode=disable"
+1 -1
View File
@@ -5,4 +5,4 @@ node_modules
.env .env
.env.* .env.*
.env.example !.env.gitea-claude-bot-dev
+10
View File
@@ -0,0 +1,10 @@
{
"mcpServers": {
"penpot-self-hosted": {
"description": "Set here, even though set globally, so it is also available for the gitea-claude action bot",
"type": "http",
"url": "https://penpot.inventory-plus-plus.com/mcp/stream?userToken=${PENPOT_MCP_USER_TOKEN}"
}
}
}
+27
View File
@@ -117,6 +117,12 @@ against the live API doesn't exist yet.
ORM/repository layer. Looking to loosely follow CQRS: writes go through ORM/repository layer. Looking to loosely follow CQRS: writes go through
domain `Store` methods, reads are mostly separate query methods on the domain `Store` methods, reads are mostly separate query methods on the
same `Store`. same `Store`.
- `domains/raw_events` - the [event-sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)
store: `Save` appends an `Event`, `LoadEventsForStore` replays a
platform+store's series. Other domains (`reports`,
`domains/accounts/mocks.go`) read from views/queries that project over
this event series rather than mutating their own standalone state. See
also the [CQRS](https://martinfowler.com/bliki/CQRS.html) note above.
- `domains/accounts/mocks.go` - the mock-platform simulation layer: - `domains/accounts/mocks.go` - the mock-platform simulation layer:
`CreateMockShop`, `CreateMockListing`, `SaveNewMockSale` / `CreateMockShop`, `CreateMockListing`, `SaveNewMockSale` /
`SaveNewMockRefund` / `SaveNewMockInventoryReset`, etc. These are the `SaveNewMockRefund` / `SaveNewMockInventoryReset`, etc. These are the
@@ -243,6 +249,27 @@ against the live API doesn't exist yet.
you test multiple identities/accounts side by side. Never enable this you test multiple identities/accounts side by side. Never enable this
outside local development. outside local development.
## Gitea
- This project is hosted on a self-hosted **Gitea** instance at
`gitea.inventory-plus-plus.com` (repo:
`angel/inventory-plus-plus`), not GitHub - the `origin` remote points at
it over SSH. Issues, pull requests, wiki, and CI (Gitea Actions - see the
Tests badge at the top of `README.md`) all live there rather than on
GitHub, even though the tooling/workflow (Actions YAML, PR-based review)
looks GitHub-shaped.
- A Gitea MCP server is available in agent sessions (tools prefixed
`mcp__angel__...` - e.g. `issue_read`/`issue_write`,
`pull_request_read`/`pull_request_write`,
`list_pull_requests`/`list_issues`, `list_branches`, `wiki_read`/
`wiki_write`) for reading/managing issues, PRs, branches, releases, and
the wiki without shelling out to `git`/`gh`. There is no GitHub CLI
(`gh`) equivalent here - use these MCP tools or `git` directly instead.
- The Gitea MCP server has **no Projects API** - it cannot read or modify
Gitea Project boards. Don't attempt to automate Project-board changes
(e.g. moving an issue between columns) through it; that has to be done
manually in the Gitea UI.
## Commit conventions ## Commit conventions
- Commit messages: imperative mood subject line, no period, body explains - Commit messages: imperative mood subject line, no period, body explains
+27 -117
View File
@@ -1,4 +1,4 @@
## Inventory++ ![Tests Passing?](https://gitea.inventory-plus-plus.com/angel/inventory-plus-plus/actions/workflows/tests.yaml/badge.svg?branch=master) ## Inventory++ [![Tests Passing?](https://gitea.inventory-plus-plus.com/angel/inventory-plus-plus/actions/workflows/tests.yaml/badge.svg?branch=master)](https://gitea.inventory-plus-plus.com/angel/inventory-plus-plus/actions?workflow=&scoped_workflow_source_repo_id=0&actor=0&status=0&branch=master)
WIP! WIP!
@@ -7,7 +7,20 @@ The benefits provided by this application will be to automatically manage shared
reducing the amount of time needed to synchronize inventory between stores. reducing the amount of time needed to synchronize inventory between stores.
# Deploying ## Where things live
Documentation is split up rather than kept in one big doc - start here:
- **[`AGENTS.md`](./AGENTS.md)** - the engineering reference: setup, `make` commands, architecture, database/migration gotchas, testing conventions, auth, and the researched platform-integration priority ranking (which platform to build next, and why).
- **[`ROADMAP.md`](./ROADMAP.md)** - in-progress and planned work, as a checklist.
- **[`research/platforms.md`](./research/platforms.md)** - the underlying API-capability and market-size research the platform-priority ranking is based on.
- **[`domains/platforms/etsy/COMPLIANCE.md`](./domains/platforms/etsy/COMPLIANCE.md)** - Etsy API usage obligations to keep satisfied when touching the Etsy integration.
- **[`diagrams/`](./diagrams)** - architecture and schema diagrams, linked from the Diagrams section below.
This project is hosted on a self-hosted [Gitea](https://gitea.inventory-plus-plus.com/angel/inventory-plus-plus) instance (not GitHub) - issues, pull requests, CI (the Tests badge above), and the wiki all live there. See `AGENTS.md`'s Gitea section for details, including the Gitea MCP server tools available to AI agents.
## Deploying
The application runs locally, from this directory. The application runs locally, from this directory.
It is deployed simply by running either from the root of the project, It is deployed simply by running either from the root of the project,
@@ -19,12 +32,7 @@ or
go run . go run .
``` ```
See `AGENTS.md` for the full setup/`make` command reference (migrations, tests, dev auth, etc).
# Development
## Testing
There's a Makefile with a number of operations for running and testing the application
## Technology ## Technology
@@ -32,7 +40,7 @@ There's a Makefile with a number of operations for running and testing the appli
### Languages ### Languages
#### Server side #### Server side
Golang, Go Templates, Golang, Go Templates
#### Front end #### Front end
HTML, CSS, Javascript HTML, CSS, Javascript
@@ -44,7 +52,6 @@ Postgres, in docker
Using [migrate](https://github.com/golang-migrate/migrate) to manage build out the database schema, and to run Using [migrate](https://github.com/golang-migrate/migrate) to manage build out the database schema, and to run
migrations. migrations.
### Tooling ### Tooling
#### Server side #### Server side
@@ -57,102 +64,16 @@ migrations.
- htmx: for strong hypermedia support - htmx: for strong hypermedia support
- hyperscript: for minimal, inline scripting, with strong integration with htmx - hyperscript: for minimal, inline scripting, with strong integration with htmx
- tailwind: for styling the front end, using tried and testing styling paradigms, conventions, and templates. - tailwind: for styling the front end, using tried and testing styling paradigms, conventions, and templates.
>>>>>>> 16a3551 (updated readme)
# Roadmap
- [ ] Etsy (WIP)
- [ ] GET ETSY AUTH (WIP)
- [x] move auth state stuff to database (out of cache)
- [x] only generate a sign up link IF they click the link on the accounts page
- [ ] get api key approved
- [ ] automatically clean up access tokens and state when expired
- [ ] access tokens
- [ ] state
- [?] Get new access token using refresh token flow
- [ ] make a FK between the etsy_store_events table and etsy_users table (store_id columns don't match types)
- [ ] Auth0
- [ ] get off dev api key?
- [x] Get new access token using refresh token flow
- [ ] test
- [x] when token is expired, redirect them to the login page, then redirect them back to where they were heading to.
- [ ] Social connections login
- [x] automatically clean up access tokens and state when expired
- [x] access tokens
- [x] state
- [ ] Complete this design document?
- [ ] Complete defining this roadmap checklist
- [ ] Website displaying an audit of store events
- [ ] Start with just a list of events for a given store (use a static test store)
- [ ] ...
- [ ] Dark mode
- [ ] don't let a listing be in multiple sync groups
- [ ] Next stores on the list (at least hypothetically)
- Shopify
- WooCommerce
- BigCommerce
- Wix
- Squarespace
- Square Online
- Zoho
- Ecwid
- Big Cartel
Bigger marketplaces:
- Amazon
- Walmart Marketplace
- Ebay
## Nice to haves
- [ ] Drop in a good logger
- [ ] log all errors caught by the http server
## Constraints
- [ ] Etsy
- [ ] API Licensed Uses and Restrictions:
- [ ] Link directly back to the product information and/or image Content on Etsy, where the Application utilizes product information and/or images.
- [ ] Provide a prominently displayed email address on Your Application for third parties to contact You with any questions or issues. You shall respond to such inquiries in a timely manner.
- [ ] Use commercially reasonable efforts to provide a terms of service and privacy policy in a visible location on your Application.
- [ ] Display item Content or product information and/or images which is more than six (6) hours older than such information is on the Website, and other Etsy Content cannot be more than twenty-four (24) hours older than such Content on the Website.
- [ ] Use the API in a manner that exceeds reasonable request volume or constitutes excessive or abusive usage. Users are allocated by default, 10,000 calls per day.
- [ ] You shall not use or alter any text, logos, Etsy's Trademarks, Etsy's signature colors, Etsy's layout, or a confusingly similar layout to Etsy's layout in such a way which may suggest endorsement or affiliation by Etsy.
- [ ] Any use of the Etsy logo or Etsy's Trademarks must be used in its entirety and must not be altered or used in a misleading way.
- [ ] You shall not use a mark which is confusingly similar to Etsy's Trademarks.
- [ ] Any use of the Etsy logo or Etsy's Trademarks in Your Application shall be less prominent than the logo or mark that primarily describes the Application and Your use of the Etsy logo shall not imply any endorsement or affiliation by Etsy.
- [ ] You may publicize, issue press or blog releases of Your Application only if You state that it was created using the Etsy API and that You in no way imply that Your Application is endorsed or certified by Etsy.
- [ ] You must place or display the following notice prominently on Your Application:
"The term 'Etsy' is a trademark of Etsy, Inc. This application uses the Etsy API but is not endorsed or certified by Etsy, Inc."
- [ ] Immediately report any security deficiencies You discover to Etsy by emailing developer@etsy.com.
Looking to follow the [CQRS](https://martinfowler.com/bliki/CQRS.html) pattern.
The database will follow the [event sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) database pattern.
All events (or commands) will be stored in a respective event series, and all database reads will be from views that are projections, reductions, aggregations of those event series.
## Application/directory structure
- /internal
- /site: website
- /webhooks: webhooks for platform events
- /domains: packages for each domain
- /store_events: storing and events
- /platforms: ecommerce platform domains
- /tiktok: interface with tiktok
- ... etc
- ... etc
![Application structure](./diagrams/application_structure.svg)
## Website hierarchy
- /site
# Architectural and Software Diagrams # Architectural and Software Diagrams
## Application structure
See `AGENTS.md`'s Architecture section for the current, maintained breakdown of packages/directories.
![Application structure](./diagrams/application_structure.svg)
## Database schemas ## Database schemas
**public** **public**
@@ -164,39 +85,28 @@ All events (or commands) will be stored in a respective event series, and all da
## Events ## Events
`domains/raw_events` is the event-sourcing store behind this - see `AGENTS.md`'s Architecture section.
### Event Sourcing Architecture ### Event Sourcing Architecture
![Event sourcing architecture](./diagrams/event_sourcing.svg) ![Event sourcing architecture](./diagrams/event_sourcing.svg)
### Event Structure ### Event Structure
![Event](./diagrams/event.svg) ![Event](./diagrams/event.svg)
### Store Event Database Tables ### Store Event Database Tables
![Event database tables](./diagrams/event_tables.svg) ![Event database tables](./diagrams/event_tables.svg)
## Platform: Etsy ## Platform: Etsy
### Signing up ### Signing up
**TODO: need an account page that can create accounts ahead of time - force users to create an account first!**
![Event database tables](./diagrams/etsy/obtaining_access_token.svg) ![Event database tables](./diagrams/etsy/obtaining_access_token.svg)
***TODO: create a page that will take billing information and include it in this process***
### Getting a new refresh token
***TODO***
### Models ### Models
![Models](./diagrams/etsy/models.svg) ![Models](./diagrams/etsy/models.svg)
See `ROADMAP.md` for what's still unbuilt in the Etsy flow (an account-creation page ahead of OAuth, billing info collection, refresh-token handling), and `domains/platforms/etsy/COMPLIANCE.md` for API usage obligations.
## All Diagrams ## All Diagrams
+38
View File
@@ -0,0 +1,38 @@
# Roadmap
- [ ] Etsy (WIP)
- [ ] GET ETSY AUTH (WIP)
- [x] move auth state stuff to database (out of cache)
- [x] only generate a sign up link IF they click the link on the accounts page
- [ ] get api key approved
- [ ] automatically clean up access tokens and state when expired
- [ ] access tokens
- [ ] state
- [ ] account-creation page ahead of OAuth (force users to create an account before connecting a store)
- [ ] collect billing information as part of that account-creation flow
- [?] Get new access token using refresh token flow
- [ ] make a FK between the etsy_store_events table and etsy_users table (store_id columns don't match types)
- [ ] build polling-based order/inventory sync against the live Etsy API - only the OAuth connection flow is implemented so far (see `research/platforms.md`'s Etsy notes: API v3 has no webhook/push system, so this has to be a poll loop)
- see `domains/platforms/etsy/COMPLIANCE.md` for Etsy API usage obligations to keep satisfied along the way
- [ ] Auth0
- [ ] get off dev api key?
- [x] Get new access token using refresh token flow
- [ ] test
- [x] when token is expired, redirect them to the login page, then redirect them back to where they were heading to.
- [ ] Social connections login
- [x] automatically clean up access tokens and state when expired
- [x] access tokens
- [x] state
- [ ] Complete this design document?
- [ ] Complete defining this roadmap checklist
- [ ] Website displaying an audit of store events
- [ ] Start with just a list of events for a given store (use a static test store)
- [ ] ...
- [ ] Dark mode
- [ ] don't let a listing be in multiple sync groups
- [ ] Next platform to build out beyond Etsy - see `AGENTS.md`'s "Platform integration priority" section for the researched, weighted ranking (currently: Shopify, then Amazon, then Tiktok Shop) instead of picking from the raw platform list
## Nice to haves
- [ ] Drop in a good logger
- [ ] log all errors caught by the http server
+19
View File
@@ -0,0 +1,19 @@
# Etsy API compliance checklist
Obligations from Etsy's API Licensed Uses and Restrictions. Anything
touching `domains/platforms/etsy` or the Etsy-facing UI should keep these
satisfied before shipping.
- [ ] Link directly back to the product information and/or image Content on Etsy, where the Application utilizes product information and/or images.
- [ ] Provide a prominently displayed email address on Your Application for third parties to contact You with any questions or issues. You shall respond to such inquiries in a timely manner.
- [ ] Use commercially reasonable efforts to provide a terms of service and privacy policy in a visible location on your Application.
- [ ] Display item Content or product information and/or images which is more than six (6) hours older than such information is on the Website, and other Etsy Content cannot be more than twenty-four (24) hours older than such Content on the Website.
- [ ] Use the API in a manner that exceeds reasonable request volume or constitutes excessive or abusive usage. Users are allocated by default, 10,000 calls per day.
- [ ] You shall not use or alter any text, logos, Etsy's Trademarks, Etsy's signature colors, Etsy's layout, or a confusingly similar layout to Etsy's layout in such a way which may suggest endorsement or affiliation by Etsy.
- [ ] Any use of the Etsy logo or Etsy's Trademarks must be used in its entirety and must not be altered or used in a misleading way.
- [ ] You shall not use a mark which is confusingly similar to Etsy's Trademarks.
- [ ] Any use of the Etsy logo or Etsy's Trademarks in Your Application shall be less prominent than the logo or mark that primarily describes the Application and Your use of the Etsy logo shall not imply any endorsement or affiliation by Etsy.
- [ ] You may publicize, issue press or blog releases of Your Application only if You state that it was created using the Etsy API and that You in no way imply that Your Application is endorsed or certified by Etsy.
- [ ] You must place or display the following notice prominently on Your Application:
"The term 'Etsy' is a trademark of Etsy, Inc. This application uses the Etsy API but is not endorsed or certified by Etsy, Inc."
- [ ] Immediately report any security deficiencies You discover to Etsy by emailing developer@etsy.com.
@@ -1,4 +1,4 @@
# Store API Research # Platform Research
https://docs.google.com/spreadsheets/d/1xWfXn-wbiHTBeqyhnq0b46_sgLGyiYc_5N5mXKYD1R8/edit?gid=0#gid=0 https://docs.google.com/spreadsheets/d/1xWfXn-wbiHTBeqyhnq0b46_sgLGyiYc_5N5mXKYD1R8/edit?gid=0#gid=0
+146
View File
@@ -0,0 +1,146 @@
package ui_test
import (
"flag"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
"ruben/inventory2/domains/accounts"
"ruben/inventory2/domains/authentication"
etsy_platform "ruben/inventory2/domains/platforms/etsy"
"ruben/inventory2/domains/raw_events"
"ruben/inventory2/domains/reports"
"ruben/inventory2/internal/testdb"
"ruben/inventory2/server/auth"
"ruben/inventory2/server/response"
"ruben/inventory2/server/ui"
)
// update regenerates the golden files under testdata/golden from the
// current rendered output, instead of comparing against them. Review the
// diff like any other golden file before committing it, e.g.:
//
// TEST_DATABASE_URL=... go test ./server/ui/... -run TestServeTemplate_Golden -update
var update = flag.Bool("update", false, "update golden files in testdata/golden instead of comparing against them")
// TestServeTemplate_Golden is a raw-HTML snapshot/regression test: it
// exercises server/ui's real routing + templating path end-to-end (real
// templates, real Store types backed by a real Postgres connection, the
// same response.HandleResponses/HandleErrors middleware production uses -
// nothing about the render path is mocked) and diffs the rendered HTML
// against a committed golden file, so an unintentional template/markup
// change shows up as a reviewable diff instead of shipping silently.
//
// This is the "raw HTML snapshot" layer described in issue #33's UI-testing
// proposal. A browser-screenshot layer (e.g. chromedp, for catching CSS/
// layout regressions raw HTML can't) and Penpot-design-fidelity comparisons
// are separate, not-yet-implemented layers of that same proposal.
//
// Only covers logged-out pages for now (no seeded oauth session/cookie);
// extend the tests table below using testdb.SeedOAuthSession plus an
// "access_token" cookie on the request to cover authenticated pages.
//
// Like other integration tests in this repo, it's skipped (not failed) if
// TEST_DATABASE_URL is unset. If a golden file hasn't been generated yet,
// the individual case is also skipped (not failed) rather than breaking
// the suite - run with -update once, review the generated file, and commit
// it to turn that case into a real regression check.
func TestServeTemplate_Golden(t *testing.T) {
// ui.Routes' templater resolves "templates/pages" (and "templates/
// components") relative to the process's working directory on every
// request, not relative to this package - it has no notion of its own
// location. In production the binary is run from the repo root, where
// ./templates lives; under `go test`, the working directory is this
// package's own directory instead, so without this the templater can't
// find any templates and every request 500s.
wd, err := os.Getwd()
require.NoError(t, err, "failed to look up the working directory")
goldenDir := filepath.Join(wd, "testdata", "golden")
repoRoot := filepath.Join(wd, "..", "..")
require.DirExists(t, filepath.Join(repoRoot, "templates"))
require.NoError(t, os.Chdir(repoRoot), "failed to change working directory to the repo root")
t.Cleanup(func() {
require.NoError(t, os.Chdir(wd), "failed to change working directory back from repo root")
})
pool := testdb.Pool(t)
logger := testdb.Logger()
accts := accounts.NewStore(logger, pool)
rawEvents := raw_events.NewStore(logger, pool)
reps := reports.NewStore(logger, pool, accts)
etsy := etsy_platform.NewPlatform(
logger,
func(acctID int64) string { return "" },
"", "",
pool,
)
authr := authentication.NewDev(pool, logger)
authM := auth.NewService(logger, authr, accts)
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(
authM.Identify,
response.HandleResponses,
response.HandleErrors,
)
ui.Routes(
logger,
router.Group("/ui"),
"/ui",
rawEvents,
accts,
reps,
etsy,
authM.Authenticate(),
true,
)
tests := []struct {
name string
path string
wantStatus int
}{
{name: "home-logged-out", path: "/ui", wantStatus: http.StatusOK},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
require.Equal(t, tt.wantStatus, rec.Code, "unexpected status for %s", tt.path)
got := rec.Body.Bytes()
golden := filepath.Join(goldenDir, tt.name+".html")
if *update {
require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0o755))
require.NoError(t, os.WriteFile(golden, got, 0o644))
return
}
want, err := os.ReadFile(golden)
if os.IsNotExist(err) {
t.Skipf(
"golden file %s does not exist yet; generate it once via `go test ./server/ui/... -run TestServeTemplate_Golden -update` (requires TEST_DATABASE_URL), then review and commit it",
golden,
)
}
require.NoError(t, err, "reading golden file %s", golden)
require.Equal(t, string(want), string(got), "rendered output for %s no longer matches %s; if this change is intentional, regenerate with -update", tt.path, golden)
})
}
}
+146
View File
@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html class="min-h-full flex flex-col items-stretch">
<head>
<title>
Inventory++
</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<link rel="stylesheet" href="/styles/index.css">
<script src="/scripts/htmx.min.js.gz"></script>
<script src="/scripts/_hyperscript.min.js.gz"></script>
<script src="/scripts/htmx-ext-sse.js"></script>
<script src="/scripts/htmx-ext-path-params.js"></script>
<script src="/scripts/idiomorph-ext.js"></script>
<script src="/scripts/hx-drag.js"></script>
<script src="/scripts/hx-resize.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<link rel="manifest" href="/favicon/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Alexandria:wght@100..900&family=Geist:wght@100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
</head>
<body
class="basis-full grow bg-background flex flex-col items-stretch"
hx-ext="morph,path-params,sse,drag"
hx-boost="true"
>
<header>
<nav
class="
w-full
text-xl
overflow-x-hidden
overflow-y-hidden
flex
justify-center
"
id="header-navbar"
>
<ul
hx-swap="morph:innerHTML"
class="
max-w-full
overflow-x-auto
overflow-y-hidden
text-nowrap
flex
basis-[fit-content]
"}}
>
<li class="pt-[1em] pb-[1em]">
<a
href="/api/auth/dev-login"
hx-boost="false"
class="p-[1em] font-display "
>
Log In
</a>
</li>
</ul>
</nav>
</header>
<main class="basis-full grow max-w-full">
<section class="flex justify-center max-w-full mt-[3em] mb-[3em]">
<h1 class="
min-[400px]:px-[calc(10vw-0.5em)]
min-[400px]:py-[calc(8vw-0.5em)]
border-[1px] border-sidebar-border rounded-lg
bg-card
text-nowrap
text-center
">
Inventory++
</h1>
</section>
<section class="flex flex-col items-center max-w-full mt-[1em] mb-[1em] gap-y-[2em]">
<p class="text-lg text-center max-w-[50%]">
Synchronize inventory automatically!
</p>
<div class="border-button border-sidebar-border bg-card text-lg text-center max-w-[50%]">
<a href="/api/auth/dev-login" hx-boost="false" class="block p-[1em] font-bold">
New Login
</a>
</div>
<p class="text-lg text-center max-w-[50%]">
...and start syncing your shops today!
</p>
</section>
</main>
<footer class="grid grid-cols-[6rem_10fr_6rem] justify-center items-center">
<address class="grow-1 col-2 flex justify-center">
<a href="mailto:contact-us@inventory-plus-plus.com" class="p-[1em] font-display text-center">Contact Us</a>
<a href="mailto:support@inventory-plus-plus.com" class="p-[1em] font-display text-center">Support</a>
</address>
</footer>
</body>
</html>