mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d0ae32d0c | ||
|
|
011985d723 | ||
|
|
d677ee61de | ||
|
|
c6b8f6e87a | ||
|
|
2bb5f93d00 | ||
|
|
b91c150300 | ||
|
|
12b119ce40 | ||
|
|
7c6f0ab4ba | ||
|
|
6d2db4b298 | ||
|
|
6397423298 | ||
|
|
3470aae8e3 | ||
|
|
184fbcdb2c | ||
|
|
2d897dd722 |
@@ -245,7 +245,10 @@ PG_BIN="/usr/lib/postgresql/17/bin"
|
||||
# Ensure proper ownership of data directory
|
||||
echo "Setting up data directory permissions..."
|
||||
mkdir -p /databasus-data/pgdata
|
||||
mkdir -p /databasus-data/temp
|
||||
mkdir -p /databasus-data/backups
|
||||
chown -R postgres:postgres /databasus-data
|
||||
chmod 700 /databasus-data/temp
|
||||
|
||||
# Initialize PostgreSQL if not already initialized
|
||||
if [ ! -s "/databasus-data/pgdata/PG_VERSION" ]; then
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<img src="assets/logo.svg" alt="Databasus Logo" width="250"/>
|
||||
|
||||
<h3>Backup tool for PostgreSQL, MySQL and MongoDB</h3>
|
||||
<p>Databasus is a free, open source and self-hosted tool to backup databases. Make backups with different storages (S3, Google Drive, FTP, etc.) and notifications about progress (Slack, Discord, Telegram, etc.). Previously known as Postgresus (see migration guide).</p>
|
||||
<p>Databasus is a free, open source and self-hosted tool to backup databases (with focus on PostgreSQL). Make backups with different storages (S3, Google Drive, FTP, etc.) and notifications about progress (Slack, Discord, Telegram, etc.). Previously known as Postgresus (see migration guide).</p>
|
||||
|
||||
<!-- Badges -->
|
||||
[](https://www.postgresql.org/)
|
||||
|
||||
@@ -122,6 +122,7 @@ func (uc *CreateMariadbBackupUsecase) buildMariadbDumpArgs(
|
||||
|
||||
if mdb.IsHttps {
|
||||
args = append(args, "--ssl")
|
||||
args = append(args, "--skip-ssl-verify-server-cert")
|
||||
}
|
||||
|
||||
if mdb.Database != nil && *mdb.Database != "" {
|
||||
@@ -265,11 +266,24 @@ func (uc *CreateMariadbBackupUsecase) createTempMyCnfFile(
|
||||
mdbConfig *mariadbtypes.MariadbDatabase,
|
||||
password string,
|
||||
) (string, error) {
|
||||
tempDir, err := os.MkdirTemp(config.GetEnv().TempFolder, "mycnf_"+uuid.New().String())
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "mycnf_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temp directory permissions: %w", err)
|
||||
}
|
||||
|
||||
myCnfFile := filepath.Join(tempDir, ".my.cnf")
|
||||
|
||||
content := fmt.Sprintf(`[client]
|
||||
@@ -287,6 +301,7 @@ port=%d
|
||||
|
||||
err = os.WriteFile(myCnfFile, []byte(content), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write .my.cnf: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -280,11 +280,24 @@ func (uc *CreateMysqlBackupUsecase) createTempMyCnfFile(
|
||||
myConfig *mysqltypes.MysqlDatabase,
|
||||
password string,
|
||||
) (string, error) {
|
||||
tempDir, err := os.MkdirTemp(config.GetEnv().TempFolder, "mycnf_"+uuid.New().String())
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "mycnf_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temp directory permissions: %w", err)
|
||||
}
|
||||
|
||||
myCnfFile := filepath.Join(tempDir, ".my.cnf")
|
||||
|
||||
content := fmt.Sprintf(`[client]
|
||||
@@ -300,6 +313,7 @@ port=%d
|
||||
|
||||
err = os.WriteFile(myCnfFile, []byte(content), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write .my.cnf: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -757,14 +757,28 @@ func (uc *CreatePostgresqlBackupUsecase) createTempPgpassFile(
|
||||
escapedPassword,
|
||||
)
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "pgpass")
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "pgpass_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temporary directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temporary directory permissions: %w", err)
|
||||
}
|
||||
|
||||
pgpassFile := filepath.Join(tempDir, ".pgpass")
|
||||
err = os.WriteFile(pgpassFile, []byte(pgpassContent), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write temporary .pgpass file: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package mariadb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
"databasus-backend/internal/util/encryption"
|
||||
"databasus-backend/internal/util/tools"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -398,8 +399,16 @@ func HasPrivilege(privileges, priv string) bool {
|
||||
|
||||
func (m *MariadbDatabase) buildDSN(password string, database string) string {
|
||||
tlsConfig := "false"
|
||||
|
||||
if m.IsHttps {
|
||||
tlsConfig = "skip-verify"
|
||||
err := mysql.RegisterTLSConfig("mariadb-skip-verify", &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if err != nil {
|
||||
// Config might already be registered, which is fine
|
||||
_ = err
|
||||
}
|
||||
tlsConfig = "mariadb-skip-verify"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
@@ -562,9 +571,9 @@ func detectPrivileges(ctx context.Context, db *sql.DB, database string) (string,
|
||||
}
|
||||
|
||||
// checkBackupPermissions verifies the user has sufficient privileges for mariadb-dump backup.
|
||||
// Required: SELECT, SHOW VIEW, PROCESS. Optional: LOCK TABLES, TRIGGER, EVENT.
|
||||
// Required: SELECT, SHOW VIEW
|
||||
func checkBackupPermissions(privileges string) error {
|
||||
requiredPrivileges := []string{"SELECT", "SHOW VIEW", "PROCESS"}
|
||||
requiredPrivileges := []string{"SELECT", "SHOW VIEW"}
|
||||
|
||||
var missingPrivileges []string
|
||||
for _, priv := range requiredPrivileges {
|
||||
@@ -575,7 +584,7 @@ func checkBackupPermissions(privileges string) error {
|
||||
|
||||
if len(missingPrivileges) > 0 {
|
||||
return fmt.Errorf(
|
||||
"insufficient permissions for backup. Missing: %s. Required: SELECT, SHOW VIEW, PROCESS",
|
||||
"insufficient permissions for backup. Missing: %s. Required: SELECT, SHOW VIEW",
|
||||
strings.Join(missingPrivileges, ", "),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
"databasus-backend/internal/util/encryption"
|
||||
"databasus-backend/internal/util/tools"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -399,8 +400,17 @@ func HasPrivilege(privileges, priv string) bool {
|
||||
|
||||
func (m *MysqlDatabase) buildDSN(password string, database string) string {
|
||||
tlsConfig := "false"
|
||||
|
||||
if m.IsHttps {
|
||||
tlsConfig = "skip-verify"
|
||||
err := mysql.RegisterTLSConfig("mysql-skip-verify", &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if err != nil {
|
||||
// Config might already be registered, which is fine
|
||||
_ = err
|
||||
}
|
||||
|
||||
tlsConfig = "mysql-skip-verify"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
@@ -532,9 +542,9 @@ func detectPrivileges(ctx context.Context, db *sql.DB, database string) (string,
|
||||
}
|
||||
|
||||
// checkBackupPermissions verifies the user has sufficient privileges for mysqldump backup.
|
||||
// Required: SELECT, SHOW VIEW, PROCESS. Optional: LOCK TABLES, TRIGGER, EVENT.
|
||||
// Required: SELECT, SHOW VIEW
|
||||
func checkBackupPermissions(privileges string) error {
|
||||
requiredPrivileges := []string{"SELECT", "SHOW VIEW", "PROCESS"}
|
||||
requiredPrivileges := []string{"SELECT", "SHOW VIEW"}
|
||||
|
||||
var missingPrivileges []string
|
||||
for _, priv := range requiredPrivileges {
|
||||
@@ -545,7 +555,7 @@ func checkBackupPermissions(privileges string) error {
|
||||
|
||||
if len(missingPrivileges) > 0 {
|
||||
return fmt.Errorf(
|
||||
"insufficient permissions for backup. Missing: %s. Required: SELECT, SHOW VIEW, PROCESS",
|
||||
"insufficient permissions for backup. Missing: %s. Required: SELECT, SHOW VIEW",
|
||||
strings.Join(missingPrivileges, ", "),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ func (uc *RestoreMariadbBackupUsecase) Execute(
|
||||
|
||||
if mdb.IsHttps {
|
||||
args = append(args, "--ssl")
|
||||
args = append(args, "--skip-ssl-verify-server-cert")
|
||||
}
|
||||
|
||||
if mdb.Database != nil && *mdb.Database != "" {
|
||||
@@ -265,11 +266,24 @@ func (uc *RestoreMariadbBackupUsecase) createTempMyCnfFile(
|
||||
mdbConfig *mariadbtypes.MariadbDatabase,
|
||||
password string,
|
||||
) (string, error) {
|
||||
tempDir, err := os.MkdirTemp(config.GetEnv().TempFolder, "mycnf_"+uuid.New().String())
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "mycnf_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temp directory permissions: %w", err)
|
||||
}
|
||||
|
||||
myCnfFile := filepath.Join(tempDir, ".my.cnf")
|
||||
|
||||
content := fmt.Sprintf(`[client]
|
||||
@@ -287,6 +301,7 @@ port=%d
|
||||
|
||||
err = os.WriteFile(myCnfFile, []byte(content), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write .my.cnf: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -257,11 +257,24 @@ func (uc *RestoreMysqlBackupUsecase) createTempMyCnfFile(
|
||||
myConfig *mysqltypes.MysqlDatabase,
|
||||
password string,
|
||||
) (string, error) {
|
||||
tempDir, err := os.MkdirTemp(config.GetEnv().TempFolder, "mycnf_"+uuid.New().String())
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "mycnf_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temp directory permissions: %w", err)
|
||||
}
|
||||
|
||||
myCnfFile := filepath.Join(tempDir, ".my.cnf")
|
||||
|
||||
content := fmt.Sprintf(`[client]
|
||||
@@ -277,6 +290,7 @@ port=%d
|
||||
|
||||
err = os.WriteFile(myCnfFile, []byte(content), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write .my.cnf: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -925,14 +925,28 @@ func (uc *RestorePostgresqlBackupUsecase) createTempPgpassFile(
|
||||
escapedPassword,
|
||||
)
|
||||
|
||||
tempDir, err := os.MkdirTemp(config.GetEnv().TempFolder, "pgpass_"+uuid.New().String())
|
||||
tempFolder := config.GetEnv().TempFolder
|
||||
if err := os.MkdirAll(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to ensure temp folder exists: %w", err)
|
||||
}
|
||||
if err := os.Chmod(tempFolder, 0700); err != nil {
|
||||
return "", fmt.Errorf("failed to set temp folder permissions: %w", err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp(tempFolder, "pgpass_"+uuid.New().String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temporary directory: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(tempDir, 0700); err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to set temporary directory permissions: %w", err)
|
||||
}
|
||||
|
||||
pgpassFile := filepath.Join(tempDir, ".pgpass")
|
||||
err = os.WriteFile(pgpassFile, []byte(pgpassContent), 0600)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("failed to write temporary .pgpass file: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,3 @@ export const GOOGLE_CLIENT_ID =
|
||||
export function getOAuthRedirectUri(): string {
|
||||
return `${window.location.origin}/auth/callback`;
|
||||
}
|
||||
|
||||
export function isOAuthEnabled(): boolean {
|
||||
return IS_CLOUD && (!!GITHUB_CLIENT_ID || !!GOOGLE_CLIENT_ID);
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { GithubOutlined, GoogleOutlined } from '@ant-design/icons';
|
||||
import { Button, message } from 'antd';
|
||||
|
||||
import {
|
||||
GITHUB_CLIENT_ID,
|
||||
GOOGLE_CLIENT_ID,
|
||||
getOAuthRedirectUri,
|
||||
isOAuthEnabled,
|
||||
} from '../../../constants';
|
||||
|
||||
export function OauthComponent() {
|
||||
if (!isOAuthEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const redirectUri = getOAuthRedirectUri();
|
||||
|
||||
const handleGitHubLogin = () => {
|
||||
if (!GITHUB_CLIENT_ID) {
|
||||
message.error('GitHub OAuth is not configured');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
client_id: GITHUB_CLIENT_ID,
|
||||
redirect_uri: redirectUri,
|
||||
state: 'github',
|
||||
scope: 'user:email',
|
||||
});
|
||||
|
||||
const githubAuthUrl = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
||||
|
||||
// Validate URL is properly formed
|
||||
new URL(githubAuthUrl);
|
||||
window.location.href = githubAuthUrl;
|
||||
} catch (error) {
|
||||
message.error('Invalid OAuth configuration');
|
||||
console.error('GitHub OAuth URL error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogleLogin = () => {
|
||||
if (!GOOGLE_CLIENT_ID) {
|
||||
message.error('Google OAuth is not configured');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
client_id: GOOGLE_CLIENT_ID,
|
||||
redirect_uri: redirectUri,
|
||||
response_type: 'code',
|
||||
scope: 'openid email profile',
|
||||
state: 'google',
|
||||
});
|
||||
|
||||
const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
||||
|
||||
// Validate URL is properly formed
|
||||
new URL(googleAuthUrl);
|
||||
window.location.href = googleAuthUrl;
|
||||
} catch (error) {
|
||||
message.error('Invalid OAuth configuration');
|
||||
console.error('Google OAuth URL error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<div className="space-y-2">
|
||||
{GITHUB_CLIENT_ID && (
|
||||
<Button
|
||||
icon={<GithubOutlined />}
|
||||
onClick={handleGitHubLogin}
|
||||
className="w-full"
|
||||
size="large"
|
||||
>
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{GOOGLE_CLIENT_ID && (
|
||||
<Button
|
||||
icon={<GoogleOutlined />}
|
||||
onClick={handleGoogleLogin}
|
||||
className="w-full"
|
||||
size="large"
|
||||
>
|
||||
Continue with Google
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,11 +2,12 @@ import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
|
||||
import { Button, Input } from 'antd';
|
||||
import { type JSX, useState } from 'react';
|
||||
|
||||
import { IS_CLOUD } from '../../../constants';
|
||||
import { GITHUB_CLIENT_ID, GOOGLE_CLIENT_ID } from '../../../constants';
|
||||
import { userApi } from '../../../entity/users';
|
||||
import { StringUtils } from '../../../shared/lib';
|
||||
import { FormValidator } from '../../../shared/lib/FormValidator';
|
||||
import { OauthComponent } from './OauthComponent';
|
||||
import { GithubOAuthComponent } from './oauth/GithubOAuthComponent';
|
||||
import { GoogleOAuthComponent } from './oauth/GoogleOAuthComponent';
|
||||
|
||||
interface SignInComponentProps {
|
||||
onSwitchToSignUp?: () => void;
|
||||
@@ -67,9 +68,14 @@ export function SignInComponent({ onSwitchToSignUp }: SignInComponentProps): JSX
|
||||
<div className="w-full max-w-[300px]">
|
||||
<div className="mb-5 text-center text-2xl font-bold">Sign in</div>
|
||||
|
||||
<OauthComponent />
|
||||
<div className="mt-4">
|
||||
<div className="space-y-2">
|
||||
<GithubOAuthComponent />
|
||||
<GoogleOAuthComponent />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{IS_CLOUD && (
|
||||
{(GOOGLE_CLIENT_ID || GITHUB_CLIENT_ID) && (
|
||||
<div className="relative my-6">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300"></div>
|
||||
|
||||
@@ -2,11 +2,12 @@ import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
|
||||
import { App, Button, Input } from 'antd';
|
||||
import { type JSX, useState } from 'react';
|
||||
|
||||
import { IS_CLOUD } from '../../../constants';
|
||||
import { GITHUB_CLIENT_ID, GOOGLE_CLIENT_ID } from '../../../constants';
|
||||
import { userApi } from '../../../entity/users';
|
||||
import { StringUtils } from '../../../shared/lib';
|
||||
import { FormValidator } from '../../../shared/lib/FormValidator';
|
||||
import { OauthComponent } from './OauthComponent';
|
||||
import { GithubOAuthComponent } from './oauth/GithubOAuthComponent';
|
||||
import { GoogleOAuthComponent } from './oauth/GoogleOAuthComponent';
|
||||
|
||||
interface SignUpComponentProps {
|
||||
onSwitchToSignIn?: () => void;
|
||||
@@ -98,9 +99,14 @@ export function SignUpComponent({ onSwitchToSignIn }: SignUpComponentProps): JSX
|
||||
<div className="w-full max-w-[300px]">
|
||||
<div className="mb-5 text-center text-2xl font-bold">Sign up</div>
|
||||
|
||||
<OauthComponent />
|
||||
<div className="mt-4">
|
||||
<div className="space-y-2">
|
||||
<GithubOAuthComponent />
|
||||
<GoogleOAuthComponent />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{IS_CLOUD && (
|
||||
{(GOOGLE_CLIENT_ID || GITHUB_CLIENT_ID) && (
|
||||
<div className="relative my-6">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300"></div>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { GithubOutlined } from '@ant-design/icons';
|
||||
import { Button, message } from 'antd';
|
||||
|
||||
import { GITHUB_CLIENT_ID, getOAuthRedirectUri } from '../../../../constants';
|
||||
|
||||
export function GithubOAuthComponent() {
|
||||
if (!GITHUB_CLIENT_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const redirectUri = getOAuthRedirectUri();
|
||||
|
||||
const handleGitHubLogin = () => {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
client_id: GITHUB_CLIENT_ID,
|
||||
redirect_uri: redirectUri,
|
||||
state: 'github',
|
||||
scope: 'user:email',
|
||||
});
|
||||
|
||||
const githubAuthUrl = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
||||
|
||||
// Validate URL is properly formed
|
||||
new URL(githubAuthUrl);
|
||||
window.location.href = githubAuthUrl;
|
||||
} catch (error) {
|
||||
message.error('Invalid OAuth configuration');
|
||||
console.error('GitHub OAuth URL error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button icon={<GithubOutlined />} onClick={handleGitHubLogin} className="w-full" size="large">
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { GoogleOutlined } from '@ant-design/icons';
|
||||
import { Button, message } from 'antd';
|
||||
|
||||
import { GOOGLE_CLIENT_ID, getOAuthRedirectUri } from '../../../../constants';
|
||||
|
||||
export function GoogleOAuthComponent() {
|
||||
if (!GOOGLE_CLIENT_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const redirectUri = getOAuthRedirectUri();
|
||||
|
||||
const handleGoogleLogin = () => {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
client_id: GOOGLE_CLIENT_ID,
|
||||
redirect_uri: redirectUri,
|
||||
response_type: 'code',
|
||||
scope: 'openid email profile',
|
||||
state: 'google',
|
||||
});
|
||||
|
||||
const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
||||
|
||||
// Validate URL is properly formed
|
||||
new URL(googleAuthUrl);
|
||||
window.location.href = googleAuthUrl;
|
||||
} catch (error) {
|
||||
message.error('Invalid OAuth configuration');
|
||||
console.error('Google OAuth URL error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button icon={<GoogleOutlined />} onClick={handleGoogleLogin} className="w-full" size="large">
|
||||
Continue with Google
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user