8fba3034d4 chore: sync main to develop (#513)
* refactor: extract shared debounce util

* fix: project search in admin highlights

* fix: add save feedback, cover images and 84px cells to highlights

* fix: show selection and empty state in basic dropdown

* docs: update SKILL-template to current codebase (#507)

* docs: update SKILL-template to current codebase

* fix: remove unnecesary check command alias

* fix: update SDK regen command from `openapi` to `sdk`

* fix: indentation on command table

---------

Co-authored-by: Daniel Subiabre <daniel.subiabre@platoniq.net>

* feat: Improve footer config (#508)

* feat: hide footer funding section if empty

* feat: update footer social link type

* fix: move footer data out of ad-hoc lone config folder

* fix: code style issues by eslint and prettier

* chore: import zod from zod package in actions (#506)

* feat: show user avatar in header (#505)

* Feat/implement reward claims (#471)

* create RewardClaims after payment

* replace ClearCart with RewardClaims

* mount RewardClaim on payment verification

* format

* match charges by fields and order to avoid duplicate claims

* guard RewardClaims whit allSucceeded, soft-delete carts via finalised flag

* format

* feat: add POST /api/checkout/rewardClaims endpoint

* refactor: make RewardClaims.svelte a  thin client that submit the cart

* fix: pass checkoutId into the verified-checkout reward claims flow

* fix: format

---------

Co-authored-by: Angel Di Pinto <angel.dipinto@platoniq.net>

* Feat/refactor activity section (#480)

* refactor ActivitySection (DonationsCard-MatchfundingCard-ProjectsCart)

* format

* refactor: apply DonationsCard (ld+json totals, batch slug[], money_total)

* refactor: ProjectsCard (ld-json totals, async, remove lang)

* refactor: MatchfundingCard (drop lang prop)

---------

Co-authored-by: Angel Di Pinto <angel.dipinto@platoniq.net>

* feat: implement promoter public profile form in /me/manage (#495)

* feat: implement promoter public profile form in /me/manage

* feat: add view profile button to /me/manage

* feat: persist social links and fix profile PATCH

* fix: load current user in /me/manage so social links show up

* fix: use design social icons on user profile

* feat: accept username or link in profile social fields

* fix: always send links on profile update so they are not cleared

* Fix: admin highlights save (#511)

* fix: cast highlight slot project ids to integer

* fix: drop highlight slots outside selected layout on save

* feat: add link, lists and quote to RichTextEditor with opt-out toolbar props (#509)

* feat: add link, lists and quote to RichTextEditor with opt-out toolbar props

* feat: add heading select to RichTextEditor and close link popover on outside click

* refactor: simplify search page state (#510)

* refactor: simplify search page state

* fix: update naming convention examples

---------

Co-authored-by: Daniel Subiabre <daniel.subiabre@platoniq.net>

* Feat/user preferences manage screen (#496)

* Render ManagePreferencesSection in /me/manage

* add ManagePreferencesSection component

* add manage preferences i18n key to es.json

* add manage preferences i18n keys

* add ManagePreferencesSection with form, language cookies and PATCH

* add dedicated /me/menege/preferences route

* link preferences screen from /me/manage sidebar

* fix: remove pointless user PATCH, adopt twMerge

---------

Co-authored-by: Angel Di Pinto <angel.dipinto@platoniq.net>

---------

Co-authored-by: Pep Villalonga <pep.villalonga@platoniq.net>
Co-authored-by: angeldipinto <72363089+angeldipinto@users.noreply.github.com>
Co-authored-by: Angel Di Pinto <angel.dipinto@platoniq.net>
2026-09-29 16:35:50 +02:00
I hate javascript. It is a cancer, every other thing shipped in any given js package is there simply to break stuff that was working fine and introduce a crappy, unstable new layer that wasn't necessary but is there because something had to be shipped, a new version needed to be introduced, changes had to be made. It is impossible to actually ship anything in javascript without having fought back-and-forth-and-back-and-forth-again against not just the language itself and its "quirks" (really just design flaws), but then the environment, and then the tooling available in that environment. It is a cancer born out of a scam that should have stayed as the browser-toy that it actually is but fucking Ryan, bless his soul he did not know what he was doing, had to package V8 into a server-available ""runtime"" (there are not enough captions for me to be precise on how much every js runtime is nothing but a joke), and the scam grows larger every day because no fucking developer out there has the gut to admit javascript simply sucks and the web is just so large that we are bound to js like sinners are to the flames of hell. I hate javascript and being afraid of every fucking update because half the packages do not know what semver is and even if they do they still ship crap that not only does not work it also breaks your perfectly working code because the concept of backwards compatibilty in this ecosystem is to only keep the crap that is the most shittiets and inefficient and useless supported while breaking every fucking thing else. I hate javascript but I guess I was Leopold II in my previous life and I have to suffer now
2026-08-11 16:02:05 +02:00
2026-08-19 12:42:17 +02:00
2025-03-17 13:16:57 -05:00
2026-05-06 13:40:28 +02:00
2026-05-06 14:25:42 +02:00

Goteo v4 Web

Frontend for the Goteo crowdfunding platform — built on Astro 7 + Svelte 5 + TailwindCSS 4, deployed on Cloudflare Workers.

Note

: This application is a client of the Goteo v4 API. You need a running v4 API instance and an OAuth client registered in it before this web app will work.

Installation

This application requires Node.js (v22.12 or later) and the pnpm package manager. It also needs a reachable Goteo v4 API instance to connect to.

1. Clone or download this repository.

git clone https://github.com/goteo/org.goteo.www
cd org.goteo.www

2. Install the dependencies.

pnpm install

3. Configure the environment.

Copy the example environment file and fill in the values for your setup.

cp .env.example .env

The minimum required values to boot the app and authenticate users are:

Variable Description
PUBLIC_API_URL Base URL of your Goteo v4 API instance (e.g. http://localhost:8090)
PUBLIC_API_VERSION API version — keep as v4
OAUTH2_CLIENT_ID / OAUTH2_CLIENT_SECRET Credentials of an OAuth client registered in the API. Must allow the grants authorization_code, refresh_token, password

NOTE: The OAuth client must exist in the v4 API instance. See the API's Authentication docs.

The remaining variables (tipping, Facebook share, object storage, HTTP Basic auth) are optional and the app falls back to sane defaults when they are empty. See .env.example for the full annotated list and the Environment variables section below.

Usage

Start the development server.

pnpm dev

The app should be live at http://localhost:4321.

The dev server runs on the Cloudflare Workers runtime (workerd), the same one used in production, with bindings read from wrangler.toml. No separate command is needed to reproduce the production runtime locally.

Other commands

Command Action
pnpm install Install dependencies
pnpm dev Dev server at localhost:4321 (Workers runtime)
pnpm build Build for production
pnpm preview Preview production build locally
pnpm format ESLint fix + Prettier write
pnpm check Prettier format check
pnpm sdk Regenerate OpenAPI SDK from live API spec
pnpm storybook Storybook dev server at localhost:6006
pnpm build-storybook Build Storybook static output

Regenerating the API SDK

The TypeScript API client under src/openapi/client/ is generated from the live API's OpenAPI spec. Regenerate it whenever the API spec changes (requires the API running and PUBLIC_API_URL / PUBLIC_API_VERSION set):

pnpm sdk

This fetches the spec from $PUBLIC_API_URL/$PUBLIC_API_VERSION/docs.json. Commit the generated files together with any config change.

Testing

Component and unit tests run on Vitest through the Storybook test addon — launch Storybook to develop and visually check components:

pnpm storybook

Before opening a PR

pnpm format         # Fix formatting (ESLint + Prettier)

Debugging

Common issues you might run into while developing:

1. Blank pages or auth failures right after setup.

This almost always means the app cannot reach the API or the OAuth client is misconfigured. Check that:

  • PUBLIC_API_URL points to a reachable v4 API instance and includes the protocol (http:// / https://).
  • An OAuth client exists in that API instance and its OAUTH2_CLIENT_ID / OAUTH2_CLIENT_SECRET match your .env.
  • The OAuth client allows the authorization_code, refresh_token and password grants.

2. Type errors against the API after the API changed.

The generated SDK is out of date. Regenerate it (API must be running):

pnpm sdk

3. Errors about missing Node.js modules (fs, path, os, child_process, …).

The dev server and production both run on the Cloudflare Workers runtime, which has no Node.js APIs. Runtime code (pages, components, services) must use Web APIs (fetch, crypto, URL, Cache) instead. Move any Node-only logic to build-time code (config files, scripts, plugins), where it is still allowed.


Stack

Layer Technology
Framework Astro 7
UI Svelte 5 (runes), TailwindCSS 4
Language TypeScript 5
Runtime Cloudflare Workers (also supports Node)
API client @hey-api/client-fetch (generated OpenAPI)
Testing Vitest (unit), Cypress (E2E), Storybook

Routes

All user-facing pages live under src/pages/[...locale]/ (locale prefix: es, en, ca).

Route Page
/ Home
/search Project search
/project/[idOrSlug] Project detail
/project/[idOrSlug]/edit Project editor (auth)
/create/project Create project (auth)
/checkout Checkout cart
/checkout/payment Payment
/checkout/post-payment Post-payment confirmation
/checkout/verify Payment verification
/checkout/wallet Wallet confirmation
/login Login
/login/callback OAuth2 callback
/logout Logout
/register Register
/me My profile (auth)
/user/[idOrHandle] Public user profile
/about About
/static/[fileName] CMS static content
/admin/charges Admin contributions (auth)
/403, /404, /500 Error pages

API endpoints under src/pages/api/:

Endpoint Purpose
/api/relay/[...path] Authenticated API proxy
/api/upload/preupload S3 pre-signed URL generation
/api/upload/postupload S3 post-upload validation

Project Structure

src/
├── actions/          # Astro server actions (form mutations)
├── auth/             # JWT sessions, OAuth2 tokens, refresh logic
├── components/       # Svelte components (PascalCase filenames)
│   ├── library/      # Reusable UI primitives + Storybook stories
│   ├── Admin/        # Admin-specific components
│   ├── icons/        # Icon SVG components
│   └── [Feature]/    # Feature-scoped (project/, profile/, Checkout/, etc.)
├── firewall/         # Route access control rules
├── i18n/             # Translations (es/en/ca) + t() store
├── layouts/          # Astro layout components
├── middleware/       # Astro request middleware (auth, locale, firewall)
├── openapi/          # OpenAPI SDK (generated) + config
├── pages/            # Astro routes
│   ├── [...locale]/  # All user-facing pages under locale prefix
│   └── api/          # Server-side API endpoints
├── services/         # Business logic & API call wrappers
├── stores/           # Svelte stores (client-side state)
│   └── drafts/       # Project draft persistence (Dexie/IndexedDB)
├── stories/          # Full-page Storybook stories
├── styles/           # Global CSS + Tailwind theme tokens
├── svgs/             # SVG Svelte components
├── types/            # Shared TypeScript interfaces
└── utils/            # Pure helper functions
    └── drafts/       # Draft DB repository helpers

Environment variables

Full list lives in .env.example. Grouped by purpose:

Variable Required Purpose
PUBLIC_API_URL ✅ Base URL of the v4 API instance
PUBLIC_API_VERSION ✅ API version (v4)
OAUTH2_CLIENT_ID / OAUTH2_CLIENT_SECRET ✅ OAuth client credentials registered in the API
PUBLIC_DEFAULT_CURRENCY Fallback currency (e.g. EUR)
PUBLIC_DEFAULT_LANGUAGE Fallback locale (es, en, ca)
PUBLIC_DEFAULT_COUNTRY Fallback ISO 3166-1 alpha-2 country (default ES), e.g. preselected tax id country
PUBLIC_DEFAULT_MAXSIZE Max upload size in bytes (default 8388608 = 8MB)
PUBLIC_TIPPING_TIPJAR_ID Tipjar ID — leave empty to disable tipping
PUBLIC_TIPPING_DEFAULT_AMOUNT Pre-filled tip amount
PUBLIC_TIPPING_DEFAULT_CHECKED "true" to pre-check the tip option
PUBLIC_FACEBOOK_APP_ID Facebook share dialog app ID
CLOUDFLARE_INCLUDE_PROCESS_ENV "true" — required for Cloudflare Workers to read env vars
BASIC_AUTH "true" to add an HTTP Basic auth layer (not a replacement for OAuth)
BASIC_AUTH_USERNAME / BASIC_AUTH_PASSWORD Credentials for the Basic auth layer
OBJECT_STORAGE_* S3-compatible storage (access key, secret, region, endpoint, bucket) for media upload

CI / Deployment

Workflow Trigger Action
deploy.yml Push to main Deploy to Cloudflare (production)
deploy.yml Push to develop Deploy to Cloudflare (staging)
prettier-check.yml PR / push Prettier format check
S
Description
web interface to the DTF crowd fund platform forked from Goteo https://github.com/GoteoFoundation/goteo/pull/669
Readme
59 MiB
Languages
Svelte 63.1%
TypeScript 30%
Astro 5.9%
JavaScript 0.8%
CSS 0.2%