mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Compare commits
24
Commits
1.2.6-rc.2
...
1.2.8-rc.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afd98ae652 | ||
|
|
8f905c8128 | ||
|
|
f43f30c9b9 | ||
|
|
6b1bea9b17 | ||
|
|
cdaed9766e | ||
|
|
d31a9ab2d2 | ||
|
|
07d49749fb | ||
|
|
a1f1da8a07 | ||
|
|
1b791fd287 | ||
|
|
2a4af58a51 | ||
|
|
b12f8ca044 | ||
|
|
519b3abd1d | ||
|
|
e08a5bd6b6 | ||
|
|
f6c597b42d | ||
|
|
f3b9032174 | ||
|
|
44b4ee2953 | ||
|
|
b77f79f42d | ||
|
|
8017bf7c8a | ||
|
|
85f4cda752 | ||
|
|
1fd2f553ed | ||
|
|
d68170eda8 | ||
|
|
51f27226e7 | ||
|
|
b345eda7aa | ||
|
|
7ab213e5e4 |
+1
-3
@@ -2,9 +2,7 @@ title = "Custom gitleaks config"
|
||||
|
||||
[allowlist]
|
||||
# Global allowlist patterns (won’t be flagged)
|
||||
regexes = [
|
||||
"s3SecretAccessKey: env.S3_SECRET_KEY ?? null",
|
||||
]
|
||||
regexes = []
|
||||
paths = [
|
||||
"src/utils/init.ts"
|
||||
]
|
||||
|
||||
+2
-2
@@ -26,5 +26,5 @@ keywords:
|
||||
- web-ui
|
||||
- agent
|
||||
license: Apache-2.0
|
||||
version: 1.2.6-rc.2
|
||||
date-released: "2026-02-15"
|
||||
version: 1.2.8-rc.1
|
||||
date-released: "2026-02-17"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<h3 align="center">Portabase</h3>
|
||||
|
||||
<p align="center" style="margin-top: 20px; font-style: italic;">
|
||||
<i>Portabase is a tool designed to simplify the backup and restoration of your database instances. It integrates seamlessly with <a href="https://github.com/Portabase/agent">Portabase agents</a> for managing operations securely and efficiently.</i>
|
||||
<i>Portabase is a tool designed to simplify the backup and restoration of your database instances. It integrates seamlessly with <a href="https://github.com/Portabase/agent-rust">Portabase agents</a> for managing operations securely and efficiently.</i>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
[](https://github.com/Portabase/portabase)
|
||||
[](https://github.com/Portabase/portabase)
|
||||
|
||||
|
||||
[![NextJS][NextJS]][NextJS-url]
|
||||
[![BetterAuth][BetterAuth]][BetterAuth-url]
|
||||
[![Drizzle][Drizzle]][Drizzle-url]
|
||||
@@ -51,14 +50,13 @@
|
||||
You have 4 ways to install Portabase:
|
||||
|
||||
- Automated CLI (recommended) - [details](https://portabase.io/docs/dashboard/setup#cli)
|
||||
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup#docker)
|
||||
- Docker Run - [details](https://portabase.io/docs/dashboard/setup#docker)
|
||||
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup#docker-compose)
|
||||
- Kubernetes with Helm (soon)
|
||||
- Development setup - [details](https://portabase.io/docs/dashboard/setup#development)
|
||||
|
||||
**Ensure Docker is installed on your machine before getting started.**
|
||||
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
[](https://github.com/Portabase/portabase/graphs/contributors)
|
||||
|
||||
@@ -35,12 +35,10 @@ export default async function RoutePage(props: PageParams<{}>) {
|
||||
<PageHeader>
|
||||
<PageTitle>Notification channels</PageTitle>
|
||||
<PageActions>
|
||||
{/*<NotifierAddEditModal adminView={false}/>*/}
|
||||
<ChannelAddEditModal kind="notification" adminView={false}/>
|
||||
</PageActions>
|
||||
</PageHeader>
|
||||
<PageContent>
|
||||
{/*<NotificationChannelsSection organizations={organizations} notificationChannels={notificationChannels}/>*/}
|
||||
<ChannelsSection kind="notification" organizations={organizations}
|
||||
channels={notificationChannels}/>
|
||||
</PageContent>
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
# context: .
|
||||
# dockerfile: docker/dockerfile/Dockerfile
|
||||
# target: prod
|
||||
image: portabase/portabase:latest
|
||||
image: portabase/portabase:1.2.7-rc.1
|
||||
ports:
|
||||
- '8887:80'
|
||||
environment:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "portabase",
|
||||
"version": "1.2.6-rc.2",
|
||||
"version": "1.2.8-rc.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack -p 8887",
|
||||
|
||||
+1
-3
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
export interface AuthProviderConfig {
|
||||
id: "google" | "github" | "credential";
|
||||
isActive: boolean;
|
||||
@@ -64,7 +62,7 @@ export const PORTABASE_DEFAULT_SETTINGS = {
|
||||
FORM_ACTION: ["'self'"],
|
||||
FRAME_ANCESTORS: ["'none'"],
|
||||
BLOCK_ALL_MIXED_CONTENT: false,
|
||||
UPGRADE_INSECURE_REQUESTS: true,
|
||||
UPGRADE_INSECURE_REQUESTS: process.env.PROJECT_URL?.startsWith("https://") || false,
|
||||
},
|
||||
PERMISSIONS_POLICY: {
|
||||
CAMERA: ["()"],
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: ./release <version>"
|
||||
echo "Example: ./release v1.0.0"
|
||||
echo "Example: ./release 1.0.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ export const ChannelForm = ({onSuccessAction, organization, defaultValues, kind}
|
||||
|
||||
<div className="flex justify-between mt-4">
|
||||
<div>
|
||||
{kind == "storage" && (
|
||||
{(!isCreate || kind == "storage") && (
|
||||
<ChannelTestButton
|
||||
kind={kind}
|
||||
organizationId={organization?.id}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ export const ChannelTestButton = ({channel, organizationId, kind}: NotifierTestC
|
||||
message: `We are testing channel ${channel.name}`,
|
||||
level: 'info',
|
||||
};
|
||||
const result = await dispatchNotification(payload, undefined, channel, organizationId);
|
||||
const result = await dispatchNotification(payload, undefined, channel.id, organizationId);
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
|
||||
+3
-17
@@ -15,8 +15,8 @@ export const env = createEnv({
|
||||
|
||||
PROJECT_NAME: z.string().optional(),
|
||||
PROJECT_DESCRIPTION: z.string().optional(),
|
||||
PROJECT_URL: z.string().optional(),
|
||||
PROJECT_SECRET: z.string().optional(),
|
||||
PROJECT_URL: z.string().regex(/^https?:\/\//, "URL must start with http:// or https://"),
|
||||
PROJECT_SECRET: z.string(),
|
||||
|
||||
SMTP_PASSWORD: z.string().optional(),
|
||||
SMTP_FROM: z.string().optional(),
|
||||
@@ -29,20 +29,13 @@ export const env = createEnv({
|
||||
AUTH_GOOGLE_SECRET: z.string().optional(),
|
||||
AUTH_GOOGLE_METHOD: z.boolean().default(false),
|
||||
|
||||
S3_ENDPOINT: z.string().optional(),
|
||||
S3_ACCESS_KEY: z.string().optional(),
|
||||
S3_SECRET_KEY: z.string().optional(),
|
||||
S3_BUCKET_NAME: z.string().optional(),
|
||||
S3_PORT: z.string().optional(),
|
||||
S3_USE_SSL: z.string().optional(),
|
||||
|
||||
STORAGE_TYPE: z.enum(["local", "s3"]).optional(),
|
||||
|
||||
RETENTION_CRON: z
|
||||
.string()
|
||||
.default(process.env.NODE_ENV === "production" ? "0 7 * * *" : "* * * * *"),
|
||||
|
||||
PRIVATE_PATH: z.string(),
|
||||
PRIVATE_PATH: z.string().optional(),
|
||||
|
||||
},
|
||||
client: {
|
||||
@@ -69,13 +62,6 @@ export const env = createEnv({
|
||||
AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET,
|
||||
AUTH_GOOGLE_METHOD: process.env.AUTH_GOOGLE_METHOD === "true",
|
||||
|
||||
S3_ENDPOINT: process.env.S3_ENDPOINT,
|
||||
S3_ACCESS_KEY: process.env.S3_ACCESS_KEY,
|
||||
S3_SECRET_KEY: process.env.S3_SECRET_KEY,
|
||||
S3_BUCKET_NAME: process.env.S3_BUCKET_NAME,
|
||||
S3_PORT: process.env.S3_PORT,
|
||||
S3_USE_SSL: process.env.S3_USE_SSL,
|
||||
|
||||
STORAGE_TYPE: process.env.STORAGE_TYPE,
|
||||
|
||||
RETENTION_CRON: process.env.RETENTION_CRON,
|
||||
|
||||
@@ -19,8 +19,6 @@ export const useUpdateCheck = () => {
|
||||
staleTime: 1000 * 60 * 60,
|
||||
});
|
||||
|
||||
console.log("newRelease", newRelease);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!newRelease) return;
|
||||
|
||||
+15
-2
@@ -192,6 +192,8 @@ export const auth = betterAuth({
|
||||
session: {
|
||||
create: {
|
||||
before: async (session, context) => {
|
||||
|
||||
|
||||
const userId = session.userId;
|
||||
|
||||
let memberships = await db.query.member.findMany({
|
||||
@@ -231,6 +233,9 @@ export const auth = betterAuth({
|
||||
// }
|
||||
// },
|
||||
after: async (session) => {
|
||||
|
||||
console.log("session", session);
|
||||
|
||||
const user = await db.query.user.findFirst({
|
||||
where: eq(drizzleDb.schemas.user.id, session.userId),
|
||||
});
|
||||
@@ -244,6 +249,12 @@ export const auth = betterAuth({
|
||||
return;
|
||||
}
|
||||
|
||||
const lastDiff = user.lastConnectedAt
|
||||
? new Date(session.createdAt).getTime() - new Date(user.lastConnectedAt).getTime()
|
||||
: Infinity;
|
||||
|
||||
if (lastDiff < 30000) return;
|
||||
|
||||
if (user.role === "pending") return;
|
||||
|
||||
const deviceInfo = getDeviceDetails(session.userAgent);
|
||||
@@ -509,7 +520,8 @@ export const checkSlugOrganization = async (slug: string) => {
|
||||
});
|
||||
|
||||
return status;
|
||||
} catch {}
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
export const getActiveMember = async () => {
|
||||
@@ -532,5 +544,6 @@ export const setActiveOrganization = async (slug: string) => {
|
||||
organizationSlug: slug,
|
||||
},
|
||||
});
|
||||
} catch {}
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import {db} from "@/db";
|
||||
import {and, eq, isNotNull, isNull} from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {withUpdatedAt} from "@/db/utils";
|
||||
|
||||
export const backupCleanTask = async () => {
|
||||
try {
|
||||
const backups = await db.query.backup.findMany({
|
||||
where: and(
|
||||
isNotNull(drizzleDb.schemas.backup.deletedAt),
|
||||
eq(drizzleDb.schemas.backup.status, "ongoing")
|
||||
)
|
||||
});
|
||||
|
||||
console.log(`Backups to clean: ${backups.length}`);
|
||||
|
||||
for (const backup of backups) {
|
||||
await db.update(drizzleDb.schemas.backup).set(withUpdatedAt({
|
||||
status: "failed",
|
||||
}))
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
}
|
||||
|
||||
} catch (e: any) {
|
||||
console.error("Backup cleanup failed:", e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import cron from "node-cron";
|
||||
import {retentionCleanTask} from "@/lib/tasks/database";
|
||||
import {env} from "@/env.mjs";
|
||||
import {backupCleanTask} from "@/lib/tasks/cleaning";
|
||||
|
||||
|
||||
export const retentionJob = cron.schedule(env.RETENTION_CRON, async () => {
|
||||
@@ -10,4 +11,13 @@ export const retentionJob = cron.schedule(env.RETENTION_CRON, async () => {
|
||||
} catch (err) {
|
||||
console.error(`[CRON] Error:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
export const cleaningJob = cron.schedule("* * * * *", async () => {
|
||||
try {
|
||||
console.log("Cleaning Job : Starting task");
|
||||
await backupCleanTask();
|
||||
} catch (err) {
|
||||
console.error(`[CRON] Error:`, err);
|
||||
}
|
||||
});
|
||||
@@ -3,7 +3,6 @@ import {getMasterServerKeyContent} from "@/features/keys/keys.action";
|
||||
|
||||
export async function generateEdgeKey(serverUrl: string, agentId: string): Promise<string> {
|
||||
const masterKey = getMasterServerKeyContent()
|
||||
console.log("Master server key: ", masterKey)
|
||||
const edgeKeyData = {
|
||||
serverUrl,
|
||||
agentId,
|
||||
|
||||
+8
-1
@@ -2,7 +2,7 @@ import {env} from "@/env.mjs";
|
||||
import {db, makeMigration} from "@/db";
|
||||
import {eq} from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {retentionJob} from "@/lib/tasks";
|
||||
import {cleaningJob, retentionJob} from "@/lib/tasks";
|
||||
import {generateRSAKeys, getOrCreateMasterKey} from "@/utils/rsa-keys";
|
||||
import {StorageProviderKind} from "@/features/storages/types";
|
||||
|
||||
@@ -17,6 +17,7 @@ export async function init() {
|
||||
await createSettingsIfNotExist()
|
||||
console.log("====Initialization completed====");
|
||||
await setupCronJobs()
|
||||
await setupCleaningJobs()
|
||||
}
|
||||
|
||||
async function setupCronJobs() {
|
||||
@@ -25,6 +26,12 @@ async function setupCronJobs() {
|
||||
console.log("==== Cron job started ====");
|
||||
}
|
||||
|
||||
async function setupCleaningJobs() {
|
||||
console.log("==== Setting up Cleaning Jobs ====");
|
||||
cleaningJob.start();
|
||||
console.log("==== Cleaning job started ====");
|
||||
}
|
||||
|
||||
async function createSettingsIfNotExist() {
|
||||
await db.transaction(async (tx) => {
|
||||
const systemSettingsValues = {
|
||||
|
||||
Reference in New Issue
Block a user