Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
35eeb87
feat: add draft for port of user registration to resource route
scheidtdav May 14, 2025
42593f0
feat: partly implement refresh token
scheidtdav May 19, 2025
dcd635f
docs: simplify contributing and add info about api routes and shared …
scheidtdav May 21, 2025
be4ebed
feat(api): finalize user registration endpoint
scheidtdav May 21, 2025
b4b8421
fix(tests): get the tests to run be reconfiguring build steps
scheidtdav May 21, 2025
8fbb075
docs(db): readd db setup and seed scripts with README info for it
scheidtdav May 21, 2025
1de6e69
fix: wrong import of utils
scheidtdav May 21, 2025
08f4405
refactor: remove leftover custom server stuff
scheidtdav May 21, 2025
4a3f8e4
fix(tests): add missing refresh token table
scheidtdav May 21, 2025
29d3034
fix(tests): reenable remaining tests for registration
scheidtdav May 22, 2025
d164738
fix(ci): remove playwright and use correct node version
scheidtdav May 27, 2025
7566724
fix(ci): run the tests with a postgres container
scheidtdav May 27, 2025
44894b4
feat(tests): add coverage report
scheidtdav May 27, 2025
77b4cc9
fix(build): reorganize server modules to correctly split client/ server
scheidtdav May 27, 2025
5612a5f
fix(build): miss an import
scheidtdav May 27, 2025
2e335d5
fix(build): remove leftovers from custom server implementation
scheidtdav May 27, 2025
bbf5430
chore(deps): bump react-router dependencies
scheidtdav May 27, 2025
bf19c7e
chore(deps): update react-router
scheidtdav May 27, 2025
7d91045
feat/user me api (#559)
scheidtdav May 27, 2025
a5699de
feat(api): add root route (#560)
scheidtdav May 27, 2025
760914b
start
JerryVincent Jun 13, 2025
81a1f9c
new commit
JerryVincent Jun 13, 2025
acf1770
tested docs
JerryVincent Jun 16, 2025
eedb806
added a route
JerryVincent Jun 16, 2025
a6245ea
Added API Docs
JerryVincent Jun 16, 2025
4712c6a
modified
JerryVincent Jun 16, 2025
f63dc07
removed unsupported packages
JerryVincent Jun 16, 2025
7741945
updated
JerryVincent Jun 16, 2025
3b620a0
Modified
JerryVincent Jun 17, 2025
f51f518
script generation without using ts-node.
JerryVincent Jun 17, 2025
5f7a6ef
modified
JerryVincent Jun 17, 2025
f9ecca4
Merge branch 'api-prod' into feat/user-registration-api
scheidtdav Jun 18, 2025
bd0e52e
fix: update package-lock.json
scheidtdav Jun 18, 2025
31f793c
Updated (#575)
JerryVincent Jun 24, 2025
511b94a
Removed duplicate Documentation section (#576)
JerryVincent Jun 25, 2025
98617c3
Update README.md
JerryVincent Jun 25, 2025
9492e13
Feat/api email and password (#561)
scheidtdav Jun 25, 2025
8a721f7
feat/api auth (#562)
scheidtdav Jun 25, 2025
ef552e1
feat(api): boxes for user endpoints (#573)
scheidtdav Jun 25, 2025
46e89cd
feat/api misc (#571)
scheidtdav Jun 25, 2025
fcf4b8d
Merge branch 'dev' into feat/user-registration-api
jona159 Jun 25, 2025
1bd97eb
feat(api): add route and test files
scheidtdav Jun 18, 2025
46b0422
feat: add test code
scheidtdav Jun 18, 2025
787b662
feat: add dummy sensors to devices and implement getting them back
scheidtdav Jun 25, 2025
95acb53
Merge branch 'dev' into feat/api-boxes-sensors
scheidtdav Jul 2, 2025
eb84619
feat: prefer dev server in no production envs and hide dev in prod
scheidtdav Jul 2, 2025
84f57ca
feat(docs): start adding docs to route
scheidtdav Jul 2, 2025
a232d6b
Merge branch 'dev' into feat/api-boxes-sensors
scheidtdav Jul 23, 2025
cf36b0a
feat: finish up to the point where we need measurements
scheidtdav Jul 23, 2025
0a35d3b
fix: api routes without need for measurements
scheidtdav Jul 30, 2025
89e8c04
fix: stats call
scheidtdav Jul 30, 2025
9a8e679
fix: remaining tests
scheidtdav Jul 30, 2025
32bdb9e
fix: frontend issue from changing the service implementation
scheidtdav Jul 30, 2025
48968e1
feat: add react mail
scheidtdav Sep 17, 2025
ad6862f
feat: add nodemailer and setup for sending mails
scheidtdav Sep 17, 2025
8ee41ee
feat: send password reset mails
scheidtdav Sep 17, 2025
f64d2f4
refactor: fix lint issues with mails
scheidtdav Sep 17, 2025
60f5683
feat: implement email confirmation mail
scheidtdav Sep 17, 2025
989bf66
feat: add mail for new users
scheidtdav Sep 17, 2025
02879e7
Merge branch 'dev' into feat/mailer
scheidtdav Oct 8, 2025
b7aa7d3
fix: rewrite package lock
scheidtdav Oct 8, 2025
8173dd2
Merge branch 'dev' into feat/mailer
scheidtdav Nov 5, 2025
523108f
fix: rewrite package lock
scheidtdav Nov 5, 2025
39b2b12
fix: packages
scheidtdav Nov 5, 2025
f25561b
feat: add email to new devices being created
scheidtdav Nov 5, 2025
0916ebb
feat: send mail when email requested to change
scheidtdav Nov 5, 2025
9da7fb6
feat: set email language properly
scheidtdav Nov 5, 2025
bebbfba
feat: implement base mail for all devices and migrate device specifics
scheidtdav Nov 7, 2025
21bc58f
Merge branch 'dev' into feat/mailer
scheidtdav Nov 7, 2025
0df03ab
fix: move ts-ignore
scheidtdav Nov 7, 2025
9b4970a
fix: use example vars for test
scheidtdav Nov 7, 2025
26f8656
feat: use ethereal.email for testing
scheidtdav Nov 12, 2025
1998425
fix: remove conflicting test
scheidtdav Nov 12, 2025
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
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ MYBADGES_SERVERADMIN_USERNAME = ""
MYBADGES_SERVERADMIN_PASSWORD = ""
MYBADGES_ISSUERID_OSEM = ""
MYBADGES_CLIENT_ID = ""
MYBADGES_CLIENT_SECRET = ""
MYBADGES_CLIENT_SECRET = ""

SMTP_HOST = "localhost"
SMTP_PORT = "1025"
SMTP_SECURE = "false"
SMTP_USERNAME = "ignored"
SMTP_PASSWORD = "ignored"
16 changes: 8 additions & 8 deletions app/components/device-detail/device-detail-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ export default function DeviceDetailBox() {

const [sensors, setSensors] = useState<SensorWithLatestMeasurement[]>();
useEffect(() => {
const sortedSensors = [...data.sensors as any].sort(
(a, b) => (a.id as unknown as number) - (b.id as unknown as number),
const sortedSensors = [...(data.sensors as any)].sort(
(a, b) => (a.id as unknown as number) - (b.id as unknown as number)
);
setSensors(sortedSensors);
}, [data]);
Expand Down Expand Up @@ -373,13 +373,13 @@ export default function DeviceDetailBox() {
?.split(",")
.includes(tag)
? "bg-green-100 dark:bg-dark-green"
: "",
: ""
)}
onClick={(event) => {
event.stopPropagation();

const currentParams = new URLSearchParams(
searchParams.toString(),
searchParams.toString()
);

// Safely retrieve and parse the current tags
Expand All @@ -395,7 +395,7 @@ export default function DeviceDetailBox() {
if (updatedTags.length > 0) {
currentParams.set(
"tags",
updatedTags.join(","),
updatedTags.join(",")
);
} else {
currentParams.delete("tags");
Expand Down Expand Up @@ -494,7 +494,7 @@ export default function DeviceDetailBox() {
id={sensor.id}
value={sensor.id}
defaultChecked={sensorIds.has(
sensor.id,
sensor.id
)}
/>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
Expand Down Expand Up @@ -560,7 +560,7 @@ export default function DeviceDetailBox() {
id={sensor.id}
value={sensor.id}
defaultChecked={sensorIds.has(
sensor.id,
sensor.id
)}
/>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
Expand Down Expand Up @@ -603,7 +603,7 @@ export default function DeviceDetailBox() {
</Card>
</Link>
);
},
}
)}
</div>
</div>
Expand Down
130 changes: 130 additions & 0 deletions app/lib/mail.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { render } from '@react-email/components'
import * as dotenv from 'dotenv'
import nodemailer from 'nodemailer'
import type SMTPTransport from 'nodemailer/lib/smtp-transport'
dotenv.config()

// Interface to load env variables
// Note these variables can possibly be undefined
// as someone could skip these varibales or not setup a .env file at all
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is something missing here?


/**
* Interface for our configuration.
* Note these variables can possibly be undefined,
* as varibales may be skipped or there is no .env file at all
*/
interface Config {
SMTP_HOST: string | undefined
SMTP_PORT: number | undefined
SMTP_SECURE: boolean | undefined
SMTP_USERNAME: string | undefined
SMTP_PASSWORD: string | undefined
}

/**
* Processes environment variables and
* builds a configuration object from it.
* @returns {Config} A mailer configuration
*/
const getConfig = (): Config => {
const config = {
SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: process.env.SMTP_PORT
? Number(process.env.SMTP_PORT)
: undefined,
SMTP_SECURE: process.env.SMTP_SECURE
? Boolean(JSON.parse(process.env.SMTP_SECURE))
: undefined,
SMTP_USERNAME: process.env.SMTP_USERNAME,
SMTP_PASSWORD: process.env.SMTP_PASSWORD,
}

// check the config for missing entries and throw an error if necessary
for (const [key, value] of Object.entries(config)) {
if (value === undefined) {
throw new Error(`Missing key ${key} in config.env`)
}
}

return config
}

const config = getConfig()

class OSEMTransporter {
private static _instance: nodemailer.Transporter | null = null
private constructor() {}
public static async getInstance(): Promise<nodemailer.Transporter> {
if (this._instance !== null) return this._instance

if (process.env.TEST) {
return await new Promise((resolve, reject) => {
nodemailer.createTestAccount((err, account) => {
if (err) reject(err)
else {
this._instance = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: account.user,
pass: account.pass,
},
})
resolve(this._instance)
}
})
})
} else {
const transportOptions: SMTPTransport.Options = {
host: config.SMTP_HOST,
port: config.SMTP_PORT,
secure: config.SMTP_SECURE,
}
if (
config.SMTP_USERNAME !== 'ignored' &&
config.SMTP_PASSWORD !== 'ignored'
) {
transportOptions['auth'] = {
user: config.SMTP_USERNAME,
pass: config.SMTP_PASSWORD,
}
}
this._instance = nodemailer.createTransport(transportOptions)
return this._instance
}
}
}
void OSEMTransporter.getInstance() // eagerly initialize the transporter

export interface MailAttachment {
filename: string
content: string
}

export const sendMail = async (mailConfig: {
recipientAddress: string
recipientName?: string
subject?: string
body: React.ReactElement
attachments?: MailAttachment[]
}) => {
try {
const mailHtml = await render(mailConfig.body)

await (
await OSEMTransporter.getInstance()
).sendMail({
from: '"openSenseMap 🌍" <[email protected]>',
to: mailConfig.recipientName
? `"${mailConfig.recipientName}" <${mailConfig.recipientAddress}>`
: mailConfig.recipientAddress,
subject: mailConfig.subject ?? 'openSenseMap',
html: mailHtml,
attachments: mailConfig.attachments,
})
} catch (err) {
console.error(err)
throw err
}
}
Loading
Loading