Otter is a self-hosted bookmark manager made with Next.js and Supabase with Mastodon integration.
Otter is a self-hosted bookmark manager made with Next.js and Supabase with Mastodon integration.
Feed (dark mode) | Feed (light mode) |
---|---|
New bookmark | Search |
Feed (showing tags sidebar) | Toots feed |
npm i -g pnpm
npm i -g vercel
npm i -g supabase
pnpm supabase:link
pnpm supabase:setup
pnpm install
ALLOW_SIGNUP
in ./src/constants.ts
to true
pnpm dev
http://localhost:5678
and create an accountSet up the following env vars using either the Vercel CLI or through the Vercel project settings. Once they are added run vc env pull
to pull them down to your local dev environment.
# Find these in your Supabase project settings > API
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key # only needed for testing APIs using the `*.rest` files
PERSONAL_MASTODON_ACCESS_TOKEN=your-personal-app-mastodon-access-token
BOT_MASTODON_ACCESS_TOKEN=your-bot-app-mastodon-access-token
OTTER_API_TOKEN=your-otter-api-token
Interactive API docs can be found in the various *.rest
files in the /app/api
directory.
POST /api/new
- create new item in OtterGET /api/new?url=https://example.com
- quick create new item in Otter. Pass in a url
query param and it will create a new item with that URL and includes its metadata tooGET /api/bookmarks
- returns all bookmarksGET /api/search?searchTerm=zander
- search bookmarkPOST /api/toot
- A PostgreSQL trigger function calls this endpoint anytime a bookmark is created or edited which then creates a new toot on two of my Mastodon accounts (@[email protected] & @[email protected]). It only sends a toot if the bookmark has the public
column set to true
.Otter has the ability to auto-toot to 2 Mastodon accounts when a new bookmark is created or edited. This is done via a PostgreSQL trigger function that calls the /api/toot
endpoint.
The trigger function below uses an environment variable in the Authorization
header to ensure only the owner of the Otter instance can call the endpoint.
create trigger "toot-otter-items"
after insert
or
update on bookmarks for each row
execute function supabase_functions.http_request (
'https://{your-otter-instance}/api/toot',
'POST',
-- replace {OTTER_API_TOKEN} with your own token
'{"Content-type":"application/json","Authorization":"{OTTER_API_TOKEN}"}',
'{}',
'1000'
);
TODO:
/api/toot
endpointALTER TYPE type ADD VALUE '???';
pnpm run supabase:types
to update the TypeScript typescase
to the TypeToIcon
componentTypeRadio
component to the BookmarkForm
componentI use various other tools to make Otter even better:
Made by Zander • zander.wtf • GitHub • Mastodon