Compare commits

..
17 Commits
19 changed files with 14577 additions and 15248 deletions
+1 -1
View File
@@ -34,4 +34,4 @@ S3_USE_SSL=true
STORAGE_TYPE=local
# Retention
RETENTION_CRON="* * * * *"
RETENTION_CRON="* * * * *"
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+63
View File
@@ -0,0 +1,63 @@
name: Docker Publish
on:
workflow_call:
inputs:
image_name:
required: false
type: string
default: 'solucetechnologies/portabase'
add_latest:
required: false
type: boolean
default: false
target:
required: false
type: string
default: 'prod'
dockerfile:
required: false
type: string
default: './docker/dockerfile/Dockerfile'
secrets:
DOCKER_USERNAME:
required: true
DOCKER_PASSWORD:
required: true
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set tags
id: set-tags
run: |
REF_NAME=${GITHUB_REF#refs/tags/}
TAGS="${{ inputs.image_name }}:$REF_NAME"
if [[ "${{ inputs.add_latest }}" == "true" ]]; then
TAGS="$TAGS,${{ inputs.image_name }}:latest"
fi
echo "tags=$TAGS" >> $GITHUB_OUTPUT
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ inputs.dockerfile }}
push: true
tags: ${{ steps.set-tags.outputs.tags }}
target: ${{ inputs.target }}
+128
View File
@@ -0,0 +1,128 @@
name: GitHub Release
on:
workflow_call:
inputs:
prerelease:
required: false
type: boolean
default: false
make_latest:
required: false
type: boolean
default: false
discord_title:
required: true
type: string
discord_color:
required: true
type: number
discord_footer:
required: true
type: string
secrets:
DISCORD_WEBHOOK:
required: true
GH_TOKEN:
required: true
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build Changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v5
with:
mode: "COMMIT"
configurationJson: |
{
"template": "#{{CHANGELOG}}",
"categories": [
{
"title": "## Feature",
"labels": ["feat", "feature"]
},
{
"title": "## Fix",
"labels": ["fix", "bug"]
},
{
"title": "## Other",
"labels": []
}
],
"label_extractor": [
{
"pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)",
"on_property": "title",
"target": "$1"
}
]
}
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: false
body: ${{ steps.build_changelog.outputs.changelog }}
prerelease: ${{ inputs.prerelease }}
make_latest: ${{ inputs.make_latest }}
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
- name: Send Discord Notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
RELEASE_INFO=$(gh release view "${{ github.ref_name }}" -R ${{ github.repository }} --json name,url,body,author)
RELEASE_TITLE=$(echo "$RELEASE_INFO" | jq -r .name)
if [ -z "$RELEASE_TITLE" ] || [ "$RELEASE_TITLE" = "null" ]; then RELEASE_TITLE="${{ github.ref_name }}"; fi
RELEASE_URL=$(echo "$RELEASE_INFO" | jq -r .url)
RELEASE_BODY=$(echo "$RELEASE_INFO" | jq -r .body)
AUTHOR_NAME="Portabase"
AUTHOR_ICON="https://github.com/Portabase.png"
PAYLOAD=$(jq -n \
--arg title "$RELEASE_TITLE" \
--arg description "$RELEASE_BODY" \
--arg url "$RELEASE_URL" \
--arg author "$AUTHOR_NAME" \
--arg icon "$AUTHOR_ICON" \
--arg discord_title "${{ inputs.discord_title }}" \
--arg discord_footer "${{ inputs.discord_footer }}" \
--argjson discord_color ${{ inputs.discord_color }} \
'{
content: $discord_title,
embeds: [{
title: $title,
url: $url,
description: $description,
color: $discord_color,
author: {
name: $author,
icon_url: $icon
},
footer: {
text: $discord_footer
}
}]
}'
)
curl -H "Content-Type: application/json" \
-d "$PAYLOAD" \
"$DISCORD_WEBHOOK"
+32
View File
@@ -0,0 +1,32 @@
name: Publish Docker image for release candidate
on:
push:
tags:
- '*-rc*'
permissions:
contents: write
packages: write
jobs:
docker_publish:
uses: ./.github/workflows/docker.yml
with:
add_latest: false
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
github_release:
needs: docker_publish
uses: ./.github/workflows/github.yml
with:
prerelease: true
make_latest: false
discord_title: "||@release-dashboard|| New release candidate published"
discord_color: 16776960
discord_footer: "Portabase"
secrets:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+27 -44
View File
@@ -1,50 +1,33 @@
name: Publish Docker image for release
on:
release:
types: [published]
push:
tags:
- '*.*.*'
- '!*-*'
permissions:
contents: write
packages: write
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
docker_publish:
uses: ./.github/workflows/docker.yml
with:
add_latest: true
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: solucetechnologies/portabase
- name: Set tags
id: set-tags
run: |
echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
TAGS="solucetechnologies/portabase:${GITHUB_REF#refs/tags/}"
if [ "${{ github.event.release.target_commitish }}" = "main" ]; then
TAGS="$TAGS,solucetechnologies/portabase:latest"
fi
echo "tags=$TAGS" >> $GITHUB_OUTPUT
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/dockerfile/Dockerfile
push: true
tags: ${{ steps.set-tags.outputs.tags }}
target: prod
github_release:
needs: docker_publish
uses: ./.github/workflows/github.yml
with:
prerelease: false
make_latest: true
discord_title: "||@release-dashboard|| New release published"
discord_color: 5814783
discord_footer: "Portabase"
secrets:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
nodeLinker: node-modules
+2 -2
View File
@@ -26,5 +26,5 @@ keywords:
- web-ui
- agent
license: Apache-2.0
version: 1.1.6
date-released: "2026-01-01"
version: 1.1.9
date-released: "2026-01-07"
+22 -249
View File
@@ -1,21 +1,16 @@
<br />
<div align="center">
<a href="https://portabase.io">
<img src="/public/images/logo.png" alt="Logo" width="80" height="80">
<img src="/.github/assets/logo.png" alt="Logo" width="80" height="80">
</a>
<h3 align="center">Portabase</h3>
<p>
Take full control of your databases with Portabase — the self-hosted, open-source platform
for automated backup, restoration, and operational management. Powered by the <strong>Portabase Agent</strong>,
every database in your infrastructure can be monitored, backed up, and managed in real time,
with zero reliance on third-party services.
</p>
<p>
Secure, lightweight, and deployable anywhere — on Docker, Kubernetes, or directly on your servers.
Designed for teams, DevOps, and enterprises who demand control, reliability, and automation at scale.
<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>
</p>
[![License: Apache](https://img.shields.io/badge/License-apache-yellow.svg)](LICENSE)
[![Docker Pulls](https://img.shields.io/docker/pulls/solucetechnologies/portabase?color=brightgreen)](https://hub.docker.com/r/solucetechnologies/portabase)
[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey)](https://github.com/Portabase/portabase)
@@ -27,11 +22,18 @@
[![Open Source](https://img.shields.io/badge/open%20source-❤️-red)](https://github.com/Portabase/portabase)
[![NextJS][NextJS]][NextJS-url]
[![BetterAuth][BetterAuth]][BetterAuth-url]
[![Drizzle][Drizzle]][Drizzle-url]
[![ShadcnUI][ShadcnUI]][ShadcnUI-url]
[![Docker][Docker]][Docker-url]
<p>
<strong>
<a href="https://portabase.io">Documentation</a> •
<a href="https://portabase.io">Website</a> •
<a href="https://portabase.io/docs">Documentation</a> •
<a href="https://www.youtube.com/watch?v=hvLbX5LN1UE">Demo</a> •
<a href="#installation">Installation</a> •
<a href="https://portabase.io/docs/dashboard/setup">Installation</a> •
<a href="https://github.com/Portabase/portabase/issues/new?labels=bug&template=bug-report---.md">Report Bug</a> •
<a href="https://github.com/Portabase/portabase/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
</strong>
@@ -42,257 +44,28 @@
</div>
---
## Installation
## ✨ About The Project
You have 4 ways to install Portabase:
**Portabase** is a server dashboard tool designed to simplify the backup and restoration of your database instances. It
integrates seamlessly with [Portabase agents](https://github.com/Portabase/agent-portabase) for managing operations
securely and efficiently.
### 🔧 Built With
- [![NextJS][NextJS]][NextJS-url]
- [![Drizzle][Drizzle]][Drizzle-url]
- [![ShadcnUI][ShadcnUI]][ShadcnUI-url]
- [![BetterAuth][BetterAuth]][BetterAuth-url]
- [![Docker][Docker]][Docker-url]
---
## 📦 Features
### 🗄️ Supported databases
- PostgreSQL
- MySQL
- MariaDB
### ⏱️ Scheduled backups
- Cron-based scheduling for full control
- Manual trigger support for on-demand backups
### 💾 Storage backends
- On-premise storage: Backups are stored directly on your VPS or server
- Cloud storage: S3-compatible backends
supported ([documentation](https://portabase.io/docs/dashboard/advanced/storage/s3))
- Full data ownership: No third-party access — your data stays under your control
### 🔔 Smart notifications
- Multi-channel delivery: Email, Slack, Discord, webhooks
- Real-time alerts: Immediate feedback on success and failure
- Custom alert policies: Database-level notification rules
- Team-ready: Designed for DevOps, on-call, and incident workflows
### 👥 Built for team environments
- Workspaces: Organize databases, notification channels, and storage backends by organization and project
- Access control: Fine-grained, role-based permissions on all resources
- Role management: Member, admin, and owner roles at both system and organization levels
### 🐳 Self-hosted & secure
- Containerized deployment: Docker-based setup for predictable installation and operations
- Privacy by design: All data remains within your own infrastructure
- Open source: Apache 2.0 licensed — fully auditable codebase
### 🤖 Portabase Agent ([details](https://github.com/Portabase/agent-portabase))
- Headless architecture: Runs locally on your infrastructure to manage backups and database operations
- Multi-target support: Single agent can connect to multiple databases across different servers
- Lightweight & efficient: Minimal resource footprint while providing full operational control
- Secure communication: Encrypted channels between agent and central dashboard
---
## 🚀 Getting Started
### Installation
You have 3 ways to install **Portabase**:
- Automated CLI (recommended) - [details](https://portabase.io/docs/cli)
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup)
- Automated CLI (recommended) - [details](https://portabase.io/docs/dashboard/setup#cli)
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup#docker)
- Kubernetes with Helm (soon)
- Development setup - [details](https://portabase.io/docs/dashboard/setup#development)
Ensure Docker is installed on your machine before getting started.
**Ensure Docker is installed on your machine before getting started.**
### Docker Compose Setup
Create a `docker-compose.yml` file with the following configuration:
```yaml
name: portabase
services:
portabase:
image: solucetechnologies/portabase:latest
env_file:
- .env
ports:
- '8887:80'
environment:
- TZ="Europe/Paris"
volumes:
- portabase-private:/app/private
depends_on:
db:
condition: service_healthy
container_name: portabase-app
db:
image: postgres:17-alpine
ports:
- "5433:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=<your_database>
- POSTGRES_USER=<database_user>
- POSTGRES_PASSWORD=<database_password>
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U <database_user> -d <your_database>" ]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres-data:
portabase-private:
```
Then run:
```bash
docker compose up -d
```
If you use reverse proxy like
Traefik : [Check this link](https://portabase.io/docs/dashboard/advanced/reverse-proxy)
#### Environment Variables
```yml
# Environment
NODE_ENV=production
# Database
DATABASE_URL=postgresql://devuser:changeme@db:5432/devdb?schema=public
# Project Info
PROJECT_NAME="Portabase"
PROJECT_DESCRIPTION="Portabase is a powerful database manager"
PROJECT_URL=http://app.portabase.io
PROJECT_SECRET=
# SMTP (Email)
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=
# Google OAuth
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
AUTH_GOOGLE_METHOD=
# S3/MinIO Configuration
S3_ENDPOINT=http://app.s3.portabase.io
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_BUCKET_NAME=portabase
S3_PORT=9000
S3_USE_SSL=true
# Storage Backend: 'local' or 's3'
STORAGE_TYPE=local
# Retention
RETENTION_CRON="* * * * *"
```
To get more information about env variables, check
that [link](https://portabase.io/docs/dashboard/advanced/environment)
### Locally (Development)
1. Clone the repository:
```bash
git clone https://github.com/Portabase/portabase
cd portabase
```
2. Start the development database for Portabase service:
```bash
docker compose up
```
3. Start the Next.js app:
```bash
make up
```
---
## 🛠️ Usage
Once the installation process is done, follow the steps to configure your instance.
### Dashboard configuration process
1. **Access the dashboard** Open `http://localhost:8887` in your browser.
2. **Sign up** Register the first user, who will automatically have the **Admin** role in the default workspace.
3. **Add your first agent** Follow [this guide](https://github.com/Portabase/agent-portabase) for setup
instructions.
4. **Create organizations and projects** Link your databases to projects to enable backups and restores.
5. **Configure backup policies** Define schedules (hourly, daily, weekly, or monthly) and retention rules.
6. **Choose a storage provider** Select where backups will be stored (local, S3, etc.).
7. **Save and start** Portabase validates your configuration and starts automated backups based on your defined
policies.
---
## 🤝 Contributing
Contributions are welcome and appreciated! Here's how to get started:
1. Fork the repository
2. Create a new branch:
```bash
git checkout -b features/your-feature
```
3. Commit your changes:
```bash
git commit -m "Add YourFeature"
```
4. Push to the branch:
```bash
git push origin features/your-feature
```
5. Open a pull request
### Top Contributors
## Contributors
[![Contributors](https://contrib.rocks/image?repo=Portabase/portabase)](https://github.com/Portabase/portabase/graphs/contributors)
---
## 📄 License
## License
Distributed under the Apache License. See `LICENSE.txt` for more details.
---
## 🙏 Acknowledgments
Thanks to all contributors and the open-source community!
Give the project a ⭐ if you like it!
[Docker]: https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=fff&style=for-the-badge
+26 -34
View File
@@ -1,45 +1,37 @@
FROM node:22-alpine AS base
ENV YARN_VERSION=4.9.1
RUN apk add --update --no-cache \
tzdata \
build-base \
libc6-compat \
openssl \
make \
tzdata
FROM base AS build-env
RUN apk add --update --no-cache \
build-base \
g++ \
jpeg-dev \
cairo-dev \
giflib-dev \
pango-dev \
make \
libtool \
autoconf \
automake \
libpng
automake
RUN corepack enable && corepack prepare yarn@${YARN_VERSION}
RUN yarn set version ${YARN_VERSION}
RUN corepack enable && corepack prepare pnpm@latest --activate
FROM base AS deps
FROM build-env AS deps
WORKDIR /app
COPY .yarn ./.yarn
COPY .yarnrc.yml ./
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm i --frozen-lockfile
RUN \
if [ -f yarn.lock ]; then yarn; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS dev
FROM build-env AS dev
WORKDIR /app
@@ -52,8 +44,11 @@ RUN chmod +x /app/docker/entrypoints/app-dev-entrypoint.sh
ENTRYPOINT ["sh","/app/docker/entrypoints/app-dev-entrypoint.sh"]
# Rebuild the source code only when needed
FROM base AS builder
FROM build-env AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
@@ -61,12 +56,11 @@ COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
RUN pnpm run build
FROM base AS prod
@@ -109,5 +103,3 @@ ENV HOSTNAME="0.0.0.0"
USER nextjs
ENTRYPOINT ["sh","/app/app-prod-entrypoint.sh"]
+3 -3
View File
@@ -3,10 +3,10 @@
set -euo pipefail
echo "▶ Running Drizzle codegen..."
npx drizzle-kit generate
pnpm drizzle-kit generate
echo "▶ Applying migrations..."
npx drizzle-kit migrate
pnpm drizzle-kit migrate
echo "▶ Starting Next.js dev server..."
exec npm run dev
exec pnpm dev
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "portabase",
"version": "1.1.8",
"version": "1.1.9",
"private": true,
"scripts": {
"dev": "next dev --turbopack -p 8887",
@@ -97,6 +97,7 @@
"devDependencies": {
"@iconify/react": "^6.0.0",
"@react-email/preview-server": "4.3.2",
"@react-email/render": "^2.0.1",
"@tailwindcss/postcss": "^4.1.7",
"@types/eslint-plugin-tailwindcss": "^3.17.0",
"@types/node": "^22.15.18",
@@ -111,6 +112,7 @@
"eslint": "^9.39.0",
"eslint-config-next": "^16.0.1",
"eslint-plugin-tailwindcss": "^3.18.0",
"framer-motion": "^12.24.7",
"postcss": "^8.5.3",
"tailwindcss": "^4.1.7",
"tsx": "^4.19.4",
@@ -118,5 +120,5 @@
"typescript": "^5.8.3",
"zenstack": "2.14.2"
},
"packageManager": "yarn@4.9.1"
"packageManager": "pnpm@10.27.0"
}
+14158
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
onlyBuiltDependencies:
- zenstack
Executable
+95
View File
@@ -0,0 +1,95 @@
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: ./release <version>"
echo "Example: ./release v1.0.0"
exit 1
fi
VERSION=$1
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" = "main" ]; then
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: On 'main' branch, only release tags (X.Y.Z) are allowed."
exit 1
fi
else
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then
echo "Error: On branch '$CURRENT_BRANCH', only RC tags matching X.Y.Z-rc.W are allowed (e.g., 1.0.0-rc.1)."
exit 1
fi
fi
CLEAN_VERSION=${VERSION#v}
CURRENT_DATE=$(date +%Y-%m-%d)
echo "Preparing release $VERSION..."
# package.json
if [ -f package.json ]; then
echo "Updating package.json..."
if sed --version >/dev/null 2>&1; then
sed -i "s/\"version\": \".*\"/\"version\": \"$CLEAN_VERSION\"/" package.json
else
sed -i '' "s/\"version\": \".*\"/\"version\": \"$CLEAN_VERSION\"/" package.json
fi
fi
# pyproject.toml
if [ -f pyproject.toml ]; then
echo "Updating pyproject.toml..."
if sed --version >/dev/null 2>&1; then
sed -i "s/^version = \".*\"/version = \"$CLEAN_VERSION\"/" pyproject.toml
else
sed -i '' "s/^version = \".*\"/version = \"$CLEAN_VERSION\"/" pyproject.toml
fi
fi
# Cargo.toml
if [ -f Cargo.toml ]; then
echo "Updating Cargo.toml..."
if sed --version >/dev/null 2>&1; then
sed -i "s/^version = \".*\"/version = \"$CLEAN_VERSION\"/" Cargo.toml
else
sed -i '' "s/^version = \".*\"/version = \"$CLEAN_VERSION\"/" Cargo.toml
fi
fi
# CITATION.cff
if [ -f CITATION.cff ]; then
echo "Updating CITATION.cff..."
if sed --version >/dev/null 2>&1; then
sed -i "s/^version: .*/version: $CLEAN_VERSION/" CITATION.cff
sed -i "s/^date-released: .*/date-released: \"$CURRENT_DATE\"/" CITATION.cff
else
sed -i '' "s/^version: .*/version: $CLEAN_VERSION/" CITATION.cff
sed -i '' "s/^date-released: .*/date-released: \"$CURRENT_DATE\"/" CITATION.cff
fi
fi
git add .
if ! git diff-index --quiet HEAD --; then
echo "Committing changes..."
git commit -m "chore(release): $VERSION"
else
echo "No changes to commit. Proceeding to tag..."
fi
if git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "Tag $VERSION already exists. Aborting."
exit 1
fi
echo "Creating tag $VERSION..."
git tag -a "$VERSION" -m "Release $VERSION"
echo "Pushing changes and tags to remote..."
git push
git push origin "$VERSION"
echo "Successfully released $VERSION!"
@@ -1,12 +1,12 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { UploadIcon } from "lucide-react";
import { toast } from "sonner";
import { uploadImageAction } from "@/features/upload/public/upload.action";
import { useMutation } from "@tanstack/react-query";
import { updateImageUserAction } from "@/components/wrappers/dashboard/profile/actions/avatar.action";
import { useRouter } from "next/navigation";
import { User } from "@/db/schema/02_user";
import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar";
import {UploadIcon} from "lucide-react";
import {toast} from "sonner";
import {uploadImageAction} from "@/features/upload/public/upload.action";
import {useMutation} from "@tanstack/react-query";
import {updateImageUserAction} from "@/components/wrappers/dashboard/profile/actions/avatar.action";
import {useRouter} from "next/navigation";
import {User} from "@/db/schema/02_user";
import React, {ChangeEvent} from "react";
export type AvatarWithUploadProps = {
@@ -55,12 +55,11 @@ export const AvatarWithUpload = (props: AvatarWithUploadProps) => {
};
return (
<div className="relative ">
<Avatar className="w-24 h-24 lg:w-32 lg:h-32 border-4 border-muted/20">
<AvatarImage src={user.image || undefined}/>
<AvatarImage className="object-cover" src={user.image || undefined}/>
<AvatarFallback className="text-3xl">{user.name.charAt(0)}</AvatarFallback>
</Avatar>
@@ -75,7 +74,7 @@ export const AvatarWithUpload = (props: AvatarWithUploadProps) => {
}}
className="cursor-pointer absolute inset-0 flex justify-center items-center opacity-0 transition-opacity hover:opacity-100 hover:bg-gray-500 hover:bg-opacity-50 rounded-full w-24 h-24 lg:w-32 lg:h-32"
>
<UploadIcon className="w-12 h-12 lg:w-16 lg:h-16 text-primary" />
<UploadIcon className="w-12 h-12 lg:w-16 lg:h-16 text-primary"/>
</div>
</div>
);
+4 -4
View File
@@ -15,7 +15,7 @@ import {Setting} from "@/db/schema/01_setting";
import * as drizzleDb from "@/db";
const privateS3ImageDir = "images/";
const imageDir = "images/";
export const uploadImageAction = userAction
@@ -39,10 +39,10 @@ export const uploadImageAction = userAction
let result: void | UploadedObjectInfo;
if (settings.storage === "local") {
fileName = `${uuid}.${fileFormat}`;
fileName = `${imageDir}${uuid}.${fileFormat}`;
result = await uploadLocal(fileName, buffer);
} else if (settings.storage === "s3") {
fileName = `${privateS3ImageDir}${uuid}.${fileFormat}`;
fileName = `${imageDir}${uuid}.${fileFormat}`;
result = await uploadS3Compatible(env.S3_BUCKET_NAME ?? "", fileName, buffer);
} else {
throw new Error(`Unsupported storage type: ${settings.storage}`);
@@ -54,7 +54,7 @@ export const uploadImageAction = userAction
async function uploadLocal(fileName: string, buffer: any) {
const localDir = "private/uploads/images/";
const localDir = "private/uploads/";
try {
await mkdir(path.join(process.cwd(), localDir), {recursive: true});
return await writeFile(path.join(process.cwd(), localDir + fileName), buffer);
-14896
View File
File diff suppressed because it is too large Load Diff