postbase 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test.yml +74 -0
- package/CLA.md +60 -0
- package/CONTRIBUTORS.md +35 -0
- package/LICENSE +661 -0
- package/README.md +211 -0
- package/admin/404.html +33 -0
- package/admin/README.md +21 -0
- package/admin/index.html +15 -0
- package/admin/jsconfig.json +20 -0
- package/admin/lib/postbase.js +222 -0
- package/admin/package-lock.json +3746 -0
- package/admin/package.json +27 -0
- package/admin/public/assets/img/admin-ui.png +0 -0
- package/admin/public/assets/img/blank-profile-picture-960_720.webp +0 -0
- package/admin/public/assets/img/chart-active-users.png +0 -0
- package/admin/public/assets/img/icon-transparent.png +0 -0
- package/admin/src/App.jsx +48 -0
- package/admin/src/auth.js +11 -0
- package/admin/src/common/formatDateTime.js +18 -0
- package/admin/src/components/AuthPanel.jsx +88 -0
- package/admin/src/components/Header.jsx +67 -0
- package/admin/src/main.jsx +6 -0
- package/admin/src/pages/Dashboard.jsx +24 -0
- package/admin/src/pages/Home.jsx +52 -0
- package/admin/src/pages/Login.jsx +10 -0
- package/admin/src/pages/authentication/Users.jsx +199 -0
- package/admin/src/pages/firestore/Database.jsx +29 -0
- package/admin/src/pages/storage/files.jsx +29 -0
- package/admin/src/postbase.js +15 -0
- package/admin/src/styles.css +3 -0
- package/admin/tailwind.config.cjs +11 -0
- package/admin/template.env +2 -0
- package/admin/vite.config.js +21 -0
- package/assets/img/HomePageScreenshot.png +0 -0
- package/assets/img/better-auth-logo-dark.136b122f.png +0 -0
- package/assets/img/better-auth-logo-light.4b03f444.png +0 -0
- package/assets/img/expresjs.png +0 -0
- package/assets/img/icon-transparent.png +0 -0
- package/assets/img/icon.png +0 -0
- package/assets/img/letsencrypt-logo-horizontal.png +0 -0
- package/assets/img/logo.png +0 -0
- package/assets/img/node.js_logo.png +0 -0
- package/assets/img/nodejsLight.svg +39 -0
- package/assets/img/postgres.png +0 -0
- package/backend/README.md +49 -0
- package/backend/admin/auth.js +9 -0
- package/backend/app.js +68 -0
- package/backend/auth.js +92 -0
- package/backend/env.js +12 -0
- package/backend/lib/postbase/adminClient.js +520 -0
- package/backend/lib/postbase/compat/admin.js +44 -0
- package/backend/lib/postbase/db.js +17 -0
- package/backend/lib/postbase/genericRouter.js +603 -0
- package/backend/lib/postbase/local-storage.js +56 -0
- package/backend/lib/postbase/metadataCache.js +32 -0
- package/backend/lib/postbase/middlewares/auth.js +57 -0
- package/backend/lib/postbase/migrations/1765239687559_rtdb-nodes.js +93 -0
- package/backend/lib/postbase/package-lock.json +5873 -0
- package/backend/lib/postbase/package.json +19 -0
- package/backend/lib/postbase/rtdb/router.js +190 -0
- package/backend/lib/postbase/rtdb/rulesEngine.js +63 -0
- package/backend/lib/postbase/rtdb/ws.js +84 -0
- package/backend/lib/postbase/rulesEngine.js +62 -0
- package/backend/lib/postbase/storage.js +130 -0
- package/backend/lib/postbase/tests/README.md +22 -0
- package/backend/lib/postbase/tests/db.js +9 -0
- package/backend/lib/postbase/tests/rtdb.rest.test.js +46 -0
- package/backend/lib/postbase/tests/rtdb.ws.test.js +113 -0
- package/backend/lib/postbase/tests/rules.js +26 -0
- package/backend/lib/postbase/tests/testServer.js +46 -0
- package/backend/lib/postbase/websocket.js +131 -0
- package/backend/local.js +6 -0
- package/backend/main.js +20 -0
- package/backend/middlewares/auth_middleware.js +10 -0
- package/backend/migrations/1762137399366-init.sql +98 -0
- package/backend/migrations/1762137399367_init_jsonb_schema.js +68 -0
- package/backend/migrations/1762149999999_enable_realtime_changes.js +48 -0
- package/backend/migrations/1765224247654_rtdb-nodes.js +93 -0
- package/backend/package-lock.json +2374 -0
- package/backend/package.json +27 -0
- package/backend/postbase_db_rules.js +128 -0
- package/backend/postbase_rtdb_rules.js +27 -0
- package/backend/postbase_storage_rules.js +45 -0
- package/backend/template.env +10 -0
- package/backend-systemd/README.md +39 -0
- package/backend-systemd/your_website.com.service +12 -0
- package/frontend/404.html +33 -0
- package/frontend/README.md +25 -0
- package/frontend/index.html +15 -0
- package/frontend/jsconfig.json +20 -0
- package/frontend/lib/postbase/auth.js +132 -0
- package/frontend/lib/postbase/compat/firebase/app.js +3 -0
- package/frontend/lib/postbase/compat/firebase/auth.js +115 -0
- package/frontend/lib/postbase/compat/firebase/database.js +11 -0
- package/frontend/lib/postbase/compat/firebase/firestore/lite.js +61 -0
- package/frontend/lib/postbase/compat/firebase/storage.js +10 -0
- package/frontend/lib/postbase/db.js +657 -0
- package/frontend/lib/postbase/package-lock.json +6284 -0
- package/frontend/lib/postbase/package.json +17 -0
- package/frontend/lib/postbase/rtdb.js +108 -0
- package/frontend/lib/postbase/storage.js +293 -0
- package/frontend/lib/postbase/tests/rtdb.client.test.js +88 -0
- package/frontend/lib/postbase/tests/waitFor.js +13 -0
- package/frontend/lib/postbase/utils.js +1 -0
- package/frontend/package-lock.json +2977 -0
- package/frontend/package.json +24 -0
- package/frontend/src/App.jsx +38 -0
- package/frontend/src/auth.js +52 -0
- package/frontend/src/components/AuthPanel.jsx +85 -0
- package/frontend/src/components/Header.jsx +54 -0
- package/frontend/src/main.jsx +5 -0
- package/frontend/src/pages/Dashboard.jsx +24 -0
- package/frontend/src/pages/Home.jsx +178 -0
- package/frontend/src/pages/Login.jsx +10 -0
- package/frontend/src/postbase.js +14 -0
- package/frontend/src/styles.css +1 -0
- package/frontend/tailwind.config.cjs +11 -0
- package/frontend/template.env +2 -0
- package/frontend/vite.config.js +18 -0
- package/git/hooks/README.md +31 -0
- package/git/hooks/post-receive +26 -0
- package/nginx/README.md +84 -0
- package/nginx/apt/www.your_website.com.conf +80 -0
- package/nginx/homebrew/www.your_website.com.conf +80 -0
- package/nginx/letsencrypt/README +14 -0
- package/package.json +8 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "postbase-frontend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Umair Ashraf",
|
|
6
|
+
"url": "http://github.com/umrashrf/postbase",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"preact": "^10.27.0",
|
|
14
|
+
"preact-iso": "^2.9.1",
|
|
15
|
+
"preact-router": "^4.1.2",
|
|
16
|
+
"tailwindcss": "^4.1.11",
|
|
17
|
+
"@tailwindcss/vite": "^4.1.11",
|
|
18
|
+
"better-auth": "^1.3.34"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@preact/preset-vite": "^2.9.3",
|
|
22
|
+
"vite": "^6.0.4"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useState } from 'preact/hooks';
|
|
2
|
+
import { Router } from 'preact-router';
|
|
3
|
+
import Header from './components/Header';
|
|
4
|
+
import Home from './pages/Home';
|
|
5
|
+
import Dashboard from './pages/Dashboard';
|
|
6
|
+
import Login from './pages/Login';
|
|
7
|
+
import { getSession } from './auth';
|
|
8
|
+
|
|
9
|
+
export default function App() {
|
|
10
|
+
const [user, setUser] = useState(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
(async () => {
|
|
14
|
+
const { data } = await getSession();
|
|
15
|
+
if (data && data.hasOwnProperty('user')) {
|
|
16
|
+
setUser(data.user);
|
|
17
|
+
}
|
|
18
|
+
})();
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="min-h-screen bg-gray-50 text-gray-800">
|
|
23
|
+
<Header user={user} />
|
|
24
|
+
|
|
25
|
+
<Router>
|
|
26
|
+
<Home path="/" user={user} />
|
|
27
|
+
<Dashboard path="/dashboard" user={user} />
|
|
28
|
+
<Login path="/login" user={user} />
|
|
29
|
+
</Router>
|
|
30
|
+
|
|
31
|
+
<footer className="bg-white border-t py-6 mt-10">
|
|
32
|
+
<div className="container mx-auto text-center text-sm text-gray-500">
|
|
33
|
+
© {new Date().getFullYear()} Postbase Demo
|
|
34
|
+
</div>
|
|
35
|
+
</footer>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createAuthClient as createBetterAuthClient } from 'better-auth/client';
|
|
2
|
+
import { phoneNumberClient } from "better-auth/client/plugins"
|
|
3
|
+
import { createAuthClient } from '../lib/postbase/auth';
|
|
4
|
+
import { createFirebaseAuthClient } from '../lib/postbase/compat/firebase/auth';
|
|
5
|
+
|
|
6
|
+
export const betterAuthClient = createBetterAuthClient({
|
|
7
|
+
baseURL: import.meta.env.VITE_API_BASE + '/auth', // Specify if on a different domain/path,
|
|
8
|
+
plugins: [
|
|
9
|
+
phoneNumberClient()
|
|
10
|
+
]
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const postbaseAuthClient = createAuthClient(betterAuthClient);
|
|
14
|
+
|
|
15
|
+
// better-auth
|
|
16
|
+
export const {
|
|
17
|
+
signUp,
|
|
18
|
+
signIn,
|
|
19
|
+
signOut: _signOut,
|
|
20
|
+
getSession,
|
|
21
|
+
sendVerificationEmail,
|
|
22
|
+
requestPasswordReset,
|
|
23
|
+
resetPassword,
|
|
24
|
+
phoneNumber,
|
|
25
|
+
deleteUser,
|
|
26
|
+
} = betterAuthClient;
|
|
27
|
+
|
|
28
|
+
// rename to logOut because firebase has reserved signOut
|
|
29
|
+
export const logout = async (...args) => {
|
|
30
|
+
const authToken = window.sessionStorage.getItem('authToken');
|
|
31
|
+
if (authToken) {
|
|
32
|
+
window.sessionStorage.removeItem('authToken');
|
|
33
|
+
}
|
|
34
|
+
// at the end because it can trigger navigation
|
|
35
|
+
await _signOut(...args);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// firebase auth
|
|
39
|
+
export const getAuth = () => postbaseAuthClient;
|
|
40
|
+
export const { getBetterAuthToken } = postbaseAuthClient;
|
|
41
|
+
|
|
42
|
+
export const {
|
|
43
|
+
createUserWithEmailAndPassword,
|
|
44
|
+
sendEmailVerification,
|
|
45
|
+
sendPasswordResetEmail,
|
|
46
|
+
signInWithEmailAndPassword,
|
|
47
|
+
updateProfile,
|
|
48
|
+
updateEmail,
|
|
49
|
+
updatePassword,
|
|
50
|
+
onAuthStateChanged,
|
|
51
|
+
signOut,
|
|
52
|
+
} = createFirebaseAuthClient(postbaseAuthClient);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useState } from 'preact/hooks';
|
|
2
|
+
import { signUp, signIn, signOut } from '../auth';
|
|
3
|
+
|
|
4
|
+
export default function AuthPanel({ user }) {
|
|
5
|
+
const [email, setEmail] = useState('');
|
|
6
|
+
const [password, setPassword] = useState('');
|
|
7
|
+
|
|
8
|
+
const loginWithGoogle = async () => {
|
|
9
|
+
alert("Finish setting up Sign in with Google. Learn more at https://www.better-auth.com/docs/authentication/google")
|
|
10
|
+
await signIn.social({
|
|
11
|
+
provider: "google",
|
|
12
|
+
callbackURL: import.meta.env.VITE_FRONTEND_URL + '/dashboard'
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="bg-white border rounded-lg p-4">
|
|
18
|
+
<h3 className="font-semibold">Account</h3>
|
|
19
|
+
{!user ? (
|
|
20
|
+
<div className="mt-3 space-y-3">
|
|
21
|
+
<button onClick={loginWithGoogle} className="w-full bg-red-600 text-white py-2 rounded">Sign in with Google</button>
|
|
22
|
+
|
|
23
|
+
<div className="flex gap-2">
|
|
24
|
+
<input className="flex-1 border p-2 rounded w-full" type="text" placeholder="email" value={email} onInput={e => setEmail(e.target.value)} />
|
|
25
|
+
<input className="flex-1 border p-2 rounded w-full" type="password" placeholder="password" value={password} onInput={e => setPassword(e.target.value)} />
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div className="flex gap-2">
|
|
29
|
+
<button className="px-3 py-2 border rounded"
|
|
30
|
+
onClick={async () => {
|
|
31
|
+
alert("Setup PostgreSQL (migration, connection string). Learn more at https://www.better-auth.com/docs/installation#create-database-tables");
|
|
32
|
+
await signUp.email(
|
|
33
|
+
{ email, password, name: email.split('@', 1)[0], callbackURL: "/dashboard" },
|
|
34
|
+
{
|
|
35
|
+
onRequest: (ctx) => {
|
|
36
|
+
//show loading
|
|
37
|
+
},
|
|
38
|
+
onSuccess: (ctx) => {
|
|
39
|
+
//redirect to the dashboard or sign in page
|
|
40
|
+
window.location = import.meta.env.VITE_FRONTEND_URL;
|
|
41
|
+
},
|
|
42
|
+
onError: (ctx) => {
|
|
43
|
+
// display the error message
|
|
44
|
+
console.error('Sign up failed', ctx);
|
|
45
|
+
alert('Sign up failed');
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}}>
|
|
49
|
+
Sign up
|
|
50
|
+
</button>
|
|
51
|
+
<button className="px-3 py-2 border rounded"
|
|
52
|
+
onClick={async () => {
|
|
53
|
+
alert("Setup PostgreSQL (migration, connection string). Learn more at https://www.better-auth.com/docs/installation#create-database-tables");
|
|
54
|
+
await signIn.email({ email, password, callbackURL: '/dashboard' });
|
|
55
|
+
}}>
|
|
56
|
+
Login
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
) : (
|
|
61
|
+
<div className="mt-3">
|
|
62
|
+
<div className="flex items-center gap-3">
|
|
63
|
+
<img src={user.image} className="w-10 h-10 rounded-full" />
|
|
64
|
+
<div>
|
|
65
|
+
<div className="font-medium">{user.name || user.email}</div>
|
|
66
|
+
<div className="text-sm text-gray-500">{user.email}</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="mt-3 flex gap-2">
|
|
71
|
+
<a href="/dashboard" className="px-3 py-2 border rounded">Dashboard</a>
|
|
72
|
+
<a href="/billing" className="px-3 py-2 border rounded">Billing</a>
|
|
73
|
+
<button onClick={() => signOut({
|
|
74
|
+
fetchOptions: {
|
|
75
|
+
onSuccess: () => {
|
|
76
|
+
location.href = import.meta.env.VITE_FRONTEND_URL;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
})} className="px-3 py-2 border rounded cursor-pointer">Logout</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
2
|
+
import { signOut } from '../auth';
|
|
3
|
+
|
|
4
|
+
export default function Header({ user }) {
|
|
5
|
+
const [menu, setMenu] = useState(false);
|
|
6
|
+
const [scrolled, setScrolled] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const onScroll = () => setScrolled(window.scrollY > 30);
|
|
10
|
+
window.addEventListener('scroll', onScroll);
|
|
11
|
+
return () => window.removeEventListener('scroll', onScroll);
|
|
12
|
+
}, []);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<header className={`w-full z-40 transition-all ${scrolled ? 'bg-white shadow' : 'bg-transparent'} backdrop-blur`}>
|
|
16
|
+
<div className="max-w-6xl mx-auto px-6 py-4 flex justify-between items-center">
|
|
17
|
+
<a href="/" className="text-2xl font-extrabold text-blue-600">Postbase Demo</a>
|
|
18
|
+
<nav className="hidden md:flex gap-8 text-sm font-medium">
|
|
19
|
+
<a href="#better-auth" className="hover:text-blue-600">BetterAuth</a>
|
|
20
|
+
<a href="#familiar-api" className="hover:text-blue-600">Familiar API</a>
|
|
21
|
+
<a href="#why" className="hover:text-blue-600">Why Postbase?</a>
|
|
22
|
+
</nav>
|
|
23
|
+
|
|
24
|
+
<div className="flex items-center gap-4">
|
|
25
|
+
{user ? (
|
|
26
|
+
<div className="relative">
|
|
27
|
+
<button onClick={() => setMenu(!menu)} className="flex items-center gap-2 px-3 py-1 border rounded-md hover:bg-gray-50">
|
|
28
|
+
<img src={user.image} className="w-8 h-8 rounded-full" alt="profile" />
|
|
29
|
+
<span className="hidden sm:inline text-sm">{user.name || user.email}</span>
|
|
30
|
+
</button>
|
|
31
|
+
{menu && (
|
|
32
|
+
<div className="absolute right-0 mt-2 bg-white border rounded-md shadow-lg w-48 z-50">
|
|
33
|
+
<a href="/dashboard" className="block px-4 py-2 hover:bg-gray-50">Dashboard</a>
|
|
34
|
+
<a href="/billing" className="block px-4 py-2 hover:bg-gray-50">Billing</a>
|
|
35
|
+
<button onClick={() => signOut({
|
|
36
|
+
fetchOptions: {
|
|
37
|
+
onSuccess: () => {
|
|
38
|
+
location.href = import.meta.env.VITE_FRONTEND_URL;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
})} className="w-full text-left px-4 py-2 hover:bg-gray-50 cursor-pointer">Logout</button>
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
) : (
|
|
46
|
+
<a href="/login" className="bg-blue-600 text-white px-4 py-2 rounded-md font-medium hover:bg-blue-700 transition">
|
|
47
|
+
Sign In
|
|
48
|
+
</a>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</header>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { betterAuthClient } from '../auth';
|
|
2
|
+
|
|
3
|
+
export default function Dashboard({ user }) {
|
|
4
|
+
if (!user) {
|
|
5
|
+
return (
|
|
6
|
+
<div className="container mx-auto py-10 text-center">
|
|
7
|
+
<p className="text-gray-600">Please sign in to view your dashboard.</p>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="container mx-auto py-10">
|
|
14
|
+
<h1 className="text-2xl font-semibold mb-6">Dashboard</h1>
|
|
15
|
+
<div className="mt-2">
|
|
16
|
+
<button className="w-full bg-red-600 text-white py-2 rounded cursor-pointer"
|
|
17
|
+
onClick={async e => {
|
|
18
|
+
await betterAuthClient.deleteUser();
|
|
19
|
+
location.href = '/';
|
|
20
|
+
}}>Close Account</button>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import AuthPanel from '../components/AuthPanel';
|
|
2
|
+
|
|
3
|
+
export default function Home({ user }) {
|
|
4
|
+
return (
|
|
5
|
+
<main className="text-gray-800">
|
|
6
|
+
{/* HERO */}
|
|
7
|
+
<section className="bg-gradient-to-br from-blue-50 to-white pt-24 pb-32">
|
|
8
|
+
<div className="max-w-6xl mx-auto px-6 grid md:grid-cols-2 gap-10">
|
|
9
|
+
<div>
|
|
10
|
+
<h1 className="text-5xl font-extrabold leading-tight text-gray-900">
|
|
11
|
+
Backend ❤️<br />
|
|
12
|
+
<span className="text-blue-600">that just works.</span>
|
|
13
|
+
</h1>
|
|
14
|
+
<p className="mt-5 text-lg text-gray-600 max-w-lg">
|
|
15
|
+
Self Hosted Firebase/Supabase Alternative using Node.js, Express.js, BetterAuth and PostgreSQL (JSONB)
|
|
16
|
+
</p>
|
|
17
|
+
<p className="mt-6 text-2xl">
|
|
18
|
+
Firebase 💔 | Supabase 💔 | Postbase ❤️
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<div className="mt-8 flex gap-4">
|
|
22
|
+
<a href="/login" className="bg-blue-600 text-white px-6 py-3 rounded-md font-medium shadow hover:bg-blue-700 transition">Login with BetterAuth</a>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div className="flex flex-col gap-2 relative">
|
|
27
|
+
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-lg shadow-lg p-6 overflow-x-auto">
|
|
28
|
+
<code className="block whitespace-pre w-xs md:w-xl lg:w-4xl text-xs text-nowrap overflow-x-auto">{
|
|
29
|
+
`import { initializeApp } from "../lib/postbase/compat/firebase/app";
|
|
30
|
+
import { getFirestore } from "../lib/postbase/compat/firebase/firestore/lite";
|
|
31
|
+
import { getStorage } from "../lib/postbase/compat/firebase/storage";
|
|
32
|
+
import { getDatabase } from "../lib/postbase/compat/firebase/database";
|
|
33
|
+
|
|
34
|
+
const firebaseConfig = {
|
|
35
|
+
baseUrl: import.meta.env.VITE_API_BASE,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const app = initializeApp(firebaseConfig);
|
|
39
|
+
|
|
40
|
+
export const db = getFirestore(app);
|
|
41
|
+
export const storage = getStorage(app);
|
|
42
|
+
export const rtdbClient = getDatabase(app);`
|
|
43
|
+
}</code>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
48
|
+
|
|
49
|
+
<section id="better-auth" className="bg-gradient-to-br from-blue-50 to-white pt-24 pb-32">
|
|
50
|
+
<div className="max-w-6xl mx-auto px-6 grid md:grid-cols-2 gap-10">
|
|
51
|
+
<div>
|
|
52
|
+
<h1 className="text-5xl font-extrabold leading-tight text-gray-900">
|
|
53
|
+
BetterAuth
|
|
54
|
+
</h1>
|
|
55
|
+
<p className="mt-5 text-lg text-gray-600 max-w-lg">
|
|
56
|
+
Did not reinvent the wheel
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<div className="mt-8 flex gap-4">
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div className="relative">
|
|
64
|
+
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-lg shadow-lg p-6 overflow-x-auto">
|
|
65
|
+
<code className="block whitespace-pre w-xs md:w-xl lg:w-4xl text-xs text-nowrap overflow-x-scroll">{
|
|
66
|
+
`import {
|
|
67
|
+
getAuth,
|
|
68
|
+
signInWithEmailAndPassword,
|
|
69
|
+
signOut,
|
|
70
|
+
} from "./auth";
|
|
71
|
+
|
|
72
|
+
const auth = getAuth();
|
|
73
|
+
|
|
74
|
+
const userCredential = await signInWithEmailAndPassword(auth, 'email', 'password');
|
|
75
|
+
|
|
76
|
+
await signOut(auth);`
|
|
77
|
+
}</code>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<section id="familiar-api" className="bg-gradient-to-br from-blue-50 to-white pt-24 pb-32">
|
|
84
|
+
<div className="max-w-6xl mx-auto px-6 grid md:grid-cols-2 gap-10">
|
|
85
|
+
<div>
|
|
86
|
+
<h1 className="text-5xl font-extrabold leading-tight text-gray-900">
|
|
87
|
+
Familiar API
|
|
88
|
+
</h1>
|
|
89
|
+
<p className="mt-5 text-lg text-gray-600 max-w-lg">
|
|
90
|
+
Inspired by Firebase's API
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<div className="mt-8 flex gap-4">
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div className="flex flex-col gap-2 relative">
|
|
98
|
+
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-lg shadow-lg p-6">
|
|
99
|
+
<code className="block whitespace-pre w-xs md:w-xl lg:w-4xl text-xs text-nowrap overflow-x-scroll">{
|
|
100
|
+
`import { db } from "./postbase";
|
|
101
|
+
|
|
102
|
+
await db.collection('users').addDoc({ name: "Umair" });
|
|
103
|
+
|
|
104
|
+
await db.collection('users').where('name', '==', 'Umair');
|
|
105
|
+
|
|
106
|
+
await db.collection('users').doc('docId').get();
|
|
107
|
+
`
|
|
108
|
+
}</code>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-lg shadow-lg p-6 overflow-x-auto">
|
|
111
|
+
<pre>
|
|
112
|
+
<code className="block whitespace-pre w-xs md:w-xl lg:w-4xl text-xs text-nowrap overflow-x-scroll">{
|
|
113
|
+
`import { storage } from "./postbase";
|
|
114
|
+
|
|
115
|
+
const profilePicFile = new File(...); // HTMLFileInput
|
|
116
|
+
|
|
117
|
+
const fileRef = storage.ref('path/to/directory/' + profilePicFile.name);
|
|
118
|
+
|
|
119
|
+
const remoteFile = await fileRef.put(profilePicFile);
|
|
120
|
+
|
|
121
|
+
const profileUrl = await remoteFile.ref.getDownloadURL();`
|
|
122
|
+
}</code>
|
|
123
|
+
</pre>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-lg shadow-lg p-6 overflow-x-auto">
|
|
126
|
+
<code className="block whitespace-pre w-xs md:w-xl lg:w-4xl text-xs text-nowrap overflow-x-scroll">{
|
|
127
|
+
`import { rtdbClient } from '../postbase';
|
|
128
|
+
|
|
129
|
+
// please check frontend/lib/postbase/tests/rtdb.client.test.js
|
|
130
|
+
`
|
|
131
|
+
}</code>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</section>
|
|
136
|
+
|
|
137
|
+
{/* FEATURES */}
|
|
138
|
+
<section id="why" className="bg-white py-24">
|
|
139
|
+
<div className="max-w-6xl mx-auto px-6 text-center">
|
|
140
|
+
<h2 className="text-3xl font-bold mb-12 text-gray-900">Why Postbase?</h2>
|
|
141
|
+
<div className="grid md:grid-cols-3 gap-10">
|
|
142
|
+
{[
|
|
143
|
+
{
|
|
144
|
+
title: "Self Hosted",
|
|
145
|
+
desc: "Based on mostly open source easily installable projects",
|
|
146
|
+
icon: "🧩"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
title: "Cost Effective",
|
|
150
|
+
desc: "Avoid surprise cloud charges",
|
|
151
|
+
icon: "💳"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
title: "Scalable",
|
|
155
|
+
desc: "Scale to millions of requests per day — no rate limits on your growth.",
|
|
156
|
+
icon: "⚡"
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
].map(f => (
|
|
160
|
+
<div key={f.title} className="bg-gray-50 border rounded-lg p-8 shadow-sm hover:shadow transition">
|
|
161
|
+
<div className="text-4xl mb-3">{f.icon}</div>
|
|
162
|
+
<h3 className="text-xl font-semibold mb-2">{f.title}</h3>
|
|
163
|
+
<p className="text-gray-600">{f.desc}</p>
|
|
164
|
+
</div>
|
|
165
|
+
))}
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</section>
|
|
169
|
+
|
|
170
|
+
{/* AUTH (optional section near footer) */}
|
|
171
|
+
<section className="bg-gray-50 py-16">
|
|
172
|
+
<div className="max-w-lg mx-auto px-6">
|
|
173
|
+
<AuthPanel user={user} />
|
|
174
|
+
</div>
|
|
175
|
+
</section>
|
|
176
|
+
</main>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { initializeApp } from "../lib/postbase/compat/firebase/app";
|
|
2
|
+
import { getFirestore } from "../lib/postbase/compat/firebase/firestore/lite";
|
|
3
|
+
import { getStorage } from "../lib/postbase/compat/firebase/storage";
|
|
4
|
+
import { getDatabase } from "../lib/postbase/compat/firebase/database";
|
|
5
|
+
|
|
6
|
+
const firebaseConfig = {
|
|
7
|
+
baseUrl: import.meta.env.VITE_API_BASE,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const app = initializeApp(firebaseConfig);
|
|
11
|
+
|
|
12
|
+
export const db = getFirestore(app);
|
|
13
|
+
export const storage = getStorage(app);
|
|
14
|
+
export const rtdbClient = getDatabase(app);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, transformWithEsbuild } from 'vite';
|
|
2
|
+
import preact from '@preact/preset-vite';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
preact({ include: /\.(mdx|js|jsx|ts|tsx)$/ }),
|
|
8
|
+
tailwindcss(),
|
|
9
|
+
],
|
|
10
|
+
optimizeDeps: {
|
|
11
|
+
exclude: [],
|
|
12
|
+
esbuildOptions: {
|
|
13
|
+
loader: {
|
|
14
|
+
'.js': 'jsx',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Git Hooks
|
|
2
|
+
|
|
3
|
+
## post-receive hook
|
|
4
|
+
|
|
5
|
+
The purpose of this hook is to automate deployment of your app with `git push`.
|
|
6
|
+
|
|
7
|
+
## How to enable post-receive hook on your server?
|
|
8
|
+
|
|
9
|
+
1. Login to your ssh server
|
|
10
|
+
2. mkdir /path/to/your/app/repo
|
|
11
|
+
3. cd /path/to/your/app/repo
|
|
12
|
+
4. git init
|
|
13
|
+
|
|
14
|
+
Locally, in your dev environment:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
git remote add deploy server_username@your_server:/your/server/your_repo
|
|
18
|
+
git push deploy
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Back to ssh server:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
ln -s `pwd`/git/hooks/post-receive `pwd`/.git/hooks/post-receive
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Allow post-receive hook to copy over files.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
sudo chown -R you_username:root /var/www/html
|
|
31
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -x
|
|
4
|
+
|
|
5
|
+
GIT_WORK_TREE="/your/server/your_repo"
|
|
6
|
+
export GIT_WORK_TREE
|
|
7
|
+
|
|
8
|
+
# This is needed for git push based deploy
|
|
9
|
+
git reset --hard
|
|
10
|
+
|
|
11
|
+
cd $GIT_WORK_TREE
|
|
12
|
+
git checkout .
|
|
13
|
+
|
|
14
|
+
cd backend
|
|
15
|
+
npm install
|
|
16
|
+
|
|
17
|
+
cd ../frontend
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
|
|
21
|
+
cd ../admin
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
|
|
25
|
+
rsync -avh /your/server/your_repo/frontend/dist/ /var/www/html/your_website
|
|
26
|
+
rsync -avh /your/server/your_repo/admin/dist/ /var/www/html/your_website/admin
|