GithubHelp home page GithubHelp logo

dullage / flatnotes Goto Github PK

View Code? Open in Web Editor NEW
981.0 11.0 52.0 1.08 MB

A self-hosted, database-less note taking web app that utilises a flat folder of markdown files for storage.

License: MIT License

Python 29.78% JavaScript 11.66% Vue 48.44% HTML 0.86% SCSS 7.42% Dockerfile 0.90% Shell 0.94%
markdown note note-taking notes notes-app wiki

flatnotes's Introduction

Docker Pulls

A self-hosted, database-less note-taking web app that utilises a flat folder of markdown files for storage.

Log into the demo site and take a look around. Note: This site resets every 15 minutes.

Contents

Design Principle

flatnotes is designed to be a distraction-free note-taking app that puts your note content first. This means:

  • A clean and simple user interface.
  • No folders, notebooks or anything like that. Just all of your notes, backed by powerful search and tagging functionality.
  • Quick access to a full-text search from anywhere in the app (keyboard shortcut "/").

Another key design principle is not to take your notes hostage. Your notes are just markdown files. There's no database, proprietary formatting, complicated folder structures or anything like that. You're free at any point to just move the files elsewhere and use another app.

Equally, the only thing flatnotes caches is the search index and that's incrementally synced on every search (and when flatnotes first starts). This means that you're free to add, edit & delete the markdown files outside of flatnotes even whilst flatnotes is running.

Features

  • Mobile responsive web interface.
  • Raw/WYSIWYG markdown editor modes.
  • Advanced search functionality.
  • Note "tagging" functionality.
  • Wikilink support to easily link to other notes ([[My Other Note]]).
  • Light/dark themes.
  • Multiple authentication options (none, read-only, username/password, 2FA).
  • Restful API.

See the wiki for more details.

Getting Started

Hosted

A quick and easy way to get started with flatnotes is to host it on PikaPods. Just click the button below and follow the instructions.

PikaPods

Self Hosted

If you'd prefer to host flatnotes yourself then the recommendation is to use Docker.

Example Docker Run Command

docker run -d \
  -e "PUID=1000" \
  -e "PGID=1000" \
  -e "FLATNOTES_AUTH_TYPE=password" \
  -e "FLATNOTES_USERNAME=user" \
  -e "FLATNOTES_PASSWORD=changeMe!" \
  -e "FLATNOTES_SECRET_KEY=aLongRandomSeriesOfCharacters" \
  -v "$(pwd)/data:/data" \
  -p "8080:8080" \
  dullage/flatnotes:latest

Example Docker Compose

version: "3"

services:
  flatnotes:
    container_name: flatnotes
    image: dullage/flatnotes:latest
    environment:
      PUID: 1000
      PGID: 1000
      FLATNOTES_AUTH_TYPE: "password"
      FLATNOTES_USERNAME: "user"
      FLATNOTES_PASSWORD: "changeMe!"
      FLATNOTES_SECRET_KEY: "aLongRandomSeriesOfCharacters"
    volumes:
      - "./data:/data"
      # Optional. Allows you to save the search index in a different location: 
      # - "./index:/data/.flatnotes"
    ports:
      - "8080:8080"
    restart: unless-stopped

See the Environment Variables article in the wiki for a full list of configuration options.

Roadmap

I want to keep flatnotes as simple and distraction-free as possible which means limiting new features. This said, I welcome feedback and suggestions.

Sponsorship

If you find this project useful, please consider buying me a beer. It would genuinely make my day.

Sponsor

Thanks

A special thanks to 2 fantastic open-source projects that make flatnotes possible.

  • Whoosh - A fast, pure Python search engine library.
  • TOAST UI Editor - A GFM Markdown and WYSIWYG editor for the browser.

flatnotes's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flatnotes's Issues

API and front end should not interact with file extension

Currently the API and the web front end load/save notes using a file extension. This extension is hidden from the user and so should also be hidden from the API. For simplicity, an extension of .md should be assumed.

Consider a server side warning message showing a count of files with a different extension.

Tree structure

I know the name is "flat" and you mention this in the design principle... but.. do you have any plans to add a lateral pane with a folder/tree/category structure so we could save the notes in a more ordered way?
Thanks for your work anyway, I love it so far!

Multiple Repositories / Multiple Users

Another suggestion :)

Since this an online editor I can host on my server it would be really awesome if other family members could use it as well. I mean, they can as it is now, but many notes are often private / not interesting to others. So a great improvement would be a possibility to create login for different users as well as being able to map multiple "repositories" (i.e. folders) into the container which an administrator can assign to different users.

Password protected + encrypted notes

Hi,
I would like to make a suggestion. For me it would be great if notes could be password protected and then encrypted in the filesystem. Sounds a bit paranoid, but sometimes I write notes with more private content and I would love if these were encrypted and couldn't be easily read from the server.
Thanks for your great project!!
Edit: I mean an option to selectively protect individual notes, not all of them globally.

Note "Tagging"

Add the ability to "tag" a note by using a hashtag anywhere in the body (e.g. #work). Notes could be tagged multiple times.

This change would also require the ability to view a list of all tags used and the notes associated with each tag.

I should be able to use the search index for this but I would need it to index hashtags (currently it indexes "#work" as just "work").

Also consider including the tag itself in the search results i.e. if you search for "work" and there is a "#work" tag then display #work as the top result allowing the user to quickly navigate to notes associated with that tag.

Improve Docker Mount Workflow

Currently the user needs to create a data folder to map to /app/data inside the container and give user 1000 (the flatnotes user) permission to read/write in it. If they don't do this then Docker creates the folder with ownership assigned to root. If that happens, flatnotes doesn't have the ability to write to the directory and crashes.

Perhaps I need to initially run as root, update the folder ownership and then switch to the flatnotes user to run the app.

Suggestions from Reddit:

You might need to make an env for the user and group ID.

you can use something like su-exec or s6-overlay to do user masking. do all your startup/init as root and then the last action is to start the app as the env provided uid/gid.

Order A-Z Page by Last Modified

Add the ability to order the A-Z page by Last Modified Date.

Consider grouping such as "Today", "Over 1 Week Ago", "Over 1 Year Ago"...

Overview of Used Tags

I just started using this and it just what I need. Great job!!

It might be useful to somewhere (perhaps related to the search area) provide a list of tags found in the notes so that the user can click on a desired tag and get a list of the notes where that tag is used.

Port 80 permission denied on Synology

Running the docker instance of flatnotes on a Synology box (DS415+) gives the following error even if I set the local port to somthing like 49161. It seems that flatnotes overrules this setting and tries to bind to local port 80 (which is not allowed):

ERROR:    [Errno 13] error while attempting to bind on address ('0.0.0.0', 80): permission denied
2022-08-26 08:10:06 [ERROR]: [Errno 13] error while attempting to bind on address ('0.0.0.0', 80): permission denied

Docker is using the bridge network.

In the Dockerfile I noticed that host (0.0.0.0) and port (80) is mentioned:

ENTRYPOINT [ "pipenv", "run", "python", "-m", "uvicorn", "main:app", "--app-dir", "flatnotes", "--host", "0.0.0.0", "--port", "80" ]

I'm not sure but I think addition of host (0.0.0.0) and port (80) to the entrypoint forces flatnotes to use the local port 80 on Synology

Improved Image Handling

Currently, images are inserted as inline base64 which isn't ideal. Equally, other attachment types (e.g. PDFs) are not supported at all. It would be good to improve this.

Permission denied within docker container

I'm using the docker-compose.yml from the readme as follows:

version: "3" 

services:
  flatnotes:
    container_name: flatnotes
    image: dullage/flatnotes:latest
    environment:
      FLATNOTES_AUTH_TYPE: "password"
      FLATNOTES_USERNAME: "<snip>"
      FLATNOTES_PASSWORD: "<snip>"
      FLATNOTES_SECRET_KEY: "aLongRandomSeriesOfCharacters"
    volumes:
      - ./data:/app/data
      - ./index:/app/data/.flatnotes
    ports:
      - 8080:80
    restart: unless-stopped

When I try to bring it up with docker-compose up, I get the following stacktrace:

flatnotes  | Traceback (most recent call last):
flatnotes  |   File "<frozen runpy>", line 198, in _run_module_as_main
flatnotes  |   File "<frozen runpy>", line 88, in _run_code
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/__main__.py", line 4, in <module>
flatnotes  |     uvicorn.main()
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/click/core.py", line 1130, in __call__
flatnotes  |     return self.main(*args, **kwargs)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/click/core.py", line 1055, in main
flatnotes  |     rv = self.invoke(ctx)
flatnotes  |          ^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/click/core.py", line 1404, in invoke
flatnotes  |     return ctx.invoke(self.callback, **ctx.params)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/click/core.py", line 760, in invoke
flatnotes  |     return __callback(*args, **kwargs)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/main.py", line 404, in main
flatnotes  |     run(
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/main.py", line 569, in run
flatnotes  |     server.run()
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/server.py", line 60, in run
flatnotes  |     return asyncio.run(self.serve(sockets=sockets))
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run
flatnotes  |     return runner.run(main)
flatnotes  |            ^^^^^^^^^^^^^^^^
flatnotes  |   File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
flatnotes  |     return self._loop.run_until_complete(task)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/usr/local/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
flatnotes  |     return future.result()
flatnotes  |            ^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/server.py", line 67, in serve
flatnotes  |     config.load()
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/config.py", line 477, in load
flatnotes  |     self.loaded_app = import_from_string(self.app)
flatnotes  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/uvicorn/importer.py", line 21, in import_from_string
flatnotes  |     module = importlib.import_module(module_str)
flatnotes  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
flatnotes  |     return _bootstrap._gcd_import(name[level:], package, level)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
flatnotes  |   File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
flatnotes  |   File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
flatnotes  |   File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
flatnotes  |   File "<frozen importlib._bootstrap_external>", line 940, in exec_module
flatnotes  |   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
flatnotes  |   File "/app/flatnotes/main.py", line 27, in <module>
flatnotes  |     flatnotes = Flatnotes(config.data_path)
flatnotes  |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/app/flatnotes/flatnotes.py", line 172, in __init__
flatnotes  |     self.index = self._load_index()
flatnotes  |                  ^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/app/flatnotes/flatnotes.py", line 196, in _load_index
flatnotes  |     return whoosh.index.create_in(
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/whoosh/index.py", line 102, in create_in
flatnotes  |     return FileIndex.create(storage, schema, indexname)
flatnotes  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/whoosh/index.py", line 425, in create
flatnotes  |     TOC.create(storage, schema, indexname)
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/whoosh/index.py", line 611, in create
flatnotes  |     toc.write(storage, indexname)
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/whoosh/index.py", line 676, in write
flatnotes  |     stream = storage.create_file(tempfilename)
flatnotes  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flatnotes  |   File "/home/flatnotes/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.11/site-packages/whoosh/filedb/filestore.py", line 490, in create_file
flatnotes  |     fileobj = open(path, mode)
flatnotes  |               ^^^^^^^^^^^^^^^^
flatnotes  | PermissionError: [Errno 13] Permission denied: '/app/data/.flatnotes/_3_0.toc.1679147739.3350751'
  • docker compose 2.9.0
  • docker 23.0.1
# docker image inspect dullage/flatnotes | grep -i created
        "Created": "2023-03-17T12:33:00.0331812Z",

Hosting Options

Hi there, nice app. Kudos to everyone involved. Based on user feedback, we added Flatnotes on PikaPods.com for 1-click hosting. Will also feature it in our monthly newsletter this week.

While I love true self-hosting, it's not for everyone and every app. PikaPods is here to fill this gap and make great apps, like Flatnotes available to a wider audience. So I also wanted to check if it's an option to add a note about this managed hosting option in the README. Happy to add a PR with some initial wording.

We also offer a revenue share for app authors. Happy to discuss that too.

How to change font size?

Stupid noob question: Is there a way to change font-size? I could not find a css file, I checked in /var/lib/docker. Thanks!

Slash in title results in server error on save

Overview:
Creating a new note with a title that contains a slash (/) character, gives an error "Unable to communicate with the server".
I have not debugged the problem or checked the code, but I assume the title is used as part of a filename, and the slash messes up the path?

Suggestions:

  • Escape the slash character so saving is possible
  • Provide a more meaningful error message, such as: Invalid characters in the title. Allowed characters are [a-z][A-Z] etc...
  • If the title is indeed used as a file path, maybe rethink this idea by having a unique randomly generated ID associated with the note and use that instead.

Re-Design

Consider:

  • Fonts
  • Colours
  • Buttons
  • Navbar
  • Layouts
  • Logo (#6)

Demo site

Create a demo site that automatically resets on a timed interval.

Inefficient mobile responsive design

This is the perfect note taking solution I was looking for, simple and clean, database-less, very fast, WYSIWYG, web-app (accessible from anywhere and any device without the need of a client), full text search, advanced searching, tagging, Docker/Podman support, Time-based one-time password, ... I really really appreciate your thoughts and efforts, thank you so much.

I want to start using it now, but the only things that are holding me (I will submit them as separate issues):

  1. Inefficient mobile responsive design.
  2. Auto RTL support.
  3. Directory structure.

I will consider this page for the first issue on the list:

Inefficient mobile responsive design: This is noticeable when the virtual keyboard appears on a mobile screen, I wish that the only sticky bars are the main one and the editor toolbar and should be as small as possible to leave space for content which is the most important thing here.

These screenshots shows the difference:

Hackmd:
image

Flatnotes:
image

Add page to list all notes

Given the similarity, this should also include a re-design of:

  • The 'Recently Modified' list.
  • The search results page.

Add ids to header tags to support anchoring

Hi. First, I really love the simplicity of flatnotes)

The only issue I have is an absence of ids for h1, h2 and other header tags. It makes tables of contents almost useless because clicking on anchor links does nothing:

# Example

- [A](#a)
	- [AA](#aa)
- [B](#b)

## A

### AA

## B

I believe TOAST UI Editor doesn't support it out of the box:

The simplest solution that comes into my mind is to use getElementsByTagName on Viewer component to get all header tags and generate all ids manually.

Generation of a valid id is an another issue, but I believe it's possible to use this code as a base. It is under MIT license, so, there should be no problems with copying it if the authors are mentioned.

Files with .MD (uppercase) extension are not recognized as markdown files

Hi,

I have an issue when I use my markdown folder as the 'database' dir. The files with .MD extension are not recognized as markdown files. As a result, these files do not show up in the frontend. To show them, I have to change all the files with .MD extension to .md.

I expect that both .MD and .md files should be treated as markdown files and show up in the frontend.

Thank you.

Header Tagging & Table of Contents

Great work!

Suggestion: add a button to insert "Table of content" at the top of the note for the larger notes where you have created many different headers.

2-Factor Authentication

Hi there! I just installed flatnotes using docker and everything is working perfectly, thanks a lot!
I wonder if you plan to add 2 factor authentication to the logon, as I'm a bit worried of a plain user/password logon for an internet-facing service.
Thanks for your great work!

Clone note option

Hi,
I have a note which I use as a template to create other notes from it, and it would be great if there was a "Clone note" option, so I don't have to copypaste the template contents to a new note. I know it's not a very important thing 😅 but would be nice to have.
Thanks!!

Hide Markdown formatting

Flatnotes is something I've been looking for, and it looks great. Have you considered hiding Markdown formatting, like in this example?:

markdown

Show more than 5 on home page?

This is great and I think will solve my folder full of .md notes and make it easier to manage them.

Is it possible to display more than 5 of the most recent on the home page?

Or maybe another page (or a config option) to show all notes? Maybe sorted by recently changed?

Auto RTL support

Like adding this HTML attribute dir="auto" to the nodes to make them change direction automatically based on the typed character.

Right now, I'm using hackmd.io they don't support auto RTL support though, but at least they support YAML so I can use something like:

---
dir: rtl
---

to make the whole page RTL when needed.

I wish I can help but I only know some basic HTML and CSS 😅

Index/Search Stemming & Accent Folding

Currently, the search terms "render" and "cafe" do not find documents that include the words "renders" and "café". The underlying search library Whoosh has the functionality to deal with this which needs to be enabled.

Font Customisation

Is it possible to change the font face used by the toast editor?
I'm not a fan of the Inter font face and I would like to use either iA Writer font or something like JetBrains Mono.

Sure, I could fork this project, change index.html to my taste and rebuild the docker image.
But maybe theming options have already been in consideration as a future change.

As an intermediate solution, could I replace/overwrite some of the files in the container by mapping them via volumes?

Note Sharing

In the spirit of simple and database-less, the current plan is to generate temporary pre-signed URLs but this needs to be explored.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.