A self-hosted platform to create and share code snippets. It features a frontend built with React and Typescript and a backend powered by ASP.NET Core 9, written in C# 13.
- Note Management: Create and share notes and code snippets with optional syntax highlighting
- Forking Notes: Fork existing notes to create new versions while maintaining a link to the original
- User Management: Register new users to create owned notes or create notes without an account
- Search and Filter: Search and filter notes by name or owner
- Permantent and Temporary Storage: Support for both permanent SQLite and in-memory storage for notes and user data
- Minimalistic Frontend: A clean frontend built with React and Typescript for a responsive user experience
- User Authentication: Secure user authentication using stateless tokens with HMAC-SHA512
The repo contains VS2022 solution and project files for C#13 and ASP.NET Core 9.
Backend source files are in src/ with test files in test.
The react frontend source code is located in frontend/ and uses npm and vite for package management and building.
src/: Contains the backend source codeAPI/: Controllers for handling HTTP requestsModels/: Data models and database commandsServices/: Service classes for business logicConfiguration/: Configuration definitions and functionality
test/: Contains unit tests for the backendfrontend/: Contains the frontend source code and configuration filessrc/: React components and pages
publish/: Output directory for the compiled frontend and backend
Run build.cmd to compile the frontend and backend and collect all required files in the publish/ folder.
Alternatively, the frontend and backend can be built manually by following these steps:
-
Build the frontend:
cd frontend npm install npm run build cd ..
-
Build the backend:
dotnet build "src/NoteBin.csproj" -c Release -o "publish" -p:PublishProfile=Publish
-
Copy the web files to the publish folder:
cp -r web publish/web
The src/appsettings.json file is used to configure various settings for the backend of the application. This file can be modified to adjust settings such as storage types, connection strings, authentication settings, and server configurations.
The appsettings.json will be copied to the publish directory when building.
-
Kestrel Settings:
- The
Kestrelsection can be used to configure the Kestrel web server, which is used to host the application. Settings such as the host address, port, and HTTPS settings can be adjusted. - Example:
"Kestrel": { "Endpoints": { "Https": { "Url": "https://localhost:7188" } } }
- For more details, refer to the Microsoft documentation on Kestrel
- The
-
NoteStorage Settings:
- The
NoteStoragesection configures how notes are stored. The available storage types areSQLiteandMemoryto create permantent or temporary storages. - Example:
"NoteStorage": { "StorageType": "SQLite", "ConnectionString": "Data Source=data/notes.db", "ContentPath": "data/notes/" }
- When
StorageTypeisSQLite, a ConnectionString is required. In-memory databases will not work properly! - The
ContentPathspecifies a folder where the actual note content will be stored and is only required, if theStorageTypeisSQLite.
- The
-
UserStorage Settings:
- The
UserStoragesection configures how user data is stored. Similar toNoteStorage, the storage type and connection string can be specified to work with permanent or temporary storage. - Example:
"UserStorage": { "StorageType": "SQLite", "ConnectionString": "Data Source=data/users.db" }
- The
-
AuthSettings:
- The
AuthSettingssection configures authentication settings. The authentication type, key file, key length, token length, and expiration duration have to be adjusted. The only supported auth type isStateless. - Example:
"AuthSettings": { "AuthType": "Stateless", "KeyFile": "data/auth.key", "KeyLength": 64, "TokenLength": 64, "ExpirationDuration": 7776000 }
- The
KeyFilewill be used to store the random secret key used to sign tokens. Deleting it will invalidate all tokens! KeyLengthandTokenLengthspecify the length in bytes used for the secret key and token generation- HMAC-SHA512 is used for generating and validating tokens
ExpirationDurationis the time in seconds until a token expires
- The
-
Logging Settings:
- The
Loggingsection configures the logging behavior of the application. You can set the log level for different categories. - Example:
"Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }
- The
Assuming appSettings.json is correctly configured, starting NoteBin.exe from the publish/ directory will start the server.
NoteBin.exe will also serve static files from the web/ directory.
On Linux, run dotnet NoteBin.dll instead or build binaries for the correct platform.
See also: NoteBin.http
Create a new user with a username and password. Usernames are unique and act as identifier for the user.
| Name | Required | Type | Description |
|---|---|---|---|
| username | Yes | string | The unique username for the new user. Length <= 32, no whitespace, characters: a-z, A-Z, 0-9, _ |
| password | Yes | string | The password for the new user. Length >= 8, must contain one uppercase, lowercase, and numeric character |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | - | |
| 400 | application/json | { "error": "Invalid username/password" } |
| 409 | application/json | { "error": "Username already exists" } |
Get the user with the name provided in the request path
| Name | Required | Type | Description |
|---|---|---|---|
| name | Yes | string | The username of the user to retrieve |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | application/json | {
"username": "string",
"creationTime": 1736018866165
}creationTime is the time since epoch in milliseconds |
| 404 | application/json | User was not found |
Create a new note
| Name | Required | Description |
|---|---|---|
| Authorization | No | Bearer <token>. For missing or invalid authorization tokens, an unowned note is created |
| Name | Required | Type | Description |
|---|---|---|---|
| name | No | string | The name/title of the note. Used as information on the UI and to find notes |
| fork | No | string | The ID of the note that this new note is forked from |
| syntax | Yes | string | The syntax highlighting language for the note content. Can be an arbitrary value that <= 32 characters that the frontend understands |
| content | Yes | string | The actual content of the note |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | application/json | {
"id": "1kpTjDFdA6"
}id is the identifier of the created note. |
| 400 | application/json | Bad Request |
| 500 | application/json | Unexpected database error or similar |
Get a note by id
| Name | Required | Type | Description |
|---|---|---|---|
| id | Yes | string | Id of the note to retrieve |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | application/json | {
"id": "O8vIr1C6UE",
"name": "Sample Note",
"owner": "TestUser",
"fork": "1kpTjDFdA6",
"creationTime": 1735942466735,
"syntax": "markdown",
"content": "..."
}owner and fork can be null, if the notes is unowned or not forked. creationTime is the time since epoch in milliseconds |
| 404 | application/json | Note was not found |
List notes with optional filters
| Name | Required | Type | Description |
|---|---|---|---|
| offset | Yes | long | The offset from where to start listing notes |
| amount | Yes | long | The number of notes to list |
| owner | No | string | The username of the owner to filter notes by |
| filter | No | string | A string to filter notes by name |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | application/json | {
"notes": [
{
"id": "O8vIr1C6UE",
"name": "Sample Note",
"owner": "TestUser",
"fork": "1kpTjDFdA6",
"creationTime": 1735942466735,
"syntax": "markdown",
"content": "..."
}
],
"total": 1
}notes is an array of note objects and total is the total number of notes matching the filters |
| 400 | application/json | Bad Request |
Authenticate a user and return a token for the client
| Name | Required | Type | Description |
|---|---|---|---|
| username | Yes | string | The username of the user |
| password | Yes | string | The password of the user |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | application/json | {
"token": "string"
}token is the token to be saved by the client |
| 401 | application/json | Unauthorized |
Validate a token from the client
| Name | Required | Description |
|---|---|---|
| Authorization | Yes | Bearer <token> |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | - | |
| 400 | - | Bad Request, if the token is missing in the Authorization header |
| 401 | - | Unauthorized, if the token is invalid |
Logout a user by invalidating the token. This only ensures that the token is valid. The client still has to forget it
| Name | Required | Description |
|---|---|---|
| Authorization | Yes | Bearer <token> |
| HTTP Code | Content-Type | Response |
|---|---|---|
| 200 | - | |
| 400 | - | Bad Request, if the token is missing in the Authorization header |
| 401 | - | Unauthorized, if the token is invalid |
