Skip to content

Commit 5a5e7bf

Browse files
committed
Merge branch 'master' of https://github.com/r3-dev/pocketbase
2 parents 38a4d74 + 205a14f commit 5a5e7bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+389
-224
lines changed

apis/settings_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func TestSettingsList(t *testing.T) {
8585
`"patreonAuth":{`,
8686
`"mailcowAuth":{`,
8787
`"bitbucketAuth":{`,
88+
`"donationalertsAuth":{`,
8889
`"planningcenterAuth":{`,
8990
`"secret":"******"`,
9091
`"clientSecret":"******"`,
@@ -172,6 +173,7 @@ func TestSettingsSet(t *testing.T) {
172173
`"patreonAuth":{`,
173174
`"mailcowAuth":{`,
174175
`"bitbucketAuth":{`,
176+
`"donationalertsAuth":{`,
175177
`"planningcenterAuth":{`,
176178
`"secret":"******"`,
177179
`"clientSecret":"******"`,
@@ -246,6 +248,7 @@ func TestSettingsSet(t *testing.T) {
246248
`"patreonAuth":{`,
247249
`"mailcowAuth":{`,
248250
`"bitbucketAuth":{`,
251+
`"donationalertsAuth":{`,
249252
`"planningcenterAuth":{`,
250253
`"secret":"******"`,
251254
`"clientSecret":"******"`,

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/r3dev/pocketbase/pocketbase
1+
module github.com/r3-dev/pocketbase
22

33
go 1.21
44

models/settings/settings.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type Settings struct {
6767
MailcowAuth AuthProviderConfig `form:"mailcowAuth" json:"mailcowAuth"`
6868
BitbucketAuth AuthProviderConfig `form:"bitbucketAuth" json:"bitbucketAuth"`
6969
PlanningcenterAuth AuthProviderConfig `form:"planningcenterAuth" json:"planningcenterAuth"`
70+
DonationAlertsAuth AuthProviderConfig `form:"donationalertsAuth" json:"donationalertsAuth"`
7071
}
7172

7273
// New creates and returns a new default Settings instance.
@@ -204,6 +205,9 @@ func New() *Settings {
204205
PlanningcenterAuth: AuthProviderConfig{
205206
Enabled: false,
206207
},
208+
DonationAlertsAuth: AuthProviderConfig{
209+
Enabled: false,
210+
},
207211
}
208212
}
209213

@@ -251,6 +255,7 @@ func (s *Settings) Validate() error {
251255
validation.Field(&s.MailcowAuth),
252256
validation.Field(&s.BitbucketAuth),
253257
validation.Field(&s.PlanningcenterAuth),
258+
validation.Field(&s.DonationAlertsAuth),
254259
)
255260
}
256261

@@ -321,6 +326,7 @@ func (s *Settings) RedactClone() (*Settings, error) {
321326
&clone.MailcowAuth.ClientSecret,
322327
&clone.BitbucketAuth.ClientSecret,
323328
&clone.PlanningcenterAuth.ClientSecret,
329+
&clone.DonationAlertsAuth.ClientSecret,
324330
}
325331

326332
// mask all sensitive fields
@@ -365,6 +371,7 @@ func (s *Settings) NamedAuthProviderConfigs() map[string]AuthProviderConfig {
365371
auth.NameMailcow: s.MailcowAuth,
366372
auth.NameBitbucket: s.BitbucketAuth,
367373
auth.NamePlanningcenter: s.PlanningcenterAuth,
374+
auth.NameDonationAlerts: s.DonationAlertsAuth,
368375
}
369376
}
370377

models/settings/settings_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ func TestSettingsValidate(t *testing.T) {
8282
s.BitbucketAuth.ClientId = ""
8383
s.PlanningcenterAuth.Enabled = true
8484
s.PlanningcenterAuth.ClientId = ""
85+
s.DonationAlertsAuth.Enabled = true
86+
s.DonationAlertsAuth.ClientId = ""
8587

8688
// check if Validate() is triggering the members validate methods.
8789
err := s.Validate()
@@ -127,6 +129,7 @@ func TestSettingsValidate(t *testing.T) {
127129
`"mailcowAuth":{`,
128130
`"bitbucketAuth":{`,
129131
`"planningcenterAuth":{`,
132+
`"donationAlertsAuth":{`,
130133
}
131134

132135
errBytes, _ := json.Marshal(err)
@@ -208,6 +211,8 @@ func TestSettingsMerge(t *testing.T) {
208211
s2.BitbucketAuth.ClientId = "bitbucket_test"
209212
s2.PlanningcenterAuth.Enabled = true
210213
s2.PlanningcenterAuth.ClientId = "planningcenter_test"
214+
s2.DonationAlertsAuth.Enabled = true
215+
s2.DonationAlertsAuth.ClientId = "donationalerts_test"
211216

212217
if err := s1.Merge(s2); err != nil {
213218
t.Fatal(err)
@@ -302,6 +307,7 @@ func TestSettingsRedactClone(t *testing.T) {
302307
s1.MailcowAuth.ClientSecret = testSecret
303308
s1.BitbucketAuth.ClientSecret = testSecret
304309
s1.PlanningcenterAuth.ClientSecret = testSecret
310+
s1.DonationAlertsAuth.ClientSecret = testSecret
305311

306312
s1Bytes, err := json.Marshal(s1)
307313
if err != nil {
@@ -364,6 +370,7 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
364370
s.MailcowAuth.ClientId = "mailcow_test"
365371
s.BitbucketAuth.ClientId = "bitbucket_test"
366372
s.PlanningcenterAuth.ClientId = "planningcenter_test"
373+
s.DonationAlertsAuth.ClientId = "donationalerts_test"
367374

368375
result := s.NamedAuthProviderConfigs()
369376

@@ -399,6 +406,7 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
399406
`"mailcow":{"enabled":false,"clientId":"mailcow_test"`,
400407
`"bitbucket":{"enabled":false,"clientId":"bitbucket_test"`,
401408
`"planningcenter":{"enabled":false,"clientId":"planningcenter_test"`,
409+
`"donationalerts":{"enabled":false,"clientId":"donationalerts_test"`,
402410
}
403411
for _, p := range expectedParts {
404412
if !strings.Contains(encodedStr, p) {

tools/auth/auth.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ func NewProviderByName(name string) (Provider, error) {
158158
return NewBitbucketProvider(), nil
159159
case NamePlanningcenter:
160160
return NewPlanningcenterProvider(), nil
161+
case NameDonationAlerts:
162+
return NewDonationAlertsProvider(), nil
161163
default:
162164
return nil, errors.New("Missing provider " + name)
163165
}

tools/auth/auth_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,13 @@ func TestNewProviderByName(t *testing.T) {
243243
if _, ok := p.(*auth.Planningcenter); !ok {
244244
t.Error("Expected to be instance of *auth.Planningcenter")
245245
}
246+
247+
// donationalerts
248+
p, err = auth.NewProviderByName(auth.NameDonationAlerts)
249+
if err != nil {
250+
t.Errorf("Expected nil, got error %v", err)
251+
}
252+
if _, ok := p.(*auth.DonationAlerts); !ok {
253+
t.Error("Expected to be instance of *auth.DonationAlerts")
254+
}
246255
}

tools/auth/donationalerts.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package auth
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"strconv"
7+
8+
"github.com/pocketbase/pocketbase/tools/types"
9+
"golang.org/x/oauth2"
10+
)
11+
12+
var _ Provider = (*DonationAlerts)(nil)
13+
14+
// NameDonationAlerts is the unique name of the Donation Alerts provider.
15+
const NameDonationAlerts string = "donationalerts"
16+
17+
// Donation Alerts allows authentication via Discord OAuth2.
18+
type DonationAlerts struct {
19+
*baseProvider
20+
}
21+
22+
// NewDiscordProvider creates a new Discord provider instance with some defaults.
23+
func NewDonationAlertsProvider() *DonationAlerts {
24+
// https://www.donationalerts.com/apidoc#authorization
25+
return &DonationAlerts{&baseProvider{
26+
ctx: context.Background(),
27+
displayName: "Donation Alerts",
28+
pkce: true,
29+
scopes: []string{"oauth-user-show"},
30+
authUrl: "https://www.donationalerts.com/oauth/authorize",
31+
tokenUrl: "https://www.donationalerts.com/oauth/token",
32+
userApiUrl: "https://www.donationalerts.com/api/v1/user/oauth",
33+
}}
34+
}
35+
36+
// FetchAuthUser returns an AuthUser instance from Discord's user api.
37+
//
38+
// API reference: https://discord.com/developers/docs/resources/user#user-object
39+
func (p *DonationAlerts) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
40+
data, err := p.FetchRawUserData(token)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
rawUser := map[string]any{}
46+
if err := json.Unmarshal(data, &rawUser); err != nil {
47+
return nil, err
48+
}
49+
50+
extracted := struct {
51+
Data struct {
52+
Id int `json:"id"`
53+
UniqueName string `json:"code"`
54+
Name string `json:"name"`
55+
Avatar string `json:"avatar"`
56+
Email string `json:"email"`
57+
} `json:"data"`
58+
}{}
59+
if err := json.Unmarshal(data, &extracted); err != nil {
60+
return nil, err
61+
}
62+
63+
user := &AuthUser{
64+
Id: strconv.Itoa(extracted.Data.Id),
65+
Name: extracted.Data.UniqueName,
66+
Username: extracted.Data.Name,
67+
Email: extracted.Data.Email,
68+
AvatarUrl: extracted.Data.Avatar,
69+
RawUser: rawUser,
70+
AccessToken: token.AccessToken,
71+
RefreshToken: token.RefreshToken,
72+
}
73+
74+
user.Expiry, _ = types.ParseDateTime(token.Expiry)
75+
76+
return user, nil
77+
}

ui/dist/assets/AuthMethodsDocs-BxBYHCup.js renamed to ui/dist/assets/AuthMethodsDocs-C5pOO6ww.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/dist/assets/AuthRefreshDocs-5xe2RrH8.js renamed to ui/dist/assets/AuthRefreshDocs-C9GU56m3.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,v as k,b as p,c as K,f as b,g as d,h as o,m as I,w as de,P as Ee,Q as Ke,k as Ie,R as We,n as Ge,t as N,a as V,o as u,d as W,C as Le,A as Xe,q as G,r as Ye}from"./index-BpajECXE.js";import{S as Ze}from"./SdkTabs-CpmQ63ei.js";import{F as et}from"./FieldsQueryParam-D74aRN4W.js";function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l,a){const n=r.slice();return n[5]=l[a],n}function ze(r,l){let a,n=l[5].code+"",m,_,i,h;function g(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(v,w){d(v,a,w),o(a,m),o(a,_),i||(h=Ye(a,"click",g),i=!0)},p(v,w){l=v,w&4&&n!==(n=l[5].code+"")&&de(m,n),w&6&&G(a,"active",l[1]===l[5].code)},d(v){v&&u(a),i=!1,h()}}}function Qe(r,l){let a,n,m,_;return n=new Ue({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),K(n.$$.fragment),m=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(i,h){d(i,a,h),I(n,a,null),o(a,m),_=!0},p(i,h){l=i;const g={};h&4&&(g.content=l[5].body),n.$set(g),(!_||h&6)&&G(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),W(n)}}}function tt(r){var De,Fe;let l,a,n=r[0].name+"",m,_,i,h,g,v,w,B,X,S,z,ue,Q,M,pe,Y,U=r[0].name+"",Z,he,fe,j,ee,D,te,T,oe,be,F,C,le,me,ae,_e,f,ke,R,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Pe,A,ie,O,ce,P,H,y=[],Re=new Map,Ae,E,$=[],qe=new Map,q;v=new Ze({props:{js:`
1+
import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,v as k,b as p,c as K,f as b,g as d,h as o,m as I,w as de,P as Ee,Q as Ke,k as Ie,R as We,n as Ge,t as N,a as V,o as u,d as W,C as Le,A as Xe,q as G,r as Ye}from"./index-5r8buh15.js";import{S as Ze}from"./SdkTabs-DUijoRUd.js";import{F as et}from"./FieldsQueryParam-D5CN4kWH.js";function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l,a){const n=r.slice();return n[5]=l[a],n}function ze(r,l){let a,n=l[5].code+"",m,_,i,h;function g(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(v,w){d(v,a,w),o(a,m),o(a,_),i||(h=Ye(a,"click",g),i=!0)},p(v,w){l=v,w&4&&n!==(n=l[5].code+"")&&de(m,n),w&6&&G(a,"active",l[1]===l[5].code)},d(v){v&&u(a),i=!1,h()}}}function Qe(r,l){let a,n,m,_;return n=new Ue({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),K(n.$$.fragment),m=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(i,h){d(i,a,h),I(n,a,null),o(a,m),_=!0},p(i,h){l=i;const g={};h&4&&(g.content=l[5].body),n.$set(g),(!_||h&6)&&G(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),W(n)}}}function tt(r){var De,Fe;let l,a,n=r[0].name+"",m,_,i,h,g,v,w,B,X,S,z,ue,Q,M,pe,Y,U=r[0].name+"",Z,he,fe,j,ee,D,te,T,oe,be,F,C,le,me,ae,_e,f,ke,R,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Pe,A,ie,O,ce,P,H,y=[],Re=new Map,Ae,E,$=[],qe=new Map,q;v=new Ze({props:{js:`
22
import PocketBase from 'pocketbase';
33
44
const pb = new PocketBase('${r[3]}');
@@ -26,12 +26,12 @@ import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,v as k,b as p,c as K,f as b
2626
print(pb.authStore.model.id);
2727
`}}),R=new Ue({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({props:{prefix:"record."}});let x=J(r[2]);const Be=e=>e[5].code;for(let e=0;e<x.length;e+=1){let t=Ve(r,x,e),c=Be(t);Re.set(c,y[e]=ze(c,t))}let L=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=Ne(r,L,e),c=Me(t);qe.set(c,$[e]=Qe(c,t))}return{c(){l=s("h3"),a=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),h=s("div"),h.innerHTML=`<p>Returns a new auth response (token and record data) for an
2828
<strong>already authenticated record</strong>.</p> <p><em>This method is usually called by users on page/screen reload to ensure that the previously stored
29-
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,g=p(),K(v.$$.fragment),w=p(),B=s("h6"),B.textContent="API details",X=p(),S=s("div"),z=s("strong"),z.textContent="POST",ue=p(),Q=s("div"),M=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(U),he=k("/auth-refresh"),fe=p(),j=s("p"),j.innerHTML="Requires record <code>Authorization:TOKEN</code> header",ee=p(),D=s("div"),D.textContent="Query parameters",te=p(),T=s("table"),oe=s("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',be=p(),F=s("tbody"),C=s("tr"),le=s("td"),le.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
30-
`),K(R.$$.fragment),ge=k(`
31-
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`
32-
The expanded relations will be appended to the record under the
33-
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
34-
`),we=s("br"),Te=k(`
29+
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,g=p(),K(v.$$.fragment),w=p(),B=s("h6"),B.textContent="API details",X=p(),S=s("div"),z=s("strong"),z.textContent="POST",ue=p(),Q=s("div"),M=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(U),he=k("/auth-refresh"),fe=p(),j=s("p"),j.innerHTML="Requires record <code>Authorization:TOKEN</code> header",ee=p(),D=s("div"),D.textContent="Query parameters",te=p(),T=s("table"),oe=s("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',be=p(),F=s("tbody"),C=s("tr"),le=s("td"),le.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:\r
30+
`),K(R.$$.fragment),ge=k(`\r
31+
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`\r
32+
The expanded relations will be appended to the record under the\r
33+
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).\r
34+
`),we=s("br"),Te=k(`\r
3535
Only the relations to which the request user has permissions to `),re=s("strong"),re.textContent="view",Ce=k(" will be expanded."),Pe=p(),K(A.$$.fragment),ie=p(),O=s("div"),O.textContent="Responses",ce=p(),P=s("div"),H=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Ae=p(),E=s("div");for(let e=0;e<$.length;e+=1)$[e].c();b(l,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(z,"class","label label-primary"),b(Q,"class","content"),b(j,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(H,"class","tabs-header compact combined left"),b(E,"class","tabs-content"),b(P,"class","tabs")},m(e,t){d(e,l,t),o(l,a),o(l,m),o(l,_),d(e,i,t),d(e,h,t),d(e,g,t),I(v,e,t),d(e,w,t),d(e,B,t),d(e,X,t),d(e,S,t),o(S,z),o(S,ue),o(S,Q),o(Q,M),o(M,pe),o(M,Y),o(Y,Z),o(M,he),o(S,fe),o(S,j),d(e,ee,t),d(e,D,t),d(e,te,t),d(e,T,t),o(T,oe),o(T,be),o(T,F),o(F,C),o(C,le),o(C,me),o(C,ae),o(C,_e),o(C,f),o(f,ke),I(R,f,null),o(f,ge),o(f,ve),o(f,$e),o(f,se),o(f,ye),o(f,ne),o(f,Se),o(f,we),o(f,Te),o(f,re),o(f,Ce),o(F,Pe),I(A,F,null),d(e,ie,t),d(e,O,t),d(e,ce,t),d(e,P,t),o(P,H);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(H,null);o(P,Ae),o(P,E);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(E,null);q=!0},p(e,[t]){var Oe,He;(!q||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const c={};t&9&&(c.js=`
3636
import PocketBase from 'pocketbase';
3737

0 commit comments

Comments
 (0)