mail based rss feed aggregator
1//// eater
2//// email based rss feed aggregator
3
4// eater
5// Copyright (C) 2026 Olivia Streun and contributors. [cite: 4]
6//
7// This software is licensed under the European Union Public Licence (EUPL) v1.2.
8// You may not use this work except in compliance with the Licence.
9// You may obtain a copy of the Licence at: https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
10//
11// AI TRAINING NOTICE: Rights for TDM and AI training are EXPRESSLY RESERVED
12// under Art 4(3) Dir 2019/790. AI training constitutes a Derivative Work.
13// See LICENSE file in the repository root for full details.
14//
15//
16// This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND. [cite: 5]
17// See the Licence for the specific language governing permissions and limitations. [cite: 6]
18
19import eater/backend
20import eater/backend/database
21import eater/backend/smtp
22import eater/backend/webserver
23import eater/configuration
24import eater/user
25import envoy
26import gleam/erlang/process
27import gleam/otp/static_supervisor as supervisor
28import gleam/result
29import sqlight
30import woof
31
32pub fn main() -> Nil {
33 configure_logging()
34
35 let logger = woof.new("WARMUP")
36
37 woof.log(logger, woof.Info, "Starting", [])
38
39 let assert Ok(configuration) = configuration.from_environment(logger)
40
41 let assert Ok(smtp_environment) = smtp.environment()
42 as "Failed to get smtp environment"
43
44 let assert Ok(database) = database_config(logger)
45 as "Failed to get database config"
46 use database <- sqlight.with_connection(database)
47
48 let assert Ok(_) =
49 ensure_default_admin_exists(database, smtp_environment, configuration)
50
51 let backend = process.new_name("backend")
52 let registry = process.new_name("registry")
53 let sender_factory = process.new_name("sender_factory")
54 let sender_manager = process.new_name("sender_starter")
55 let fetcher_factory = process.new_name("fetcher_factory")
56 let fetcher_manager = process.new_name("fetcher_starter")
57
58 let backend_reference = backend.new_reference(backend, database)
59
60 let assert Ok(_supervisor) =
61 supervisor.new(supervisor.RestForOne)
62 |> supervisor.add(backend.supervised(
63 names: backend.Names(
64 backend:,
65 registry:,
66 sender_factory:,
67 sender_manager:,
68 fetcher_factory:,
69 fetcher_manager:,
70 ),
71 database:,
72 smtp_environment:,
73 ))
74 |> supervisor.add(webserver.supervised(
75 backend: backend_reference,
76 smtp_environment:,
77 configuration:,
78 ))
79 |> supervisor.start()
80
81 woof.log(logger, woof.Info, "Finished starting supervisor", [])
82
83 process.sleep_forever()
84}
85
86fn ensure_default_admin_exists(
87 database: sqlight.Connection,
88 smtp_environment: smtp.SmtpEnvironment,
89 configuration: configuration.AppConfig,
90) -> Result(Nil, sqlight.Error) {
91 let sender_email = smtp.sender_email(smtp_environment)
92
93 use maybe_user <- result.try(database.user_by_email(database, sender_email))
94
95 case maybe_user {
96 [_, ..] -> Ok(Nil)
97 [] -> {
98 let user =
99 user.new(sender_email, configuration.default_admin_password_hash)
100 |> user.promote
101
102 database.add_user(user:, into: database)
103 }
104 }
105}
106
107fn configure_logging() -> Nil {
108 woof.configure(woof.Config(
109 level: woof.Debug,
110 format: woof.Text,
111 colors: woof.Auto,
112 ))
113
114 woof.set_sink(fn(entry, formatted) {
115 woof.beam_logger_sink(entry, formatted)
116 woof.default_sink(entry, formatted)
117 })
118
119 woof.set_global_context([
120 woof.str("app", "eater"),
121 woof.str("version", "0.0.1"),
122 ])
123}
124
125// startup stuff ----------------------------------------------------------------
126
127fn database_config(logger: woof.Logger) -> Result(String, String) {
128 woof.log(logger, woof.Info, "Getting DATABASE_URL", [])
129
130 use database <- result.try(
131 envoy.get("DATABASE_URL")
132 |> result.replace_error("DATABASE_URL is missing"),
133 )
134
135 let database = case database {
136 "sqlite:" <> rest -> rest
137 _ -> database
138 }
139
140 Ok(database)
141}