Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/r3-dev/pocketbase
Browse files Browse the repository at this point in the history
  • Loading branch information
YSX7 committed Apr 16, 2024
2 parents 38a4d74 + 205a14f commit 5a5e7bf
Show file tree
Hide file tree
Showing 41 changed files with 389 additions and 224 deletions.
3 changes: 3 additions & 0 deletions apis/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func TestSettingsList(t *testing.T) {
`"patreonAuth":{`,
`"mailcowAuth":{`,
`"bitbucketAuth":{`,
`"donationalertsAuth":{`,
`"planningcenterAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
Expand Down Expand Up @@ -172,6 +173,7 @@ func TestSettingsSet(t *testing.T) {
`"patreonAuth":{`,
`"mailcowAuth":{`,
`"bitbucketAuth":{`,
`"donationalertsAuth":{`,
`"planningcenterAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
Expand Down Expand Up @@ -246,6 +248,7 @@ func TestSettingsSet(t *testing.T) {
`"patreonAuth":{`,
`"mailcowAuth":{`,
`"bitbucketAuth":{`,
`"donationalertsAuth":{`,
`"planningcenterAuth":{`,
`"secret":"******"`,
`"clientSecret":"******"`,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/r3dev/pocketbase/pocketbase
module github.com/r3-dev/pocketbase

go 1.21

Expand Down
7 changes: 7 additions & 0 deletions models/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type Settings struct {
MailcowAuth AuthProviderConfig `form:"mailcowAuth" json:"mailcowAuth"`
BitbucketAuth AuthProviderConfig `form:"bitbucketAuth" json:"bitbucketAuth"`
PlanningcenterAuth AuthProviderConfig `form:"planningcenterAuth" json:"planningcenterAuth"`
DonationAlertsAuth AuthProviderConfig `form:"donationalertsAuth" json:"donationalertsAuth"`
}

// New creates and returns a new default Settings instance.
Expand Down Expand Up @@ -204,6 +205,9 @@ func New() *Settings {
PlanningcenterAuth: AuthProviderConfig{
Enabled: false,
},
DonationAlertsAuth: AuthProviderConfig{
Enabled: false,
},
}
}

Expand Down Expand Up @@ -251,6 +255,7 @@ func (s *Settings) Validate() error {
validation.Field(&s.MailcowAuth),
validation.Field(&s.BitbucketAuth),
validation.Field(&s.PlanningcenterAuth),
validation.Field(&s.DonationAlertsAuth),
)
}

Expand Down Expand Up @@ -321,6 +326,7 @@ func (s *Settings) RedactClone() (*Settings, error) {
&clone.MailcowAuth.ClientSecret,
&clone.BitbucketAuth.ClientSecret,
&clone.PlanningcenterAuth.ClientSecret,
&clone.DonationAlertsAuth.ClientSecret,
}

// mask all sensitive fields
Expand Down Expand Up @@ -365,6 +371,7 @@ func (s *Settings) NamedAuthProviderConfigs() map[string]AuthProviderConfig {
auth.NameMailcow: s.MailcowAuth,
auth.NameBitbucket: s.BitbucketAuth,
auth.NamePlanningcenter: s.PlanningcenterAuth,
auth.NameDonationAlerts: s.DonationAlertsAuth,
}
}

Expand Down
8 changes: 8 additions & 0 deletions models/settings/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func TestSettingsValidate(t *testing.T) {
s.BitbucketAuth.ClientId = ""
s.PlanningcenterAuth.Enabled = true
s.PlanningcenterAuth.ClientId = ""
s.DonationAlertsAuth.Enabled = true
s.DonationAlertsAuth.ClientId = ""

// check if Validate() is triggering the members validate methods.
err := s.Validate()
Expand Down Expand Up @@ -127,6 +129,7 @@ func TestSettingsValidate(t *testing.T) {
`"mailcowAuth":{`,
`"bitbucketAuth":{`,
`"planningcenterAuth":{`,
`"donationAlertsAuth":{`,
}

errBytes, _ := json.Marshal(err)
Expand Down Expand Up @@ -208,6 +211,8 @@ func TestSettingsMerge(t *testing.T) {
s2.BitbucketAuth.ClientId = "bitbucket_test"
s2.PlanningcenterAuth.Enabled = true
s2.PlanningcenterAuth.ClientId = "planningcenter_test"
s2.DonationAlertsAuth.Enabled = true
s2.DonationAlertsAuth.ClientId = "donationalerts_test"

if err := s1.Merge(s2); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -302,6 +307,7 @@ func TestSettingsRedactClone(t *testing.T) {
s1.MailcowAuth.ClientSecret = testSecret
s1.BitbucketAuth.ClientSecret = testSecret
s1.PlanningcenterAuth.ClientSecret = testSecret
s1.DonationAlertsAuth.ClientSecret = testSecret

s1Bytes, err := json.Marshal(s1)
if err != nil {
Expand Down Expand Up @@ -364,6 +370,7 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
s.MailcowAuth.ClientId = "mailcow_test"
s.BitbucketAuth.ClientId = "bitbucket_test"
s.PlanningcenterAuth.ClientId = "planningcenter_test"
s.DonationAlertsAuth.ClientId = "donationalerts_test"

result := s.NamedAuthProviderConfigs()

Expand Down Expand Up @@ -399,6 +406,7 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
`"mailcow":{"enabled":false,"clientId":"mailcow_test"`,
`"bitbucket":{"enabled":false,"clientId":"bitbucket_test"`,
`"planningcenter":{"enabled":false,"clientId":"planningcenter_test"`,
`"donationalerts":{"enabled":false,"clientId":"donationalerts_test"`,
}
for _, p := range expectedParts {
if !strings.Contains(encodedStr, p) {
Expand Down
2 changes: 2 additions & 0 deletions tools/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ func NewProviderByName(name string) (Provider, error) {
return NewBitbucketProvider(), nil
case NamePlanningcenter:
return NewPlanningcenterProvider(), nil
case NameDonationAlerts:
return NewDonationAlertsProvider(), nil
default:
return nil, errors.New("Missing provider " + name)
}
Expand Down
9 changes: 9 additions & 0 deletions tools/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,13 @@ func TestNewProviderByName(t *testing.T) {
if _, ok := p.(*auth.Planningcenter); !ok {
t.Error("Expected to be instance of *auth.Planningcenter")
}

// donationalerts
p, err = auth.NewProviderByName(auth.NameDonationAlerts)
if err != nil {
t.Errorf("Expected nil, got error %v", err)
}
if _, ok := p.(*auth.DonationAlerts); !ok {
t.Error("Expected to be instance of *auth.DonationAlerts")
}
}
77 changes: 77 additions & 0 deletions tools/auth/donationalerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package auth

import (
"context"
"encoding/json"
"strconv"

"github.com/pocketbase/pocketbase/tools/types"
"golang.org/x/oauth2"
)

var _ Provider = (*DonationAlerts)(nil)

// NameDonationAlerts is the unique name of the Donation Alerts provider.
const NameDonationAlerts string = "donationalerts"

// Donation Alerts allows authentication via Discord OAuth2.
type DonationAlerts struct {
*baseProvider
}

// NewDiscordProvider creates a new Discord provider instance with some defaults.
func NewDonationAlertsProvider() *DonationAlerts {
// https://www.donationalerts.com/apidoc#authorization
return &DonationAlerts{&baseProvider{
ctx: context.Background(),
displayName: "Donation Alerts",
pkce: true,
scopes: []string{"oauth-user-show"},
authUrl: "https://www.donationalerts.com/oauth/authorize",
tokenUrl: "https://www.donationalerts.com/oauth/token",
userApiUrl: "https://www.donationalerts.com/api/v1/user/oauth",
}}
}

// FetchAuthUser returns an AuthUser instance from Discord's user api.
//
// API reference: https://discord.com/developers/docs/resources/user#user-object
func (p *DonationAlerts) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
data, err := p.FetchRawUserData(token)
if err != nil {
return nil, err
}

rawUser := map[string]any{}
if err := json.Unmarshal(data, &rawUser); err != nil {
return nil, err
}

extracted := struct {
Data struct {
Id int `json:"id"`
UniqueName string `json:"code"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Email string `json:"email"`
} `json:"data"`
}{}
if err := json.Unmarshal(data, &extracted); err != nil {
return nil, err
}

user := &AuthUser{
Id: strconv.Itoa(extracted.Data.Id),
Name: extracted.Data.UniqueName,
Username: extracted.Data.Name,
Email: extracted.Data.Email,
AvatarUrl: extracted.Data.Avatar,
RawUser: rawUser,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
}

user.Expiry, _ = types.ParseDateTime(token.Expiry)

return user, nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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:`
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:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${r[3]}');
Expand Down Expand Up @@ -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
print(pb.authStore.model.id);
`}}),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
<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
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.:
`),K(R.$$.fragment),ge=k(`
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`
The expanded relations will be appended to the record under the
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
`),we=s("br"),Te=k(`
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
`),K(R.$$.fragment),ge=k(`\r
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`\r
The expanded relations will be appended to the record under the\r
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).\r
`),we=s("br"),Te=k(`\r
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=`
import PocketBase from 'pocketbase';
Expand Down
Loading

0 comments on commit 5a5e7bf

Please sign in to comment.