Compare commits

...
10 Commits
Author SHA1 Message Date
github-actions[bot] 98b72988c4 chore: release v1.4.12 2026-03-04 12:30:32 +00:00
317473e528 fix: Update CONTRIBUTING.md (#135)
Co-authored-by: Théo LAGACHE <theo.lagache@soluce-technologies.com>
2026-03-04 13:30:04 +01:00
github-actions[bot] e13c5ddf51 chore: release v1.4.11 2026-03-04 12:24:36 +00:00
90be3ab731 fix: password input on sensible fields (#134)
Co-authored-by: Théo LAGACHE <theo.lagache@soluce-technologies.com>
2026-03-04 13:24:10 +01:00
github-actions[bot] 85c0445a64 chore: release v1.4.10 2026-03-04 12:02:54 +00:00
6a227ccd7c fix: Feat/flow (#133)
* fix

* fix

* fix

* fix

* Update docker.yml

* Update auto-release.yml

* Update pr-checker.yml

* Update pr-checker.yml

* fix: citation version

* Update .release-it.json

---------

Co-authored-by: Théo LAGACHE <theo.lagache@soluce-technologies.com>
2026-03-04 13:02:24 +01:00
github-actions[bot] 3d9a21f854 chore: release v1.4.9 2026-03-04 11:58:24 +00:00
d4229a985d fix: Feat/flow (#132)
* fix

* fix

* fix

* fix

* Update docker.yml

* Update auto-release.yml

* Update pr-checker.yml

* Update pr-checker.yml

* fix: citation version

---------

Co-authored-by: Théo LAGACHE <theo.lagache@soluce-technologies.com>
2026-03-04 12:57:56 +01:00
github-actions[bot] c6e0921dea chore: release v1.4.8 2026-03-04 10:57:55 +00:00
346a8a4375 fix: Feat/flow (#131)
* fix

* fix

* fix

* fix

* Update docker.yml

* Update auto-release.yml

* Update pr-checker.yml

---------

Co-authored-by: Théo LAGACHE <theo.lagache@soluce-technologies.com>
2026-03-04 11:57:11 +01:00
11 changed files with 419 additions and 357 deletions
+2 -2
View File
@@ -72,7 +72,7 @@ If you encounter a bug or have a suggestion for improvement, follow these steps:
3. **Push your branch** 3. **Push your branch**
```bash ```bash
git push origin feature/<feature-name> git push origin feat/<feature-name>
``` ```
4. **Open a Pull Request (PR)** 4. **Open a Pull Request (PR)**
@@ -82,7 +82,7 @@ If you encounter a bug or have a suggestion for improvement, follow these steps:
## Code Style Guidelines ## Code Style Guidelines
- Follow the [specific coding style guide] (e.g., Prettier, ESLint, PEP8). - Follow the [specific coding style guide] (e.g., Prettier, ESLint, PEP8,Biome).
- Use meaningful variable names and include comments where necessary. - Use meaningful variable names and include comments where necessary.
- Tests before submitting your changes. - Tests before submitting your changes.
+31 -12
View File
@@ -7,22 +7,41 @@ on:
- dev - dev
jobs: jobs:
dry-run: validate-code:
name: Dry run name: Code Integrity Check
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Setup pnpm
uses: docker/setup-buildx-action@v3 uses: pnpm/action-setup@v4
- name: Build image - name: Setup Node.js
uses: docker/build-push-action@v6 uses: actions/setup-node@v4
with: with:
context: . node-version: "lts/*"
file: ./docker/dockerfile/Dockerfile cache: "pnpm"
push: false
tags: portabase/portabase:pr-${{ github.event.number }} - name: Install dependencies
cache-from: type=gha run: pnpm install --frozen-lockfile
cache-to: type=gha,mode=max
- name: Check code formatting
run: pnpm run format:check
continue-on-error: true
- name: Run Linter
run: pnpm run lint
continue-on-error: true
- name: Type Check
run: pnpm run typecheck
continue-on-error: true
- name: Run Tests
run: pnpm run test
continue-on-error: true
- name: Test Application Build
run: pnpm run build
+5 -11
View File
@@ -16,17 +16,11 @@
"preset": "angular" "preset": "angular"
}, },
"@release-it/bumper": { "@release-it/bumper": {
"files": ["package.json", "CITATION.cff"], "out": {
"bumpFiles": [ "file": "CITATION.cff",
{ "path": "version",
"file": "CITATION.cff", "type": "text/plain"
"versionRegExp": "version:\\s*\"([0-9]+\\.[0-9]+\\.[0-9]+)\"" }
},
{
"file": "package.json",
"versionRegExp": "\"version\":\\s*\"([0-9]+\\.[0-9]+\\.[0-9]+)\""
}
]
} }
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "portabase", "name": "portabase",
"version": "1.4.7", "version": "1.4.12",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack -p 8887", "dev": "next dev --turbopack -p 8887",
@@ -1,30 +1,38 @@
import {UseFormReturn} from "react-hook-form"; import { UseFormReturn } from "react-hook-form";
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; import {
import {Input} from "@/components/ui/input"; FormControl,
import {Separator} from "@/components/ui/separator"; FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
type NotifierDiscordFormProps = { type NotifierDiscordFormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const NotifierDiscordForm = ({form}: NotifierDiscordFormProps) => { export const NotifierDiscordForm = ({ form }: NotifierDiscordFormProps) => {
return ( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.discordWebhook" name="config.discordWebhook"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Discord Webhook URL</FormLabel> <FormLabel>Discord Webhook URL</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="https://discord.com/api/webhooks/..."/> <PasswordInput
</FormControl> {...field}
<FormMessage/> placeholder="https://discord.com/api/webhooks/..."
</FormItem> />
)} </FormControl>
/> <FormMessage />
</> </FormItem>
) )}
} />
</>
);
};
@@ -1,43 +1,49 @@
import {UseFormReturn} from "react-hook-form"; import type { UseFormReturn } from "react-hook-form";
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; import {
import {Input} from "@/components/ui/input"; FormControl,
import {Separator} from "@/components/ui/separator"; FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
type NotifierGotifyFormProps = { type NotifierGotifyFormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const NotifierGotifyForm = ({form}: NotifierGotifyFormProps) => { export const NotifierGotifyForm = ({ form }: NotifierGotifyFormProps) => {
return ( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.gotifyServerUrl" name="config.gotifyServerUrl"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Gotify Server URL</FormLabel> <FormLabel>Gotify Server URL</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="https://gotify.example.com"/> <Input {...field} placeholder="https://gotify.example.com" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.gotifyAppToken" name="config.gotifyAppToken"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Application Token</FormLabel> <FormLabel>Application Token</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="A1b2C3d4E5f6G7h"/> <PasswordInput {...field} placeholder="A1b2C3d4E5f6G7h" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
</> </>
) );
} };
@@ -49,7 +49,7 @@ export const NotifierNtfyForm = ({form}: NotifierNtfyFormProps) => {
<FormItem> <FormItem>
<FormLabel>Access Token (Optional)</FormLabel> <FormLabel>Access Token (Optional)</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="tk_..."/> <PasswordInput {...field} placeholder="tk_..."/>
</FormControl> </FormControl>
<p className="text-xs text-muted-foreground">Only required for protected topics or self-hosted instances with auth.</p> <p className="text-xs text-muted-foreground">Only required for protected topics or self-hosted instances with auth.</p>
<FormMessage/> <FormMessage/>
@@ -1,31 +1,38 @@
import { UseFormReturn } from "react-hook-form";
import {UseFormReturn} from "react-hook-form"; import {
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; FormControl,
import {Input} from "@/components/ui/input"; FormField,
import {Separator} from "@/components/ui/separator"; FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
type NotifierSmtpFormProps = { type NotifierSmtpFormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const NotifierSlackForm = ({form}: NotifierSmtpFormProps) => { export const NotifierSlackForm = ({ form }: NotifierSmtpFormProps) => {
return( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.slackWebhook" name="config.slackWebhook"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Slack Webhook URL</FormLabel> <FormLabel>Slack Webhook URL</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="https://hooks.slack.com/services/..."/> <PasswordInput
</FormControl> {...field}
<FormMessage/> placeholder="https://hooks.slack.com/services/..."
</FormItem> />
)} </FormControl>
/> <FormMessage />
</> </FormItem>
) )}
} />
</>
);
};
@@ -1,64 +1,84 @@
import {UseFormReturn} from "react-hook-form"; import { UseFormReturn } from "react-hook-form";
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; import {
import {Input} from "@/components/ui/input"; FormControl,
import {Separator} from "@/components/ui/separator"; FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
type NotifierTelegramFormProps = { type NotifierTelegramFormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const NotifierTelegramForm = ({form}: NotifierTelegramFormProps) => { export const NotifierTelegramForm = ({ form }: NotifierTelegramFormProps) => {
return ( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.telegramBotToken" name="config.telegramBotToken"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Telegram Bot Token</FormLabel> <FormLabel>Telegram Bot Token</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"/> <PasswordInput
</FormControl> {...field}
<FormMessage/> placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
</FormItem> />
)} </FormControl>
/> <FormMessage />
<FormField </FormItem>
control={form.control} )}
name="config.telegramChatId" />
render={({field}) => ( <FormField
<FormItem> control={form.control}
<FormLabel>Telegram Chat ID</FormLabel> name="config.telegramChatId"
<FormControl> render={({ field }) => (
<Input {...field} placeholder="-100123456789 or 123456789"/> <FormItem>
</FormControl> <FormLabel>Telegram Chat ID</FormLabel>
<p className="text-xs text-muted-foreground"> <FormControl>
You must start the conversation with the bot first (<strong>/start</strong>). <br/> <Input {...field} placeholder="-100123456789 or 123456789" />
For groups, add the bot to the group. <br/> </FormControl>
You can use <a href="https://t.me/userinfobot" target="_blank" rel="noopener noreferrer" className="underline">@userinfobot</a> to find your ID. <p className="text-xs text-muted-foreground">
</p> You must start the conversation with the bot first (
<FormMessage/> <strong>/start</strong>). <br />
</FormItem> For groups, add the bot to the group. <br />
)} You can use{" "}
/> <a
<FormField href="https://t.me/userinfobot"
control={form.control} target="_blank"
name="config.telegramTopicId" rel="noopener noreferrer"
render={({field}) => ( className="underline"
<FormItem> >
<FormLabel>Telegram Topic ID</FormLabel> @userinfobot
<FormControl> </a>{" "}
<Input {...field} placeholder="123456"/> to find your ID.
</FormControl> </p>
<p className="text-xs text-muted-foreground"> <FormMessage />
Unique identifier for the target message thread (topic) of the forum; for forum supergroups only </FormItem>
</p> )}
<FormMessage/> />
</FormItem> <FormField
)} control={form.control}
/> name="config.telegramTopicId"
</> render={({ field }) => (
) <FormItem>
} <FormLabel>Telegram Topic ID</FormLabel>
<FormControl>
<Input {...field} placeholder="123456" />
</FormControl>
<p className="text-xs text-muted-foreground">
Unique identifier for the target message thread (topic) of the
forum; for forum supergroups only
</p>
<FormMessage />
</FormItem>
)}
/>
</>
);
};
@@ -1,65 +1,72 @@
import {UseFormReturn} from "react-hook-form"; import { UseFormReturn } from "react-hook-form";
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; import {
import {Input} from "@/components/ui/input"; FormControl,
import {Separator} from "@/components/ui/separator"; FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
type NotifierWebhookFormProps = { type NotifierWebhookFormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const NotifierWebhookForm = ({form}: NotifierWebhookFormProps) => { export const NotifierWebhookForm = ({ form }: NotifierWebhookFormProps) => {
return ( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.webhookUrl" name="config.webhookUrl"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Webhook URL</FormLabel> <FormLabel>Webhook URL</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="https://example.com/api/webhook"/> <Input {...field} placeholder="https://example.com/api/webhook" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<div className="flex gap-4"> <div className="flex gap-4">
<div className="flex-1"> <div className="flex-1">
<FormField <FormField
control={form.control} control={form.control}
name="config.webhookSecretHeader" name="config.webhookSecretHeader"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Header Name (Optional)</FormLabel> <FormLabel>Header Name (Optional)</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="X-Webhook-Secret"/> <Input {...field} placeholder="X-Webhook-Secret" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<FormField <FormField
control={form.control} control={form.control}
name="config.webhookSecret" name="config.webhookSecret"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Secret Value (Optional)</FormLabel> <FormLabel>Secret Value (Optional)</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="Secret value..."/> <PasswordInput {...field} placeholder="Secret value..." />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
</div> </div>
</div> </div>
<p className="text-xs text-muted-foreground mt-2"> <p className="text-xs text-muted-foreground mt-2">
If provided, the secret will be sent in the specified header (defaults to <code>X-Webhook-Secret</code>). If provided, the secret will be sent in the specified header (defaults
</p> to <code>X-Webhook-Secret</code>).
</> </p>
) </>
} );
};
@@ -1,115 +1,116 @@
import {UseFormReturn} from "react-hook-form"; import { UseFormReturn } from "react-hook-form";
import {FormControl, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form"; import {
import {Input} from "@/components/ui/input"; FormControl,
import {Separator} from "@/components/ui/separator"; FormField,
import {PasswordInput} from "@/components/ui/password-input"; FormItem,
import {Switch} from "@/components/ui/switch"; FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { PasswordInput } from "@/components/ui/password-input";
import { Switch } from "@/components/ui/switch";
type StorageS3FormProps = { type StorageS3FormProps = {
form: UseFormReturn<any, any, any> form: UseFormReturn<any, any, any>;
} };
export const StorageS3Form = ({form}: StorageS3FormProps) => { export const StorageS3Form = ({ form }: StorageS3FormProps) => {
return ( return (
<> <>
<Separator className="my-1"/> <Separator className="my-1" />
<FormField <FormField
control={form.control} control={form.control}
name="config.endPointUrl" name="config.endPointUrl"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Endpoint URL</FormLabel> <FormLabel>Endpoint URL</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="s3.amazonaws.com"/> <Input {...field} placeholder="s3.amazonaws.com" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.region" name="config.region"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Region</FormLabel> <FormLabel>Region</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="us-east-1"/> <Input {...field} placeholder="us-east-1" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.accessKey" name="config.accessKey"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Access Key</FormLabel> <FormLabel>Access Key</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="A1b2C3d4E5f6G7h"/> <Input {...field} placeholder="A1b2C3d4E5f6G7h" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.secretKey" name="config.secretKey"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Secret Key</FormLabel> <FormLabel>Secret Key</FormLabel>
<FormControl> <FormControl>
<PasswordInput {...field} placeholder="A1b2C3d4E5f6G7h"/> <PasswordInput {...field} placeholder="A1b2C3d4E5f6G7h" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.bucketName" name="config.bucketName"
render={({field}) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Bucket name</FormLabel> <FormLabel>Bucket name</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="portabase-dev"/> <Input {...field} placeholder="portabase-dev" />
</FormControl> </FormControl>
<FormMessage/> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.port" name="config.port"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Port</FormLabel> <FormLabel>Port</FormLabel>
<FormControl> <FormControl>
<Input {...field} type="number" placeholder="443" /> <Input {...field} type="number" placeholder="443" />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="config.ssl" name="config.ssl"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Use SSL</FormLabel> <FormLabel>Use SSL</FormLabel>
<FormControl> <FormControl>
<Switch <Switch checked={field.value} onCheckedChange={field.onChange} />
checked={field.value} </FormControl>
onCheckedChange={field.onChange} <FormMessage />
/> </FormItem>
</FormControl> )}
<FormMessage /> />
</FormItem> </>
)} );
/> };
</>
)
}