@profullstack/agenticjobs 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/cli/index.d.ts +9 -1
- package/dist/cli/index.js +99 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.ts +16 -0
- package/dist/client/client.js +8 -0
- package/dist/client/client.js.map +1 -1
- package/dist/config.d.ts +12 -2
- package/dist/config.js +44 -4
- package/dist/config.js.map +1 -1
- package/dist/core/applications.js +4 -1
- package/dist/core/applications.js.map +1 -1
- package/dist/core/auth.d.ts +2 -0
- package/dist/core/auth.js +2 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/candidates.d.ts +42 -0
- package/dist/core/candidates.js +143 -0
- package/dist/core/candidates.js.map +1 -0
- package/dist/core/import-job.d.ts +36 -0
- package/dist/core/import-job.js +221 -0
- package/dist/core/import-job.js.map +1 -0
- package/dist/core/jobs.d.ts +29 -0
- package/dist/core/jobs.js +101 -31
- package/dist/core/jobs.js.map +1 -1
- package/dist/core/mail.d.ts +64 -0
- package/dist/core/mail.js +125 -0
- package/dist/core/mail.js.map +1 -0
- package/dist/core/resumes.d.ts +23 -1
- package/dist/core/resumes.js +86 -9
- package/dist/core/resumes.js.map +1 -1
- package/dist/directory/fetch.d.ts +11 -1
- package/dist/directory/fetch.js +23 -8
- package/dist/directory/fetch.js.map +1 -1
- package/dist/directory/registry.d.ts +1 -0
- package/dist/directory/registry.js +17 -1
- package/dist/directory/registry.js.map +1 -1
- package/dist/markup/resume.js +7 -1
- package/dist/markup/resume.js.map +1 -1
- package/dist/schema/job.d.ts +13 -10
- package/dist/schema/job.js.map +1 -1
- package/dist/schema/query.js +8 -4
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/text.d.ts +12 -1
- package/dist/schema/text.js +14 -3
- package/dist/schema/text.js.map +1 -1
- package/dist/server/app.d.ts +6 -1
- package/dist/server/app.js +7 -2
- package/dist/server/app.js.map +1 -1
- package/dist/server/deps.d.ts +3 -0
- package/dist/server/routes/api.js +201 -24
- package/dist/server/routes/api.js.map +1 -1
- package/dist/server/routes/discovery.js +162 -1
- package/dist/server/routes/discovery.js.map +1 -1
- package/dist/server/routes/pages.js +44 -5
- package/dist/server/routes/pages.js.map +1 -1
- package/dist/server/serve.js +22 -7
- package/dist/server/serve.js.map +1 -1
- package/dist/views/auth.d.ts +1 -1
- package/dist/views/auth.js +3 -1
- package/dist/views/auth.js.map +1 -1
- package/dist/views/candidates.d.ts +47 -0
- package/dist/views/candidates.js +21 -0
- package/dist/views/candidates.js.map +1 -0
- package/dist/views/jobs.d.ts +13 -0
- package/dist/views/jobs.js +35 -10
- package/dist/views/jobs.js.map +1 -1
- package/dist/views/layout.js +2 -2
- package/dist/views/layout.js.map +1 -1
- package/dist/views/me.js +3 -1
- package/dist/views/me.js.map +1 -1
- package/dist/views/post.js +1 -1
- package/dist/views/post.js.map +1 -1
- package/docs/openjob.md +16 -9
- package/migrations/0006_board_only_applications.sql +34 -0
- package/migrations/0007_public_resumes.sql +24 -0
- package/migrations/0008_backfill_public_slugs.sql +57 -0
- package/migrations/0009_shorten_runaway_slugs.sql +21 -0
- package/package.json +16 -17
- package/web/public/app.css +45 -2
- package/web/public/sw.js +29 -5
package/dist/core/jobs.js
CHANGED
|
@@ -11,7 +11,8 @@ const SELECT = `
|
|
|
11
11
|
select j.id, j.slug, j.title, j.description, j.employment_type, j.workplace, j.seniority,
|
|
12
12
|
j.location, j.remote_regions, j.salary_min, j.salary_max, j.salary_currency,
|
|
13
13
|
j.salary_period, j.salary_equity, j.tags, j.stack, j.requirements, j.responsibilities,
|
|
14
|
-
j.agent_policy, j.apply_via, j.apply_url, j.apply_email, j.apply_schema,
|
|
14
|
+
j.agent_policy, j.apply_via, j.apply_url, j.apply_email, j.apply_schema,
|
|
15
|
+
j.apply_source_url, j.status,
|
|
15
16
|
j.published_at, j.expires_at, j.created_at, j.updated_at,
|
|
16
17
|
o.id as org_id, o.slug as org_slug, o.name as org_name, o.website as org_website,
|
|
17
18
|
o.logo_url as org_logo_url, o.description as org_description,
|
|
@@ -30,13 +31,16 @@ function toOrganisation(row) {
|
|
|
30
31
|
createdAt: row.org_created_at,
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Every job is applied to here.
|
|
36
|
+
*
|
|
37
|
+
* Rows written before offsite applications were removed still carry
|
|
38
|
+
* `apply_via = 'url'` or `'email'`, and they read back as board applications:
|
|
39
|
+
* the migration rewrites the column, and this is what makes a row the
|
|
40
|
+
* migration has not reached yet safe to serve. The board's own form is the one
|
|
41
|
+
* target that cannot go missing.
|
|
42
|
+
*/
|
|
33
43
|
function toApplyMethod(row) {
|
|
34
|
-
if (row.apply_via === 'url' && row.apply_url)
|
|
35
|
-
return { via: 'url', url: row.apply_url };
|
|
36
|
-
if (row.apply_via === 'email' && row.apply_email)
|
|
37
|
-
return { via: 'email', email: row.apply_email };
|
|
38
|
-
// A job whose declared method lost its target still has to be applicable,
|
|
39
|
-
// and the board's own form is the one target that cannot go missing.
|
|
40
44
|
return { via: 'board', schema: row.apply_schema ?? DEFAULT_APPLY_SCHEMA };
|
|
41
45
|
}
|
|
42
46
|
export function toJob(row) {
|
|
@@ -170,7 +174,8 @@ export function normaliseInput(input, orgId) {
|
|
|
170
174
|
const title = clean(input['title'], 140);
|
|
171
175
|
if (title.length < 3)
|
|
172
176
|
return 'A title of at least 3 characters is required.';
|
|
173
|
-
|
|
177
|
+
// Markdown, so the line breaks are the structure and must survive.
|
|
178
|
+
const description = clean(input['description'], 20_000, { multiline: true });
|
|
174
179
|
if (description.length < 20)
|
|
175
180
|
return 'A description of at least 20 characters is required.';
|
|
176
181
|
const employmentType = isEmploymentType(input['employmentType'])
|
|
@@ -209,28 +214,20 @@ export function normaliseInput(input, orgId) {
|
|
|
209
214
|
agentPolicy,
|
|
210
215
|
apply,
|
|
211
216
|
expiresAt: date(input['expiresAt']),
|
|
217
|
+
// Set by the importer, never by a poster: it records where a listing was
|
|
218
|
+
// read from, and a hand-typed one was read from nowhere.
|
|
219
|
+
sourceUrl: null,
|
|
212
220
|
};
|
|
213
221
|
}
|
|
214
222
|
function normaliseApply(input) {
|
|
223
|
+
// Applications happen here. A listing that points somewhere else is a link
|
|
224
|
+
// to a job rather than a job, and an agent cannot complete a form it cannot
|
|
225
|
+
// reach. Refused with the reason rather than silently rewritten to `board`,
|
|
226
|
+
// because an employer who asked for an offsite link and got a board form
|
|
227
|
+
// without being told would find out from the applications.
|
|
215
228
|
const via = clean(input['applyVia'], 10) || 'board';
|
|
216
|
-
if (via
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const parsed = new URL(url);
|
|
220
|
-
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
221
|
-
return 'An application link must be http or https.';
|
|
222
|
-
}
|
|
223
|
-
return { via: 'url', url: parsed.toString() };
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
return 'That application link is not a URL.';
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
if (via === 'email') {
|
|
230
|
-
const email = clean(input['applyEmail'], 200).toLowerCase();
|
|
231
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
|
|
232
|
-
return 'That application address is not valid.';
|
|
233
|
-
return { via: 'email', email };
|
|
229
|
+
if (via !== 'board') {
|
|
230
|
+
return 'Applications are taken on this board. Import a job from a URL instead of linking out to one.';
|
|
234
231
|
}
|
|
235
232
|
const schema = input['applySchema'];
|
|
236
233
|
if (schema !== undefined && schema !== null) {
|
|
@@ -320,9 +317,9 @@ export async function createJob(pool, input) {
|
|
|
320
317
|
slug, org_id, title, description, employment_type, workplace, seniority, location,
|
|
321
318
|
remote_regions, salary_min, salary_max, salary_currency, salary_period, salary_equity,
|
|
322
319
|
tags, stack, requirements, responsibilities, agent_policy, apply_via, apply_url,
|
|
323
|
-
apply_email, apply_schema, expires_at, status
|
|
320
|
+
apply_email, apply_schema, apply_source_url, expires_at, status
|
|
324
321
|
) values (
|
|
325
|
-
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,'draft'
|
|
322
|
+
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,'draft'
|
|
326
323
|
) returning id`, [
|
|
327
324
|
slug,
|
|
328
325
|
input.orgId,
|
|
@@ -344,9 +341,10 @@ export async function createJob(pool, input) {
|
|
|
344
341
|
input.responsibilities,
|
|
345
342
|
input.agentPolicy,
|
|
346
343
|
input.apply.via,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
344
|
+
null,
|
|
345
|
+
null,
|
|
346
|
+
JSON.stringify(input.apply.schema),
|
|
347
|
+
input.sourceUrl,
|
|
350
348
|
input.expiresAt,
|
|
351
349
|
]);
|
|
352
350
|
const id = result.rows[0]?.id;
|
|
@@ -367,6 +365,78 @@ export async function setStatus(pool, id, status) {
|
|
|
367
365
|
where id = $1`, [id, status]);
|
|
368
366
|
return getJobById(pool, id);
|
|
369
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Refresh a listing from a re-import.
|
|
370
|
+
*
|
|
371
|
+
* Only the fields an import can actually supply are touched, and only when the
|
|
372
|
+
* new value is present: a re-import that could not read a location must not
|
|
373
|
+
* erase one a person typed in. Status is untouched, so refreshing a published
|
|
374
|
+
* listing does not unpublish it and refreshing a draft does not publish it.
|
|
375
|
+
*/
|
|
376
|
+
export async function updateJobFromImport(pool, id, fields) {
|
|
377
|
+
await pool.query(`update jobs
|
|
378
|
+
set title = coalesce($2, title),
|
|
379
|
+
description = coalesce($3, description),
|
|
380
|
+
employment_type = coalesce($4, employment_type),
|
|
381
|
+
workplace = coalesce($5, workplace),
|
|
382
|
+
location = coalesce($6, location),
|
|
383
|
+
apply_source_url = coalesce($7, apply_source_url),
|
|
384
|
+
updated_at = now()
|
|
385
|
+
where id = $1`, [
|
|
386
|
+
id,
|
|
387
|
+
fields.title ?? null,
|
|
388
|
+
fields.description ?? null,
|
|
389
|
+
fields.employmentType ?? null,
|
|
390
|
+
fields.workplace ?? null,
|
|
391
|
+
fields.location ?? null,
|
|
392
|
+
fields.sourceUrl ?? null,
|
|
393
|
+
]);
|
|
394
|
+
return getJobById(pool, id);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Rewrite a listing's content.
|
|
398
|
+
*
|
|
399
|
+
* Takes the same shape `createJob` takes, so an edit cannot put a listing into
|
|
400
|
+
* a state a post could not have created. Slug, status and org are untouched:
|
|
401
|
+
* the slug is the listing's public URL and editing must not break links to it.
|
|
402
|
+
*/
|
|
403
|
+
export async function editJob(pool, id, input) {
|
|
404
|
+
await pool.query(`update jobs
|
|
405
|
+
set title = $2, description = $3, employment_type = $4, workplace = $5,
|
|
406
|
+
seniority = $6, location = $7, remote_regions = $8,
|
|
407
|
+
salary_min = $9, salary_max = $10, salary_currency = $11,
|
|
408
|
+
salary_period = $12, salary_equity = $13,
|
|
409
|
+
tags = $14, stack = $15, requirements = $16, responsibilities = $17,
|
|
410
|
+
agent_policy = $18, expires_at = $19, updated_at = now()
|
|
411
|
+
where id = $1`, [
|
|
412
|
+
id,
|
|
413
|
+
input.title,
|
|
414
|
+
input.description,
|
|
415
|
+
input.employmentType,
|
|
416
|
+
input.workplace,
|
|
417
|
+
input.seniority,
|
|
418
|
+
input.location,
|
|
419
|
+
input.remoteRegions,
|
|
420
|
+
input.salaryMin,
|
|
421
|
+
input.salaryMax,
|
|
422
|
+
input.salaryCurrency,
|
|
423
|
+
input.salaryPeriod,
|
|
424
|
+
input.salaryEquity,
|
|
425
|
+
input.tags,
|
|
426
|
+
input.stack,
|
|
427
|
+
input.requirements,
|
|
428
|
+
input.responsibilities,
|
|
429
|
+
input.agentPolicy,
|
|
430
|
+
input.expiresAt,
|
|
431
|
+
]);
|
|
432
|
+
return getJobById(pool, id);
|
|
433
|
+
}
|
|
434
|
+
/** The listing imported from this URL, if there is one. */
|
|
435
|
+
export async function getJobBySourceUrl(pool, url) {
|
|
436
|
+
const result = await pool.query(`select id from jobs where apply_source_url = $1 order by created_at desc limit 1`, [url]);
|
|
437
|
+
const id = result.rows[0]?.id;
|
|
438
|
+
return id === undefined ? null : getJobById(pool, id);
|
|
439
|
+
}
|
|
370
440
|
export async function countJobs(pool) {
|
|
371
441
|
const result = await pool.query(`select
|
|
372
442
|
count(*) filter (
|
package/dist/core/jobs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/core/jobs.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/core/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sending the one email this board sends: a sign-in link.
|
|
3
|
+
*
|
|
4
|
+
* Delivery is optional and always has been - a laptop run has no mail
|
|
5
|
+
* provider, and the link is printed instead. What was wrong before is that
|
|
6
|
+
* *every* instance took that path, because the config had an SMTP_URL nobody
|
|
7
|
+
* ever read. `delivered` is a fact about a send that happened now, not a
|
|
8
|
+
* restatement of which environment variables are set.
|
|
9
|
+
*
|
|
10
|
+
* @profullstack/emailer wraps Resend's HTTP API with no runtime dependencies:
|
|
11
|
+
* a fetch is the whole client, so there is no connection to hold and no port
|
|
12
|
+
* to be blocked.
|
|
13
|
+
*/
|
|
14
|
+
import type { Config } from '../config.ts';
|
|
15
|
+
/** How long a link lasts, in minutes, taken from the TTL that enforces it. */
|
|
16
|
+
export declare const MAGIC_LINK_MINUTES: number;
|
|
17
|
+
export interface Message {
|
|
18
|
+
to: string;
|
|
19
|
+
subject: string;
|
|
20
|
+
html: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
export interface Mailer {
|
|
24
|
+
send: (message: Message) => Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A mailer, or null when this instance is not configured to send.
|
|
28
|
+
*
|
|
29
|
+
* Null is a supported state, not a failure: it is what makes `pnpm dev` work
|
|
30
|
+
* without an account anywhere.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createMailer(config: Config, log?: (message: string) => void): Mailer | null;
|
|
33
|
+
/**
|
|
34
|
+
* The sign-in email.
|
|
35
|
+
*
|
|
36
|
+
* When the link came from a terminal it carries that terminal's code, and the
|
|
37
|
+
* email says so and shows it. That is not decoration: a device flow where the
|
|
38
|
+
* mail cannot be matched to the request in front of you is one where a link
|
|
39
|
+
* phished out of an inbox approves an attacker's terminal instead.
|
|
40
|
+
*/
|
|
41
|
+
export declare function magicLinkMessage(options: {
|
|
42
|
+
to: string;
|
|
43
|
+
url: string;
|
|
44
|
+
boardName: string;
|
|
45
|
+
redirect: string | null;
|
|
46
|
+
minutes: number;
|
|
47
|
+
}): Message;
|
|
48
|
+
/**
|
|
49
|
+
* Send the sign-in link, or fall back to the server log.
|
|
50
|
+
*
|
|
51
|
+
* The log is the fallback for both "no provider configured" and "the provider
|
|
52
|
+
* refused": the link is minted either way and is the only way into the
|
|
53
|
+
* account, so an operator being able to read it out of their own logs beats
|
|
54
|
+
* losing it. It goes to the *log*, never to the browser - a page that shows
|
|
55
|
+
* the link to whoever typed the address is not a sign-in, it is a way to take
|
|
56
|
+
* over any address you can spell.
|
|
57
|
+
*/
|
|
58
|
+
export declare function deliverMagicLink(options: {
|
|
59
|
+
mailer: Mailer | null;
|
|
60
|
+
boardName: string;
|
|
61
|
+
email: string;
|
|
62
|
+
url: string;
|
|
63
|
+
redirect: string | null;
|
|
64
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sending the one email this board sends: a sign-in link.
|
|
3
|
+
*
|
|
4
|
+
* Delivery is optional and always has been - a laptop run has no mail
|
|
5
|
+
* provider, and the link is printed instead. What was wrong before is that
|
|
6
|
+
* *every* instance took that path, because the config had an SMTP_URL nobody
|
|
7
|
+
* ever read. `delivered` is a fact about a send that happened now, not a
|
|
8
|
+
* restatement of which environment variables are set.
|
|
9
|
+
*
|
|
10
|
+
* @profullstack/emailer wraps Resend's HTTP API with no runtime dependencies:
|
|
11
|
+
* a fetch is the whole client, so there is no connection to hold and no port
|
|
12
|
+
* to be blocked.
|
|
13
|
+
*/
|
|
14
|
+
import { createEmailer } from '@profullstack/emailer';
|
|
15
|
+
import { escapeHtml } from "../markup/escape.js";
|
|
16
|
+
import { MAGIC_TTL_MS } from "./auth.js";
|
|
17
|
+
/** How long a link lasts, in minutes, taken from the TTL that enforces it. */
|
|
18
|
+
export const MAGIC_LINK_MINUTES = Math.round(MAGIC_TTL_MS / 60_000);
|
|
19
|
+
/**
|
|
20
|
+
* A mailer, or null when this instance is not configured to send.
|
|
21
|
+
*
|
|
22
|
+
* Null is a supported state, not a failure: it is what makes `pnpm dev` work
|
|
23
|
+
* without an account anywhere.
|
|
24
|
+
*/
|
|
25
|
+
export function createMailer(config, log = (message) => console.error(message)) {
|
|
26
|
+
if (config.resendApiKey === null)
|
|
27
|
+
return null;
|
|
28
|
+
const emailer = createEmailer({ resendApiKey: config.resendApiKey, defaultFrom: config.mailFrom });
|
|
29
|
+
return {
|
|
30
|
+
send: async (message) => {
|
|
31
|
+
try {
|
|
32
|
+
const result = await emailer.send({
|
|
33
|
+
from: config.mailFrom,
|
|
34
|
+
to: message.to,
|
|
35
|
+
subject: message.subject,
|
|
36
|
+
html: message.html,
|
|
37
|
+
text: message.text,
|
|
38
|
+
});
|
|
39
|
+
// An unverified sending domain is a 403 whatever the key is, and a
|
|
40
|
+
// dead key is a 401. Both arrive here as `sent: false` with the
|
|
41
|
+
// provider's own sentence, which is the thing worth logging.
|
|
42
|
+
if (!result.sent)
|
|
43
|
+
log(`mail: ${message.to} not sent: ${result.error ?? 'unknown error'}`);
|
|
44
|
+
return result.sent;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// A provider outage must not turn a sign-in attempt into a 500. The
|
|
48
|
+
// link is already minted and still works; the person just has to ask
|
|
49
|
+
// for another one.
|
|
50
|
+
log(`mail: ${message.to} failed: ${error.message}`);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The `code` a /device redirect carries, so the email can show it. */
|
|
57
|
+
function deviceCodeOf(redirect) {
|
|
58
|
+
if (redirect === null || !redirect.startsWith('/device'))
|
|
59
|
+
return null;
|
|
60
|
+
const query = redirect.indexOf('?');
|
|
61
|
+
if (query === -1)
|
|
62
|
+
return null;
|
|
63
|
+
const code = new URLSearchParams(redirect.slice(query + 1)).get('code');
|
|
64
|
+
return code === null || code.trim() === '' ? null : code.trim();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The sign-in email.
|
|
68
|
+
*
|
|
69
|
+
* When the link came from a terminal it carries that terminal's code, and the
|
|
70
|
+
* email says so and shows it. That is not decoration: a device flow where the
|
|
71
|
+
* mail cannot be matched to the request in front of you is one where a link
|
|
72
|
+
* phished out of an inbox approves an attacker's terminal instead.
|
|
73
|
+
*/
|
|
74
|
+
export function magicLinkMessage(options) {
|
|
75
|
+
const code = deviceCodeOf(options.redirect);
|
|
76
|
+
const board = options.boardName;
|
|
77
|
+
const subject = code === null ? `Sign in to ${board}` : `Approve your terminal on ${board}`;
|
|
78
|
+
const lead = code === null
|
|
79
|
+
? `Use the link below to sign in to ${board}. There is no password.`
|
|
80
|
+
: `A terminal asked to sign in to ${board} with the code ${code}. Open the link below to approve it.`;
|
|
81
|
+
const caution = code === null
|
|
82
|
+
? `If you did not ask to sign in, ignore this email. Nobody can use the link but you.`
|
|
83
|
+
: `If that code is not the one your terminal is showing, do not open the link.`;
|
|
84
|
+
const text = [
|
|
85
|
+
lead,
|
|
86
|
+
'',
|
|
87
|
+
options.url,
|
|
88
|
+
'',
|
|
89
|
+
`The link lasts ${options.minutes} minutes and works once.`,
|
|
90
|
+
caution,
|
|
91
|
+
].join('\n');
|
|
92
|
+
const url = escapeHtml(options.url);
|
|
93
|
+
const html = [
|
|
94
|
+
`<p>${escapeHtml(lead)}</p>`,
|
|
95
|
+
`<p><a href="${url}">${url}</a></p>`,
|
|
96
|
+
`<p style="color:#666;font-size:12px">The link lasts ${options.minutes} minutes and works once. ${escapeHtml(caution)}</p>`,
|
|
97
|
+
].join('');
|
|
98
|
+
return { to: options.to, subject, html, text };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Send the sign-in link, or fall back to the server log.
|
|
102
|
+
*
|
|
103
|
+
* The log is the fallback for both "no provider configured" and "the provider
|
|
104
|
+
* refused": the link is minted either way and is the only way into the
|
|
105
|
+
* account, so an operator being able to read it out of their own logs beats
|
|
106
|
+
* losing it. It goes to the *log*, never to the browser - a page that shows
|
|
107
|
+
* the link to whoever typed the address is not a sign-in, it is a way to take
|
|
108
|
+
* over any address you can spell.
|
|
109
|
+
*/
|
|
110
|
+
export async function deliverMagicLink(options) {
|
|
111
|
+
if (options.mailer !== null) {
|
|
112
|
+
const sent = await options.mailer.send(magicLinkMessage({
|
|
113
|
+
to: options.email,
|
|
114
|
+
url: options.url,
|
|
115
|
+
boardName: options.boardName,
|
|
116
|
+
redirect: options.redirect,
|
|
117
|
+
minutes: MAGIC_LINK_MINUTES,
|
|
118
|
+
}));
|
|
119
|
+
if (sent)
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
console.log(`magic link for ${options.email}: ${options.url}`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=mail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../../src/core/mail.ts"],"names":[],"mappings":"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"}
|
package/dist/core/resumes.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface Resume {
|
|
|
19
19
|
markdown: string;
|
|
20
20
|
parsed: OpenResume | null;
|
|
21
21
|
visibility: Visibility;
|
|
22
|
+
/** The board-wide address, minted when a resume is first shared. */
|
|
23
|
+
publicSlug: string | null;
|
|
22
24
|
sourceName: string | null;
|
|
23
25
|
createdAt: string;
|
|
24
26
|
updatedAt: string;
|
|
@@ -26,7 +28,27 @@ export interface Resume {
|
|
|
26
28
|
export declare function listResumes(pool: pg.Pool, userId: string): Promise<Resume[]>;
|
|
27
29
|
export declare function getResume(pool: pg.Pool, userId: string, slug: string): Promise<Resume | null>;
|
|
28
30
|
export declare function getResumeById(pool: pg.Pool, id: string): Promise<Resume | null>;
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Give a shared resume a board-wide address, once.
|
|
33
|
+
*
|
|
34
|
+
* Named after the person, because the point of a candidate page is that it
|
|
35
|
+
* carries their name. Falls back to the resume's own title, and then to a
|
|
36
|
+
* random suffix, so a resume with no name in it is still addressable.
|
|
37
|
+
*
|
|
38
|
+
* The slug is minted on first share and then kept even if the resume is made
|
|
39
|
+
* private again and shared later: a URL that someone has already sent to an
|
|
40
|
+
* employer must not come back pointing at a different person.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ensurePublicSlug(pool: pg.Pool, resume: Resume): Promise<string | null>;
|
|
43
|
+
/** The directory: resumes their owner chose to list. */
|
|
44
|
+
export declare function listPublicResumes(pool: pg.Pool, limit?: number): Promise<Resume[]>;
|
|
45
|
+
/**
|
|
46
|
+
* One candidate page.
|
|
47
|
+
*
|
|
48
|
+
* Serves `link` as well as `public`, which is the difference between the two:
|
|
49
|
+
* a link resume is reachable by anyone holding the URL and is not listed.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getPublicResume(pool: pg.Pool, publicSlug: string): Promise<Resume | null>;
|
|
30
52
|
export declare function getSharedResume(pool: pg.Pool, id: string): Promise<Resume | null>;
|
|
31
53
|
export interface SaveResume {
|
|
32
54
|
title?: string;
|
package/dist/core/resumes.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { parseResume, resumeTemplate } from "../markup/resume.js";
|
|
10
10
|
import { clean, slugify, suffix } from "../schema/text.js";
|
|
11
|
+
import { nameOf } from "./candidates.js";
|
|
11
12
|
export const VISIBILITIES = ['private', 'link', 'public'];
|
|
12
13
|
export function isVisibility(value) {
|
|
13
14
|
return typeof value === 'string' && VISIBILITIES.includes(value);
|
|
@@ -21,6 +22,7 @@ function toResume(row) {
|
|
|
21
22
|
markdown: row.markdown,
|
|
22
23
|
parsed: row.parsed,
|
|
23
24
|
visibility: isVisibility(row.visibility) ? row.visibility : 'private',
|
|
25
|
+
publicSlug: row.public_slug,
|
|
24
26
|
sourceName: row.source_name,
|
|
25
27
|
createdAt: row.created_at,
|
|
26
28
|
updatedAt: row.updated_at,
|
|
@@ -29,8 +31,8 @@ function toResume(row) {
|
|
|
29
31
|
// The source bytes are never in the default projection: they are megabytes of
|
|
30
32
|
// PDF that no page needs, and selecting them by habit is how a list endpoint
|
|
31
33
|
// starts moving 40MB.
|
|
32
|
-
const SELECT = `select id, user_id, slug, title, markdown, parsed, visibility,
|
|
33
|
-
created_at, updated_at from resumes`;
|
|
34
|
+
const SELECT = `select id, user_id, slug, title, markdown, parsed, visibility, public_slug,
|
|
35
|
+
source_name, created_at, updated_at from resumes`;
|
|
34
36
|
export async function listResumes(pool, userId) {
|
|
35
37
|
const result = await pool.query(`${SELECT} where user_id = $1 order by updated_at desc`, [userId]);
|
|
36
38
|
return result.rows.map(toResume);
|
|
@@ -49,6 +51,79 @@ export async function getResumeById(pool, id) {
|
|
|
49
51
|
return row === undefined ? null : toResume(row);
|
|
50
52
|
}
|
|
51
53
|
/** For a shared link: only resumes the owner has opened up. */
|
|
54
|
+
/** Slugs are capped at this many characters, cut back to a whole word. */
|
|
55
|
+
const SLUG_MAX = 60;
|
|
56
|
+
function publicSlugBase(name) {
|
|
57
|
+
const full = slugify(name);
|
|
58
|
+
if (full === '')
|
|
59
|
+
return 'candidate';
|
|
60
|
+
if (full.length <= SLUG_MAX)
|
|
61
|
+
return full;
|
|
62
|
+
const cut = full.slice(0, SLUG_MAX);
|
|
63
|
+
const lastDash = cut.lastIndexOf('-');
|
|
64
|
+
const trimmed = lastDash > 20 ? cut.slice(0, lastDash) : cut;
|
|
65
|
+
return trimmed.replace(/-+$/, '') || 'candidate';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Saving a shared resume is what gives it its address.
|
|
69
|
+
*
|
|
70
|
+
* This lives here rather than in a route because it is a rule about the model:
|
|
71
|
+
* a resume that says it is shared has somewhere to be. It was in the web
|
|
72
|
+
* handler only, so publishing through the API or the CLI minted nothing and
|
|
73
|
+
* the resume stayed invisible with no sign of why.
|
|
74
|
+
*/
|
|
75
|
+
async function withPublicSlug(pool, resume) {
|
|
76
|
+
const publicSlug = await ensurePublicSlug(pool, resume);
|
|
77
|
+
return publicSlug === resume.publicSlug ? resume : { ...resume, publicSlug };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Give a shared resume a board-wide address, once.
|
|
81
|
+
*
|
|
82
|
+
* Named after the person, because the point of a candidate page is that it
|
|
83
|
+
* carries their name. Falls back to the resume's own title, and then to a
|
|
84
|
+
* random suffix, so a resume with no name in it is still addressable.
|
|
85
|
+
*
|
|
86
|
+
* The slug is minted on first share and then kept even if the resume is made
|
|
87
|
+
* private again and shared later: a URL that someone has already sent to an
|
|
88
|
+
* employer must not come back pointing at a different person.
|
|
89
|
+
*/
|
|
90
|
+
export async function ensurePublicSlug(pool, resume) {
|
|
91
|
+
if (resume.visibility === 'private')
|
|
92
|
+
return resume.publicSlug;
|
|
93
|
+
if (resume.publicSlug !== null)
|
|
94
|
+
return resume.publicSlug;
|
|
95
|
+
// Bounded, and cut at a word boundary. A slug is a URL, and a URL built out
|
|
96
|
+
// of somebody's document has to survive that document being malformed: an
|
|
97
|
+
// unbounded one produced a three thousand character address the first time
|
|
98
|
+
// this ran against a resume whose line breaks had been flattened.
|
|
99
|
+
const base = publicSlugBase(nameOf(resume));
|
|
100
|
+
for (let attempt = 0; attempt < 25; attempt += 1) {
|
|
101
|
+
const candidate = attempt === 0 ? base : `${base}-${attempt + 1}`;
|
|
102
|
+
const claimed = await pool.query(`update resumes set public_slug = $2 where id = $1 and public_slug is null
|
|
103
|
+
and not exists (select 1 from resumes where public_slug = $2)
|
|
104
|
+
returning public_slug`, [resume.id, candidate]);
|
|
105
|
+
if (claimed.rows.length > 0)
|
|
106
|
+
return candidate;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/** The directory: resumes their owner chose to list. */
|
|
111
|
+
export async function listPublicResumes(pool, limit = 100) {
|
|
112
|
+
const result = await pool.query(`${SELECT} where visibility = 'public' and public_slug is not null
|
|
113
|
+
order by updated_at desc limit $1`, [Math.min(200, Math.max(1, limit))]);
|
|
114
|
+
return result.rows.map(toResume);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* One candidate page.
|
|
118
|
+
*
|
|
119
|
+
* Serves `link` as well as `public`, which is the difference between the two:
|
|
120
|
+
* a link resume is reachable by anyone holding the URL and is not listed.
|
|
121
|
+
*/
|
|
122
|
+
export async function getPublicResume(pool, publicSlug) {
|
|
123
|
+
const result = await pool.query(`${SELECT} where public_slug = $1 and visibility in ('link', 'public')`, [publicSlug]);
|
|
124
|
+
const row = result.rows[0];
|
|
125
|
+
return row === undefined ? null : toResume(row);
|
|
126
|
+
}
|
|
52
127
|
export async function getSharedResume(pool, id) {
|
|
53
128
|
const result = await pool.query(`${SELECT} where id = $1 and visibility in ('link', 'public')`, [id]);
|
|
54
129
|
const row = result.rows[0];
|
|
@@ -68,7 +143,8 @@ async function uniqueSlug(pool, userId, title) {
|
|
|
68
143
|
return `${base}-${Date.now().toString(36)}`;
|
|
69
144
|
}
|
|
70
145
|
export async function createResume(pool, userId, input) {
|
|
71
|
-
|
|
146
|
+
// OpenResume Markdown: the line breaks are the document.
|
|
147
|
+
const markdown = clean(input.markdown, 200_000, { multiline: true });
|
|
72
148
|
const parsed = parseResume(markdown);
|
|
73
149
|
// The document's own h1 is a better title than "Resume", and it is what the
|
|
74
150
|
// candidate would have typed anyway.
|
|
@@ -77,7 +153,7 @@ export async function createResume(pool, userId, input) {
|
|
|
77
153
|
const result = await pool.query(`insert into resumes (user_id, slug, title, markdown, parsed, visibility,
|
|
78
154
|
source_name, source_mime, source_bytes)
|
|
79
155
|
values ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9)
|
|
80
|
-
returning id, user_id, slug, title, markdown, parsed, visibility, source_name,
|
|
156
|
+
returning id, user_id, slug, title, markdown, parsed, visibility, public_slug, source_name,
|
|
81
157
|
created_at, updated_at`, [
|
|
82
158
|
userId,
|
|
83
159
|
slug,
|
|
@@ -92,10 +168,11 @@ export async function createResume(pool, userId, input) {
|
|
|
92
168
|
const row = result.rows[0];
|
|
93
169
|
if (row === undefined)
|
|
94
170
|
throw new Error('resume insert returned no row');
|
|
95
|
-
return toResume(row);
|
|
171
|
+
return withPublicSlug(pool, toResume(row));
|
|
96
172
|
}
|
|
97
173
|
export async function updateResume(pool, userId, slug, input) {
|
|
98
|
-
|
|
174
|
+
// OpenResume Markdown: the line breaks are the document.
|
|
175
|
+
const markdown = clean(input.markdown, 200_000, { multiline: true });
|
|
99
176
|
const parsed = parseResume(markdown);
|
|
100
177
|
const result = await pool.query(`update resumes
|
|
101
178
|
set markdown = $3,
|
|
@@ -103,10 +180,10 @@ export async function updateResume(pool, userId, slug, input) {
|
|
|
103
180
|
title = coalesce(nullif($5, ''), title),
|
|
104
181
|
visibility = coalesce($6, visibility)
|
|
105
182
|
where user_id = $1 and slug = $2
|
|
106
|
-
returning id, user_id, slug, title, markdown, parsed, visibility,
|
|
107
|
-
created_at, updated_at`, [userId, slug, markdown, JSON.stringify(parsed), clean(input.title, 120), input.visibility ?? null]);
|
|
183
|
+
returning id, user_id, slug, title, markdown, parsed, visibility, public_slug,
|
|
184
|
+
source_name, created_at, updated_at`, [userId, slug, markdown, JSON.stringify(parsed), clean(input.title, 120), input.visibility ?? null]);
|
|
108
185
|
const row = result.rows[0];
|
|
109
|
-
return row === undefined ? null : toResume(row);
|
|
186
|
+
return row === undefined ? null : withPublicSlug(pool, toResume(row));
|
|
110
187
|
}
|
|
111
188
|
export async function deleteResume(pool, userId, slug) {
|
|
112
189
|
const result = await pool.query(`delete from resumes where user_id = $1 and slug = $2`, [
|