Skip to content

Conversation

@ThatsOurJake
Copy link
Contributor

@ThatsOurJake ThatsOurJake commented Sep 30, 2025

Feature: The ability export and import the settings of Feishin

Motivation: I don't host Feishin as a docker image and instead use the standalone apps, I have one on my macbook and one of my windows PC and would like the settings across these apps to be synced. As a initial concept the settings are exported as JSON and imported into the other app instance.

Changes:

  • New control within the "Advanced" tab (reason is the action is considered destructive so users use at their own risk)
  • Drag and Drop zone allowing the files to be read
  • Zod schema for the settings which allows us to validate and transform the imported file
  • Export the schema with the current schema version, allowing us to run migrate on the imported json
  • Visual diff so the user knows what is changing
  • New localisation keys
    • Note: The keys were sorted alphabetically as this pattern looked to be mostly adopted in this file, however some existing keys were also not sorted and got caught in the tidy-up. The keys added are:
      • "exportImportSettings_control_description"
      • "exportImportSettings_control_exportText"
      • "exportImportSettings_control_importText"
      • "exportImportSettings_control_title"
      • "exportImportSettings_destructiveWarning"
      • "exportImportSettings_importBtn"
      • "exportImportSettings_importModalTitle"
      • "exportImportSettings_importSuccess"
      • "exportImportSettings_notValidJSON"
      • "exportImportSettings_offendingKeyError"
      • "dragDropZone.*"

Evidence
The below screenshots show parts of the new feature in action:
img1
img2
img3
img4
img5

- This zone allows the dropping of files
- The zone allows validation by parent
- The zone allows customisation like icon shown
- Ability to import settings from a JSON file
- Validation to ensure file compatibility
- Visualiser for viewing string differences
- Moved all hardcoded values to be en localised
@vercel
Copy link

vercel bot commented Sep 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
feishin Ready Ready Preview Comment Oct 23, 2025 10:13am

@ThatsOurJake
Copy link
Contributor Author

Sidenote - I cannot see why Vercel failed 😄 - If someone with perms could let me know if it's something I've done, that'd be great 🤗

Copy link
Collaborator

@kgarner7 kgarner7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Does this export/import support upgrading versions? E.g., if you export an older version of the settings is imported. Alternatively, you may also just reject older versions of the settings. See the various *.store migrate functions for examples
  2. This does not do meaningful validation of the imported data. A silly example is converting a bool value to an int, but more serous is custom CSS bypassing sanitation. Additionally, some functional things may just silently (or fatally) break (such as invalid custom fonts, bad sidebar routes).

@ThatsOurJake
Copy link
Contributor Author

  1. Does this export/import support upgrading versions? E.g., if you export an older version of the settings is imported. Alternatively, you may also just reject older versions of the settings. See the various *.store migrate functions for examples
  2. This does not do meaningful validation of the imported data. A silly example is converting a bool value to an int, but more serous is custom CSS bypassing sanitation. Additionally, some functional things may just silently (or fatally) break (such as invalid custom fonts, bad sidebar routes).

1 - Brill, I will take a look at the migrate functions, I'll likely look for this PR to block any versions for now that mismatch the version in the current settings.

2 - Makes sense, do you have any suggestions on meaningful validation, does the app have any form of validation that could be leveraged here? I don't want to introduce a validator (e.g. ZOD, Valibot etc) as that could be a fairly big change to the codebase without proper approvals from yourself and other maintainers.

@kgarner7
Copy link
Collaborator

  1. Does this export/import support upgrading versions? E.g., if you export an older version of the settings is imported. Alternatively, you may also just reject older versions of the settings. See the various *.store migrate functions for examples
  2. This does not do meaningful validation of the imported data. A silly example is converting a bool value to an int, but more serous is custom CSS bypassing sanitation. Additionally, some functional things may just silently (or fatally) break (such as invalid custom fonts, bad sidebar routes).

1 - Brill, I will take a look at the migrate functions, I'll likely look for this PR to block any versions for now that mismatch the version in the current settings.

2 - Makes sense, do you have any suggestions on meaningful validation, does the app have any form of validation that could be leveraged here? I don't want to introduce a validator (e.g. ZOD, Valibot etc) as that could be a fairly big change to the codebase without proper approvals from yourself and other maintainers.

  1. I think for upgrades, if you also export the setting store version, you could then try to call the migrate function (or if needed, it can be externalized so that it can be called). So, export version + settings, call migrate first, and then step 2.
  2. I don't see why zod validation of the scheme when importing would be very expensive. There's already the SettingState interface (and initialState) which you could use as a base. You could make it a separate file that's lazy-loaded, for example, and then it'll only really be noticed if someone actually does an import. Note that zod schema validation alone (without custom validators) wouldn't be enough, as some fields (as far as I can tell right now, mainly just font.custom, font.system and css.content, and general.sidebarItems) need additional validation/sanitation.

Thanks for taking a stab at this. These two issues are why I was personally not keen to implement it.

@ThatsOurJake
Copy link
Contributor Author

Super - I will give it another crack with this feedback in mind :)

This commit contains the code to move settings to using ZOD, the reason for this is so that we can validate the settings schema that is being imported.

This commit also adds various validation and transforms to ensure the settings being reimported match values we expect.

I also removed the original crude validation and replaced it with the new ZOD parser that will handle this for us.

Finally the "styles-settings" component will listen to any external content updates and update its value, the reasoning is the external import wouldn't update the existing value.
@ThatsOurJake
Copy link
Contributor Author

ThatsOurJake commented Oct 1, 2025

@kgarner7 - This commit is a slightly chunky one but has done the following at an attempt to address your comments :)

  • All types / interfaces within the settings store are now inferred from a ZOD schemas
  • The migrate func and store version are leveraged to migrate the imported json file
  • I have replaced the existing validation and instead now use both safeParse and Parse allowing better validation
image

As a starting point, the keys you mentioned specifically above (apart from system font) have had their own validation / sanitisation rules

  • The fonts are have been extracted to create an Enum to test against (the image above)
  • Custom css follows the same functions the current custom styling component uses
  • Sidebar items seemed to be straight forward as the types are readily avaliable

A note to add, as much as I agree with there are maybe nuances with the certain setting keys, I also would like to mention that this was the main reason I added this setting under the "Advanced" tab. I agree in trying to ensure what is imported is good data but also if a user does want to ruin their data externally and get confused on why it doesn't work when importing I feel is on them and not us as developers. I believe this is the same affect as someone copying the local storage key and doing the same thing albeit with the import we now has the measures in place to try stop the input of bad data :)

With that said - I believe with the new in place ZOD schema though that if there are cases of further validation that it'll now be easier to implement which is a win for the project 🙌🏻

One final note - I believe that after this PR the logic for gathering the system fonts can be moved from the component itself, creating a cached list. This can then be used in both the font setting component and then leveraged via the ZOD validator, but I felt that was too much for this PR :)

@ThatsOurJake ThatsOurJake requested a review from kgarner7 October 3, 2025 18:17
Copy link
Collaborator

@kgarner7 kgarner7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, I like this, but given that my own schema is apparently already improper (I have a few column fields which somehow have no width), this is in practice harder.

(Example schema. Yes, I have some different keys, but the problem is tables.sideQueue.columns, userFavorite / userRating. I don't know why)
feishin-settings.json

Some thoughts:

  1. Is it possible to fix broken parts of the schema? (e.g., just fix or reset just the problematic blocks.
  2. Is it possible to maybe limit validation only to "critical" keys? (I don't know what that is, tbh). Maybe @jeffvli has better ideas?
  3. If validation is removed for (most) of the code (I do like the font/css changes regardless, so that might be good anyway), it might be useful to have an emergency reset settings.
  4. Settings were upgraded, so please d omerge that fix.

Obviously, users can just modify localStorage, but my biggest concern is that importing a file does not cause the state to break (and allow injecting at least invalid CSS, some of which could override the urls).
Some subfields are easy to validate, others are much harder, and knowing if something is "broken" can be tricky, and I honestly don't know how to approach it.

@ThatsOurJake
Copy link
Contributor Author

Thanks for the settings file - I'll take a look at this :)

I think it would be possible to update / fix parts of broken schema, we have the initial state so I don't see any problems with there being some sort of step being like "Hey your schema is not quite right, we need to replace xyz with these default values"

The Settings UI also has the "Reset" if the user gets in a pickle, but you're right that we should try not break the state, the state breaking could result in the UI crashing and forcing a a clear storage.

@vercel
Copy link

vercel bot commented Oct 6, 2025

@ThatsOurJake is attempting to deploy a commit to the jeffvli's projects Team on Vercel.

A member of the Team first needs to authorize it.

@ThatsOurJake
Copy link
Contributor Author

@kgarner7 - I had a look into tables.sideQueue.columns, when you resize any of these columns in the queue the new width is never stored in the settings store (local storage) so you can resize these columns, when you close it the value doesn't get remembered.

If this is a bug or not outside of this, it is a tough call on which direction we take. I do wonder though if this is possible if this feature is something we community outsource to test? Make this feature an alpha / pre-release build open up a discussion for people to submit their issues and we can understand how people are using settings, it might also provide some insights into ways settings aren't "working" like your issue with there not being a default width on the queue sidebar.

@jeffvli
Copy link
Owner

jeffvli commented Oct 7, 2025

You can ignore any settings that pertain to the table / grid config. Those are currently being reworked in https://github.com/jeffvli/feishin/tree/feat/new-lists.

Unfortunately I haven't been following very closely due to lack of time, but I will review all the discussion soon.

@jeffvli
Copy link
Owner

jeffvli commented Oct 11, 2025

Overall I'm fine with the PR's functionality as it's currently implemented.

  • Make sure to rebase development into the branch and handle any new settings that were added (I went ahead and added some)
  • Resolve all linting issues

@ThatsOurJake
Copy link
Contributor Author

Brill - Thanks @jeffvli, I'll action these comments shortly and re-request reviews 🎉

- Split Settings schema into two parts, schema that is validated on import and schema that is not
- Schemas are merged to make the full SettingsStateSchema
- Migration is done as part of validation
- Updated the store version to v10 as there has been changes to the settings
- Migrate will now add the fields from v9 to v10
@ThatsOurJake
Copy link
Contributor Author

@jeffvli & @kgarner7 - This PR has now been updated with the actions from our discussions. Table is still part of the schema but not part of the validation and I've also updated the settings store version and added a migration for the new settings that were added.

Let me know of any further changes and thanks both for your help on this 🙌🏻

- the build was failing due to ids not being mapped to their enum values
@ThatsOurJake
Copy link
Contributor Author

@jeffvli & @kgarner7 - this is now ready for re-review 🙌🏻

@jeffvli jeffvli dismissed kgarner7’s stale review October 29, 2025 03:53

Validation was added

@jeffvli jeffvli merged commit a9f2b08 into jeffvli:development Oct 29, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants