This commit is contained in:
Andras Bacsai
2021-06-22 10:19:20 +02:00
committed by GitHub
parent 2ff9c5fed5
commit 807d526ffa
15 changed files with 762 additions and 253 deletions

View File

@@ -23,7 +23,6 @@
}
return {};
}
</script>
<script lang="ts">
@@ -37,8 +36,9 @@
import Tooltip from '$components/Tooltip.svelte';
import compareVersions from 'compare-versions';
import packageJson from '../../package.json';
import { dashboard } from '$store';
import { dashboard, settings } from '$store';
import { browser } from '$app/env';
$settings.clientId = import.meta.env.VITE_GITHUB_APP_CLIENTID !== 'null' ? import.meta.env.VITE_GITHUB_APP_CLIENTID : null
$dashboard = initDashboard;
const branch =
process.env.NODE_ENV === 'production' &&
@@ -53,7 +53,7 @@
let upgradeDisabled = false;
let upgradeDone = false;
let showAck = false;
let globalFeatureFlag = browser && localStorage.getItem('globalFeatureFlag')
let globalFeatureFlag = browser && localStorage.getItem('globalFeatureFlag');
const options = {
duration: 2000
};
@@ -104,7 +104,6 @@
localStorage.setItem('automaticErrorReportsAck', 'true');
showAck = false;
}
</script>
<SvelteToast {options} />
@@ -136,69 +135,136 @@
class:border-purple-500={$page.path === '/dashboard/databases'}
>
<div class="w-10 pt-4 pb-4"><img src="/favicon.png" alt="coolLabs logo" /></div>
<Tooltip position="right" label="Applications">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-green-500 mt-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/dashboard/applications')}
class:text-green-500={$page.path === '/dashboard/applications' ||
$page.path.startsWith('/application')}
class:bg-warmGray-700={$page.path === '/dashboard/applications' ||
$page.path.startsWith('/application')}
>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><rect x="4" y="4" width="16" height="16" rx="2" ry="2" /><rect
x="9"
y="9"
width="6"
height="6"
/><line x1="9" y1="1" x2="9" y2="4" /><line x1="15" y1="1" x2="15" y2="4" /><line
x1="9"
y1="20"
x2="9"
y2="23"
/><line x1="15" y1="20" x2="15" y2="23" /><line x1="20" y1="9" x2="23" y2="9" /><line
x1="20"
y1="14"
x2="23"
y2="14"
/><line x1="1" y1="9" x2="4" y2="9" /><line x1="1" y1="14" x2="4" y2="14" /></svg
{#if $settings.clientId}
<Tooltip position="right" label="Applications">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-green-500 mt-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/dashboard/applications')}
class:text-green-500={$page.path === '/dashboard/applications' ||
$page.path.startsWith('/application')}
class:bg-warmGray-700={$page.path === '/dashboard/applications' ||
$page.path.startsWith('/application')}
>
</div>
</Tooltip>
<Tooltip position="right" label="Databases">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-purple-500 my-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/dashboard/databases')}
class:text-purple-500={$page.path === '/dashboard/databases' ||
$page.path.startsWith('/database')}
class:bg-warmGray-700={$page.path === '/dashboard/databases' ||
$page.path.startsWith('/database')}
>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><rect x="4" y="4" width="16" height="16" rx="2" ry="2" /><rect
x="9"
y="9"
width="6"
height="6"
/><line x1="9" y1="1" x2="9" y2="4" /><line x1="15" y1="1" x2="15" y2="4" /><line
x1="9"
y1="20"
x2="9"
y2="23"
/><line x1="15" y1="20" x2="15" y2="23" /><line
x1="20"
y1="9"
x2="23"
y2="9"
/><line x1="20" y1="14" x2="23" y2="14" /><line x1="1" y1="9" x2="4" y2="9" /><line
x1="1"
y1="14"
x2="4"
y2="14"
/></svg
>
</div>
</Tooltip>
<Tooltip position="right" label="Databases">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-purple-500 my-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/dashboard/databases')}
class:text-purple-500={$page.path === '/dashboard/databases' ||
$page.path.startsWith('/database')}
class:bg-warmGray-700={$page.path === '/dashboard/databases' ||
$page.path.startsWith('/database')}
>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
/>
</svg>
</div>
</Tooltip>
{:else}
<Tooltip
position="right"
label="Applications disabled, no GitHub Integration detected"
size="large"
>
<div class="p-2 text-warmGray-700 mt-4 transition-all duration-100 cursor-pointer">
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
/>
</svg>
</div>
</Tooltip>
stroke-linecap="round"
stroke-linejoin="round"
><rect x="4" y="4" width="16" height="16" rx="2" ry="2" /><rect
x="9"
y="9"
width="6"
height="6"
/><line x1="9" y1="1" x2="9" y2="4" /><line x1="15" y1="1" x2="15" y2="4" /><line
x1="9"
y1="20"
x2="9"
y2="23"
/><line x1="15" y1="20" x2="15" y2="23" /><line
x1="20"
y1="9"
x2="23"
y2="9"
/><line x1="20" y1="14" x2="23" y2="14" /><line x1="1" y1="9" x2="4" y2="9" /><line
x1="1"
y1="14"
x2="4"
y2="14"
/></svg
>
</div>
</Tooltip>
<Tooltip position="right" label="Databases disabled, no GitHub Integration detected" size="large">
<div
class="p-2 text-warmGray-700 my-4 transition-all duration-100 cursor-pointer"
>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
/>
</svg>
</div>
</Tooltip>
{/if}
<Tooltip position="right" label="Services">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-blue-500 transition-all duration-100 cursor-pointer"
@@ -226,29 +292,29 @@
</Tooltip>
<div class="flex-1" />
{#if globalFeatureFlag}
<Tooltip position="right" label="Server(s)">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-red-500 mb-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/servers')}
class:text-red-500={$page.path === '/servers' || $page.path.startsWith('/servers')}
class:bg-warmGray-700={$page.path === '/servers' || $page.path.startsWith('/servers')}
>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
<Tooltip position="right" label="Servers">
<div
class="p-2 hover:bg-warmGray-700 rounded hover:text-red-500 mb-4 transition-all duration-100 cursor-pointer"
on:click={() => goto('/servers')}
class:text-red-500={$page.path === '/servers' || $page.path.startsWith('/servers')}
class:bg-warmGray-700={$page.path === '/servers' || $page.path.startsWith('/servers')}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"
/>
</svg>
</div>
</Tooltip>
<svg
class="w-8"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"
/>
</svg>
</div>
</Tooltip>
{/if}
<Tooltip position="right" label="Settings">
<button

View File

@@ -0,0 +1,103 @@
import mongoose from 'mongoose';
import Settings from '$models/Settings';
import User from '$models/User';
import bcrypt from 'bcrypt';
import cuid from 'cuid';
import jsonwebtoken from 'jsonwebtoken';
import type { Request } from '@sveltejs/kit';
const saltRounds = 15;
export async function post(request: Request) {
const { email, password } = request.body
const { JWT_SIGN_KEY } = process.env;
const settings = await Settings.findOne({ applicationName: 'coolify' });
const registeredUsers = await User.find().countDocuments();
const foundUser = await User.findOne({ email });
try {
let uid = cuid();
if (foundUser) {
if (foundUser.type === 'github') {
return {
status: 500,
body: {
error: 'Wrong password or email address.'
}
};
}
uid = foundUser.uid;
if (!await bcrypt.compare(password, foundUser.password)) {
return {
status: 500,
body: {
error: 'Wrong password or email address.'
}
};
}
} else {
if (registeredUsers === 0) {
const newUser = new User({
_id: new mongoose.Types.ObjectId(),
email,
uid,
type: 'email',
password: await bcrypt.hash(password, saltRounds)
});
const defaultSettings = new Settings({
_id: new mongoose.Types.ObjectId()
});
try {
await newUser.save();
await defaultSettings.save();
} catch (error) {
return {
status: 500,
error: error.message || error
};
}
} else {
if (!settings?.allowRegistration) {
return {
status: 500,
body: {
error: 'Registration disabled, enable it in settings.'
}
};
} else {
const newUser = new User({
_id: new mongoose.Types.ObjectId(),
email,
uid,
type: 'email',
password: await bcrypt.hash(password, saltRounds)
});
try {
await newUser.save();
} catch (error) {
return {
status: 500,
error: error.message || error
};
}
}
}
}
const coolToken = jsonwebtoken.sign({}, JWT_SIGN_KEY, {
expiresIn: 15778800,
algorithm: 'HS256',
audience: 'coolLabs',
issuer: 'coolLabs',
jwtid: uid,
subject: `User:${uid}`,
notBefore: -1000
});
request.locals.session.data = { coolToken, ghToken: null };
return {
status: 200,
body: {}
};
} catch (error) {
return { status: 500, body: { error: error.message || error } };
}
}

View File

@@ -33,7 +33,8 @@ export async function get(request: Request) {
_id: new mongoose.Types.ObjectId(),
email,
avatar: avatar_url,
uid
uid,
type: 'github'
});
const defaultSettings = new Settings({
_id: new mongoose.Types.ObjectId()
@@ -68,7 +69,8 @@ export async function get(request: Request) {
_id: new mongoose.Types.ObjectId(),
email,
avatar: avatar_url,
uid
uid,
type: 'github'
});
try {
await newUser.save();

View File

@@ -1,3 +1,18 @@
<script context="module" lang="ts">
/**
* @type {import('@sveltejs/kit').Load}
*/
export async function load(session) {
if (!browser && !process.env.VITE_GITHUB_APP_CLIENTID) {
return {
status: 301,
redirect: '/dashboard/services'
};
}
return {};
}
</script>
<script>
import { application, initialApplication, initConf, dashboard, prApplication } from '$store';
import { onDestroy } from 'svelte';
@@ -58,7 +73,6 @@
onDestroy(() => {
$application = JSON.parse(JSON.stringify(initialApplication));
});
</script>
{#await loadConfiguration()}

View File

@@ -1,8 +1,28 @@
<script context="module" lang="ts">
import { request } from '$lib/request';
/**
* @type {import('@sveltejs/kit').Load}
*/
export async function load(session) {
if (!browser && !process.env.VITE_GITHUB_APP_CLIENTID) {
return {
status: 301,
redirect: '/dashboard/services'
};
}
return {
props: {
initDashboard: await request('/api/v1/dashboard', session)
}
};
}
</script>
<script lang="ts">
import { goto } from '$app/navigation';
import { dashboard, dateOptions } from '$store';
import { dashboard, dateOptions, settings } from '$store';
import { fade } from 'svelte/transition';
import { browser } from '$app/env';
</script>
<div

View File

@@ -1,3 +1,18 @@
<script context="module" lang="ts">
/**
* @type {import('@sveltejs/kit').Load}
*/
export async function load(session) {
if (!browser && !process.env.VITE_GITHUB_APP_CLIENTID) {
return {
status: 301,
redirect: '/dashboard/services'
};
}
return {};
}
</script>
<script>
import { goto } from '$app/navigation';
import MongoDb from '$components/Database/SVGs/MongoDb.svelte';
@@ -8,7 +23,7 @@
import { dashboard } from '$store';
import { fade } from 'svelte/transition';
import Redis from '$components/Database/SVGs/Redis.svelte';
import { browser } from '$app/env';
</script>
<div class="py-5 text-left px-6 text-3xl tracking-tight font-bold flex items-center">
@@ -59,9 +74,7 @@
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"
/>
{:else if database.configuration.general.type == 'redis'}
<Redis
customClass="w-10 h-10 absolute top-0 left-0 -m-4"
/>
<Redis customClass="w-10 h-10 absolute top-0 left-0 -m-4" />
{:else if database.configuration.general.type == 'clickhouse'}
<Clickhouse
customClass="w-10 h-10 fill-current text-red-600 absolute top-0 left-0 -m-4"

View File

@@ -1,10 +1,15 @@
<script>
import { browser } from '$app/env';
import { goto } from '$app/navigation';
import { session } from '$app/stores';
import { toast } from '@zerodevx/svelte-toast';
import PasswordField from '$components/PasswordField.svelte';
import { request } from '$lib/request';
import { settings } from '$store';
import Loading from '$components/Loading.svelte';
let loading = false;
let email = null;
let password = null;
async function login() {
const left = screen.width / 2 - 1020 / 2;
const top = screen.height / 2 - 618 / 2;
@@ -22,11 +27,26 @@
const timer = setInterval(() => {
if (newWindow?.closed) {
clearInterval(timer);
browser && location.reload()
browser && location.reload();
}
}, 100);
}
async function loginWithEmail() {
try {
loading = true;
await request('/api/v1/login/email', $session, {
body: {
email,
password
}
});
browser && location.reload();
} catch (error) {
browser && toast.push(error.error || error || 'Ooops something went wrong.');
} finally {
loading = false;
}
}
</script>
<div class="flex justify-center items-center h-screen w-full bg-warmGray-900">
@@ -37,24 +57,54 @@
>
<span class="border-gradient">Coolify</span>
</p>
<h2 class="text-2xl md:text-3xl font-extrabold text-white">
<h2 class="text-2xl md:text-3xl font-extrabold text-white py-10">
An open-source, hassle-free, self-hostable<br />
<span class="text-indigo-400">Heroku</span>
& <span class="text-green-400">Netlify</span> alternative
</h2>
<div class="text-center py-10">
{#if !$session.isLoggedIn}
<button
class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold"
on:click={login}>Login with Github</button
>
{:else}
<button
class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold"
on:click={() => goto('/dashboard/applications')}>Get Started</button
>
{/if}
</div>
{#if loading}
<Loading fullscreen={false}/>
{:else}
<div class="text-center py-10 max-w-7xl">
{#if !$session.isLoggedIn}
{#if $settings.clientId}
<button
class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold"
on:click={login}>Login with GitHub</button
>
{:else}
<div>
<div class="grid grid-flow-row gap-2 items-center pb-6">
<div class="grid grid-flow-row">
<label for="Email" class="">Email address</label>
<input
class="border-2"
id="Email"
bind:value={email}
placeholder="hi@coollabs.io"
/>
</div>
<div class="grid grid-flow-row">
<label for="Password" class="">Password</label>
<PasswordField bind:value={password} isEditable />
</div>
</div>
<div class="space-x-4 pt-10">
<button
class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold"
on:click={loginWithEmail}>Login with Email</button
>
</div>
</div>
{/if}
{:else}
<button
class="text-white bg-warmGray-800 hover:bg-warmGray-700 rounded p-2 px-10 font-bold"
on:click={() => goto('/dashboard/applications')}>Get Started</button
>
{/if}
</div>
{/if}
</div>
</div>
</div>