mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-16 17:23:24 +03:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c57beb4d7 | ||
|
|
2a984eeaf1 | ||
|
|
be6e25a167 | ||
|
|
888557171d | ||
|
|
4d337a20c5 |
@@ -1,3 +1,12 @@
|
|||||||
|
## [](https://github.com/stonith404/pocket-id/compare/v0.24.0...v) (2025-01-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* audit log table overflow if row data is long ([4d337a2](https://github.com/stonith404/pocket-id/commit/4d337a20c5cb92ef80bb7402f9b99b08e3ad0b6b))
|
||||||
|
* optional arguments not working with `create-one-time-access-token.sh` ([8885571](https://github.com/stonith404/pocket-id/commit/888557171d61589211b10f70dce405126216ad61))
|
||||||
|
* remove restrictive validation for group names ([be6e25a](https://github.com/stonith404/pocket-id/commit/be6e25a167de8bf07075b46f09d9fc1fa6c74426))
|
||||||
|
|
||||||
## [](https://github.com/stonith404/pocket-id/compare/v0.23.0...v) (2025-01-11)
|
## [](https://github.com/stonith404/pocket-id/compare/v0.23.0...v) (2025-01-11)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -10,6 +10,24 @@ The goal of Pocket ID is to be a simple and easy-to-use. There are other self-ho
|
|||||||
|
|
||||||
Additionally, what makes Pocket ID special is that it only supports [passkey](https://www.passkeys.io/) authentication, which means you don’t need a password. Some people might not like this idea at first, but I believe passkeys are the future, and once you try them, you’ll love them. For example, you can now use a physical Yubikey to sign in to all your self-hosted services easily and securely.
|
Additionally, what makes Pocket ID special is that it only supports [passkey](https://www.passkeys.io/) authentication, which means you don’t need a password. Some people might not like this idea at first, but I believe passkeys are the future, and once you try them, you’ll love them. For example, you can now use a physical Yubikey to sign in to all your self-hosted services easily and securely.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [ Pocket ID](#-pocket-id)
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
|
- [Setup](#setup)
|
||||||
|
- [Before you start](#before-you-start)
|
||||||
|
- [Installation with Docker (recommended)](#installation-with-docker-recommended)
|
||||||
|
- [Unraid](#unraid)
|
||||||
|
- [Stand-alone Installation](#stand-alone-installation)
|
||||||
|
- [Nginx Reverse Proxy](#nginx-reverse-proxy)
|
||||||
|
- [Proxy Services with Pocket ID](#proxy-services-with-pocket-id)
|
||||||
|
- [Update](#update)
|
||||||
|
- [Docker](#docker)
|
||||||
|
- [Stand-alone](#stand-alone)
|
||||||
|
- [Environment variables](#environment-variables)
|
||||||
|
- [Account recovery](#account-recovery)
|
||||||
|
- [Contribute](#contribute)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
@@ -157,6 +175,16 @@ docker compose up -d
|
|||||||
| `PORT` | `3000` | no | The port on which the frontend should listen. |
|
| `PORT` | `3000` | no | The port on which the frontend should listen. |
|
||||||
| `BACKEND_PORT` | `8080` | no | The port on which the backend should listen. |
|
| `BACKEND_PORT` | `8080` | no | The port on which the backend should listen. |
|
||||||
|
|
||||||
|
## Account recovery
|
||||||
|
|
||||||
|
There are two ways to create a one-time access link for a user:
|
||||||
|
|
||||||
|
1. **UI**: An admin can create a one-time access link for the user in the admin panel under the "Users" tab by clicking on the three dots next to the user's name and selecting "One-time link".
|
||||||
|
2. **Terminal**: You can create a one-time access link for a user by running the `scripts/create-one-time-access-token.sh` script. To execute this script with Docker you have to run the following command:
|
||||||
|
```bash
|
||||||
|
docker compose exec pocket-id sh "sh scripts/create-one-time-access-token.sh <username or email>"
|
||||||
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
You're very welcome to contribute to Pocket ID! Please follow the [contribution guide](/CONTRIBUTING.md) to get started.
|
You're very welcome to contribute to Pocket ID! Please follow the [contribution guide](/CONTRIBUTING.md) to get started.
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ type UserGroupDtoWithUserCount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserGroupCreateDto struct {
|
type UserGroupCreateDto struct {
|
||||||
FriendlyName string `json:"friendlyName" binding:"required,min=3,max=30"`
|
FriendlyName string `json:"friendlyName" binding:"required,min=2,max=50"`
|
||||||
Name string `json:"name" binding:"required,min=3,max=30,userGroupName"`
|
Name string `json:"name" binding:"required,min=2,max=255"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserGroupUpdateUsersDto struct {
|
type UserGroupUpdateUsersDto struct {
|
||||||
|
|||||||
@@ -28,13 +28,6 @@ var validateUsername validator.Func = func(fl validator.FieldLevel) bool {
|
|||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
var validateUserGroupName validator.Func = func(fl validator.FieldLevel) bool {
|
|
||||||
// The string can only contain lowercase letters, numbers, and underscores
|
|
||||||
regex := "^[a-z0-9_]*$"
|
|
||||||
matched, _ := regexp.MatchString(regex, fl.Field().String())
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
|
|
||||||
var validateClaimKey validator.Func = func(fl validator.FieldLevel) bool {
|
var validateClaimKey validator.Func = func(fl validator.FieldLevel) bool {
|
||||||
// The string can only contain letters and numbers
|
// The string can only contain letters and numbers
|
||||||
regex := "^[A-Za-z0-9]*$"
|
regex := "^[A-Za-z0-9]*$"
|
||||||
@@ -53,13 +46,6 @@ func init() {
|
|||||||
log.Fatalf("Failed to register custom validation: %v", err)
|
log.Fatalf("Failed to register custom validation: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
|
||||||
if err := v.RegisterValidation("userGroupName", validateUserGroupName); err != nil {
|
|
||||||
log.Fatalf("Failed to register custom validation: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||||
if err := v.RegisterValidation("claimKey", validateClaimKey); err != nil {
|
if err := v.RegisterValidation("claimKey", validateClaimKey); err != nil {
|
||||||
log.Fatalf("Failed to register custom validation: %v", err)
|
log.Fatalf("Failed to register custom validation: %v", err)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pocket-id-frontend",
|
"name": "pocket-id-frontend",
|
||||||
"version": "0.24.0",
|
"version": "0.24.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --port 3000",
|
"dev": "vite dev --port 3000",
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
<p class="text-muted-foreground mt-3 text-sm">No items found</p>
|
<p class="text-muted-foreground mt-3 text-sm">No items found</p>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="w-full">
|
<div class="w-full overflow-x-auto">
|
||||||
{#if !withoutSearch}
|
{#if !withoutSearch}
|
||||||
<Input
|
<Input
|
||||||
class="mb-4 max-w-sm"
|
class="mb-4 max-w-sm"
|
||||||
@@ -111,11 +111,11 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Table.Root>
|
<Table.Root class="min-w-full table-auto">
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
{#if selectedIds}
|
{#if selectedIds}
|
||||||
<Table.Head>
|
<Table.Head class="w-12">
|
||||||
<Checkbox checked={allChecked} onCheckedChange={(c) => onAllCheck(c as boolean)} />
|
<Checkbox checked={allChecked} onCheckedChange={(c) => onAllCheck(c as boolean)} />
|
||||||
</Table.Head>
|
</Table.Head>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
{#each items.data as item}
|
{#each items.data as item}
|
||||||
<Table.Row class={selectedIds?.includes(item.id) ? 'bg-muted/20' : ''}>
|
<Table.Row class={selectedIds?.includes(item.id) ? 'bg-muted/20' : ''}>
|
||||||
{#if selectedIds}
|
{#if selectedIds}
|
||||||
<Table.Cell>
|
<Table.Cell class="w-12">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedIds.includes(item.id)}
|
checked={selectedIds.includes(item.id)}
|
||||||
onCheckedChange={(c) => onCheck(c as boolean, item.id)}
|
onCheckedChange={(c) => onCheck(c as boolean, item.id)}
|
||||||
@@ -165,9 +165,7 @@
|
|||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table.Root>
|
</Table.Root>
|
||||||
|
|
||||||
<div
|
<div class="mt-5 flex flex-col-reverse items-center justify-between gap-3 sm:flex-row">
|
||||||
class="mt-5 flex flex-col-reverse items-center justify-between gap-3 space-x-2 sm:flex-row"
|
|
||||||
>
|
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<p class="text-sm font-medium">Items per page</p>
|
<p class="text-sm font-medium">Items per page</p>
|
||||||
<Select.Root
|
<Select.Root
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full flex-col gap-5">
|
<div class="flex w-full flex-col gap-5 overflow-x-hidden">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -22,12 +22,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
friendlyName: z.string().min(2).max(30),
|
friendlyName: z.string().min(2).max(50),
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.min(2)
|
.min(2)
|
||||||
.max(30)
|
.max(255)
|
||||||
.regex(/^[a-z0-9_]+$/, 'Name can only contain lowercase letters, numbers, and underscores')
|
|
||||||
});
|
});
|
||||||
type FormSchema = typeof formSchema;
|
type FormSchema = typeof formSchema;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
DB_PATH="./backend/data/pocket-id.db"
|
DB_PATH="./backend/data/pocket-id.db"
|
||||||
DB_PROVIDER="${DB_PROVIDER:=sqlite}"
|
DB_PROVIDER="${DB_PROVIDER:=sqlite}"
|
||||||
USER_IDENTIFIER="$1"
|
|
||||||
|
|
||||||
# Parse command-line arguments for the -d flag (database path)
|
# Parse command-line arguments for the -d flag (database path)
|
||||||
while getopts ":d:" opt; do
|
while getopts ":d:" opt; do
|
||||||
@@ -15,10 +14,12 @@ while getopts ":d:" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Shift past the processed options
|
||||||
shift $((OPTIND - 1))
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
# Ensure username or email is provided as a parameter
|
# Ensure username or email is provided as a parameter
|
||||||
if [ -z "$1" ]; then
|
USER_IDENTIFIER="$1"
|
||||||
|
if [ -z "$USER_IDENTIFIER" ]; then
|
||||||
echo "Usage: $0 [-d <database_path>] <username or email>"
|
echo "Usage: $0 [-d <database_path>] <username or email>"
|
||||||
if [ "$DB_PROVIDER" == "sqlite" ]; then
|
if [ "$DB_PROVIDER" == "sqlite" ]; then
|
||||||
echo "-d <database_path> (optional): Path to the SQLite database file. Default: $DB_PATH"
|
echo "-d <database_path> (optional): Path to the SQLite database file. Default: $DB_PATH"
|
||||||
@@ -104,4 +105,4 @@ else
|
|||||||
echo "Error creating access token."
|
echo "Error creating access token."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "================================================="
|
echo "================================================="
|
||||||
Reference in New Issue
Block a user