Skip to content

Latest commit

 

History

History
607 lines (381 loc) · 20 KB

README.md

File metadata and controls

607 lines (381 loc) · 20 KB

TeddyCloud next Generation Frontend

Welcome to the next generation of the TeddyCloud Administration Frontend!

If you are using this repository for the first time, please refer to the General React Information section first.

TeddyCloud configuration

You'll need to allow CORS for your teddyCloud instance used for development. The easiest variant is to set CORS Allow-Originⓘ to *.

Optional: same-origin dev proxy

With web login enabled, images and audio are not loaded in the setup above: the browser requests them without the login token. Set VITE_APP_TEDDYCLOUD_DEV_PROXY=true in your .env.local to let the dev server proxy all requests except /web to the teddyCloud instance set in VITE_APP_TEDDYCLOUD_API_URL. The browser then only talks to the dev server, and no CORS configuration is needed.

The dev server connects to VITE_APP_TEDDYCLOUD_API_URL as configured. If your teddyCloud is behind a reverse proxy that routes by hostname (SNI), use the hostname there, not the IP.

If access to your teddyCloud is secured by a client certificate, put the PKCS#12 file (.p12, with certificate and private key) into the certs folder and set TEDDYCLOUD_DEV_CLIENT_P12=<file name> and TEDDYCLOUD_DEV_CLIENT_P12_PASSWORD=<password> in your .env.local.

NPM Environment file '.env'

Please place an environment file '.env.local' in the teddycloud_web directory.

VITE_APP_TEDDYCLOUD_API_URL=http://<teddycloud-ip>
VITE_APP_TEDDYCLOUD_PORT_HTTPS=3443
VITE_APP_TEDDYCLOUD_PORT_HTTP=3000
SSL_CRT_FILE=./certs/localhost.pem
SSL_KEY_FILE=./certs/localhost-key.pem

VITE_APP_TEDDYCLOUD_PORT_HTTPS and VITE_APP_TEDDYCLOUD_PORT_HTTP should match the ones entered in the package.json. If you don't change them, these are the ones from the example above.

Parallel http/https setup

needed for ESP32 Box Flashing section

You need to provide certificates for https. Use for example mkcert. The generated certificates must be stored in the certs folder of the project (the folder is git-ignored). Certificates in the project root are still found.

mkcert -install
mkdir -p certs
mkcert -cert-file certs/localhost.pem -key-file certs/localhost-key.pem localhost

You must also allow unsecure content in chrome (HowTo) to be able to connect to teddycloud server in https context.

If you don't need the ESP32 Box flashing section working, you can adapt the package.json and change the following:

"scripts": {
        "start-http": "cross-env PORT=3000 vite",
        "start-https": "cross-env HTTPS=true PORT=3443 vite",
        "start": "concurrently \"npm run start-http\" \"npm run start-https\"",
        "build": "tsc && vite build",
        "preview": "vite preview",
        "format": "prettier . --write",
        "format:check": "prettier . --check",
        "check:translations": "node scripts/check-translations.mjs"
    },

to

"scripts": {
        "start": "npm run start",
        "build": "tsc && vite build",
        "preview": "vite preview",
        "format": "prettier . --write",
        "format:check": "prettier . --check",
        "check:translations": "node scripts/check-translations.mjs"
     },

Start NPM / teddyCloud

Use ./start_dev.sh to start the NPM server in development mode. Be patient, it may take a while. Be sure your teddyCloud instance is also running.

If you just need the http variant, simply call dotenv -e .env.development.local npm start-http

Tests

Unit tests use Vitest and live next to the code they cover (*.test.ts). Run them with npm test.

Project Structure & Architecture (Frontend)

The TeddyCloud frontend follows a topic-based architecture. Pages and components are organized by feature domains (e.g. tonies, tonieboxes,settings, community). Each feature contains its own components, hooks, and modals. This keeps all relevant code physically close, reduces duplication, and maintains clarity.

This architecture represents the intended structure of the frontend; however, due to legacy code and ongoing migration efforts, not all parts of the project fully adhere to it yet.

If you encounter areas that are not yet aligned with this architecture or appear messy, please feel free to clean them up as part of ongoing improvements.


Project Structure

Pages

Location: src/pages/<topic>/...

Purpose:

  • Represent application routes.
  • Provide layout and structure (StyledLayout, StyledSider, breadcrumbs).
  • Compose topic-specific components.
  • Contain minimal logic.

Guidelines:

  • Pages should stay thin.
  • Do not embed business logic in a Page.
  • Let the page assemble layout + feature components + navigation.

Components

Location: src/components/<topic>/...

Feature areas (e.g. tonies, settings) contain sub-folders for specific features:

src/components/tonies/encoder/...
src/components/settings/notificationlist/...
src/components/tonieboxes/tonieboxeslist/...

Purpose:

  • Implement UI behavior for a domain-specific feature.
  • Organize related components, hooks, and modals within the same feature folder.
  • Keep JSX + view logic here, push non-view logic into hooks.

When to split into a hook?

  • When a component grows beyond UI concerns.
  • When API logic, state machines, or repeated side effects appear.

Hooks

Hooks follow the same topic-based structure.

Feature-specific hooks

Location examples:

src/components/tonies/encoder/hooks/useEncoder.ts
src/components/settings/notificationlist/hooks/useNotificationsList.ts

Rules:

  • Hooks that belong to one feature stay inside that feature’s folder.
  • They encapsulate its behavior: API calls, state transitions, validation, etc.
  • They are not shared across unrelated domains.

This avoids a global “hooks graveyard”.

Shared hooks (rare)

Only domain-agnostic, multi-topic hooks belong here:

src/hooks/useDebounce.ts
src/hooks/useMediaQuery.ts

If it is not 100% generic → keep it in the feature folder.


Modals

Modals follow the feature they belong to.

Example:

src/components/tonies/filebrowser/modals/CreateDirectoryModal.tsx

Guidelines:

  • State (open/close, selected path, etc.) is controlled in the parent component/hook.
  • Modal components receive all required data via props.
  • Modals should not contain business logic (fetch, validation) unless tightly coupled to the feature.

Utilities

Generic helpers live in:

src/utils/

Allowed:

  • Validators
  • Formatter functions
  • Encoding helpers
  • URL builders
  • Type utilities

Not allowed:

  • React logic
  • State management
  • Component-specific utilities

Coding Guidelines

There are no complete guidelines defined currently, only some fragments which you shall read and follow. The following rules complement the project structure described above.

Keep changelog up to date

All changes must be added to the central CHANGELOG.md file. Whenever you implement a change, add a new entry under the correct version. If the next version does not yet exist in the changelog, create a new version block and append your changes there. Reference related GitHub issues or pull requests whenever possible. Pull requests are checked by the GitHub Actions workflow .github/workflows/changelog-check.yml. The workflow verifies that CHANGELOG.md was changed and that at least one new changelog entry was added.


Design framework

We are using the AntD framework. If you add anything new, try to use AntD components. AntD also provides a wide range of icons.

More details can be found here:

When building components, prefer AntD components and patterns over custom HTML whenever possible, to keep the UI consistent.


Usage of colors

As we support dark and light themes, do not use hard-coded colors like #000000. Instead, always use the AntD theme tokens.

If not already added, extend your file like this:

import { theme } from "antd";
...
const { useToken } = theme;
...

export const TonieCard: React.FC<{
    overlay: string;
}> = ({ overlay }) => {
    const { token } = useToken();
    ...
}

Then you can use:

token.*
// e.g. token.colorTextDisabled

This ensures that your component respects both light and dark themes.


Usage of translations

Always use t("...") instead of hard-coded text.

  • English (en), German (de), French (fr) and Spanish (es) are actively maintained. New strings must be added to all four translation JSON files.
  • Additional languages are optional to maintain. Missing translations in these languages are allowed, but contributions are welcome.
  • Use meaningful, structured keys (e.g. settings.notifications.title, tonies.encoder.uploadHint).
  • Avoid inline strings in JSX, especially in pages and reusable components.
  • Run npm run translation:check before opening a pull request.

Generated and minified files

Generated, bundled or minified files should not be formatted with Prettier.

Examples:

  • wasm glue code
  • generated API clients
  • third-party vendor bundles
  • minified JavaScript assets

These files should be excluded via .prettierignore.


Adding new API request methods

If you need to add a new API request to the TeddyCloud API, please use one of the existing helper methods in src/api/apis/TeddyCloudApi.ts:

  • apiPostTeddyCloudRaw
  • apiPostTeddyCloudFormDataRaw
  • apiGetTeddyCloudApiRaw
  • or any other already existing method in TeddyCloudApi.ts

If none of the existing methods meet your needs, add the new request to src/api/apis/TeddyCloudApi.ts. We prefer to have all API requests centralized in this file. One reason is the upcoming authentication for accessing the API and the possibility to reintroduce generated clients later.


Backend URLs and the URL prefix

The web UI can be served under a URL prefix that is only known at runtime (see Serving under a URL prefix). Never hardcode root-absolute backend paths like /content/..., /api/... or /web/... in src:

  • API requests through TeddyCloudApi already take the prefix into account.
  • For URLs handed to the browser directly (<img src>, audio sources, downloads, EventSource, ...), use backendUrl(path) from src/utils/basePath.ts. It adds the dev-only VITE_APP_TEDDYCLOUD_API_URL and the runtime prefix.
  • For other same-origin paths (router basename, i18n, WASM scripts, iframes), use withBase(path).

Both helpers leave absolute URLs (https:, data:, blob:, ...) unchanged and are safe to apply twice.


Linking to other sites

If you need to link to another source, element, or URL, please check if it is already defined in src/constants/urls.ts.

  • If it is, use the existing variable instead of hardcoding the URL.
  • If it isn’t, consider adding it as a variable in src/constants/urls.ts.

You may need the same URL more than once; defining it in one place ensures that updates are centralized.

Some URLs already defined (partial list):

  • tonieboxDefaultImageUrl = "https://cdn.tonies.de/thumbnails/03-0009-i.png"
  • telegramGroupUrl = "https://t.me/toniebox_reverse_engineering"
  • forumUrl = "https://forum.revvox.de/"
  • gitHubUrl = "https://github.com/toniebox-reverse-engineering"
  • wikiUrl = "https://tonies-wiki.revvox.de/docs/tools/teddycloud/"

Summary

  • Pages: routing and layout per topic, minimal logic.
  • Components: topic-specific UI and behavior; move heavy logic into hooks.
  • Feature hooks: live next to the components they serve (src/components/<topic>/<feature>/hooks/).
  • Shared hooks: only for generic, cross-feature helpers in src/hooks/.
  • Modals: in the topic’s component tree, with parent-controlled state.
  • Utils: pure helpers without React or domain-specific dependencies.
  • Changelog, translations, and design tokens: keep them consistent and up to date.

This combined structure and guideline set should be followed for all new code and refactorings.

Tips and Tricks

Serving under a URL prefix

The production build works unchanged behind a reverse proxy sub-path or Home Assistant ingress. There is nothing to configure: the prefix is everything in the browser URL before the first /web path segment, e.g. /teddycloud for https://proxy/teddycloud/web/.

The proxy has to strip the prefix and forward everything below it to the root of teddyCloud, not only /web (the UI also calls /api, /content, /v1, /reverse, ...). Example for nginx:

location /teddycloud/ {
    proxy_pass http://teddycloud:80/; # the trailing slash strips /teddycloud
}

Then open https://proxy/teddycloud/web/. The prefix itself must not contain a /web segment.

Missing img_unknown.png

As the img_unknown.png is part of the teddycloud server, normally it's not shown in the running dev environment. To solve that you can do the following:

  1. Just add img_unknown.png to the /public folder.

  2. Restart the dev environment.

After these steps, the img_unknown.png should be shown. The file is already part of the file .gitignore so you do not have to care about accidentally committing this file.

Crashing Dev Environment

Sometimes it happens, that the dev environment unexpectedly crashes. Even after a new start it can crash immediately. To solve that problem, you can delete the node_modules folder and call npm install again. Then the node_modules will be reloaded and you should be able to restart the dev environment.

GitHub Actions npm ci lockfile errors

The GitHub Actions workflow uses npm ci instead of npm install.

This is intentional: npm ci performs a clean, reproducible install and fails if package.json and package-lock.json are not in sync.

If CI fails with an error like:

npm ci can only install packages when your package.json and package-lock.json are in sync

regenerate the lockfile locally:

rm -rf node_modules package-lock.json
npm install
npm ci

If npm ci succeeds locally, commit the regenerated package-lock.json.

Known Weaknesses

API Definitions

Ideally, the TeddyCloudApi.ts should be generated using OpenApiTools with the swagger.yaml. However, it was actually changed manually and new API functions were added directly. This should be revised in the future. Until then, you should NOT generate the API with the OpenApiTools, as this will break the frontend.

Due to security reasons we removed "@openapitools/openapi-generator-cli" from the devDependencies completely. If you want to refactor the manually changed api functions and bring back support for OpenApiTools, add it and the following in the package.json:

"scripts": {
    ...
    "api:generate": "rm -rf ./src/api && openapi-generator-cli generate -i ./api/swagger.yaml -g typescript-fetch -o ./src/api --additional-properties=typescriptThreePlus=true"
    ...
}

General React App information

Getting Started with Vite

This project was bootstrapped with Vite.

Typical development workflow:

Preconditions

You have node.js and python installed.

Formatting check

This project uses Prettier to keep TypeScript, TSX, JavaScript, JSON, CSS and Markdown formatting consistent.

Before opening a pull request, run:

npm run format:check

If formatting issues are reported, fix them with:

npm run format

Then commit the changed files.

The CI workflow runs the same formatting check (and additionally a production build) on pull requests, as well as on pushes to master and develop.

Translation check

Before opening a pull request, run:

npm run translation:check

The check uses English (en) as the reference language. English (en), German (de), French (fr) and Spanish (es) are actively maintained and must contain all required translation keys.

Additional languages are optional to maintain. Missing keys in optional languages are reported as warnings only.

The check also reports statically used translation keys that are missing from en.json. Dynamically constructed translation keys cannot be resolved reliably and are therefore ignored by this source-code check.

Install dotenv

Debian

sudo apt install python3-dotenv-cli

Windows

pip install python-dotenv

followed by

pip install "python-dotenv[cli]"

Install additional packages

You need to install cross-env:

npm install --save-dev cross-env

Available Scripts

In the project directory, you can run:

npm run format

Formats the project using Prettier.

npm run format

Use this before committing if files were changed.

npm run format:check

Checks whether all files match the configured Prettier formatting rules.

npm run format:check

This command does not modify files. It is also used by GitHub Actions to verify formatting in pull requests.

If the check fails locally, run:

npm run format

Then commit the formatted files.

npm run translation:check

Checks the translation files and statically used translation keys.

npm run translation:check

English (en) is used as the reference language. German (de), French (fr) and Spanish (es) are actively maintained and must contain all required translation keys.

Additional languages are optional to maintain. Missing keys in these languages are reported as warnings only.

npm start

Runs the app in the development mode. Open http://localhost:3000 to view the http variant in the browser. Open https://localhost:3443 to view the https variant in the browser.

The page will reload if you make edits. You will also see any lint errors in the console.

If you changed the default ports, adapt the links above accordingly.

npm run build

Builds the app for production to the build folder. It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes. Your app is ready to be deployed!

See the section about deployment for more information.

npm run preview

After building the app, you can test the optimized (minified) production build using this command. Unlike React’s development mode, which enables features such as hot reloading, detailed error overlays, and repeated API calls for strict mode checks, the preview mode runs the app exactly as it will behave in production.

This means:

  • API calls are executed only once (no double-invocation from React Strict Mode)
  • Performance reflects the real-world production setup
  • You can verify that your build works correctly before deployment

Learn More

You can learn more in the Vite documentation.

To learn React, check out the React documentation.

Attribution

The Open Toniebox Guide ([teddycloud]/tonieboxes/boxsetup/openboxguide / src\pages\tonieboxes\boxsetup\OpenBoxGuidePage.tsx) is based on the following two excellent guides from iFixIt.com:

Both guides were originally written and illustrated by Tobias Isakeit, who also created all the images used here.

Special thanks to Tobias for providing such clear and detailed instructions!

The icons used are from here:

Thanks for the original authors for these great icons.

The country flags are derived from here:

Thanks to Nikolay Kuchumov for creating this flag collection!