A simple single-node key-value storage service built with ASP.NET Core 10 and PostgreSQL.
Data is isolated by user: the same key may exist for different owners, while JWT-based authentication prevents users from accessing entries owned by other users.
- PostgreSQL stores users, ASP.NET Core Identity password hashes, refresh token hashes, entry metadata, and values up to 10 KiB (
bytea). - Larger values, as well as values received without a
Content-Lengthheader, are streamed directly to local blob storage. TheIBlobStoreabstraction allows the local filesystem to be replaced with S3/MinIO without changing the API. - SHA-256 ETags are used to identify and verify stored content.
- A unique
(OwnerId, Key)index provides upsert semantics for a key within the owner's scope. - Blob writes use a temporary file + rename strategy to prevent partially written blobs from becoming visible.
-
Register a user or log in:
POST /v1/auth/register POST /v1/auth/login
Request body:
{ "email": "[email protected]", "password": "..." } -
Pass the access token with authenticated requests:
Authorization: Bearer <accessToken> -
Store or update a value:
PUT /v1/entries/{key}
The request body contains the raw value.
Supported headers:
Content-TypeX-Value-Kind:String,File, orBinaryX-File-Name— optional file nameX-Metadata— optional JSON metadata
-
Retrieve a value:
GET /v1/entries/{key}
The content is streamed to the client. Range requests are supported for blob-backed values.
-
Retrieve metadata without downloading the content:
GET /v1/entries/metadata/{key}
-
Delete an entry:
DELETE /v1/entries/{key}
-
Refresh authentication tokens or log out:
POST /v1/auth/refresh POST /v1/auth/logout
Request body:
{ "refreshToken": "..." }
Простое однонодовое key-value хранилище на ASP.NET Core 10 и PostgreSQL.
Данные изолированы по пользователям: одинаковый ключ может существовать у разных владельцев, при этом JWT-аутентификация не позволяет пользователю получить доступ к чужой записи.
- PostgreSQL хранит пользователей, хеши паролей ASP.NET Core Identity, хеши refresh-токенов, метаданные записей и значения размером до 10 Кб (
bytea). - Более крупные значения, а также значения, поступившие без заголовка
Content-Length, потоково записываются в локальное blob-хранилище. АбстракцияIBlobStoreпозволяет заменить локальный диск на S3/MinIO без изменения API. - Для идентификации и проверки содержимого используется SHA-256 ETag.
- Уникальный индекс
(OwnerId, Key)обеспечивает upsert одного ключа в пределах владельца. - Запись blob выполняется по схеме temporary file + rename, поэтому частично записанный blob не становится доступен клиентам.
-
Регистрация пользователя и вход:
POST /v1/auth/register POST /v1/auth/login
Тело запроса:
{ "email": "[email protected]", "password": "..." } -
Для авторизованных запросов передавайте access token:
Authorization: Bearer <accessToken> -
Сохранение или обновление значения:
PUT /v1/entries/{key}
В теле запроса передаются raw-данные.
Поддерживаемые заголовки:
Content-TypeX-Value-Kind:String,FileилиBinaryX-File-Name— необязательное имя файлаX-Metadata— необязательные метаданные в формате JSON
-
Получение значения:
GET /v1/entries/{key}
Данные потоково возвращаются клиенту. Для значений в blob-хранилище поддерживаются range-запросы.
-
Получение метаданных без скачивания содержимого:
GET /v1/entries/metadata/{key}
-
Удаление записи:
DELETE /v1/entries/{key}
-
Обновление токенов и выход:
POST /v1/auth/refresh POST /v1/auth/logout
Тело запроса:
{ "refreshToken": "..." }