Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a5f46a6
wip, refactor, create subscriber update, etc.
supun-io Feb 25, 2026
a398a90
side project: sentry config
supun-io Feb 25, 2026
556cb0b
subscriber lists done
supun-io Feb 26, 2026
07d310d
clean subscriber endpoints
supun-io Feb 26, 2026
988bce0
create subscriber endpoint updated
supun-io Feb 26, 2026
89c23a0
wip
supun-io Feb 26, 2026
3642bf3
wip
supun-io Feb 27, 2026
3b7a466
cleanup lists
supun-io Mar 2, 2026
8703b93
API docs
supun-io Mar 2, 2026
38ed546
wip endpoint
supun-io Mar 3, 2026
2ff5549
input planned
supun-io Mar 3, 2026
5627cff
create subscriber, validate metadata, etc.
supun-io Mar 3, 2026
59ae7b5
update subscriber wip
supun-io Mar 3, 2026
e6f6cb0
lists strategy, before tests
supun-io Mar 3, 2026
7dc9c43
lists strategy done, metadata strategy before tests
supun-io Mar 3, 2026
86a1db4
saving list removals
supun-io Mar 3, 2026
de1d927
wip
supun-io Mar 3, 2026
dbfbd38
record list removal
supun-io Mar 3, 2026
574008b
tests wiop
supun-io Mar 3, 2026
a555ab8
tests
supun-io Mar 3, 2026
dd95305
removing a removal when re-adding
supun-io Mar 4, 2026
7db9acd
sending confirmation mail
supun-io Mar 4, 2026
c42d5bc
merge
supun-io Apr 20, 2026
7891e21
creaate subscribers wip
supun-io Mar 22, 2026
93c4607
set up
supun-io Apr 19, 2026
3330ef6
console: add and edit subscriber
supun-io Apr 20, 2026
47b9f77
minor changes
supun-io Apr 20, 2026
4901435
wip
supun-io Apr 20, 2026
b1fe205
form subscribe done
supun-io Apr 20, 2026
2762e0e
unsubscribe / preferences
supun-io Apr 21, 2026
ebc8f13
some phpstan fixes
supun-io Apr 21, 2026
71a4cef
unsubscribe message handler: before
supun-io Apr 22, 2026
8cc2a2b
handler wrote
supun-io Apr 22, 2026
a294ebb
relay webhook handling
supun-io Apr 22, 2026
a1b308e
phpstan fixed
supun-io Apr 22, 2026
7349af5
frontend fix
supun-io Apr 22, 2026
4185e5c
prettier
supun-io Apr 22, 2026
f3d5522
one migration only
supun-io Apr 28, 2026
c59a08b
pretteier, phpstan
supun-io Apr 28, 2026
5cf25df
test fix
supun-io Apr 28, 2026
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
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ backend/vendor
backend/var
frontend/.svelte-kit
frontend/node_modules
frontend/build
frontend/build
embed/node_modules
11 changes: 3 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: "24.9.0"

- name: Archive
run: cd archive && npm ci && npm run lint
run: docker build -t archive-dev . --target archive-dev && docker run archive-dev npm run lint

- name: Embed
run: cd embed && npm ci && npm run lint
run: docker build -t embed-dev . --target embed-dev && docker run embed-dev npm run lint

- name: Frontend
run: cd frontend && npm ci && npm run lint
run: docker build -t frontend-dev . --target frontend-dev && docker run frontend-dev npm run lint
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"[svelte]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
}
12 changes: 12 additions & 0 deletions DEV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Code Quality

```bash
# php tests (use --filter to run specific tests)
docker compose run --rm backend vendor/bin/phpunit

# phpstan
docker compose run --rm backend vendor/bin/phpstan --memory-limit=1G

# prettier
docker compose run --rm frontend npm run format
```
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ WORKDIR /app/frontend
COPY frontend/package.json \
frontend/svelte.config.js \
frontend/vite.config.ts \
frontend/.prettierrc frontend/.prettierignore \
frontend/tsconfig.json /app/frontend/
# copy code
COPY frontend/src /app/frontend/src
Expand Down Expand Up @@ -46,6 +47,7 @@ WORKDIR /app/archive
COPY archive/package.json archive/package-lock.json \
archive/svelte.config.js \
archive/vite.config.ts \
archive/.prettierrc archive/.prettierignore \
archive/tsconfig.json /app/archive/
# copy code
COPY archive/src /app/archive/src
Expand Down Expand Up @@ -85,6 +87,7 @@ COPY embed/package.json embed/package-lock.json \
embed/tsconfig.json \
embed/tsconfig.app.json \
embed/tsconfig.node.json \
embed/.prettierrc embed/.prettierignore \
/app/embed/

COPY embed/src /app/embed/src
Expand Down
16 changes: 3 additions & 13 deletions archive/src/lib/actions/subscriptionActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,9 @@ interface UnsubscribeResponse {
lists: List[];
}

export function unsubscribe(token: string) {
export function changePreferences(token: string, list_ids: number[]) {
return publicApi.post<UnsubscribeResponse>({
endpoint: SUBSCRIBER_PREFIX + '/unsubscribe',
data: { token }
});
}

export function resubscribe(list_ids: number[], token: string) {
return publicApi.patch<void>({
endpoint: SUBSCRIBER_PREFIX + '/resubscribe',
data: {
list_ids,
token
}
endpoint: SUBSCRIBER_PREFIX + '/preferences',
data: { token, list_ids }
});
}
8 changes: 4 additions & 4 deletions archive/src/routes/unsubscribe/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import IconEyeglasses from '@hyvor/icons/IconEyeglasses';
import { newsletterStore, listsStore } from '$lib/archiveStore';
import Resubscribe from './Resubscribe.svelte';
import { unsubscribe } from '$lib/actions/subscriptionActions';
import { changePreferences } from '$lib/actions/subscriptionActions';

let isLoading = $state(true);
let previewMode = $state(false);
Expand All @@ -32,7 +32,7 @@
return;
}

unsubscribe(token)
changePreferences(token, [])
.then((data) => {
listsStore.set(data.lists);
})
Expand Down Expand Up @@ -63,9 +63,9 @@
{:else}
<Notice
heading="Unsubscribe successful"
message="You have unsubscribed from all lists of <strong>{$newsletterStore.name}</strong>.
message="You've been unsubscribed from <strong>{$newsletterStore.name}</strong>.
<br />
If this was a mistake, you can easily resubscribe below. Thank you."
If you change your mind, you can resubscribe below."
icon={IconEnvelopeSlash}
>
<Resubscribe lists={$listsStore} {token} bind:error />
Expand Down
85 changes: 50 additions & 35 deletions archive/src/routes/unsubscribe/Resubscribe.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import type { List } from '$lib/types';
import Switch from './Switch.svelte';
import Button from '../@components/Button.svelte';
import { resubscribe } from '$lib/actions/subscriptionActions';
import { changePreferences } from '$lib/actions/subscriptionActions';
import { fade } from 'svelte/transition';

interface Props {
lists: List[];
Expand All @@ -13,7 +13,9 @@

let { lists, token, error = $bindable() }: Props = $props();

let selectedListsIds: number[] = $state(lists.map((list) => list.id));
let selectedListsIds: number[] = $state([]);
let saving = $state(false);
let saved = $state(false);

function handleListSwitch(listId: number) {
return (event: Event) => {
Expand All @@ -40,56 +42,55 @@
return;
}

resubscribe(selectedListsIds, token).catch((e) => {
error = e.message || 'An unexpected error occurred';
});
saving = true;

changePreferences(token, selectedListsIds)
.then(() => {
saved = true;
setTimeout(() => {
saved = false;
}, 2000);
})
.catch((e) => {
error = e.message || 'An unexpected error occurred';
})
.finally(() => {
saving = false;
});
}
</script>

<div class="resubscribe">
<div class="lists" transition:slide={{ duration: 400 }} class:hidden={lists.length === 0}>
<div class="lists" class:hidden={lists.length === 0}>
{#each lists as list (list.id)}
<label class="list">
<div class="list-name-description">
<div class="list-name">{list.name}</div>
<div class="list-description">{list.description}</div>
</div>
<Switch
checked={selectedListsIds.includes(list.id)}
onchange={handleListSwitch(list.id)}
/>
<Switch checked={selectedListsIds.includes(list.id)} onchange={handleListSwitch(list.id)} />
</label>
{/each}
</div>

<div class="select">
<Button
color="var(--hp-accent-text-light)"
backgroundColor="transparent"
size="x-small"
style="font-weight: 500;"
onclick={handleSelectAll}
>
Select all
</Button>
<Button
color="var(--hp-accent-text-light)"
backgroundColor="transparent"
size="x-small"
style="font-weight: 500;"
onclick={handleDeselectAll}
>
Deselect all
</Button>
<button onclick={handleDeselectAll}>Deselect</button>
<button onclick={handleSelectAll}>Select all</button>
</div>

<Button size="small" onclick={handleSave}>Save preferences</Button>
<div>
<Button onclick={handleSave} disabled={saving}>Save preferences</Button>
</div>

{#if saved}
<div class="saved" transition:fade>Preferences saved</div>
{/if}
</div>

<style>
.lists {
margin: auto;
width: 70%;
margin-top: 30px;
}

.list {
Expand All @@ -114,10 +115,24 @@
}

.select {
margin-top: 10px;
margin-bottom: 30px;
display: flex;
flex-direction: column;
gap: 2px;
margin: 5px 9% 10px auto;
width: 20%;
gap: 6px;
justify-content: flex-end;
}

.select button {
font-size: 12px;
}

.select button:hover {
text-decoration: underline;
}

.saved {
margin-top: 10px;
font-size: 14px;
color: var(--hp-text-light);
}
</style>
9 changes: 4 additions & 5 deletions backend/.env
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ URL_ARCHIVE=https://hyvorpost.email
# One of: debug, info, notice, warning, error, critical, alert, emergency
LOG_LEVEL=info


### ============ HYVOR CLOUD ============ ###

# Deployment type
Expand All @@ -54,13 +53,11 @@ COMMS_KEY=
# @deprecated. Migrate to DEPLOYMENT env
IS_CLOUD=true


### ============ DATABASE ============ ###

# PostgreSQL database is the single source of truth
DATABASE_URL=""


### ============ MAIL ============ ###

# Hyvor Relay configuration
Expand All @@ -87,7 +84,6 @@ NOTIFICATION_MAIL_REPLY_TO=
# If not set, if will fallback to RELAY_API_KEY
NOTIFICATION_RELAY_API_KEY=


### ============ FILE STORAGE ============ ###

# You can use any S3-compatibly storage like
Expand All @@ -100,14 +96,17 @@ S3_ACCESS_KEY_ID=key-id
S3_SECRET_ACCESS_KEY=access-key
S3_BUCKET=hyvor-post

### ============ INTEGRATIONS ============ ###

# Sentry-compatible DSN for error tracking
SENTRY_DSN=

### ============ SCALING ============ ###

# Set the number of workers to run
# Default is x2 CPUs
WORKERS=


### ============ DOCKER IMAGE ============ ###

# Defaults (do not change or add to docs):
Expand Down
6 changes: 2 additions & 4 deletions backend/.env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ HYVOR_PRIVATE_INSTANCE=http://hyvor.internal
COMMS_KEY=BXnQt5EOIT3NwHYvJ0diIa+6iK1YeVTjQfz6XVSFHFg=
HYVOR_FAKE=1

###> symfony/mailer ###
# MAILER_DSN=null://null
MAILER_DSN=smtp://user:pass@hyvor-service-mailpit:1025
###< symfony/mailer ###
NOTIFICATION_MAIL_FROM_ADDRESS=notifications@hyvor.localhost
NOTIFICATION_MAIL_FROM_NAME=HYVOR
NOTIFICATION_MAIL_REPLY_TO=support@hyvor.localhost

RELAY_URL=
RELAY_API_KEY=
# dev emails sent here
MAILER_DSN=smtp://user:pass@hyvor-service-mailpit:1025

###> filesystem ###
FILESYSTEM_ADAPTER=file
Expand Down
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/.env.dev.local
/.env.local.php
/.env.*.local
/.env.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
Expand Down
3 changes: 2 additions & 1 deletion backend/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"twig/cssinliner-extra": "^3.21",
"twig/extra-bundle": "^3.21",
"symfony/dom-crawler": "7.4.*",
"zenstruck/messenger-monitor-bundle": "^0.6.0"
"zenstruck/messenger-monitor-bundle": "^0.6.0",
"sentry/sentry-symfony": "^5.9"
},
"bump-after-update": true,
"sort-packages": true,
Expand Down
Loading
Loading