@better-giving/fundraiser 3.0.6 → 3.0.8
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/dist/cloudsearch.d.mts +3 -2
- package/dist/db.d.mts +37 -19
- package/dist/db.mjs +68 -3
- package/dist/index.d.mts +3 -16
- package/dist/index.mjs +1 -0
- package/dist/interfaces.d.mts +28 -0
- package/dist/interfaces.mjs +1 -0
- package/dist/schema.d.mts +11 -31
- package/dist/schema.mjs +16 -17
- package/package.json +4 -2
- package/src/cloudsearch.mts +4 -3
- package/src/db.mts +73 -15
- package/src/index.mts +7 -22
- package/src/interfaces.mts +31 -0
- package/src/schema.mts +22 -54
package/dist/cloudsearch.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ToDoc, ToHitFields, ToUpdate } from "@better-giving/types/cloudsearch";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type { IFundInternal } from "./interfaces.mjs";
|
|
3
|
+
import type { IFundNew } from "./schema.mjs";
|
|
4
|
+
export interface CloudsearchFund extends Pick<IFundNew, "name" | "description" | "logo" | "banner" | "featured" | "members" | "target">, Pick<IFundInternal, "id" | "env" | "active" | "verified" | "donation_total_usd" | "creator_id" | "creator_name"> {
|
|
4
5
|
/** iso | "9999-12-31T23:59:59Z" year 9999 */
|
|
5
6
|
expiration: string;
|
|
6
7
|
}
|
package/dist/db.d.mts
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { Db, type TxType } from "@better-giving/db";
|
|
2
|
+
import type { IFund } from "./interfaces.mjs";
|
|
3
|
+
import type { IFundUpdate } from "./schema.mjs";
|
|
4
|
+
export declare class FundDb extends Db {
|
|
5
|
+
static readonly table = "funds";
|
|
6
|
+
static readonly slug_env_gsi = "slug-env-gsi";
|
|
7
|
+
key_fund(id: string): {
|
|
8
|
+
PK: string;
|
|
9
|
+
SK: string;
|
|
10
|
+
};
|
|
11
|
+
fund_record(data: IFund): {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
banner: string;
|
|
15
|
+
logo: string;
|
|
16
|
+
members: number[];
|
|
17
|
+
target: string;
|
|
18
|
+
videos: string[];
|
|
19
|
+
featured: boolean;
|
|
20
|
+
npo_owner: number;
|
|
21
|
+
expiration?: string | undefined;
|
|
22
|
+
slug?: string | undefined;
|
|
23
|
+
id: string;
|
|
24
|
+
env: import("@better-giving/schemas").Environment;
|
|
25
|
+
active: boolean;
|
|
26
|
+
verified: boolean;
|
|
27
|
+
donation_total_usd: number;
|
|
28
|
+
settings: import("./interfaces.mjs").IFundSettings;
|
|
29
|
+
creator_id: string;
|
|
30
|
+
creator_name: string;
|
|
31
|
+
PK: string;
|
|
32
|
+
SK: string;
|
|
33
|
+
};
|
|
34
|
+
fund_put_txi(data: IFund): TxType["Put"];
|
|
35
|
+
/**@param id - slug or uuid */
|
|
36
|
+
fund(id: string): Promise<IFund | undefined>;
|
|
37
|
+
fund_edit(id: string, { target, slug, ...update }: IFundUpdate): Promise<Record<string, any>>;
|
|
20
38
|
}
|
package/dist/db.mjs
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
};
|
|
1
|
+
import { QueryCommand, UpdateCommand, } from "@aws-sdk/lib-dynamodb";
|
|
2
|
+
import { Db, UpdateBuilder } from "@better-giving/db";
|
|
3
|
+
import { UUID_REGEX } from "valibot";
|
|
4
|
+
export class FundDb extends Db {
|
|
5
|
+
static table = "funds";
|
|
6
|
+
static slug_env_gsi = "slug-env-gsi";
|
|
7
|
+
key_fund(id) {
|
|
8
|
+
return {
|
|
9
|
+
PK: `Fund#${id}`,
|
|
10
|
+
SK: `Fund#${id}`,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
fund_record(data) {
|
|
14
|
+
return {
|
|
15
|
+
...this.key_fund(data.id),
|
|
16
|
+
...data,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
fund_put_txi(data) {
|
|
20
|
+
return {
|
|
21
|
+
TableName: FundDb.table,
|
|
22
|
+
Item: this.fund_record(data),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**@param id - slug or uuid */
|
|
26
|
+
async fund(id) {
|
|
27
|
+
const q = {
|
|
28
|
+
TableName: FundDb.table,
|
|
29
|
+
Limit: 1,
|
|
30
|
+
};
|
|
31
|
+
if (UUID_REGEX.test(id)) {
|
|
32
|
+
q.KeyConditionExpression = "PK = :PK AND SK = :SK";
|
|
33
|
+
q.ExpressionAttributeValues = this.key_fund(id);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
q.IndexName = FundDb.slug_env_gsi;
|
|
37
|
+
q.KeyConditionExpression = "slug = :slug AND env = :env";
|
|
38
|
+
q.ExpressionAttributeValues = {
|
|
39
|
+
":slug": id,
|
|
40
|
+
":env": this.env,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const { Items: i = [] } = await this.client.send(new QueryCommand(q));
|
|
44
|
+
return i[0] && this.sans_keys(i[0]);
|
|
45
|
+
}
|
|
46
|
+
async fund_edit(id, { target, slug, ...update }) {
|
|
47
|
+
const updates = new UpdateBuilder();
|
|
48
|
+
if (slug)
|
|
49
|
+
updates.set("slug", slug);
|
|
50
|
+
if (slug === "")
|
|
51
|
+
updates.remove("slug");
|
|
52
|
+
if (target || target === "0") {
|
|
53
|
+
updates.set("target", target);
|
|
54
|
+
}
|
|
55
|
+
for (const [k, v] of Object.entries(update)) {
|
|
56
|
+
if (v === undefined)
|
|
57
|
+
continue;
|
|
58
|
+
updates.set(k, v);
|
|
59
|
+
}
|
|
60
|
+
const command = new UpdateCommand({
|
|
61
|
+
TableName: FundDb.table,
|
|
62
|
+
Key: this.key_fund(id),
|
|
63
|
+
ReturnValues: "ALL_NEW",
|
|
64
|
+
...updates.collect(),
|
|
65
|
+
});
|
|
66
|
+
return this.client.send(command).then((res) => res.Attributes ?? {});
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type { Environment, FundSettings, FundUpdate, FundsEndowMemberOfParams, FundsParams, NewFund, } from "./schema.mjs";
|
|
5
|
-
export type { FundItem };
|
|
6
|
-
export interface FundMember {
|
|
7
|
-
id: number;
|
|
8
|
-
name: string;
|
|
9
|
-
logo: string | undefined;
|
|
10
|
-
banner: string | undefined;
|
|
11
|
-
}
|
|
12
|
-
export interface SingleFund extends Omit<NewFund, "members">, FundInternal {
|
|
13
|
-
members: FundMember[];
|
|
14
|
-
}
|
|
15
|
-
export interface FundsPage extends IPageNumbered<FundItem> {
|
|
16
|
-
}
|
|
1
|
+
export type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
2
|
+
export type { IFundNew, IFundUpdate, IFundsNpoMemberOfSearchObj, IFundsSearchObj, DonateMethodId, Environment, } from "./schema.mjs";
|
|
3
|
+
export { FundDb } from "./db.mjs";
|
|
17
4
|
export declare const MAX_EXPIRATION = "9999-12-31T23:59:59Z";
|
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DonateMethodId, Environment } from "@better-giving/schemas";
|
|
2
|
+
import type { IPageNumbered } from "@better-giving/types/api";
|
|
3
|
+
import type { CloudsearchFund } from "./cloudsearch.mjs";
|
|
4
|
+
import type { IFundNew } from "./schema.mjs";
|
|
5
|
+
export interface IFundSettings {
|
|
6
|
+
hide_bg_tip: boolean;
|
|
7
|
+
donateMethods?: DonateMethodId[];
|
|
8
|
+
}
|
|
9
|
+
export interface IFundCreator {
|
|
10
|
+
/** "{number}" - endow id, "{email} - user*/
|
|
11
|
+
creator_id: string;
|
|
12
|
+
creator_name: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IFundInternal extends IFundCreator {
|
|
15
|
+
/** uuid */
|
|
16
|
+
id: string;
|
|
17
|
+
env: Environment;
|
|
18
|
+
/** fund can be closed before expiration */
|
|
19
|
+
active: boolean;
|
|
20
|
+
verified: boolean;
|
|
21
|
+
/** to date received: initialized to `0` */
|
|
22
|
+
donation_total_usd: number;
|
|
23
|
+
settings: IFundSettings;
|
|
24
|
+
}
|
|
25
|
+
export interface IFund extends IFundNew, IFundInternal {
|
|
26
|
+
}
|
|
27
|
+
export interface IFundsPage extends IPageNumbered<CloudsearchFund> {
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/schema.d.mts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { type DonateMethodId } from "@better-giving/schemas";
|
|
2
|
-
import type { Environment } from "@better-giving/types/list";
|
|
3
1
|
import { type InferOutput } from "valibot";
|
|
4
2
|
export { type DonateMethodId, slug } from "@better-giving/schemas";
|
|
5
3
|
export type { Environment } from "@better-giving/types/list";
|
|
6
|
-
export declare const
|
|
4
|
+
export declare const fund_id: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UuidAction<string, undefined>]>;
|
|
7
5
|
/**
|
|
8
6
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
9
7
|
* 0 - none
|
|
10
8
|
*/
|
|
11
9
|
export declare const npo_owner: import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 0, undefined>]>;
|
|
12
|
-
export declare const
|
|
10
|
+
export declare const fund_new: import("valibot").ObjectSchema<{
|
|
13
11
|
readonly name: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
14
12
|
readonly description: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
15
13
|
readonly banner: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UrlAction<string, undefined>]>;
|
|
@@ -24,7 +22,7 @@ export declare const newFund: import("valibot").ObjectSchema<{
|
|
|
24
22
|
readonly npo_owner: import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 0, undefined>]>;
|
|
25
23
|
readonly slug: import("valibot").OptionalSchema<import("valibot").LazySchema<import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]> | import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").MaxLengthAction<string, 30, ({ requirement: r }: import("valibot").MaxLengthIssue<string, 30>) => string>, import("valibot").RegexAction<string, "should not be an id">, import("valibot").RegexAction<string, "allowed: numbers | letters | - | . | _ | ~">, import("valibot").ExcludesAction<string, "..", "should not contain double periods">, import("valibot").CustomSchema<string, "should not start with dot">, import("valibot").CustomSchema<string, "should not end with dot">]>>, never>;
|
|
26
24
|
}, undefined>;
|
|
27
|
-
export declare const
|
|
25
|
+
export declare const fund_update: Omit<Omit<import("valibot").ObjectSchema<{
|
|
28
26
|
readonly name: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
29
27
|
readonly description: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
30
28
|
readonly banner: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UrlAction<string, undefined>]>;
|
|
@@ -132,38 +130,20 @@ export declare const fundUpdate: Omit<Omit<import("valibot").ObjectSchema<{
|
|
|
132
130
|
readonly issue: import("valibot").StringIssue | import("valibot").NumberIssue | import("valibot").MinValueIssue<number, 0> | import("valibot").NonEmptyIssue<string> | import("valibot").UrlIssue<string> | import("valibot").ArrayIssue | import("valibot").BooleanIssue | import("valibot").LiteralIssue | import("valibot").UnionIssue<import("valibot").StringIssue | import("valibot").NumberIssue | import("valibot").MinValueIssue<number, 0> | import("valibot").LiteralIssue> | import("valibot").CustomIssue | import("valibot").MaxLengthIssue<string, 30> | import("valibot").RegexIssue<string> | import("valibot").ExcludesIssue<string, ".."> | import("valibot").ObjectIssue;
|
|
133
131
|
} | undefined;
|
|
134
132
|
};
|
|
135
|
-
export declare const
|
|
133
|
+
export declare const funds_search: import("valibot").ObjectSchema<{
|
|
136
134
|
/** search text */
|
|
137
135
|
readonly query: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, never>;
|
|
138
136
|
/** input str: from url */
|
|
139
|
-
readonly page: import("valibot").
|
|
137
|
+
readonly page: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").TransformAction<string, number>, import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 1, undefined>]>]>;
|
|
140
138
|
}, undefined>;
|
|
141
|
-
export declare const
|
|
142
|
-
readonly
|
|
139
|
+
export declare const funds_npo_memberof_search: import("valibot").ObjectSchema<{
|
|
140
|
+
readonly npo_profile_featured: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").TransformAction<string, boolean>, import("valibot").BooleanSchema<undefined>]>, never>;
|
|
143
141
|
}, undefined>;
|
|
144
|
-
export interface
|
|
142
|
+
export interface IFundNew extends InferOutput<typeof fund_new> {
|
|
145
143
|
}
|
|
146
|
-
export interface
|
|
144
|
+
export interface IFundUpdate extends InferOutput<typeof fund_update> {
|
|
147
145
|
}
|
|
148
|
-
export interface
|
|
146
|
+
export interface IFundsSearchObj extends InferOutput<typeof funds_search> {
|
|
149
147
|
}
|
|
150
|
-
export interface
|
|
151
|
-
}
|
|
152
|
-
export interface FundSettings {
|
|
153
|
-
hide_bg_tip: boolean;
|
|
154
|
-
donateMethods?: DonateMethodId[];
|
|
155
|
-
}
|
|
156
|
-
export interface FundInternal {
|
|
157
|
-
/** uuid */
|
|
158
|
-
id: string;
|
|
159
|
-
env: Environment;
|
|
160
|
-
/** fund can be closed before expiration */
|
|
161
|
-
active: boolean;
|
|
162
|
-
verified: boolean;
|
|
163
|
-
/** to date received: initialized to `0` */
|
|
164
|
-
donation_total_usd: number;
|
|
165
|
-
/** "{number}" - endow id, "{email} - user*/
|
|
166
|
-
creator_id: string;
|
|
167
|
-
creator_name: string;
|
|
168
|
-
settings: FundSettings;
|
|
148
|
+
export interface IFundsNpoMemberOfSearchObj extends InferOutput<typeof funds_npo_memberof_search> {
|
|
169
149
|
}
|
package/dist/schema.mjs
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
-
import { slug } from "@better-giving/schemas";
|
|
2
|
-
import { url, array, boolean, integer, isoTimestamp, literal, maxLength, minValue, nonEmpty, number, object, optional, partial, pick, pipe, string, transform,
|
|
1
|
+
import { $, $int_gte1, slug } from "@better-giving/schemas";
|
|
2
|
+
import { url, array, boolean, integer, isoTimestamp, literal, maxLength, minValue, nonEmpty, number, object, optional, partial, pick, pipe, string, transform, union, uuid, } from "valibot";
|
|
3
3
|
export { slug } from "@better-giving/schemas";
|
|
4
|
-
const
|
|
5
|
-
export const fundId = pipe(str, uuid());
|
|
4
|
+
export const fund_id = pipe($, uuid());
|
|
6
5
|
/**
|
|
7
6
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
8
7
|
* 0 - none
|
|
9
8
|
*/
|
|
10
9
|
export const npo_owner = pipe(number(), integer(), minValue(0));
|
|
11
|
-
export const
|
|
12
|
-
name: pipe(
|
|
13
|
-
description: pipe(
|
|
14
|
-
banner: pipe(
|
|
15
|
-
logo: pipe(
|
|
10
|
+
export const fund_new = object({
|
|
11
|
+
name: pipe($, nonEmpty("required")),
|
|
12
|
+
description: pipe($, nonEmpty("required")),
|
|
13
|
+
banner: pipe($, url()),
|
|
14
|
+
logo: pipe($, url()),
|
|
16
15
|
/** endowment ids */
|
|
17
16
|
members: pipe(array(pipe(number(), integer(), minValue(1))), nonEmpty(), maxLength(10)),
|
|
18
17
|
featured: boolean(),
|
|
19
|
-
expiration: optional(pipe(
|
|
18
|
+
expiration: optional(pipe($, isoTimestamp("invalid date"), minValue(new Date().toISOString()) //created each parsing
|
|
20
19
|
)),
|
|
21
20
|
/** `"0"` - none, {"number"} = fixed */
|
|
22
21
|
target: union([
|
|
23
22
|
literal("smart"),
|
|
24
23
|
pipe(string(), transform((v) => +v), number(), minValue(0), transform((v) => v.toString())),
|
|
25
24
|
]),
|
|
26
|
-
videos: array(pipe(
|
|
25
|
+
videos: array(pipe($, url())),
|
|
27
26
|
npo_owner,
|
|
28
27
|
slug: optional(slug),
|
|
29
28
|
});
|
|
30
|
-
export const
|
|
29
|
+
export const fund_update = partial(pick(fund_new, [
|
|
31
30
|
"name",
|
|
32
31
|
"description",
|
|
33
32
|
"banner",
|
|
@@ -37,13 +36,13 @@ export const fundUpdate = partial(pick(newFund, [
|
|
|
37
36
|
"videos",
|
|
38
37
|
"slug",
|
|
39
38
|
]));
|
|
40
|
-
export const
|
|
39
|
+
export const funds_search = object({
|
|
41
40
|
/** search text */
|
|
42
|
-
query: optional(
|
|
41
|
+
query: optional($),
|
|
43
42
|
/** input str: from url */
|
|
44
|
-
page:
|
|
43
|
+
page: $int_gte1,
|
|
45
44
|
});
|
|
46
|
-
export const
|
|
45
|
+
export const funds_npo_memberof_search = object({
|
|
47
46
|
/*
|
|
48
47
|
* this endow is the only member (not an index fund),
|
|
49
48
|
* and is approved:
|
|
@@ -52,5 +51,5 @@ export const fundsEndowMemberOfParams = object({
|
|
|
52
51
|
*
|
|
53
52
|
* input str: from url
|
|
54
53
|
*/
|
|
55
|
-
|
|
54
|
+
npo_profile_featured: optional(pipe($, transform((x) => x === "true"), boolean())),
|
|
56
55
|
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-giving/fundraiser",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"valibot": "0.42.0",
|
|
6
6
|
"@better-giving/schemas": "2.0.0",
|
|
7
|
-
"@better-giving/types": "1.1.8"
|
|
7
|
+
"@better-giving/types": "1.1.8",
|
|
8
|
+
"@better-giving/db": "2.0.1",
|
|
9
|
+
"@aws-sdk/lib-dynamodb": "3.485.0"
|
|
8
10
|
},
|
|
9
11
|
"devDependencies": {
|
|
10
12
|
"@better-giving/config": "1.1.2"
|
package/src/cloudsearch.mts
CHANGED
|
@@ -3,11 +3,12 @@ import type {
|
|
|
3
3
|
ToHitFields,
|
|
4
4
|
ToUpdate,
|
|
5
5
|
} from "@better-giving/types/cloudsearch";
|
|
6
|
-
import type {
|
|
6
|
+
import type { IFundInternal } from "./interfaces.mjs";
|
|
7
|
+
import type { IFundNew } from "./schema.mjs";
|
|
7
8
|
|
|
8
9
|
export interface CloudsearchFund
|
|
9
10
|
extends Pick<
|
|
10
|
-
|
|
11
|
+
IFundNew,
|
|
11
12
|
| "name"
|
|
12
13
|
| "description"
|
|
13
14
|
| "logo"
|
|
@@ -17,7 +18,7 @@ export interface CloudsearchFund
|
|
|
17
18
|
| "target"
|
|
18
19
|
>,
|
|
19
20
|
Pick<
|
|
20
|
-
|
|
21
|
+
IFundInternal,
|
|
21
22
|
| "id"
|
|
22
23
|
| "env"
|
|
23
24
|
| "active"
|
package/src/db.mts
CHANGED
|
@@ -1,20 +1,78 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
QueryCommand,
|
|
3
|
+
type QueryCommandInput,
|
|
4
|
+
UpdateCommand,
|
|
5
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
6
|
+
import { Db, type TxType, UpdateBuilder } from "@better-giving/db";
|
|
7
|
+
import { UUID_REGEX } from "valibot";
|
|
8
|
+
import type { IFund } from "./interfaces.mjs";
|
|
9
|
+
import type { IFundUpdate } from "./schema.mjs";
|
|
2
10
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
export class FundDb extends Db {
|
|
12
|
+
static readonly table = "funds";
|
|
13
|
+
static readonly slug_env_gsi = "slug-env-gsi";
|
|
14
|
+
key_fund(id: string) {
|
|
15
|
+
return {
|
|
16
|
+
PK: `Fund#${id}`,
|
|
17
|
+
SK: `Fund#${id}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
fund_record(data: IFund) {
|
|
21
|
+
return {
|
|
22
|
+
...this.key_fund(data.id),
|
|
23
|
+
...data,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
fund_put_txi(data: IFund): TxType["Put"] {
|
|
27
|
+
return {
|
|
28
|
+
TableName: FundDb.table,
|
|
29
|
+
Item: this.fund_record(data),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**@param id - slug or uuid */
|
|
33
|
+
async fund(id: string): Promise<IFund | undefined> {
|
|
34
|
+
const q: QueryCommandInput = {
|
|
35
|
+
TableName: FundDb.table,
|
|
36
|
+
Limit: 1,
|
|
37
|
+
};
|
|
38
|
+
if (UUID_REGEX.test(id)) {
|
|
39
|
+
q.KeyConditionExpression = "PK = :PK AND SK = :SK";
|
|
40
|
+
q.ExpressionAttributeValues = this.key_fund(id);
|
|
41
|
+
} else {
|
|
42
|
+
q.IndexName = FundDb.slug_env_gsi;
|
|
43
|
+
q.KeyConditionExpression = "slug = :slug AND env = :env";
|
|
44
|
+
q.ExpressionAttributeValues = {
|
|
45
|
+
":slug": id,
|
|
46
|
+
":env": this.env,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
6
49
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
50
|
+
const { Items: i = [] } = await this.client.send(new QueryCommand(q));
|
|
51
|
+
return i[0] && this.sans_keys(i[0]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async fund_edit(id: string, { target, slug, ...update }: IFundUpdate) {
|
|
55
|
+
const updates = new UpdateBuilder();
|
|
56
|
+
|
|
57
|
+
if (slug) updates.set("slug", slug);
|
|
58
|
+
if (slug === "") updates.remove("slug");
|
|
59
|
+
|
|
60
|
+
if (target || target === "0") {
|
|
61
|
+
updates.set("target", target);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const [k, v] of Object.entries(update)) {
|
|
65
|
+
if (v === undefined) continue;
|
|
66
|
+
updates.set(k, v);
|
|
67
|
+
}
|
|
12
68
|
|
|
13
|
-
|
|
14
|
-
|
|
69
|
+
const command = new UpdateCommand({
|
|
70
|
+
TableName: FundDb.table,
|
|
71
|
+
Key: this.key_fund(id),
|
|
72
|
+
ReturnValues: "ALL_NEW",
|
|
73
|
+
...updates.collect(),
|
|
74
|
+
});
|
|
15
75
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export interface Keys extends Pick<Attributes, "slug" | "env"> {}
|
|
19
|
-
export interface Record extends DbRecord {} //all attributes are copied to this index
|
|
76
|
+
return this.client.send(command).then((res) => res.Attributes ?? {});
|
|
77
|
+
}
|
|
20
78
|
}
|
package/src/index.mts
CHANGED
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
3
|
-
import type { FundInternal, NewFund } from "./schema.mjs";
|
|
1
|
+
export type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
4
2
|
export type {
|
|
3
|
+
IFundNew,
|
|
4
|
+
IFundUpdate,
|
|
5
|
+
IFundsNpoMemberOfSearchObj,
|
|
6
|
+
IFundsSearchObj,
|
|
7
|
+
DonateMethodId,
|
|
5
8
|
Environment,
|
|
6
|
-
FundSettings,
|
|
7
|
-
FundUpdate,
|
|
8
|
-
FundsEndowMemberOfParams,
|
|
9
|
-
FundsParams,
|
|
10
|
-
NewFund,
|
|
11
9
|
} from "./schema.mjs";
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
export interface FundMember {
|
|
15
|
-
id: number;
|
|
16
|
-
name: string;
|
|
17
|
-
logo: string | undefined;
|
|
18
|
-
banner: string | undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface SingleFund extends Omit<NewFund, "members">, FundInternal {
|
|
22
|
-
members: FundMember[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface FundsPage extends IPageNumbered<FundItem> {}
|
|
10
|
+
export { FundDb } from "./db.mjs";
|
|
26
11
|
|
|
27
12
|
export const MAX_EXPIRATION = "9999-12-31T23:59:59Z";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DonateMethodId, Environment } from "@better-giving/schemas";
|
|
2
|
+
import type { IPageNumbered } from "@better-giving/types/api";
|
|
3
|
+
import type { CloudsearchFund } from "./cloudsearch.mjs";
|
|
4
|
+
import type { IFundNew } from "./schema.mjs";
|
|
5
|
+
|
|
6
|
+
export interface IFundSettings {
|
|
7
|
+
hide_bg_tip: boolean;
|
|
8
|
+
donateMethods?: DonateMethodId[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface IFundCreator {
|
|
12
|
+
/** "{number}" - endow id, "{email} - user*/
|
|
13
|
+
creator_id: string;
|
|
14
|
+
creator_name: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IFundInternal extends IFundCreator {
|
|
18
|
+
/** uuid */
|
|
19
|
+
id: string;
|
|
20
|
+
env: Environment;
|
|
21
|
+
/** fund can be closed before expiration */
|
|
22
|
+
active: boolean;
|
|
23
|
+
verified: boolean;
|
|
24
|
+
/** to date received: initialized to `0` */
|
|
25
|
+
donation_total_usd: number;
|
|
26
|
+
settings: IFundSettings;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IFund extends IFundNew, IFundInternal {}
|
|
30
|
+
|
|
31
|
+
export interface IFundsPage extends IPageNumbered<CloudsearchFund> {}
|
package/src/schema.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Environment } from "@better-giving/types/list";
|
|
1
|
+
import { $, $int_gte1, slug } from "@better-giving/schemas";
|
|
3
2
|
import {
|
|
4
3
|
url,
|
|
5
4
|
type InferOutput,
|
|
@@ -19,7 +18,6 @@ import {
|
|
|
19
18
|
pipe,
|
|
20
19
|
string,
|
|
21
20
|
transform,
|
|
22
|
-
trim,
|
|
23
21
|
union,
|
|
24
22
|
uuid,
|
|
25
23
|
} from "valibot";
|
|
@@ -27,20 +25,18 @@ import {
|
|
|
27
25
|
export { type DonateMethodId, slug } from "@better-giving/schemas";
|
|
28
26
|
export type { Environment } from "@better-giving/types/list";
|
|
29
27
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
export const fundId = pipe(str, uuid());
|
|
28
|
+
export const fund_id = pipe($, uuid());
|
|
33
29
|
/**
|
|
34
30
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
35
31
|
* 0 - none
|
|
36
32
|
*/
|
|
37
33
|
export const npo_owner = pipe(number(), integer(), minValue(0));
|
|
38
34
|
|
|
39
|
-
export const
|
|
40
|
-
name: pipe(
|
|
41
|
-
description: pipe(
|
|
42
|
-
banner: pipe(
|
|
43
|
-
logo: pipe(
|
|
35
|
+
export const fund_new = object({
|
|
36
|
+
name: pipe($, nonEmpty("required")),
|
|
37
|
+
description: pipe($, nonEmpty("required")),
|
|
38
|
+
banner: pipe($, url()),
|
|
39
|
+
logo: pipe($, url()),
|
|
44
40
|
/** endowment ids */
|
|
45
41
|
members: pipe(
|
|
46
42
|
array(pipe(number(), integer(), minValue(1))),
|
|
@@ -50,7 +46,7 @@ export const newFund = object({
|
|
|
50
46
|
featured: boolean(),
|
|
51
47
|
expiration: optional(
|
|
52
48
|
pipe(
|
|
53
|
-
|
|
49
|
+
$,
|
|
54
50
|
isoTimestamp("invalid date"),
|
|
55
51
|
minValue(new Date().toISOString()) //created each parsing
|
|
56
52
|
)
|
|
@@ -66,13 +62,13 @@ export const newFund = object({
|
|
|
66
62
|
transform((v) => v.toString())
|
|
67
63
|
),
|
|
68
64
|
]),
|
|
69
|
-
videos: array(pipe(
|
|
65
|
+
videos: array(pipe($, url())),
|
|
70
66
|
npo_owner,
|
|
71
67
|
slug: optional(slug),
|
|
72
68
|
});
|
|
73
69
|
|
|
74
|
-
export const
|
|
75
|
-
pick(
|
|
70
|
+
export const fund_update = partial(
|
|
71
|
+
pick(fund_new, [
|
|
76
72
|
"name",
|
|
77
73
|
"description",
|
|
78
74
|
"banner",
|
|
@@ -84,22 +80,14 @@ export const fundUpdate = partial(
|
|
|
84
80
|
])
|
|
85
81
|
);
|
|
86
82
|
|
|
87
|
-
export const
|
|
83
|
+
export const funds_search = object({
|
|
88
84
|
/** search text */
|
|
89
|
-
query: optional(
|
|
85
|
+
query: optional($),
|
|
90
86
|
/** input str: from url */
|
|
91
|
-
page:
|
|
92
|
-
pipe(
|
|
93
|
-
str,
|
|
94
|
-
transform((x) => +x),
|
|
95
|
-
number(),
|
|
96
|
-
integer(),
|
|
97
|
-
minValue(1)
|
|
98
|
-
)
|
|
99
|
-
),
|
|
87
|
+
page: $int_gte1,
|
|
100
88
|
});
|
|
101
89
|
|
|
102
|
-
export const
|
|
90
|
+
export const funds_npo_memberof_search = object({
|
|
103
91
|
/*
|
|
104
92
|
* this endow is the only member (not an index fund),
|
|
105
93
|
* and is approved:
|
|
@@ -108,37 +96,17 @@ export const fundsEndowMemberOfParams = object({
|
|
|
108
96
|
*
|
|
109
97
|
* input str: from url
|
|
110
98
|
*/
|
|
111
|
-
|
|
99
|
+
npo_profile_featured: optional(
|
|
112
100
|
pipe(
|
|
113
|
-
|
|
101
|
+
$,
|
|
114
102
|
transform((x) => x === "true"),
|
|
115
103
|
boolean()
|
|
116
104
|
)
|
|
117
105
|
),
|
|
118
106
|
});
|
|
119
107
|
|
|
120
|
-
export interface
|
|
121
|
-
export interface
|
|
122
|
-
export interface
|
|
123
|
-
export interface
|
|
124
|
-
extends InferOutput<typeof
|
|
125
|
-
|
|
126
|
-
export interface FundSettings {
|
|
127
|
-
hide_bg_tip: boolean;
|
|
128
|
-
donateMethods?: DonateMethodId[];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface FundInternal {
|
|
132
|
-
/** uuid */
|
|
133
|
-
id: string;
|
|
134
|
-
env: Environment;
|
|
135
|
-
/** fund can be closed before expiration */
|
|
136
|
-
active: boolean;
|
|
137
|
-
verified: boolean;
|
|
138
|
-
/** to date received: initialized to `0` */
|
|
139
|
-
donation_total_usd: number;
|
|
140
|
-
/** "{number}" - endow id, "{email} - user*/
|
|
141
|
-
creator_id: string;
|
|
142
|
-
creator_name: string;
|
|
143
|
-
settings: FundSettings;
|
|
144
|
-
}
|
|
108
|
+
export interface IFundNew extends InferOutput<typeof fund_new> {}
|
|
109
|
+
export interface IFundUpdate extends InferOutput<typeof fund_update> {}
|
|
110
|
+
export interface IFundsSearchObj extends InferOutput<typeof funds_search> {}
|
|
111
|
+
export interface IFundsNpoMemberOfSearchObj
|
|
112
|
+
extends InferOutput<typeof funds_npo_memberof_search> {}
|