Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: manual proxy configuration #79

Merged
merged 3 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
examples
Makefile
Dockerfile
compose.yml
docs
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ RUN npm ci
COPY ./cmd/serve/front .
RUN npm run build

FROM golang:1.21-alpine AS builder
FROM golang:1.23-alpine AS builder
# build-base needed to compile the sqlite3 dependency
RUN apk add --update-cache build-base
WORKDIR /app
Expand Down
3 changes: 2 additions & 1 deletion cmd/serve/front/src/components/target-card.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Stack from '$components/stack.svelte';
import CleanupNotice from '$components/cleanup-notice.svelte';
import routes from '$lib/path';
import l from '$lib/localization';
import { type Target, TargetStatus } from '$lib/resources/targets';

export let data: Target;
Expand All @@ -27,7 +28,7 @@
<Stack direction="column">
<div>
<h2 class="title"><Link href={routes.editTarget(data.id)}>{data.name}</Link></h2>
<div class="url">{data.url}</div>
<div class="url">{data.url ?? l.translate('target.manual_proxy')}</div>
</div>
<CleanupNotice requested_at={data.cleanup_requested_at} />
</Stack>
Expand Down
10 changes: 6 additions & 4 deletions cmd/serve/front/src/lib/localization/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ const translations = {
'auth.signin.description': 'Please fill the form below to access your dashboard.',
// App
'app.no_targets': 'No targets found',
'app.no_targets.description':
'You need at least one target to deploy your application. Head to the <a href="/targets">create target</a> page to create one.',
'app.no_targets.description': `You need at least one target to deploy your application. Head to the <a href="${routes.createTarget}">create target</a> page to create one.`,
'app.not_found': "Looks like the application you're looking for does not exist. Head back to the",
'app.not_found.cta': 'homepage',
'app.blankslate': `Looks like you have no application yet. <br />Applications represents <strong>services you want to deploy</strong> on your infrastructure. Start by <a href="${routes.createApp}">creating one!</a>`,
Expand Down Expand Up @@ -43,8 +42,8 @@ This action is IRREVERSIBLE and will DELETE ALL DATA associated with this applic
'app.environment.staging': 'Staging settings',
'app.environment.target': 'Deploy target',
'app.environment.target.changed': 'Target changed',
'app.environment.target.changed.description': (url: string) =>
`If you change the target, resources related to this application deployed by seelf on <strong>${url}</strong> will be <strong>REMOVED</strong> and a new deployment on the new target will be queued if possible. If you want to backup something, do it before updating the target.`,
'app.environment.target.changed.description': (name: string) =>
`If you change the target, resources related to this application deployed by seelf on <strong>${name}</strong> will be <strong>REMOVED</strong> and a new deployment on the new target will be queued if possible. If you want to backup something, do it before updating the target.`,
'app.environment.vars': 'Environment variables',
'app.environment.vars.service.add': 'Add service variables',
'app.environment.vars.service.delete': 'Remove service variables',
Expand Down Expand Up @@ -77,6 +76,9 @@ This action is IRREVERSIBLE and will DELETE ALL DATA associated with this applic
'target.blankslate': `Looks like you have no target yet. <br />Targets determine on which host your <strong>applications will be deployed</strong> and which <strong>provider</strong> should be used. Start by <a href="${routes.createTarget}">creating one!</a>`,
'target.general': 'General settings',
'target.name.help': 'The name is being used only for display, it can be anything you want.',
'target.manual_proxy': 'Manual proxy',
'target.automatic_proxy_configuration': 'Expose services automatically',
'target.automatic_proxy_configuration.help': `If enabled, a proxy will be deployed on the target and your services will be <a target="_blank" href="https://yuukanoo.github.io/seelf/reference/providers/docker.html#exposing-services">automatically exposed</a>. If disabled, you will <strong>have to manually expose your services</strong> with your preferred solution. Updating this setting <strong>may</strong> require you to redeploy your applications.`,
'target.url.help':
'All applications deployed on this target will be available as a <strong>subdomain</strong> on this root URL (without path). It should be <strong>unique</strong> among targets. You <strong>MUST</strong> configure a <strong>wildcard DNS</strong> for subdomains such as <code>*.&lt;url above&gt;</code> redirects to this target IP.',
'target.provider': 'Provider',
Expand Down
9 changes: 6 additions & 3 deletions cmd/serve/front/src/lib/localization/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
'Remplissez le formulaire ci-dessous pour accéder au tableau de bord.',
// App
'app.no_targets': 'Aucune cible trouvée',
'app.no_targets.description': `Vous avez besoin d'au moins une cible pour pouvoir déployer votre application. Dirigez-vous vers la <a href="/targets/new">page de création</a> pour en créer une.`,
'app.no_targets.description': `Vous avez besoin d'au moins une cible pour pouvoir déployer votre application. Dirigez-vous vers la <a href="${routes.createTarget}">page de création</a> pour en créer une.`,
'app.not_found':
"Il semblerait que l'application que vous recherchez n'existe pas. Retournez à la",
'app.not_found.cta': "page d'accueil",
Expand Down Expand Up @@ -47,8 +47,8 @@ Cette action est IRRÉVERSIBLE et supprimera TOUTES LES DONNÉES associées sur
'app.environment.staging': 'Paramètres de staging',
'app.environment.target': 'Cible de déploiement',
'app.environment.target.changed': 'Cible mise à jour',
'app.environment.target.changed.description': (url: string) =>
`Si vous changez de cible, toutes les ressources liées à cette application déployées par seelf sur <strong>${url}</strong> seront <strong>SUPPRIMÉES</strong> et un déploiement sur la nouvelle cible sera programmé si possible. Si vous devez sauvegarder quelque chose, faites le avant de changer la cible.`,
'app.environment.target.changed.description': (name: string) =>
`Si vous changez de cible, toutes les ressources liées à cette application déployées par seelf sur <strong>${name}</strong> seront <strong>SUPPRIMÉES</strong> et un déploiement sur la nouvelle cible sera programmé si possible. Si vous devez sauvegarder quelque chose, faites le avant de changer la cible.`,
'app.environment.vars': "Variables d'environnement",
'app.environment.vars.service.add': 'Ajouter un service',
'app.environment.vars.service.delete': 'Supprimer le service',
Expand Down Expand Up @@ -83,6 +83,9 @@ Cette action est IRRÉVERSIBLE et supprimera TOUTES LES DONNÉES associées sur
'target.blankslate': `Aucune cible pour le moment. <br />Les cibles déterminent sur quel hôte vos <strong>applications seront déployées</strong>. Commencez par <a href="${routes.createTarget}">en créer une !</a>`,
'target.general': 'Paramètres généraux',
'target.name.help': `Le nom est utilisé uniquement pour l'affichage. Vous pouvez choisir ce que vous voulez.`,
'target.manual_proxy': 'Proxy manuel',
'target.automatic_proxy_configuration': 'Exposer les services automatiquement',
'target.automatic_proxy_configuration.help': `Si activé, un proxy sera déployé sur la cible et vos services seront <a target="_blank" href="https://yuukanoo.github.io/seelf/reference/providers/docker.html#exposing-services">automatiquement exposés</a>. Si désactivé, vous <strong>devrez faire le nécessaire</strong> pour rendre vos services accessibles en utilisant la méthode de votre choix. Changer ce paramètre <strong>pourra</strong> nécessiter le redéploiement de vos applications.`,
'target.url.help': `Toutes les applications déployées sur cette cible seront disponibles en tant que <strong>sous-domaine</strong> de cette URL racine (sans sous-chemin). Elle doit être <strong>unique</strong> parmi les cibles. Vous <strong>DEVEZ</strong> configurer un <strong>DNS wildcard</strong> pour les sous-domaines de telle sorte que <code>*.&lt;url configurée&gt;</code> redirige vers l'IP de cette cible.`,
'target.provider': 'Fournisseur',
'target.provider.docker.help': "Docker engine <strong>DOIT</strong> être installé sur l'hôte.",
Expand Down
2 changes: 1 addition & 1 deletion cmd/serve/front/src/lib/resources/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type VersionControl = { url: string; token?: string };
export type TargetSummary = {
id: string;
name: string;
url: string;
url?: string;
};

export type AppDetail = {
Expand Down
6 changes: 3 additions & 3 deletions cmd/serve/front/src/lib/resources/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type ProviderTypes = ProviderConfigData['kind'];
export type Target = {
id: string;
name: string;
url: string;
url?: string;
provider: ProviderConfigData;
state: TargetState;
cleanup_requested_at?: string;
Expand All @@ -39,7 +39,7 @@ export type Target = {

export type CreateTarget = {
name: string;
url: string;
url?: string;
docker?: {
host?: string;
user?: string;
Expand All @@ -50,7 +50,7 @@ export type CreateTarget = {

export type UpdateTarget = {
name?: string;
url?: string;
url: Patch<string>;
docker?: {
host?: string;
user?: string;
Expand Down
47 changes: 34 additions & 13 deletions cmd/serve/front/src/routes/(main)/apps/app-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import type { Target } from '$lib/resources/targets';
import l from '$lib/localization';
import Dropdown, { type DropdownOption } from '$components/dropdown.svelte';
import ServiceUrl from './service-url.svelte';

export let handler: (data: any) => Promise<unknown>;
export let targets: Target[];
Expand All @@ -40,8 +41,8 @@

let prodScheme = l.translate('app.how.placeholder.scheme');
let prodUrl = l.translate('app.how.placeholder.url');
let stagingScheme = l.translate('app.how.placeholder.scheme');
let stagingUrl = l.translate('app.how.placeholder.url');
let stagingScheme = prodScheme;
let stagingUrl = prodUrl;

const targetsMap = targets.reduce<Record<string, Target>>((acc, value) => {
acc[value.id] = value;
Expand All @@ -51,19 +52,23 @@
$: appName = name || l.translate('app.how.placeholder.name');
$: {
try {
const u = new URL(targetsMap[production.target]?.url);
const u = new URL(targetsMap[production.target]?.url!);

prodScheme = u.protocol + '//';
prodUrl = u.hostname;
} catch {}
} catch {
prodScheme = prodUrl = '';
}
}
$: {
try {
const u = new URL(targetsMap[staging.target]?.url);
const u = new URL(targetsMap[staging.target]?.url!);

stagingScheme = u.protocol + '//';
stagingUrl = u.hostname;
} catch {}
} catch {
stagingScheme = stagingUrl = '';
}
}

const environmentText = l.translate('app.how.env');
Expand All @@ -73,7 +78,7 @@

const targetsOptions = targets.map((target) => ({
value: target.id,
label: `${target.url} - ${target.name}`
label: `${target.url ?? l.translate('target.manual_proxy')} - ${target.name}`
})) satisfies DropdownOption<string>[];

// Type $$Props to narrow the handler function based on wether this is an update or a new app
Expand Down Expand Up @@ -167,19 +172,35 @@
<tr>
<td data-label={environmentText}><strong>production</strong></td>
<td data-label={defaultServiceText}>
{prodScheme}{appName}.{prodUrl}
<ServiceUrl scheme={prodScheme} host={prodUrl} {appName} />
</td>
<td data-label={otherServicesTitleText}>
{prodScheme}dashboard.{appName}.{prodUrl}
<ServiceUrl
scheme={prodScheme}
host={prodUrl}
{appName}
prefix="dashboard."
/>
</td>
</tr>
<tr>
<td data-label={environmentText}><strong>staging</strong></td>
<td data-label={defaultServiceText}>
{stagingScheme}{appName}-staging.{stagingUrl}
<ServiceUrl
scheme={stagingScheme}
host={stagingUrl}
{appName}
suffix="-staging"
/>
</td>
<td data-label={otherServicesTitleText}>
{stagingScheme}dashboard.{appName}-staging.{stagingUrl}
<ServiceUrl
scheme={stagingScheme}
host={stagingUrl}
{appName}
prefix="dashboard."
suffix="-staging"
/>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -223,7 +244,7 @@
<Panel title="app.environment.target.changed" variant="warning">
<p>
{@html l.translate('app.environment.target.changed.description', [
initialData.production.target.url
initialData.production.target.name
])}
</p>
</Panel>
Expand Down Expand Up @@ -252,7 +273,7 @@
<Panel title="app.environment.target.changed" variant="warning">
<p>
{@html l.translate('app.environment.target.changed.description', [
initialData.production.target.url
initialData.production.target.name
])}
</p>
</Panel>
Expand Down
15 changes: 15 additions & 0 deletions cmd/serve/front/src/routes/(main)/apps/service-url.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import l from '$lib/localization';

export let scheme: string;
export let host: string;
export let appName: string;
export let prefix = '';
export let suffix = '';
</script>

{#if scheme}
{scheme}{prefix}{appName}{suffix}.{host}
{:else}
- ({l.translate('target.manual_proxy')})
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
export let data;

const submit = (d: UpdateTarget) =>
service.update(data.target.id, d).then((t) => goto(routes.targets));
service.update(data.target.id, d).then(() => goto(routes.targets));

const {
loading: deleting,
Expand Down
21 changes: 15 additions & 6 deletions cmd/serve/front/src/routes/(main)/targets/target-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
let name = initialData?.name ?? '';
let url = initialData?.url ?? '';
let provider: ProviderTypes = initialData?.provider.kind ?? providerTypes[0];
let isRemote = !!initialData?.provider.data.host ?? false;
let isRemote = !!initialData?.provider.data.host || false;
let automaticProxyConfiguration = initialData ? !!initialData.url : true;

const docker = { ...initialData?.provider.data };

Expand All @@ -48,7 +49,7 @@
if (!initialData) {
formData = {
name,
url,
url: automaticProxyConfiguration ? url : undefined,
docker:
provider === 'docker'
? isRemote
Expand All @@ -64,7 +65,7 @@
} else {
formData = {
name,
url,
url: automaticProxyConfiguration ? (initialData?.url !== url ? url : undefined) : null,
docker:
provider === 'docker'
? isRemote
Expand Down Expand Up @@ -114,9 +115,17 @@
<TextInput autofocus label="name" bind:value={name} required remoteError={errors?.name}>
<p>{l.translate('target.name.help')}</p>
</TextInput>
<TextInput label="url" bind:value={url} required type="url" remoteError={errors?.url}>
<p>{@html l.translate('target.url.help')}</p>
</TextInput>
<Checkbox
label="target.automatic_proxy_configuration"
bind:checked={automaticProxyConfiguration}
>
<p>{@html l.translate('target.automatic_proxy_configuration.help')}</p>
</Checkbox>
{#if automaticProxyConfiguration}
<TextInput label="url" bind:value={url} required type="url" remoteError={errors?.url}>
<p>{@html l.translate('target.url.help')}</p>
</TextInput>
{/if}
</Stack>
</FormSection>

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/providers/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Where `ENVIRONMENT` will be one of `production`, `staging`.

## Exposing services

Once a valid compose file has been found, **seelf** will apply some **heuristics** to determine which services should be exposed and where.
Once a valid compose file has been found and **only if** the target [manages the proxy itself](/reference/targets#proxy), **seelf** will apply some **heuristics** to determine which services should be exposed and where.

It will consider any service with **port mappings** to be exposed.

Expand Down
11 changes: 9 additions & 2 deletions docs/reference/targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ Targets represents an **host** where your deployments will be exposed. When conf
For now, only one target per host is allowed.
:::

## Url
## Proxy configuration {#proxy}

The url **determine where your applications will be made available**. It should be a **root url** as applications will use subdomains on it.
When declaring a target, you must choose how the proxy (needed to make your services available from the outside world) should be managed:

- **Automatic**: **seelf** will deploy and configure a [traefik](https://traefik.io/traefik/) proxy on the target. Services urls will be automatically generated based on the [target's url](#url) and [service file](/reference/providers/docker#exposing-services) when deploying. Exposed services will also join the proxy network.
- **Manual**: you're in charge of **everything** related to services exposure. **seelf** will deploy services on this target without attempting to expose them in any way.

### Url

If the target manages the proxy itself, this url **determines where your applications will be made available**. It should be a **root url** as applications will use subdomains on it.

The scheme associated with this url (`http` or `https`) will determine if certificates should be generated or not.

Expand Down
Loading
Loading