├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── casdoor ├── adapter.go ├── user.go └── user_adapter.go ├── cloud ├── balance.go ├── balance_test.go ├── client.go ├── cms.go ├── conf.go ├── ecs.go ├── ecs_test.go └── slb.go ├── conf └── app.conf ├── controllers ├── account.go ├── base.go ├── github.go ├── ip.go ├── issue.go ├── machine.go ├── program.go ├── report.go ├── round.go ├── student.go ├── token_jwt_key.pem ├── util.go └── webhook.go ├── go.mod ├── go.sum ├── ip ├── 17monipdb.dat ├── ip.go └── ip17mon.go ├── main.go ├── object ├── adapter.go ├── collaborator_test.go ├── crawl.go ├── crawl_conf.go ├── crawl_test.go ├── event.go ├── issue.go ├── machine.go ├── machine_cloud.go ├── machine_sync.go ├── machine_test.go ├── org_repositories.go ├── org_webhook.go ├── payLoad.go ├── program.go ├── report.go ├── round.go ├── round_test.go ├── student.go ├── student_test.go └── util.go ├── proxy └── proxy.go ├── routers ├── filter.go └── router.go ├── ssh └── ssh.go ├── util ├── encoding.go ├── githubAPI.go ├── json.go ├── log.go ├── path.go ├── string.go ├── time.go └── webhookAPI.go └── web ├── .gitignore ├── craco.config.js ├── package.json ├── public ├── favicon.png ├── index.html └── manifest.json ├── src ├── App.js ├── App.less ├── App.test.js ├── AuthCallback.js ├── Conf.js ├── CustomGithubCorner.js ├── IssueEditPage.js ├── IssueListPage.js ├── MachineEditPage.js ├── MachineListPage.js ├── ProgramEditPage.js ├── ProgramListPage.js ├── RankingPage.js ├── ReportEditPage.js ├── ReportListPage.js ├── RoundEditPage.js ├── RoundListPage.js ├── ServiceTable.js ├── Setting.js ├── SigninPage.js ├── StudentEditPage.js ├── StudentListPage.js ├── backend │ ├── AccountBackend.js │ ├── MachineBackend.js │ ├── ProgramBackend.js │ ├── ReportBackend.js │ ├── RoundBackend.js │ ├── StudentBackend.js │ └── issueBackend.js ├── index.css ├── index.js ├── serviceWorker.js └── setupTests.js └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.go linguist-detectable=true 2 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .idea/ 18 | *.iml 19 | 20 | tmp/ 21 | tmpFiles/ 22 | *.tmp 23 | logs/ 24 | lastupdate.tmp 25 | commentsRouter*.go -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Casbin-OA 2 | 3 | Casbin-OA is An official manuscript processing, evaluation and display system for Casbin technical writers 4 | 5 | ## Online demo 6 | 7 | Deployed site: https://oa.casbin.com/ 8 | 9 | ## Architecture 10 | 11 | Casbin-oa contains 2 parts: 12 | 13 | Name | Description | Language | Source code 14 | ----|------|----|---- 15 | Frontend | Web frontend UI for Casbin-oa | Javascript + React | https://github.com/casbin/casbin-oa/tree/master/web 16 | Backend | RESTful API backend for Casbin-oa | Golang + Beego + MySQL | https://github.com/casbin/casbin-oa/ 17 | 18 | 19 | ## Installation 20 | 21 | Casbin-OA uses Casdoor to manage members. So you need to create an organization and an application for Casnode in a 22 | Casdoor instance. 23 | 24 | ### Necessary configuration 25 | 26 | - ##### Get the code: 27 | 28 | ```shell 29 | go get github.com/casdoor/casdoor 30 | go get github.com/casbin/casbin-oa 31 | ``` 32 | or 33 | ```shell 34 | git clone https://github.com/casdoor/casdoor 35 | git clone https://github.com/casbin/casbin-oa.git 36 | ``` 37 | 38 | - Setup database: 39 | 40 | Casbin-oa will store its student, report and topics informations in a MySQL database named: `casbin_oa`, will create 41 | it if not existed. The DB connection string can be specified at: https://github.com/casbin/casbin-oa/tree/master/conf 42 | 43 | ```ini 44 | dataSourceName = root:123@tcp(localhost:3306)/ 45 | ``` 46 | 47 | Casbin-oa uses XORM to connect to DB, so all DBs supported by XORM can also be used. 48 | 49 | - Run backend (in port 10000): 50 | 51 | ```shell 52 | go run main.go 53 | ``` 54 | 55 | - Run frontend (in the same machine's port 9000): 56 | 57 | ```shell 58 | cd web 59 | ## npm 60 | npm install 61 | npm run start 62 | ## yarn 63 | yarn install 64 | yarn run start 65 | ``` 66 | 67 | - Open browser: 68 | 69 | http://localhost:9000/ 70 | 71 | ### Optional configuration 72 | 73 | #### Setup your forum to enable some third-party login platform 74 | 75 | Casbin-OA uses Casdoor to manage members. If you want to log in with oauth, you should 76 | see [casdoor oauth configuration](https://casdoor.org/docs/provider/oauth/overview/). 77 | 78 | #### OSS, Mail, and SMS services 79 | 80 | Casbin-OA uses Casdoor to upload files to cloud storage, send Emails and send SMSs. See Casdoor for more details. 81 | 82 | #### Github corner 83 | 84 | We added a Github icon in the upper right corner, linking to your Github repository address. You could 85 | set `ShowGithubCorner` to hidden it. 86 | 87 | Configuration: 88 | 89 | ``` 90 | export const ShowGithubCorner = true 91 | 92 | export const GithubRepo = "https://github.com/casbin/casbin-oa" //your github repository 93 | ``` 94 | 95 | -------------------------------------------------------------------------------- /casdoor/adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package casdoor 16 | 17 | import ( 18 | "runtime" 19 | 20 | "github.com/astaxie/beego" 21 | _ "github.com/go-sql-driver/mysql" 22 | "xorm.io/xorm" 23 | ) 24 | 25 | var ( 26 | adapter *Adapter = nil 27 | CasdoorOrganization string 28 | CasdoorApplication string 29 | ) 30 | 31 | type Session struct { 32 | SessionKey string `xorm:"char(64) notnull pk"` 33 | SessionData []uint8 `xorm:"blob"` 34 | SessionExpiry int `xorm:"notnull"` 35 | } 36 | 37 | func InitCasdoorAdapter() { 38 | casdoorDbName := beego.AppConfig.String("casdoorDbName") 39 | if casdoorDbName == "" { 40 | return 41 | } 42 | 43 | adapter = NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"), beego.AppConfig.String("casdoorDbName")) 44 | 45 | CasdoorOrganization = beego.AppConfig.String("casdoorOrganization") 46 | CasdoorApplication = beego.AppConfig.String("casdoorApplication") 47 | } 48 | 49 | // Adapter represents the MySQL adapter for policy storage. 50 | type Adapter struct { 51 | driverName string 52 | dataSourceName string 53 | dbName string 54 | Engine *xorm.Engine 55 | } 56 | 57 | // finalizer is the destructor for Adapter. 58 | func finalizer(a *Adapter) { 59 | err := a.Engine.Close() 60 | if err != nil { 61 | panic(err) 62 | } 63 | } 64 | 65 | // NewAdapter is the constructor for Adapter. 66 | func NewAdapter(driverName string, dataSourceName string, dbName string) *Adapter { 67 | a := &Adapter{} 68 | a.driverName = driverName 69 | a.dataSourceName = dataSourceName 70 | a.dbName = dbName 71 | 72 | // Open the DB, create it if not existed. 73 | a.open() 74 | 75 | // Call the destructor when the object is released. 76 | runtime.SetFinalizer(a, finalizer) 77 | 78 | return a 79 | } 80 | 81 | func (a *Adapter) open() { 82 | Engine, err := xorm.NewEngine(a.driverName, a.dataSourceName+a.dbName) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | a.Engine = Engine 88 | } 89 | 90 | func (a *Adapter) close() { 91 | a.Engine.Close() 92 | a.Engine = nil 93 | } 94 | -------------------------------------------------------------------------------- /casdoor/user.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package casdoor 16 | 17 | import "github.com/casdoor/casdoor-go-sdk/casdoorsdk" 18 | 19 | func GetUsers() []*casdoorsdk.User { 20 | if adapter != nil { 21 | return getUsers() 22 | } else { 23 | users, err := casdoorsdk.GetUsers() 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | return users 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /casdoor/user_adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package casdoor 16 | 17 | import "github.com/casdoor/casdoor-go-sdk/casdoorsdk" 18 | 19 | func getUsers() []*casdoorsdk.User { 20 | owner := CasdoorOrganization 21 | application := CasdoorApplication 22 | 23 | if adapter == nil { 24 | panic("casdoor adapter is nil") 25 | } 26 | 27 | users := []*casdoorsdk.User{} 28 | err := adapter.Engine.Desc("created_time").Find(&users, &casdoorsdk.User{Owner: owner, SignupApplication: application}) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | return users 34 | } 35 | -------------------------------------------------------------------------------- /cloud/balance.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "fmt" 19 | "math" 20 | "time" 21 | 22 | "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" 23 | ) 24 | 25 | func getTargetInstanceCount(slbRate int) int { 26 | res := int(math.Floor(float64(slbRate-1)/60.0)) + 1 27 | 28 | res = res - 3 29 | if res < 0 { 30 | res = 0 31 | } 32 | 33 | //fmt.Printf("target instance count = %d\n", res) 34 | return res 35 | } 36 | 37 | func getNewInstanceName(instanceCount int) string { 38 | return fmt.Sprintf("auto-%03d", instanceCount+1) 39 | } 40 | 41 | func getInstanceIdAndCreationTimeFromInstances(instances []ecs.Instance, instanceName string) (string, string) { 42 | for _, instance := range instances { 43 | if instance.InstanceName == instanceName { 44 | return instance.InstanceId, instance.CreationTime 45 | } 46 | } 47 | return "", "" 48 | } 49 | 50 | func getPassedMinutes(creationTime string) int { 51 | t, _ := time.Parse("2006-01-02T15:04Z", creationTime) 52 | duration := time.Since(t) 53 | return int(duration.Minutes()) 54 | } 55 | 56 | func doBalance() { 57 | instances := GetInstances() 58 | instanceCount := len(instances) 59 | 60 | slbRate := GetSlbPacketRate() 61 | //slbRate = 60 62 | if slbRate == -1 { 63 | return 64 | } 65 | 66 | targetInstanceCount := getTargetInstanceCount(slbRate) + 5 67 | if instanceCount == targetInstanceCount { 68 | fmt.Printf("instance_count: [%d] == target_instance_count: [%d], no change\n", instanceCount, targetInstanceCount) 69 | } else if instanceCount < targetInstanceCount { 70 | fmt.Printf("instance_count: [%d] < target_instance_count: [%d], will add instance..\n", instanceCount, targetInstanceCount) 71 | 72 | newInstanceName := getNewInstanceName(instanceCount) 73 | AddInstance(newInstanceName) 74 | } else { 75 | lastInstanceName := getNewInstanceName(instanceCount - 1) 76 | lastInstanceId, lastInstanceCreationTime := getInstanceIdAndCreationTimeFromInstances(instances, lastInstanceName) 77 | 78 | passedMinutes := getPassedMinutes(lastInstanceCreationTime) 79 | if passedMinutes < coolDownMinutes { 80 | fmt.Printf("instance_count: [%d] > target_instance_count: [%d], will keep instance because cooldown minutes: [%d] < [%d]\n", instanceCount, targetInstanceCount, passedMinutes, coolDownMinutes) 81 | } else { 82 | fmt.Printf("instance_count: [%d] > target_instance_count: [%d], will delete instance..\n", instanceCount, targetInstanceCount) 83 | 84 | DeleteInstance(lastInstanceId, lastInstanceName) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /cloud/balance_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | ) 21 | 22 | func TestDoBalance(t *testing.T) { 23 | for { 24 | doBalance() 25 | time.Sleep(30 * time.Second) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cloud/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "github.com/aliyun/alibaba-cloud-sdk-go/services/cms" 19 | "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" 20 | "github.com/aliyun/alibaba-cloud-sdk-go/services/slb" 21 | ) 22 | 23 | var ecsClient *ecs.Client 24 | var slbClient *slb.Client 25 | var cmsClient *cms.Client 26 | 27 | func init() { 28 | ecsClient = initEcsClient() 29 | slbClient = initSlbClient() 30 | cmsClient = initCmsClient() 31 | } 32 | 33 | func initEcsClient() *ecs.Client { 34 | client, err := ecs.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | return client 40 | } 41 | 42 | func initSlbClient() *slb.Client { 43 | client, err := slb.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | return client 49 | } 50 | 51 | func initCmsClient() *cms.Client { 52 | client, err := cms.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret) 53 | if err != nil { 54 | panic(err) 55 | } 56 | 57 | return client 58 | } 59 | -------------------------------------------------------------------------------- /cloud/cms.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/aliyun/alibaba-cloud-sdk-go/services/cms" 21 | "github.com/casbin/casbin-oa/util" 22 | ) 23 | 24 | type Dimension struct { 25 | InstanceId string `json:"instanceId"` 26 | } 27 | 28 | type Datapoint struct { 29 | Timestamp int `json:"timestamp"` 30 | UserId string `json:"userId"` 31 | InstanceId string `json:"instanceId"` 32 | Vip string `json:"vip"` 33 | Maximum float64 `json:"Maximum"` 34 | Minimum float64 `json:"Minimum"` 35 | Average float64 `json:"Average"` 36 | Sum float64 `json:"Sum"` 37 | } 38 | 39 | func GetSlbPacketRate() int { 40 | r := cms.CreateDescribeMetricLastRequest() 41 | r.MetricName = "InstancePacketRX" 42 | r.Period = "60" 43 | r.Dimensions = util.StructToJson([]Dimension{{InstanceId: slbId}}) 44 | r.Namespace = "acs_slb_dashboard" 45 | 46 | resp, err := cmsClient.DescribeMetricLast(r) 47 | if err != nil { 48 | println(err) 49 | return -1 50 | } 51 | 52 | var datapoints []Datapoint 53 | err = util.JsonToStruct(resp.Datapoints, &datapoints) 54 | if err != nil { 55 | println(err) 56 | return -1 57 | } 58 | 59 | if len(datapoints) <= 0 { 60 | return -1 61 | } 62 | 63 | res := int(datapoints[0].Average) 64 | fmt.Printf("[%s] SLB packet rate = %d\n", util.GetCurrentTimeFormatted(), res) 65 | 66 | return res 67 | } 68 | -------------------------------------------------------------------------------- /cloud/conf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | var regionId = "" 18 | var accessKeyId = "" 19 | var accessKeySecret = "" 20 | 21 | var vsgId = "" 22 | var slbId = "" 23 | var coolDownMinutes = 10 24 | -------------------------------------------------------------------------------- /cloud/ecs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | "time" 21 | 22 | "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 23 | "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" 24 | ) 25 | 26 | func GetInstances() []ecs.Instance { 27 | r := ecs.CreateDescribeInstancesRequest() 28 | r.InstanceChargeType = "PostPaid" 29 | r.PageSize = requests.NewInteger(100) 30 | r.PageNumber = requests.NewInteger(1) 31 | 32 | var resp *ecs.DescribeInstancesResponse 33 | var err error 34 | for i := 0; i < 100; i++ { 35 | resp, err = ecsClient.DescribeInstances(r) 36 | if err != nil { 37 | continue 38 | } 39 | break 40 | } 41 | 42 | instances := resp.Instances.Instance 43 | res := []ecs.Instance{} 44 | for _, instance := range instances { 45 | if strings.HasPrefix(instance.InstanceName, "auto") { 46 | res = append(res, instance) 47 | } 48 | } 49 | 50 | return res 51 | } 52 | 53 | func AddInstance(instanceName string) { 54 | r := ecs.CreateRunInstancesRequest() 55 | r.LaunchTemplateName = "auto" 56 | 57 | resp, err := ecsClient.RunInstances(r) 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | instanceId := resp.InstanceIdSets.InstanceIdSet[0] 63 | 64 | renameInstance(instanceId, instanceName) 65 | AddServerToSlb(instanceId, 9095) 66 | 67 | fmt.Printf("1 instance added, name = %s\n", instanceName) 68 | } 69 | 70 | func renameInstanceOnce(r *ecs.ModifyInstanceAttributeRequest) { 71 | time.Sleep(3000 * time.Millisecond) 72 | for i := 0; i < 100; i++ { 73 | _, err := ecsClient.ModifyInstanceAttribute(r) 74 | if err != nil { 75 | if i == 99 { 76 | panic(err) 77 | } 78 | 79 | fmt.Printf("renameInstance() error: %s\n", err.Error()) 80 | time.Sleep(2000 * time.Millisecond) 81 | continue 82 | } 83 | break 84 | } 85 | } 86 | 87 | func renameInstance(instanceId string, instanceName string) { 88 | r := ecs.CreateModifyInstanceAttributeRequest() 89 | r.InstanceId = instanceId 90 | r.InstanceName = instanceName 91 | 92 | renameInstanceOnce(r) 93 | renameInstanceOnce(r) 94 | renameInstanceOnce(r) 95 | 96 | fmt.Printf("instance: %s renamed to: %s\n", instanceId, instanceName) 97 | } 98 | 99 | func DeleteInstance(instanceId string, instanceName string) { 100 | r := ecs.CreateDeleteInstancesRequest() 101 | r.InstanceId = &[]string{instanceId} 102 | r.Force = requests.NewBoolean(true) 103 | 104 | _, err := ecsClient.DeleteInstances(r) 105 | if err != nil { 106 | panic(err) 107 | } 108 | 109 | fmt.Printf("1 instance deleted, name = %s\n", instanceName) 110 | } 111 | -------------------------------------------------------------------------------- /cloud/ecs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | ) 21 | 22 | func TestGetInstances(t *testing.T) { 23 | instances := GetInstances() 24 | fmt.Printf("%v\n", instances) 25 | } 26 | -------------------------------------------------------------------------------- /cloud/slb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud 16 | 17 | import ( 18 | "github.com/aliyun/alibaba-cloud-sdk-go/services/slb" 19 | "github.com/casbin/casbin-oa/util" 20 | ) 21 | 22 | type Server struct { 23 | ServerId string `json:"ServerId"` 24 | Port int `json:"Port"` 25 | Weight int `json:"Weight"` 26 | Type string `json:"Type"` 27 | } 28 | 29 | func GetVsgServerIdMap() map[string]int { 30 | r := slb.CreateDescribeVServerGroupAttributeRequest() 31 | r.VServerGroupId = vsgId 32 | 33 | resp, err := slbClient.DescribeVServerGroupAttribute(r) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | servers := resp.BackendServers.BackendServer 39 | res := map[string]int{} 40 | for _, server := range servers { 41 | id := server.ServerId 42 | res[id] = 1 43 | } 44 | return res 45 | } 46 | 47 | func AddServerToSlb(serverId string, port int) { 48 | r := slb.CreateAddVServerGroupBackendServersRequest() 49 | r.VServerGroupId = vsgId 50 | r.BackendServers = util.StructToJson([]Server{{ 51 | ServerId: serverId, 52 | Port: port, 53 | Weight: 100, 54 | Type: "ecs", 55 | }}) 56 | 57 | for i := 0; i < 100; i++ { 58 | _, err := slbClient.AddVServerGroupBackendServers(r) 59 | if err != nil { 60 | continue 61 | } 62 | break 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /conf/app.conf: -------------------------------------------------------------------------------- 1 | appname = casbin-oa 2 | httpport = 10000 3 | runmode = dev 4 | SessionOn = true 5 | copyrequestbody = true 6 | driverName = mysql 7 | dataSourceName = root:123@tcp(localhost:3306)/ 8 | dbName = casbin_oa 9 | redisEndpoint = 10 | httpProxy = "127.0.0.1:10808" 11 | casdoorEndpoint = http://localhost:8000 12 | clientId = 0ba528121ea87b3eb54d 13 | clientSecret = xxx 14 | casdoorDbName = casdoor 15 | casdoorOrganization = "casbin" 16 | casdoorApplication = "app-casbin-oa" 17 | defaultOrg = casbin, casdoor ,casnode, casbin-lua, casbin-net, casbin-rs, casbin-ex, casbin-ruby, casbin-cpp, casbin-js, casbin4d, node-casbin, scala-casbin, dart-casbin, pycasbin, jcasbin, php-casbin, SwiftCasbin, k8s-authz, casbin-mesh 18 | githubAccessToken = "" 19 | defaultOwner = "admin" 20 | defaultProgram = "talent2022" -------------------------------------------------------------------------------- /controllers/account.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | _ "embed" 19 | 20 | "github.com/astaxie/beego" 21 | "github.com/casbin/casbin-oa/casdoor" 22 | "github.com/casdoor/casdoor-go-sdk/casdoorsdk" 23 | ) 24 | 25 | var CasdoorEndpoint = beego.AppConfig.String("casdoorEndpoint") 26 | var ClientId = beego.AppConfig.String("clientId") 27 | var ClientSecret = beego.AppConfig.String("clientSecret") 28 | var CasdoorOrganization = beego.AppConfig.String("casdoorOrganization") 29 | var CasdoorApplication = beego.AppConfig.String("casdoorApplication") 30 | 31 | //go:embed token_jwt_key.pem 32 | var JwtPublicKey string 33 | 34 | func init() { 35 | casdoorsdk.InitConfig(CasdoorEndpoint, ClientId, ClientSecret, JwtPublicKey, CasdoorOrganization, CasdoorApplication) 36 | } 37 | 38 | func (c *ApiController) Signin() { 39 | code := c.Input().Get("code") 40 | state := c.Input().Get("state") 41 | 42 | token, err := casdoorsdk.GetOAuthToken(code, state) 43 | if err != nil { 44 | c.ResponseError(err.Error()) 45 | return 46 | } 47 | 48 | claims, err := casdoorsdk.ParseJwtToken(token.AccessToken) 49 | if err != nil { 50 | c.ResponseError(err.Error()) 51 | return 52 | } 53 | 54 | claims.AccessToken = token.AccessToken 55 | c.SetSessionClaims(claims) 56 | 57 | c.ResponseOk(claims) 58 | } 59 | 60 | func (c *ApiController) Signout() { 61 | c.SetSessionClaims(nil) 62 | 63 | c.ResponseOk() 64 | } 65 | 66 | func (c *ApiController) GetAccount() { 67 | if c.RequireSignedIn() { 68 | return 69 | } 70 | 71 | claims := c.GetSessionClaims() 72 | 73 | c.ResponseOk(claims) 74 | } 75 | 76 | func (c *ApiController) GetUsers() { 77 | users := casdoor.GetUsers() 78 | c.Data["json"] = users 79 | c.ServeJSON() 80 | } 81 | -------------------------------------------------------------------------------- /controllers/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/gob" 19 | 20 | "github.com/astaxie/beego" 21 | "github.com/casdoor/casdoor-go-sdk/casdoorsdk" 22 | ) 23 | 24 | type ApiController struct { 25 | beego.Controller 26 | } 27 | 28 | func init() { 29 | gob.Register(casdoorsdk.Claims{}) 30 | } 31 | 32 | func (c *ApiController) GetSessionClaims() *casdoorsdk.Claims { 33 | s := c.GetSession("user") 34 | if s == nil { 35 | return nil 36 | } 37 | 38 | claims := s.(casdoorsdk.Claims) 39 | return &claims 40 | } 41 | 42 | func (c *ApiController) SetSessionClaims(claims *casdoorsdk.Claims) { 43 | if claims == nil { 44 | c.DelSession("user") 45 | return 46 | } 47 | 48 | c.SetSession("user", *claims) 49 | } 50 | 51 | func (c *ApiController) GetSessionUser() *casdoorsdk.User { 52 | claims := c.GetSessionClaims() 53 | if claims == nil { 54 | return nil 55 | } 56 | 57 | return &claims.User 58 | } 59 | -------------------------------------------------------------------------------- /controllers/github.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "github.com/casbin/casbin-oa/object" 19 | "github.com/casbin/casbin-oa/util" 20 | ) 21 | 22 | var projectId int64 = 7961866 23 | 24 | func (c *ApiController) GetRepositoryByOrg() { 25 | org := c.Input().Get("org") 26 | 27 | c.Data["json"] = object.GetRepositoryByOrganization(org) 28 | c.ServeJSON() 29 | } 30 | 31 | func (c *ApiController) GetProjectColumns() { 32 | 33 | c.Data["json"] = util.GetProjectColumns(projectId) 34 | c.ServeJSON() 35 | } 36 | 37 | func (c *ApiController) GetGithubUserByUsername() { 38 | username := c.Input().Get("username") 39 | 40 | c.Data["json"] = util.GetUserByUsername(username) 41 | c.ServeJSON() 42 | } 43 | -------------------------------------------------------------------------------- /controllers/ip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "net/http" 19 | "strings" 20 | 21 | "github.com/casbin/casbin-oa/ip" 22 | ) 23 | 24 | func getIpFromRequest(req *http.Request) string { 25 | clientIp := req.Header.Get("x-forwarded-for") 26 | if clientIp == "" { 27 | ipPort := strings.Split(req.RemoteAddr, ":") 28 | if len(ipPort) >= 1 && len(ipPort) <= 2 { 29 | clientIp = ipPort[0] 30 | } else if len(ipPort) > 2 { 31 | idx := strings.LastIndex(req.RemoteAddr, ":") 32 | clientIp = req.RemoteAddr[0:idx] 33 | clientIp = strings.TrimLeft(clientIp, "[") 34 | clientIp = strings.TrimRight(clientIp, "]") 35 | } 36 | } 37 | 38 | return clientIp 39 | } 40 | 41 | func (c *ApiController) IsMainlandIp() { 42 | clientIp := getIpFromRequest(c.Ctx.Request) 43 | isMainlandIp := ip.IsMainlandIp(clientIp) 44 | 45 | c.Data["json"] = isMainlandIp 46 | c.ServeJSON() 47 | } 48 | -------------------------------------------------------------------------------- /controllers/issue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/casbin/casbin-oa/object" 21 | ) 22 | 23 | func (c *ApiController) GetIssue() { 24 | c.Data["json"] = object.GetIssues() 25 | c.ServeJSON() 26 | } 27 | 28 | func (c *ApiController) GetIssueByName() { 29 | name := c.Input().Get("name") 30 | 31 | c.Data["json"] = object.GetIssueByName(name) 32 | c.ServeJSON() 33 | } 34 | 35 | func (c *ApiController) UpdateIssue() { 36 | name := c.Input().Get("name") 37 | var issue object.Issue 38 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &issue) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | c.Data["json"] = object.UpdateIssue(name, &issue) 44 | c.ServeJSON() 45 | } 46 | 47 | func (c *ApiController) AddIssue() { 48 | var issue object.Issue 49 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &issue) 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | c.Data["json"] = object.AddIssue(&issue) 55 | c.ServeJSON() 56 | } 57 | 58 | func (c *ApiController) DeleteIssue() { 59 | var issue object.Issue 60 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &issue) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | c.Data["json"] = object.DeleteIssue(&issue) 66 | c.ServeJSON() 67 | } 68 | -------------------------------------------------------------------------------- /controllers/machine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/casbin/casbin-oa/object" 21 | ) 22 | 23 | func (c *ApiController) GetMachines() { 24 | owner := c.Input().Get("owner") 25 | 26 | c.Data["json"] = object.GetMachines(owner) 27 | c.ServeJSON() 28 | } 29 | 30 | func (c *ApiController) GetMachine() { 31 | id := c.Input().Get("id") 32 | 33 | c.Data["json"] = object.GetProcessIdSyncedMachine(id) 34 | c.ServeJSON() 35 | } 36 | 37 | func (c *ApiController) UpdateMachine() { 38 | id := c.Input().Get("id") 39 | 40 | var machine object.Machine 41 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &machine) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | affected := object.UpdateMachine(id, &machine) 47 | go machine.DoActions() 48 | 49 | c.Data["json"] = affected 50 | c.ServeJSON() 51 | } 52 | 53 | func (c *ApiController) AddMachine() { 54 | var machine object.Machine 55 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &machine) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | c.Data["json"] = object.AddMachine(&machine) 61 | c.ServeJSON() 62 | } 63 | 64 | func (c *ApiController) DeleteMachine() { 65 | var machine object.Machine 66 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &machine) 67 | if err != nil { 68 | panic(err) 69 | } 70 | 71 | c.Data["json"] = object.DeleteMachine(&machine) 72 | c.ServeJSON() 73 | } 74 | -------------------------------------------------------------------------------- /controllers/program.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/casbin/casbin-oa/object" 21 | ) 22 | 23 | func (c *ApiController) GetPrograms() { 24 | owner := c.Input().Get("owner") 25 | 26 | c.Data["json"] = object.GetPrograms(owner) 27 | c.ServeJSON() 28 | } 29 | 30 | func (c *ApiController) GetProgram() { 31 | id := c.Input().Get("id") 32 | 33 | c.Data["json"] = object.GetProgram(id) 34 | c.ServeJSON() 35 | } 36 | 37 | func (c *ApiController) UpdateProgram() { 38 | id := c.Input().Get("id") 39 | 40 | var program object.Program 41 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &program) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | c.Data["json"] = object.UpdateProgram(id, &program) 47 | c.ServeJSON() 48 | } 49 | 50 | func (c *ApiController) AddProgram() { 51 | var program object.Program 52 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &program) 53 | if err != nil { 54 | panic(err) 55 | } 56 | 57 | c.Data["json"] = object.AddProgram(&program) 58 | c.ServeJSON() 59 | } 60 | 61 | func (c *ApiController) DeleteProgram() { 62 | var program object.Program 63 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &program) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | c.Data["json"] = object.DeleteProgram(&program) 69 | c.ServeJSON() 70 | } 71 | -------------------------------------------------------------------------------- /controllers/report.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | "time" 20 | 21 | "github.com/casbin/casbin-oa/object" 22 | ) 23 | 24 | func (c *ApiController) GetReports() { 25 | owner := c.Input().Get("owner") 26 | 27 | c.Data["json"] = object.GetReports(owner) 28 | c.ServeJSON() 29 | } 30 | 31 | func (c *ApiController) GetFilteredReports() { 32 | owner := c.Input().Get("owner") 33 | program := c.Input().Get("program") 34 | 35 | c.Data["json"] = object.GetFilteredReports(owner, program) 36 | c.ServeJSON() 37 | } 38 | 39 | func (c *ApiController) GetReport() { 40 | id := c.Input().Get("id") 41 | 42 | c.Data["json"] = object.GetReport(id) 43 | c.ServeJSON() 44 | } 45 | 46 | func (c *ApiController) UpdateReport() { 47 | id := c.Input().Get("id") 48 | 49 | var report object.Report 50 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &report) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | c.Data["json"] = object.UpdateReport(id, &report) 56 | c.ServeJSON() 57 | } 58 | 59 | func (c *ApiController) AddReport() { 60 | var report object.Report 61 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &report) 62 | if err != nil { 63 | panic(err) 64 | } 65 | 66 | c.Data["json"] = object.AddReport(&report) 67 | c.ServeJSON() 68 | } 69 | 70 | func (c *ApiController) DeleteReport() { 71 | var report object.Report 72 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &report) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | c.Data["json"] = object.DeleteReport(&report) 78 | c.ServeJSON() 79 | } 80 | 81 | func (c *ApiController) AutoUpdateReport() { 82 | id := c.Input().Get("id") 83 | startDate := c.Input().Get("startDate") 84 | endDate := c.Input().Get("endDate") 85 | author := c.Input().Get("author") 86 | 87 | var student object.Student 88 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &student) 89 | if err != nil { 90 | panic(err) 91 | } 92 | 93 | layout := "2006-01-02" 94 | startDateT, _ := time.ParseInLocation(layout, startDate, time.UTC) 95 | endDateT, _ := time.ParseInLocation(layout, endDate, time.UTC) 96 | 97 | c.Data["json"] = object.AutoUpdateReportText(id, author, startDateT, endDateT, student) 98 | c.ServeJSON() 99 | } 100 | -------------------------------------------------------------------------------- /controllers/round.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/casbin/casbin-oa/object" 21 | ) 22 | 23 | func (c *ApiController) GetRounds() { 24 | owner := c.Input().Get("owner") 25 | 26 | c.Data["json"] = object.GetRounds(owner) 27 | c.ServeJSON() 28 | } 29 | 30 | func (c *ApiController) GetFilteredRounds() { 31 | owner := c.Input().Get("owner") 32 | program := c.Input().Get("program") 33 | 34 | c.Data["json"] = object.GetFilteredRounds(owner, program) 35 | c.ServeJSON() 36 | } 37 | 38 | func (c *ApiController) GetRound() { 39 | id := c.Input().Get("id") 40 | 41 | c.Data["json"] = object.GetRound(id) 42 | c.ServeJSON() 43 | } 44 | 45 | func (c *ApiController) UpdateRound() { 46 | id := c.Input().Get("id") 47 | 48 | var round object.Round 49 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &round) 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | c.Data["json"] = object.UpdateRound(id, &round) 55 | c.ServeJSON() 56 | } 57 | 58 | func (c *ApiController) AddRound() { 59 | var round object.Round 60 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &round) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | c.Data["json"] = object.AddRound(&round) 66 | c.ServeJSON() 67 | } 68 | 69 | func (c *ApiController) DeleteRound() { 70 | var round object.Round 71 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &round) 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | c.Data["json"] = object.DeleteRound(&round) 77 | c.ServeJSON() 78 | } 79 | -------------------------------------------------------------------------------- /controllers/student.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "github.com/casbin/casbin-oa/object" 21 | ) 22 | 23 | func (c *ApiController) GetStudents() { 24 | owner := c.Input().Get("owner") 25 | 26 | c.Data["json"] = object.GetStudents(owner) 27 | c.ServeJSON() 28 | } 29 | 30 | func (c *ApiController) GetFilteredStudents() { 31 | owner := c.Input().Get("owner") 32 | program := c.Input().Get("program") 33 | 34 | c.Data["json"] = object.GetFilteredStudents(owner, program) 35 | c.ServeJSON() 36 | } 37 | 38 | func (c *ApiController) GetStudent() { 39 | owner := c.Input().Get("owner") 40 | name := c.Input().Get("name") 41 | program := c.Input().Get("program") 42 | 43 | c.Data["json"] = object.GetStudent(owner, name, program) 44 | c.ServeJSON() 45 | } 46 | 47 | func (c *ApiController) UpdateStudent() { 48 | owner := c.Input().Get("owner") 49 | name := c.Input().Get("name") 50 | program := c.Input().Get("program") 51 | 52 | var student object.Student 53 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &student) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | c.Data["json"] = object.UpdateStudent(owner, name, program, &student) 59 | c.ServeJSON() 60 | } 61 | 62 | func (c *ApiController) AddStudent() { 63 | var student object.Student 64 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &student) 65 | if err != nil { 66 | panic(err) 67 | } 68 | 69 | c.Data["json"] = object.AddStudent(&student) 70 | c.ServeJSON() 71 | } 72 | 73 | func (c *ApiController) DeleteStudent() { 74 | var student object.Student 75 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &student) 76 | if err != nil { 77 | panic(err) 78 | } 79 | 80 | c.Data["json"] = object.DeleteStudent(&student) 81 | c.ServeJSON() 82 | } 83 | -------------------------------------------------------------------------------- /controllers/token_jwt_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE+TCCAuGgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMDYxHTAbBgNVBAoTFENh 3 | c2Rvb3IgT3JnYW5pemF0aW9uMRUwEwYDVQQDEwxDYXNkb29yIENlcnQwHhcNMjEx 4 | MDE1MDgxMTUyWhcNNDExMDE1MDgxMTUyWjA2MR0wGwYDVQQKExRDYXNkb29yIE9y 5 | Z2FuaXphdGlvbjEVMBMGA1UEAxMMQ2FzZG9vciBDZXJ0MIICIjANBgkqhkiG9w0B 6 | AQEFAAOCAg8AMIICCgKCAgEAsInpb5E1/ym0f1RfSDSSE8IR7y+lw+RJjI74e5ej 7 | rq4b8zMYk7HeHCyZr/hmNEwEVXnhXu1P0mBeQ5ypp/QGo8vgEmjAETNmzkI1NjOQ 8 | CjCYwUrasO/f/MnI1C0j13vx6mV1kHZjSrKsMhYY1vaxTEP3+VB8Hjg3MHFWrb07 9 | uvFMCJe5W8+0rKErZCKTR8+9VB3janeBz//zQePFVh79bFZate/hLirPK0Go9P1g 10 | OvwIoC1A3sarHTP4Qm/LQRt0rHqZFybdySpyWAQvhNaDFE7mTstRSBb/wUjNCUBD 11 | PTSLVjC04WllSf6Nkfx0Z7KvmbPstSj+btvcqsvRAGtvdsB9h62Kptjs1Yn7GAuo 12 | I3qt/4zoKbiURYxkQJXIvwCQsEftUuk5ew5zuPSlDRLoLByQTLbx0JqLAFNfW3g/ 13 | pzSDjgd/60d6HTmvbZni4SmjdyFhXCDb1Kn7N+xTojnfaNkwep2REV+RMc0fx4Gu 14 | hRsnLsmkmUDeyIZ9aBL9oj11YEQfM2JZEq+RVtUx+wB4y8K/tD1bcY+IfnG5rBpw 15 | IDpS262boq4SRSvb3Z7bB0w4ZxvOfJ/1VLoRftjPbLIf0bhfr/AeZMHpIKOXvfz4 16 | yE+hqzi68wdF0VR9xYc/RbSAf7323OsjYnjjEgInUtRohnRgCpjIk/Mt2Kt84Kb0 17 | wn8CAwEAAaMQMA4wDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn2lf 18 | DKkLX+F1vKRO/5gJ+Plr8P5NKuQkmwH97b8CS2gS1phDyNgIc4/LSdzuf4Awe6ve 19 | C06lVdWSIis8UPUPdjmT2uMPSNjwLxG3QsrimMURNwFlLTfRem/heJe0Zgur9J1M 20 | 8haawdSdJjH2RgmFoDeE2r8NVRfhbR8KnCO1ddTJKuS1N0/irHz21W4jt4rxzCvl 21 | 2nR42Fybap3O/g2JXMhNNROwZmNjgpsF7XVENCSuFO1jTywLaqjuXCg54IL7XVLG 22 | omKNNNcc8h1FCeKj/nnbGMhodnFWKDTsJcbNmcOPNHo6ixzqMy/Hqc+mWYv7maAG 23 | Jtevs3qgMZ8F9Qzr3HpUc6R3ZYYWDY/xxPisuKftOPZgtH979XC4mdf0WPnOBLqL 24 | 2DJ1zaBmjiGJolvb7XNVKcUfDXYw85ZTZQ5b9clI4e+6bmyWqQItlwt+Ati/uFEV 25 | XzCj70B4lALX6xau1kLEpV9O1GERizYRz5P9NJNA7KoO5AVMp9w0DQTkt+LbXnZE 26 | HHnWKy8xHQKZF9sR7YBPGLs/Ac6tviv5Ua15OgJ/8dLRZ/veyFfGo2yZsI+hKVU5 27 | nCCJHBcAyFnm1hdvdwEdH33jDBjNB6ciotJZrf/3VYaIWSalADosHAgMWfXuWP+h 28 | 8XKXmzlxuHbTMQYtZPDgspS5aK+S4Q9wb8RRAYo= 29 | -----END CERTIFICATE----- 30 | -------------------------------------------------------------------------------- /controllers/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | type Response struct { 18 | Status string `json:"status"` 19 | Msg string `json:"msg"` 20 | Data interface{} `json:"data"` 21 | Data2 interface{} `json:"data2"` 22 | } 23 | 24 | func (c *ApiController) ResponseOk(data ...interface{}) { 25 | resp := Response{Status: "ok"} 26 | switch len(data) { 27 | case 2: 28 | resp.Data2 = data[1] 29 | fallthrough 30 | case 1: 31 | resp.Data = data[0] 32 | } 33 | c.Data["json"] = resp 34 | c.ServeJSON() 35 | } 36 | 37 | func (c *ApiController) ResponseError(error string, data ...interface{}) { 38 | resp := Response{Status: "error", Msg: error} 39 | switch len(data) { 40 | case 2: 41 | resp.Data2 = data[1] 42 | fallthrough 43 | case 1: 44 | resp.Data = data[0] 45 | } 46 | c.Data["json"] = resp 47 | c.ServeJSON() 48 | } 49 | 50 | func (c *ApiController) RequireSignedIn() bool { 51 | if c.GetSessionUser() == nil { 52 | c.ResponseError("please sign in first") 53 | return true 54 | } 55 | 56 | return false 57 | } 58 | 59 | func (c *ApiController) RequireAdmin() bool { 60 | user := c.GetSessionUser() 61 | if user == nil || !user.IsAdmin { 62 | c.ResponseError("this operation requires admin privilege") 63 | return true 64 | } 65 | 66 | return false 67 | } 68 | -------------------------------------------------------------------------------- /controllers/webhook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controllers 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | 21 | "github.com/casbin/casbin-oa/object" 22 | "github.com/casbin/casbin-oa/util" 23 | "github.com/google/go-github/v38/github" 24 | ) 25 | 26 | func (c *ApiController) WebhookOpen() { 27 | var issueEvent github.IssuesEvent 28 | var pullRequestEvent github.PullRequestEvent 29 | json.Unmarshal(c.Ctx.Input.RequestBody, &pullRequestEvent) 30 | 31 | var result bool 32 | if pullRequestEvent.PullRequest != nil { 33 | result = PullRequestOpen(pullRequestEvent) 34 | } else { 35 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &issueEvent) 36 | if err != nil { 37 | panic(err) 38 | } 39 | result = IssueOpen(issueEvent) 40 | } 41 | 42 | c.Data["json"] = result 43 | c.ServeJSON() 44 | } 45 | 46 | func IssueOpen(issueEvent github.IssuesEvent) bool { 47 | if issueEvent.GetAction() != "opened" { 48 | return false 49 | } 50 | owner, repo := util.GetOwnerAndNameFromId(issueEvent.Repo.GetFullName()) 51 | issueWebhook := object.GetIssueIfExist(owner, repo) 52 | if issueWebhook != nil { 53 | issueNumber := issueEvent.Issue.GetNumber() 54 | 55 | label := util.GetIssueLabel(issueEvent.Issue.GetTitle(), issueEvent.Issue.GetBody()) 56 | if label != "" { 57 | go util.SetIssueLabel(owner, repo, issueNumber, label) 58 | } 59 | 60 | if issueWebhook.ProjectId != -1 { 61 | go util.AddIssueToProjectCard(issueWebhook.ProjectId, issueEvent.GetIssue().GetID()) 62 | } 63 | 64 | if issueWebhook.Assignee != "" { 65 | go util.SetIssueAssignee(owner, repo, issueNumber, issueWebhook.Assignee) 66 | 67 | } 68 | 69 | if len(issueWebhook.AtPeople) != 0 { 70 | go util.AtPeople(issueWebhook.AtPeople, owner, repo, issueNumber) 71 | } 72 | 73 | } 74 | return true 75 | } 76 | 77 | func PullRequestOpen(pullRequestEvent github.PullRequestEvent) bool { 78 | if pullRequestEvent.GetAction() != "opened" { 79 | return false 80 | } 81 | owner, repo := util.GetOwnerAndNameFromId(pullRequestEvent.Repo.GetFullName()) 82 | issueWebhook := object.GetIssueIfExist(owner, repo) 83 | 84 | if issueWebhook != nil { 85 | atPeople := issueWebhook.AtPeople 86 | sender := pullRequestEvent.Sender.GetLogin() 87 | 88 | if len(atPeople) != 0 { 89 | members := util.GetOrgMembers(owner) 90 | for i := 0; i < len(atPeople); i++ { 91 | _, existed := members[atPeople[i]] 92 | if !existed || atPeople[i] == sender { 93 | atPeople = append(atPeople[:i], atPeople[i+1:]...) 94 | i = i - 1 95 | } 96 | } 97 | if len(atPeople) != 0 { 98 | go util.RequestReviewers(owner, repo, pullRequestEvent.GetNumber(), atPeople) 99 | 100 | var commentStr string 101 | for i := range atPeople { 102 | commentStr = fmt.Sprintf("%s @%s", commentStr, atPeople[i]) 103 | } 104 | commentStr = fmt.Sprintf("%s %s", commentStr, "please review") 105 | 106 | go util.Comment(commentStr, owner, repo, pullRequestEvent.GetNumber()) 107 | } 108 | } 109 | } 110 | return true 111 | } 112 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/casbin/casbin-oa 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/aliyun/alibaba-cloud-sdk-go v1.62.517 7 | github.com/astaxie/beego v1.12.3 8 | github.com/casdoor/casdoor-go-sdk v0.32.0 9 | github.com/chromedp/chromedp v0.7.6 10 | github.com/go-sql-driver/mysql v1.5.0 11 | github.com/google/go-github/v38 v38.0.0 12 | github.com/jmespath/go-jmespath v0.4.0 // indirect 13 | github.com/melbahja/goph v1.2.1 14 | github.com/mileusna/crontab v1.0.1 15 | golang.org/x/crypto v0.14.0 16 | golang.org/x/net v0.16.0 17 | golang.org/x/oauth2 v0.13.0 18 | golang.org/x/text v0.13.0 19 | gopkg.in/yaml.v2 v2.4.0 // indirect 20 | xorm.io/core v0.7.2 21 | xorm.io/xorm v1.0.4 22 | ) 23 | -------------------------------------------------------------------------------- /ip/17monipdb.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casbin/casbin-oa/7a6a67b29004dd5a338e5cdf3c93996a956b1557/ip/17monipdb.dat -------------------------------------------------------------------------------- /ip/ip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import "fmt" 18 | 19 | func InitIpDb() { 20 | err := Init("ip/17monipdb.dat") 21 | if err != nil { 22 | panic(err) 23 | } 24 | } 25 | 26 | func IsMainlandIp(ip string) bool { 27 | info, err := Find(ip) 28 | if err != nil { 29 | fmt.Printf("error: ip = %s, error = %s\n", ip, err.Error()) 30 | return false 31 | //panic(err) 32 | } 33 | 34 | return info.Country == "中国" 35 | } 36 | -------------------------------------------------------------------------------- /ip/ip17mon.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "bytes" 19 | "encoding/binary" 20 | "errors" 21 | "io/ioutil" 22 | "net" 23 | ) 24 | 25 | const Null = "N/A" 26 | 27 | var ( 28 | ErrInvalidIp = errors.New("invalid ip format") 29 | std *Locator 30 | ) 31 | 32 | // Init defaut locator with dataFile 33 | func Init(dataFile string) (err error) { 34 | if std != nil { 35 | return 36 | } 37 | std, err = NewLocator(dataFile) 38 | return 39 | } 40 | 41 | // Init defaut locator with data 42 | func InitWithData(data []byte) { 43 | if std != nil { 44 | return 45 | } 46 | std = NewLocatorWithData(data) 47 | return 48 | } 49 | 50 | // Find locationInfo by ip string 51 | // It will return err when ipstr is not a valid format 52 | func Find(ipstr string) (*LocationInfo, error) { 53 | return std.Find(ipstr) 54 | } 55 | 56 | // Find locationInfo by uint32 57 | func FindByUint(ip uint32) *LocationInfo { 58 | return std.FindByUint(ip) 59 | } 60 | 61 | //----------------------------------------------------------------------------- 62 | 63 | // New locator with dataFile 64 | func NewLocator(dataFile string) (loc *Locator, err error) { 65 | data, err := ioutil.ReadFile(dataFile) 66 | if err != nil { 67 | return 68 | } 69 | loc = NewLocatorWithData(data) 70 | return 71 | } 72 | 73 | // New locator with data 74 | func NewLocatorWithData(data []byte) (loc *Locator) { 75 | loc = new(Locator) 76 | loc.init(data) 77 | return 78 | } 79 | 80 | type Locator struct { 81 | textData []byte 82 | indexData1 []uint32 83 | indexData2 []int 84 | indexData3 []int 85 | index []int 86 | } 87 | 88 | type LocationInfo struct { 89 | Country string 90 | Region string 91 | City string 92 | Isp string 93 | } 94 | 95 | // Find locationInfo by ip string 96 | // It will return err when ipstr is not a valid format 97 | func (loc *Locator) Find(ipstr string) (info *LocationInfo, err error) { 98 | ip := net.ParseIP(ipstr).To4() 99 | if ip == nil || ip.To4() == nil { 100 | err = ErrInvalidIp 101 | return 102 | } 103 | info = loc.FindByUint(binary.BigEndian.Uint32([]byte(ip))) 104 | return 105 | } 106 | 107 | // Find locationInfo by uint32 108 | func (loc *Locator) FindByUint(ip uint32) (info *LocationInfo) { 109 | end := len(loc.indexData1) - 1 110 | if ip>>24 != 0xff { 111 | end = loc.index[(ip>>24)+1] 112 | } 113 | idx := loc.findIndexOffset(ip, loc.index[ip>>24], end) 114 | off := loc.indexData2[idx] 115 | return newLocationInfo(loc.textData[off : off+loc.indexData3[idx]]) 116 | } 117 | 118 | // binary search 119 | func (loc *Locator) findIndexOffset(ip uint32, start, end int) int { 120 | for start < end { 121 | mid := (start + end) / 2 122 | if ip > loc.indexData1[mid] { 123 | start = mid + 1 124 | } else { 125 | end = mid 126 | } 127 | } 128 | 129 | if loc.indexData1[end] >= ip { 130 | return end 131 | } 132 | 133 | return start 134 | } 135 | 136 | func (loc *Locator) init(data []byte) { 137 | textoff := int(binary.BigEndian.Uint32(data[:4])) 138 | 139 | loc.textData = data[textoff-1024:] 140 | 141 | loc.index = make([]int, 256) 142 | for i := 0; i < 256; i++ { 143 | off := 4 + i*4 144 | loc.index[i] = int(binary.LittleEndian.Uint32(data[off : off+4])) 145 | } 146 | 147 | nidx := (textoff - 4 - 1024 - 1024) / 8 148 | 149 | loc.indexData1 = make([]uint32, nidx) 150 | loc.indexData2 = make([]int, nidx) 151 | loc.indexData3 = make([]int, nidx) 152 | 153 | for i := 0; i < nidx; i++ { 154 | off := 4 + 1024 + i*8 155 | loc.indexData1[i] = binary.BigEndian.Uint32(data[off : off+4]) 156 | loc.indexData2[i] = int(uint32(data[off+4]) | uint32(data[off+5])<<8 | uint32(data[off+6])<<16) 157 | loc.indexData3[i] = int(data[off+7]) 158 | } 159 | return 160 | } 161 | 162 | func newLocationInfo(str []byte) *LocationInfo { 163 | 164 | var info *LocationInfo 165 | 166 | fields := bytes.Split(str, []byte("\t")) 167 | switch len(fields) { 168 | case 4: 169 | // free version 170 | info = &LocationInfo{ 171 | Country: string(fields[0]), 172 | Region: string(fields[1]), 173 | City: string(fields[2]), 174 | } 175 | case 5: 176 | // pay version 177 | info = &LocationInfo{ 178 | Country: string(fields[0]), 179 | Region: string(fields[1]), 180 | City: string(fields[2]), 181 | Isp: string(fields[4]), 182 | } 183 | default: 184 | panic("unexpected ip info:" + string(str)) 185 | } 186 | 187 | if len(info.Country) == 0 { 188 | info.Country = Null 189 | } 190 | if len(info.Region) == 0 { 191 | info.Region = Null 192 | } 193 | if len(info.City) == 0 { 194 | info.City = Null 195 | } 196 | if len(info.Isp) == 0 { 197 | info.Isp = Null 198 | } 199 | return info 200 | } 201 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/astaxie/beego" 19 | "github.com/astaxie/beego/plugins/cors" 20 | _ "github.com/astaxie/beego/session/redis" 21 | "github.com/casbin/casbin-oa/casdoor" 22 | "github.com/casbin/casbin-oa/ip" 23 | "github.com/casbin/casbin-oa/object" 24 | "github.com/casbin/casbin-oa/proxy" 25 | "github.com/casbin/casbin-oa/routers" 26 | _ "github.com/casbin/casbin-oa/routers" 27 | ) 28 | 29 | func main() { 30 | object.InitAdapter() 31 | casdoor.InitCasdoorAdapter() 32 | proxy.InitHttpClient() 33 | object.RegularUpdate() 34 | object.RegularRedeliver() 35 | object.RedeliverAllOrgWebhook() 36 | ip.InitIpDb() 37 | 38 | beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ 39 | AllowOrigins: []string{"*"}, 40 | AllowMethods: []string{"GET", "PUT", "PATCH"}, 41 | AllowHeaders: []string{"Origin"}, 42 | ExposeHeaders: []string{"Content-Length"}, 43 | AllowCredentials: true, 44 | })) 45 | 46 | //beego.DelStaticPath("/static") 47 | beego.SetStaticPath("/static", "web/build/static") 48 | // https://studygolang.com/articles/2303 49 | beego.InsertFilter("*", beego.BeforeRouter, routers.Static) 50 | 51 | beego.BConfig.WebConfig.Session.SessionName = "casbin_oa_session_id" 52 | if beego.AppConfig.String("redisEndpoint") == "" { 53 | beego.BConfig.WebConfig.Session.SessionProvider = "file" 54 | beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp" 55 | } else { 56 | beego.BConfig.WebConfig.Session.SessionProvider = "redis" 57 | beego.BConfig.WebConfig.Session.SessionProviderConfig = beego.AppConfig.String("redisEndpoint") 58 | } 59 | beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30 60 | 61 | beego.Run() 62 | } 63 | -------------------------------------------------------------------------------- /object/adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "fmt" 19 | "runtime" 20 | 21 | "github.com/astaxie/beego" 22 | _ "github.com/go-sql-driver/mysql" 23 | "xorm.io/xorm" 24 | ) 25 | 26 | var adapter *Adapter 27 | 28 | func InitConfig() { 29 | err := beego.LoadAppConfig("ini", "../conf/app.conf") 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | InitAdapter() 35 | } 36 | 37 | func InitAdapter() { 38 | adapter = NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"), beego.AppConfig.String("dbName")) 39 | adapter.createTable() 40 | } 41 | 42 | // Adapter represents the MySQL adapter for policy storage. 43 | type Adapter struct { 44 | driverName string 45 | dataSourceName string 46 | dbName string 47 | Engine *xorm.Engine 48 | } 49 | 50 | // finalizer is the destructor for Adapter. 51 | func finalizer(a *Adapter) { 52 | err := a.Engine.Close() 53 | if err != nil { 54 | panic(err) 55 | } 56 | } 57 | 58 | // NewAdapter is the constructor for Adapter. 59 | func NewAdapter(driverName string, dataSourceName string, dbName string) *Adapter { 60 | a := &Adapter{} 61 | a.driverName = driverName 62 | a.dataSourceName = dataSourceName 63 | a.dbName = dbName 64 | 65 | // Open the DB, create it if not existed. 66 | a.open() 67 | 68 | // Call the destructor when the object is released. 69 | runtime.SetFinalizer(a, finalizer) 70 | 71 | return a 72 | } 73 | 74 | func (a *Adapter) createDatabase() error { 75 | engine, err := xorm.NewEngine(a.driverName, a.dataSourceName) 76 | if err != nil { 77 | return err 78 | } 79 | defer engine.Close() 80 | 81 | _, err = engine.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s default charset utf8 COLLATE utf8_general_ci", a.dbName)) 82 | return err 83 | } 84 | 85 | func (a *Adapter) open() { 86 | if a.driverName != "postgres" { 87 | if err := a.createDatabase(); err != nil { 88 | panic(err) 89 | } 90 | } 91 | 92 | engine, err := xorm.NewEngine(a.driverName, a.dataSourceName+a.dbName) 93 | if err != nil { 94 | panic(err) 95 | } 96 | 97 | a.Engine = engine 98 | } 99 | 100 | func (a *Adapter) close() { 101 | a.Engine.Close() 102 | a.Engine = nil 103 | } 104 | 105 | func (a *Adapter) createTable() { 106 | err := a.Engine.Sync2(new(Program)) 107 | if err != nil { 108 | panic(err) 109 | } 110 | 111 | err = a.Engine.Sync2(new(Student)) 112 | if err != nil { 113 | panic(err) 114 | } 115 | 116 | err = a.Engine.Sync2(new(Round)) 117 | if err != nil { 118 | panic(err) 119 | } 120 | 121 | err = a.Engine.Sync2(new(Report)) 122 | if err != nil { 123 | panic(err) 124 | } 125 | 126 | err = a.Engine.Sync2(new(Issue)) 127 | if err != nil { 128 | panic(err) 129 | } 130 | 131 | err = a.Engine.Sync2(new(Machine)) 132 | if err != nil { 133 | panic(err) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /object/collaborator_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "github.com/casbin/casbin-oa/proxy" 10 | "github.com/casbin/casbin-oa/util" 11 | "github.com/google/go-github/v38/github" 12 | ) 13 | 14 | func listDirectCollaborators(org string) (map[string][]string, error) { 15 | client := util.GetClient() 16 | opt := &github.RepositoryListByOrgOptions{ 17 | ListOptions: github.ListOptions{PerPage: 100}, 18 | } 19 | 20 | result := make(map[string][]string) 21 | 22 | i := 1 23 | for { 24 | repos, resp, err := client.Repositories.ListByOrg(context.Background(), org, opt) 25 | if err != nil { 26 | return nil, fmt.Errorf("failed to list repositories for org %s: %v", org, err) 27 | } 28 | 29 | for _, repo := range repos { 30 | repoName := repo.GetName() 31 | if repoName == "" { 32 | continue 33 | } 34 | 35 | collabs, _, err := client.Repositories.ListCollaborators( 36 | context.Background(), 37 | org, 38 | repoName, 39 | &github.ListCollaboratorsOptions{Affiliation: "direct"}, 40 | ) 41 | if err != nil { 42 | fmt.Printf("[%s] failed to list collaborators for repo %s: %v\n", time.Now().Format("2006-01-02 15:04:05"), repoName, err) 43 | continue 44 | } 45 | 46 | var usernames []string 47 | for _, c := range collabs { 48 | usernames = append(usernames, c.GetLogin()) 49 | } 50 | 51 | repoURL := fmt.Sprintf("https://github.com/%s/%s/settings/access", org, repoName) 52 | result[repoURL] = usernames 53 | 54 | if len(usernames) > 0 { 55 | fmt.Printf("[%s] [%d] Repo: %s, Collaborators: %v\n", time.Now().Format("2006-01-02 15:04:05"), i, repoURL, usernames) 56 | } 57 | i += 1 58 | } 59 | 60 | if resp.NextPage == 0 { 61 | break 62 | } 63 | opt.Page = resp.NextPage 64 | } 65 | 66 | return result, nil 67 | } 68 | 69 | func TestListDirectCollaborators(t *testing.T) { 70 | InitConfig() 71 | proxy.InitHttpClient() 72 | 73 | org := "casbin" 74 | _, err := listDirectCollaborators(org) 75 | if err != nil { 76 | t.Fatalf("Error listing collaborators: %v\n", err) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /object/crawl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "fmt" 19 | "regexp" 20 | "strings" 21 | "time" 22 | 23 | "github.com/casbin/casbin-oa/util" 24 | "github.com/chromedp/chromedp" 25 | ) 26 | 27 | var reTitle *regexp.Regexp 28 | var reMember *regexp.Regexp 29 | 30 | type TopicInfo struct { 31 | IsHit bool `json:"isHit"` 32 | Title string `json:"title"` 33 | Member string `json:"member"` 34 | } 35 | 36 | func init() { 37 | reTitle = regexp.MustCompile(`"topic-link">(.*?)`) 38 | reMember = regexp.MustCompile(`/member/(.*?)"`) 39 | } 40 | 41 | func getPage(url string) []string { 42 | ctx, cancel := getContext() 43 | defer cancel() 44 | 45 | var topic1 string 46 | var topic2 string 47 | 48 | err := chromedp.Run(ctx, 49 | chromedp.Navigate(url), 50 | 51 | chromedp.Sleep(1*time.Second), 52 | chromedp.OuterHTML(`//*[@id="Main"]/div[2]/div[3]`, &topic1), 53 | chromedp.OuterHTML(`//*[@id="Main"]/div[2]/div[4]`, &topic2), 54 | //chromedp.Sleep(1*time.Second), 55 | ) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | res := []string{topic1, topic2} 61 | return res 62 | } 63 | 64 | func parseTopicInfo(s string) *TopicInfo { 65 | isHit := false 66 | if strings.Contains(s, "background") { 67 | isHit = true 68 | } 69 | 70 | title := "" 71 | m := reTitle.FindStringSubmatch(s) 72 | if m != nil { 73 | title = m[1] 74 | } 75 | 76 | member := "" 77 | m = reMember.FindStringSubmatch(s) 78 | if m != nil { 79 | member = m[1] 80 | } 81 | 82 | res := &TopicInfo{ 83 | IsHit: isHit, 84 | Title: title, 85 | Member: member, 86 | } 87 | return res 88 | } 89 | 90 | func updateTopicString() { 91 | ss := getPage(Url) 92 | 93 | topicInfos := []*TopicInfo{} 94 | for _, s := range ss { 95 | topicInfo := parseTopicInfo(s) 96 | if !topicInfo.IsHit { 97 | continue 98 | } 99 | 100 | //fmt.Printf("%s: %v\n", util.GetCurrentTime(), topicInfo) 101 | topicInfos = append(topicInfos, topicInfo) 102 | } 103 | 104 | if len(topicInfos) == 0 { 105 | fmt.Printf("%s: \n", util.GetCurrentTime()) 106 | return 107 | } 108 | 109 | now := time.Now() 110 | roundName := fmt.Sprintf("%s-%s", ProgramName, now.Format("2006-01-02")) 111 | studentName := strings.ReplaceAll(now.Format("15:04"), ":", "-") 112 | 113 | report := &Report{ 114 | Owner: "admin", 115 | Name: fmt.Sprintf("report_%s_%s_%s", ProgramName, roundName, studentName), 116 | CreatedTime: util.GetCurrentTime(), 117 | Program: ProgramName, 118 | Round: roundName, 119 | Student: studentName, 120 | Mentor: "", 121 | Text: util.StructToJson(topicInfos), 122 | Score: len(topicInfos), 123 | Events: []*Event{}, 124 | } 125 | 126 | AddReport(report) 127 | fmt.Printf("%v\n", report) 128 | } 129 | -------------------------------------------------------------------------------- /object/crawl_conf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | var ProgramName = "" 18 | var Url = "" 19 | -------------------------------------------------------------------------------- /object/crawl_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/mileusna/crontab" 22 | ) 23 | 24 | func TestGetPage(t *testing.T) { 25 | InitConfig() 26 | 27 | updateTopicString() 28 | ctab := crontab.New() 29 | ctab.MustAddJob("* * * * *", updateTopicString) 30 | time.Sleep(time.Duration(1<<63 - 1)) 31 | } 32 | -------------------------------------------------------------------------------- /object/event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "context" 19 | "encoding/json" 20 | "fmt" 21 | "strings" 22 | "time" 23 | 24 | "github.com/casbin/casbin-oa/util" 25 | "github.com/google/go-github/v38/github" 26 | ) 27 | 28 | type Event struct { 29 | Type string `json:"type"` 30 | Title string `json:"title"` 31 | HtmlURL string `json:"html_url"` 32 | CreateAt string `json:"create_at"` 33 | State string `json:"state"` 34 | OrgName string `json:"org_name"` 35 | RepoName string `json:"repo_name"` 36 | Number int `json:"number"` 37 | } 38 | 39 | func GetEvents(author string, orgMap map[string]bool, startDate time.Time, endDate time.Time) []*Event { 40 | client := util.GetClient() 41 | activity := client.Activity 42 | 43 | curPage := 1 44 | options := github.ListOptions{Page: curPage, PerPage: 100} 45 | 46 | Events, _, err := activity.ListEventsPerformedByUser(context.Background(), author, true, &options) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | i := 0 52 | 53 | var authorEvents []*Event 54 | prMap := make(map[string]bool) 55 | issueMap := make(map[string]bool) 56 | reviewMap := make(map[string]bool) 57 | for { 58 | i++ 59 | if len(Events) == 0 { 60 | return authorEvents 61 | } 62 | 63 | if i > len(Events) { 64 | i = 0 65 | curPage++ 66 | options = github.ListOptions{Page: curPage} 67 | Events, _, err = activity.ListEventsPerformedByUser(context.Background(), author, true, &options) 68 | if err != nil { 69 | if strings.Contains(err.Error(), "pagination is limited for this resource") { 70 | fmt.Printf("GetEvents() error: %s\n", err.Error()) 71 | break 72 | } 73 | panic(err) 74 | } 75 | continue 76 | } 77 | curEvent := Events[i-1] 78 | creatTime := *curEvent.CreatedAt 79 | if creatTime.After(endDate) { 80 | continue 81 | } 82 | if creatTime.Before(startDate) { 83 | return authorEvents 84 | } 85 | 86 | fullName := *curEvent.Repo.Name 87 | orgName, repoName := util.GetOwnerAndNameFromId(fullName) 88 | 89 | _, okOrg := orgMap[orgName] 90 | _, okFullName := orgMap[fullName] 91 | if !okOrg && !okFullName { 92 | continue 93 | } 94 | 95 | Date := fmt.Sprintf("%d", creatTime.YearDay()-startDate.YearDay()) 96 | 97 | if *curEvent.Type == "PullRequestEvent" { 98 | var payLoad PayLoad 99 | json.Unmarshal(*curEvent.RawPayload, &payLoad) 100 | request := payLoad.PullRequest 101 | state := GetPRState(orgName, repoName, request.GetNumber()) 102 | _, ok := prMap[*request.HTMLURL] 103 | if ok { 104 | continue 105 | } else { 106 | prMap[*request.HTMLURL] = true 107 | } 108 | event := Event{Type: "PR", Title: *request.Title, HtmlURL: *request.HTMLURL, CreateAt: Date, State: state, OrgName: orgName, RepoName: repoName, Number: *request.Number} 109 | authorEvents = append(authorEvents, &event) 110 | 111 | } else if *curEvent.Type == "IssueCommentEvent" { 112 | var payLoad PayLoad 113 | json.Unmarshal(*curEvent.RawPayload, &payLoad) 114 | comment := payLoad.Comment 115 | issue := payLoad.Issue 116 | body := *comment.Body 117 | issueId := fmt.Sprintf("%s%s%v", orgName, repoName, *issue.Number) 118 | _, ok := issueMap[issueId] 119 | if ok { 120 | continue 121 | } else { 122 | issueMap[issueId] = true 123 | } 124 | if len(body) > 100 { 125 | body = fmt.Sprintf("%s%s", body[0:99], ". . .") 126 | } 127 | body = strings.ReplaceAll(body, "\r\n", " ") 128 | body = strings.ReplaceAll(body, "\n", "") 129 | event := Event{Type: "IssueComment", Title: body, HtmlURL: *comment.HTMLURL, CreateAt: Date, OrgName: orgName, RepoName: repoName, Number: *issue.Number} 130 | authorEvents = append(authorEvents, &event) 131 | } else if *curEvent.Type == "PullRequestReviewEvent" { 132 | var payLoad PayLoad 133 | json.Unmarshal(*curEvent.RawPayload, &payLoad) 134 | pr := payLoad.PullRequest 135 | if *pr.User.Login == author { 136 | continue 137 | } 138 | review := payLoad.Review 139 | 140 | reviewURL := *review.HTMLURL 141 | _, ok := reviewMap[reviewURL] 142 | if ok { 143 | continue 144 | } else { 145 | reviewMap[reviewURL] = true 146 | } 147 | 148 | event := Event{Type: "CodeReview", HtmlURL: reviewURL, CreateAt: Date, OrgName: orgName, RepoName: repoName, Number: *pr.Number} 149 | authorEvents = append(authorEvents, &event) 150 | } 151 | 152 | } 153 | return authorEvents 154 | } 155 | 156 | func GetPRState(org string, repo string, pullNumber int) string { 157 | client := util.GetClient() 158 | pr, _, err := client.PullRequests.Get(context.Background(), org, repo, pullNumber) 159 | if err != nil { 160 | panic(err) 161 | } 162 | if pr.MergedAt != nil { 163 | return "Merged" 164 | } 165 | if *pr.Draft && *pr.State == "open" { 166 | return "Draft" 167 | } 168 | 169 | return *pr.State 170 | } 171 | -------------------------------------------------------------------------------- /object/issue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import "xorm.io/core" 18 | 19 | type Issue struct { 20 | Name string `xorm:"varchar(100) notnull pk" json:"name"` 21 | Org string `xorm:"varchar(100)" json:"org"` 22 | Repo string `xorm:"varchar(100)" json:"repo"` 23 | Assignee string `xorm:"varchar(1000)" json:"assignee"` 24 | ProjectName string `xorm:"varchar(1000)" json:"project_name"` 25 | ProjectId int64 `xorm:"varchar(100)" json:"project_id"` 26 | AtPeople []string `xorm:"varchar(1000)" json:"at_people"` 27 | } 28 | 29 | func GetIssues() []*Issue { 30 | issueWebhooks := []*Issue{} 31 | err := adapter.Engine.Find(&issueWebhooks) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | return issueWebhooks 37 | } 38 | 39 | func GetIssueByName(name string) *Issue { 40 | 41 | issueWebhook := Issue{Name: name} 42 | existed, err := adapter.Engine.Get(&issueWebhook) 43 | if err != nil { 44 | panic(err) 45 | } 46 | if existed { 47 | return &issueWebhook 48 | } else { 49 | return nil 50 | } 51 | } 52 | 53 | func GetIssueByOrgAndRepo(org string, repo string) *Issue { 54 | var existed bool 55 | var err error 56 | var issueWebhook Issue 57 | if repo != "All" { 58 | issueWebhook = Issue{Org: org, Repo: repo} 59 | existed, err = adapter.Engine.Get(&issueWebhook) 60 | } else { 61 | issueWebhook = Issue{Org: org} 62 | existed, err = adapter.Engine.Where("repo = 'All' ").Get(&issueWebhook) 63 | } 64 | 65 | if err != nil { 66 | panic(err) 67 | } 68 | if existed { 69 | return &issueWebhook 70 | } else { 71 | return nil 72 | } 73 | } 74 | 75 | func GetIssueIfExist(owner string, repo string) *Issue { 76 | issueWebhook := GetIssueByOrgAndRepo(owner, repo) 77 | 78 | if issueWebhook == nil { 79 | issueWebhook = GetIssueByOrgAndRepo(owner, "All") 80 | } 81 | 82 | return issueWebhook 83 | } 84 | 85 | func AddIssue(issueWebhook *Issue) bool { 86 | affected, err := adapter.Engine.Insert(issueWebhook) 87 | if err != nil { 88 | panic(err) 89 | } 90 | 91 | return affected != 0 92 | } 93 | 94 | func UpdateIssue(name string, issueWebhook *Issue) bool { 95 | if GetIssueByName(name) == nil { 96 | return false 97 | } 98 | 99 | _, err := adapter.Engine.ID(core.PK{name}).AllCols().Update(issueWebhook) 100 | if err != nil { 101 | panic(err) 102 | } 103 | 104 | return true 105 | } 106 | 107 | func DeleteIssue(issueWebhook *Issue) bool { 108 | affected, err := adapter.Engine.ID(core.PK{issueWebhook.Name}).Delete(&Issue{}) 109 | if err != nil { 110 | panic(err) 111 | } 112 | 113 | return affected != 0 114 | } 115 | 116 | func GetWebhookOrgs() []string { 117 | issues := []*Issue{} 118 | var orgs []string 119 | err := adapter.Engine.Select("org").GroupBy("org").Find(&issues) 120 | if err != nil { 121 | panic(err) 122 | } 123 | 124 | for i := 0; i < len(issues); i++ { 125 | orgs = append(orgs, issues[i].Org) 126 | } 127 | 128 | return orgs 129 | } 130 | -------------------------------------------------------------------------------- /object/machine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/casbin/casbin-oa/util" 21 | "xorm.io/core" 22 | ) 23 | 24 | type Service struct { 25 | No int `json:"no"` 26 | Name string `json:"name"` 27 | Path string `json:"path"` 28 | Port int `json:"port"` 29 | ProcessId int `json:"processId"` 30 | ExpectedStatus string `json:"expectedStatus"` 31 | Status string `json:"status"` 32 | SubStatus string `json:"subStatus"` 33 | Message string `json:"message"` 34 | } 35 | 36 | type Machine struct { 37 | Owner string `xorm:"varchar(100) notnull pk" json:"owner"` 38 | Name string `xorm:"varchar(100) notnull pk" json:"name"` 39 | CreatedTime string `xorm:"varchar(100)" json:"createdTime"` 40 | 41 | Description string `xorm:"varchar(100)" json:"description"` 42 | Ip string `xorm:"varchar(100)" json:"ip"` 43 | Port int `json:"port"` 44 | Username string `xorm:"varchar(100)" json:"username"` 45 | Password string `xorm:"varchar(100)" json:"password"` 46 | Language string `xorm:"varchar(100)" json:"language"` 47 | AutoQuery bool `json:"autoQuery"` 48 | IsPermanent bool `json:"isPermanent"` 49 | 50 | Services []*Service `json:"services"` 51 | } 52 | 53 | func GetMachines(owner string) []*Machine { 54 | machines := []*Machine{} 55 | err := adapter.Engine.Desc("created_time").Find(&machines, &Machine{Owner: owner}) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | return machines 61 | } 62 | 63 | func getMachine(owner string, name string) *Machine { 64 | machine := Machine{Owner: owner, Name: name} 65 | existed, err := adapter.Engine.Get(&machine) 66 | if err != nil { 67 | panic(err) 68 | } 69 | 70 | if existed { 71 | return &machine 72 | } else { 73 | return nil 74 | } 75 | } 76 | 77 | func GetMachine(id string) *Machine { 78 | owner, name := util.GetOwnerAndNameFromId(id) 79 | return getMachine(owner, name) 80 | } 81 | 82 | func updateMachine(owner string, name string, machine *Machine) bool { 83 | if getMachine(owner, name) == nil { 84 | return false 85 | } 86 | 87 | _, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(machine) 88 | if err != nil { 89 | panic(err) 90 | } 91 | 92 | //return affected != 0 93 | return true 94 | } 95 | 96 | func UpdateMachine(id string, machine *Machine) bool { 97 | owner, name := util.GetOwnerAndNameFromId(id) 98 | return updateMachine(owner, name, machine) 99 | } 100 | 101 | func AddMachine(machine *Machine) bool { 102 | affected, err := adapter.Engine.Insert(machine) 103 | if err != nil { 104 | panic(err) 105 | } 106 | 107 | return affected != 0 108 | } 109 | 110 | func AddMachines(machines []*Machine) bool { 111 | affected, err := adapter.Engine.Insert(machines) 112 | if err != nil { 113 | panic(err) 114 | } 115 | 116 | return affected != 0 117 | } 118 | 119 | func DeleteMachine(machine *Machine) bool { 120 | affected, err := adapter.Engine.ID(core.PK{machine.Owner, machine.Name}).Delete(&Machine{}) 121 | if err != nil { 122 | panic(err) 123 | } 124 | 125 | return affected != 0 126 | } 127 | 128 | func deleteImpermanentMachines() bool { 129 | affected, err := adapter.Engine.Where("is_permanent=?", false).Delete(&Machine{}) 130 | if err != nil { 131 | panic(err) 132 | } 133 | 134 | return affected != 0 135 | } 136 | 137 | func (machine *Machine) getId() string { 138 | return fmt.Sprintf("%s/%s", machine.Owner, machine.Name) 139 | } 140 | -------------------------------------------------------------------------------- /object/machine_cloud.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" 19 | "github.com/casbin/casbin-oa/cloud" 20 | ) 21 | 22 | func getMachineFromInstance(instance ecs.Instance) *Machine { 23 | machine := &Machine{ 24 | Owner: "admin", 25 | Name: instance.InstanceName, 26 | CreatedTime: instance.CreationTime, 27 | Description: instance.Description, 28 | Ip: instance.PublicIpAddress.IpAddress[0], 29 | Port: 22, 30 | Username: "Administrator", 31 | Password: "123", 32 | Language: "zh", 33 | AutoQuery: true, 34 | IsPermanent: false, 35 | Services: []*Service{{ 36 | No: 0, 37 | Name: "casbintest", 38 | Path: "C:/github_repos/casbintest", 39 | Port: 9095, 40 | ProcessId: 0, 41 | ExpectedStatus: "Running", 42 | Status: "", 43 | SubStatus: "", 44 | Message: "", 45 | }}, 46 | } 47 | return machine 48 | } 49 | 50 | func getMachinesFromInstances(instances []ecs.Instance) []*Machine { 51 | machines := []*Machine{} 52 | for _, instance := range instances { 53 | machine := getMachineFromInstance(instance) 54 | machines = append(machines, machine) 55 | } 56 | return machines 57 | } 58 | 59 | func syncImpermanentMachines() { 60 | deleteImpermanentMachines() 61 | instances := cloud.GetInstances() 62 | machines := getMachinesFromInstances(instances) 63 | AddMachines(machines) 64 | 65 | serverIdMap := cloud.GetVsgServerIdMap() 66 | for _, instance := range instances { 67 | serverId := instance.InstanceId 68 | if _, ok := serverIdMap[serverId]; !ok { 69 | cloud.AddServerToSlb(serverId, 9095) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /object/machine_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | ) 21 | 22 | func TestSyncMachine(t *testing.T) { 23 | InitConfig() 24 | 25 | machine := getMachine("admin", "casbintest") 26 | machine.syncProcessIds() 27 | machine.DoActions() 28 | updateMachine(machine.Owner, machine.Name, machine) 29 | } 30 | 31 | func TestDeployMachineService(t *testing.T) { 32 | InitConfig() 33 | 34 | machine := getMachine("admin", "casbintest") 35 | machine.syncProcessIds() 36 | for _, service := range machine.Services { 37 | if service.Name != "casbin-oa" { 38 | continue 39 | } 40 | 41 | var err error 42 | 43 | err = doStop(machine, service) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | err = doPull(machine, service) 49 | if err != nil { 50 | if !strings.Contains(err.Error(), "wincredman") && !strings.Contains(err.Error(), "bash: /dev/tty") { 51 | panic(err) 52 | } 53 | } 54 | 55 | err = doBuild(machine, service) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | err = doDeploy(machine, service) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | err = doStart(machine, service) 66 | if err != nil { 67 | panic(err) 68 | } 69 | } 70 | } 71 | 72 | func TestSyncImpermanentMachines(t *testing.T) { 73 | InitConfig() 74 | 75 | syncImpermanentMachines() 76 | } 77 | -------------------------------------------------------------------------------- /object/org_repositories.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "context" 19 | "strings" 20 | 21 | "github.com/astaxie/beego" 22 | "github.com/casbin/casbin-oa/util" 23 | "github.com/google/go-github/v38/github" 24 | ) 25 | 26 | type OrgRepositories struct { 27 | Organization string `json:"organization"` 28 | Repositories []string `json:"repositories"` 29 | } 30 | 31 | func GetRepositoryByOrganization(org string) OrgRepositories { 32 | client := util.GetClient() 33 | curPage := 1 34 | var repositories []string 35 | options := github.RepositoryListByOrgOptions{ListOptions: github.ListOptions{PerPage: 100, Page: curPage}} 36 | 37 | lists, _, err := client.Repositories.ListByOrg(context.Background(), org, &options) 38 | for { 39 | if err != nil { 40 | panic(err) 41 | } 42 | if len(lists) != 0 { 43 | for i := range lists { 44 | repositories = append(repositories, *lists[i].Name) 45 | } 46 | curPage++ 47 | options = github.RepositoryListByOrgOptions{ListOptions: github.ListOptions{PerPage: 100, Page: curPage}} 48 | lists, _, err = client.Repositories.ListByOrg(context.Background(), org, &options) 49 | } else { 50 | break 51 | } 52 | } 53 | 54 | return OrgRepositories{Organization: org, Repositories: repositories} 55 | } 56 | 57 | func getDefaultOrg() map[string]bool { 58 | 59 | defaultOrgMap := make(map[string]bool) 60 | orgStr := beego.AppConfig.String("defaultOrg") 61 | 62 | orgStr = strings.Replace(orgStr, " ", "", -1) 63 | orgStr = strings.Replace(orgStr, "\n", "", -1) 64 | organizations := strings.Split(orgStr, ",") 65 | for i := range organizations { 66 | defaultOrgMap[organizations[i]] = true 67 | } 68 | return defaultOrgMap 69 | } 70 | -------------------------------------------------------------------------------- /object/org_webhook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "github.com/casbin/casbin-oa/util" 19 | "github.com/google/go-github/v38/github" 20 | "github.com/mileusna/crontab" 21 | ) 22 | 23 | type OrgWebhook struct { 24 | Org string `json:"org"` 25 | Webhooks []*github.Hook `json:"webhooks"` 26 | } 27 | 28 | func GetAllOrgWebhooks() []*OrgWebhook { 29 | orgWebhooks := []*OrgWebhook{} 30 | orgs := GetWebhookOrgs() 31 | 32 | for i := 0; i < len(orgs); i++ { 33 | webhooks := util.GetOrgWebhooks(orgs[i]) 34 | newOrgWebhook := OrgWebhook{Org: orgs[i], Webhooks: webhooks} 35 | orgWebhooks = append(orgWebhooks, &newOrgWebhook) 36 | } 37 | 38 | return orgWebhooks 39 | } 40 | 41 | func ReDeliverOrgWebhook(orgWebhook OrgWebhook) { 42 | orgName := orgWebhook.Org 43 | for _, webhook := range orgWebhook.Webhooks { 44 | webhookMap := make(map[string]bool) 45 | delivers := util.GetWebhookDelivers(orgName, webhook.GetID()) 46 | 47 | for _, deliver := range delivers { 48 | if deliver.GetAction() == "opened" { 49 | if deliver.GetStatus() == "OK" { 50 | webhookMap[deliver.GetGUID()] = true 51 | } else if !deliver.GetRedelivery() { 52 | _, ok := webhookMap[deliver.GetGUID()] 53 | if !ok { 54 | util.RedeliverWebhook(orgName, webhook.GetID(), deliver.GetID()) 55 | } 56 | } 57 | } 58 | } 59 | 60 | } 61 | } 62 | 63 | func RedeliverAllOrgWebhook() { 64 | orgWebhooks := GetAllOrgWebhooks() 65 | 66 | for _, orgWebhook := range orgWebhooks { 67 | go ReDeliverOrgWebhook(*orgWebhook) 68 | } 69 | } 70 | 71 | func RegularRedeliver() { 72 | ctab := crontab.New() 73 | ctab.MustAddJob("*/5 * * * *", RedeliverAllOrgWebhook) 74 | } 75 | -------------------------------------------------------------------------------- /object/payLoad.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import "github.com/google/go-github/v38/github" 18 | 19 | type PayLoad struct { 20 | Review github.PullRequestReview `json:"review"` 21 | PullRequest github.PullRequest `json:"pull_request"` 22 | Issue github.Issue `json:"issue"` 23 | Comment github.PullRequestComment `json:"comment"` 24 | } 25 | -------------------------------------------------------------------------------- /object/program.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "github.com/casbin/casbin-oa/util" 19 | "xorm.io/core" 20 | ) 21 | 22 | type Program struct { 23 | Owner string `xorm:"varchar(100) notnull pk" json:"owner"` 24 | Name string `xorm:"varchar(100) notnull pk" json:"name"` 25 | CreatedTime string `xorm:"varchar(100)" json:"createdTime"` 26 | 27 | Title string `xorm:"varchar(100)" json:"title"` 28 | Url string `xorm:"varchar(100)" json:"url"` 29 | StartDate string `xorm:"varchar(100)" json:"startDate"` 30 | EndDate string `xorm:"varchar(100)" json:"endDate"` 31 | Provider2 string `xorm:"varchar(100)" json:"provider2"` 32 | CanApply bool `json:"canApply"` 33 | Type string `xorm:"varchar(100)" json:"type"` 34 | } 35 | 36 | func GetPrograms(owner string) []*Program { 37 | programs := []*Program{} 38 | err := adapter.Engine.Desc("created_time").Find(&programs, &Program{Owner: owner}) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | return programs 44 | } 45 | 46 | func getProgram(owner string, name string) *Program { 47 | program := Program{Owner: owner, Name: name} 48 | existed, err := adapter.Engine.Get(&program) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | if existed { 54 | return &program 55 | } else { 56 | return nil 57 | } 58 | } 59 | 60 | func GetProgram(id string) *Program { 61 | owner, name := util.GetOwnerAndNameFromId(id) 62 | return getProgram(owner, name) 63 | } 64 | 65 | func UpdateProgram(id string, program *Program) bool { 66 | owner, name := util.GetOwnerAndNameFromId(id) 67 | if getProgram(owner, name) == nil { 68 | return false 69 | } 70 | 71 | _, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(program) 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | //return affected != 0 77 | return true 78 | } 79 | 80 | func AddProgram(program *Program) bool { 81 | affected, err := adapter.Engine.Insert(program) 82 | if err != nil { 83 | panic(err) 84 | } 85 | 86 | return affected != 0 87 | } 88 | 89 | func DeleteProgram(program *Program) bool { 90 | affected, err := adapter.Engine.ID(core.PK{program.Owner, program.Name}).Delete(&Program{}) 91 | if err != nil { 92 | panic(err) 93 | } 94 | 95 | return affected != 0 96 | } 97 | -------------------------------------------------------------------------------- /object/round.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "math" 19 | "time" 20 | 21 | "github.com/casbin/casbin-oa/util" 22 | "xorm.io/core" 23 | ) 24 | 25 | type Round struct { 26 | Owner string `xorm:"varchar(100) notnull pk" json:"owner"` 27 | Name string `xorm:"varchar(100) notnull pk" json:"name"` 28 | CreatedTime string `xorm:"varchar(100)" json:"createdTime"` 29 | 30 | Title string `xorm:"varchar(100)" json:"title"` 31 | Program string `xorm:"varchar(100)" json:"program"` 32 | StartDate string `xorm:"varchar(100)" json:"startDate"` 33 | EndDate string `xorm:"varchar(100)" json:"endDate"` 34 | } 35 | 36 | func GetRounds(owner string) []*Round { 37 | rounds := []*Round{} 38 | err := adapter.Engine.Asc("created_time").Find(&rounds, &Round{Owner: owner}) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | return rounds 44 | } 45 | 46 | func GetFilteredRounds(owner string, program string) []*Round { 47 | rounds := []*Round{} 48 | err := adapter.Engine.Asc("created_time").Find(&rounds, &Round{Owner: owner, Program: program}) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | return rounds 54 | } 55 | 56 | func GetLateRound(owner string, program string) *Round { 57 | 58 | DateTemplate := "2006-01-02" 59 | round := Round{Owner: owner, Program: program} 60 | now := time.Now().Format(DateTemplate) 61 | has, err := adapter.Engine.Where("end_date <= ?", now).Desc("end_date").Limit(1).Get(&round) 62 | if err != nil { 63 | panic(err) 64 | } 65 | if !has { 66 | return nil 67 | } 68 | 69 | EndDate, _ := time.ParseInLocation(DateTemplate, round.EndDate, time.UTC) 70 | if math.Abs(float64(time.Now().YearDay()-EndDate.YearDay())) <= 2 { 71 | return &round 72 | } 73 | return nil 74 | } 75 | 76 | func getRound(owner string, name string) *Round { 77 | round := Round{Owner: owner, Name: name} 78 | existed, err := adapter.Engine.Get(&round) 79 | if err != nil { 80 | panic(err) 81 | } 82 | 83 | if existed { 84 | return &round 85 | } else { 86 | return nil 87 | } 88 | } 89 | 90 | func GetRound(id string) *Round { 91 | owner, name := util.GetOwnerAndNameFromId(id) 92 | return getRound(owner, name) 93 | } 94 | 95 | func UpdateRound(id string, round *Round) bool { 96 | owner, name := util.GetOwnerAndNameFromId(id) 97 | if getRound(owner, name) == nil { 98 | return false 99 | } 100 | 101 | _, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(round) 102 | if err != nil { 103 | panic(err) 104 | } 105 | 106 | //return affected != 0 107 | return true 108 | } 109 | 110 | func AddRound(round *Round) bool { 111 | affected, err := adapter.Engine.Insert(round) 112 | if err != nil { 113 | panic(err) 114 | } 115 | 116 | return affected != 0 117 | } 118 | 119 | func DeleteRound(round *Round) bool { 120 | affected, err := adapter.Engine.ID(core.PK{round.Owner, round.Name}).Delete(&Round{}) 121 | if err != nil { 122 | panic(err) 123 | } 124 | 125 | return affected != 0 126 | } 127 | -------------------------------------------------------------------------------- /object/round_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func getDateFromString(date string) time.Time { 24 | dateString := date + "T00:00:00+08:00" 25 | res, err := time.Parse(time.RFC3339, dateString) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | return res 31 | } 32 | 33 | func getAddedDate(t time.Time, i int) string { 34 | return t.AddDate(0, 0, i).Format("2006-01-02") 35 | } 36 | 37 | func TestAddRounds(t *testing.T) { 38 | InitConfig() 39 | 40 | startDate := getDateFromString("2022-08-15") 41 | 42 | now := time.Now() 43 | date := now.Format("2006-01-02") 44 | date = "2021-09-24" 45 | for i := 29; i < 35; i++ { 46 | round := &Round{ 47 | Owner: "admin", 48 | Name: fmt.Sprintf("talent2022-week-%d", i), 49 | CreatedTime: fmt.Sprintf("%sT00:00:%02d+08:00", date, i), 50 | Title: fmt.Sprintf("第%d周", i), 51 | Program: "talent2022", 52 | StartDate: getAddedDate(startDate, 7*i), 53 | EndDate: getAddedDate(startDate, 7*(i+1)), 54 | } 55 | 56 | AddRound(round) 57 | fmt.Printf("%v\n", round) 58 | } 59 | } 60 | 61 | func TestAddRounds2(t *testing.T) { 62 | InitConfig() 63 | 64 | startDate := getDateFromString("2022-01-13") 65 | 66 | for i := 0; i < 20; i++ { 67 | newDate := getAddedDate(startDate, i) 68 | round := &Round{ 69 | Owner: "admin", 70 | Name: fmt.Sprintf("%s-%s", ProgramName, newDate), 71 | CreatedTime: fmt.Sprintf("%sT00:00:00+08:00", newDate), 72 | Title: newDate, 73 | Program: ProgramName, 74 | StartDate: getAddedDate(startDate, i), 75 | EndDate: getAddedDate(startDate, i+1), 76 | } 77 | 78 | AddRound(round) 79 | fmt.Printf("%v\n", round) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /object/student.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "github.com/casdoor/casdoor-go-sdk/casdoorsdk" 19 | "xorm.io/core" 20 | ) 21 | 22 | type Student struct { 23 | Owner string `xorm:"varchar(100) notnull pk" json:"owner"` 24 | Name string `xorm:"varchar(100) notnull pk" json:"name"` 25 | Program string `xorm:"varchar(100) notnull pk" json:"program"` 26 | CreatedTime string `xorm:"varchar(100)" json:"createdTime"` 27 | OrgRepositories []*OrgRepositories `xorm:"varchar(1000)" json:"org_repositories"` 28 | Mentor string `xorm:"varchar(100)" json:"mentor"` 29 | } 30 | 31 | func GetStudents(owner string) []*Student { 32 | students := []*Student{} 33 | err := adapter.Engine.Desc("created_time").Find(&students, &Student{Owner: owner}) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | return students 39 | } 40 | 41 | func GetFilteredStudents(owner string, program string) []*Student { 42 | students := []*Student{} 43 | err := adapter.Engine.Desc("created_time").Find(&students, &Student{Owner: owner, Program: program}) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | return students 49 | } 50 | 51 | func GetStudent(owner string, name string, program string) *Student { 52 | student := Student{Owner: owner, Name: name, Program: program} 53 | existed, err := adapter.Engine.Get(&student) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | if existed { 59 | return &student 60 | } else { 61 | return nil 62 | } 63 | } 64 | 65 | func UpdateStudent(owner string, name string, program string, student *Student) bool { 66 | if GetStudent(owner, name, program) == nil { 67 | return false 68 | } 69 | 70 | _, err := adapter.Engine.ID(core.PK{owner, name, program}).AllCols().Update(student) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | //return affected != 0 76 | return true 77 | } 78 | 79 | func AddStudent(student *Student) bool { 80 | affected, err := adapter.Engine.Insert(student) 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | return affected != 0 86 | } 87 | 88 | func DeleteStudent(student *Student) bool { 89 | affected, err := adapter.Engine.ID(core.PK{student.Owner, student.Name, student.Program}).Delete(&Student{}) 90 | if err != nil { 91 | panic(err) 92 | } 93 | 94 | return affected != 0 95 | } 96 | 97 | func GetStudentGithubMap(students []*Student, users []*casdoorsdk.User) map[string]string { 98 | userMap := make(map[string]*casdoorsdk.User) 99 | studentGithubMap := make(map[string]string) 100 | 101 | for i := range users { 102 | userMap[users[i].Name] = users[i] 103 | } 104 | 105 | for i := range students { 106 | var githubUsername string 107 | studentName := students[i].Name 108 | 109 | user, ok := userMap[studentName] 110 | if ok { 111 | githubUsername, ok = user.Properties["oauth_GitHub_username"] 112 | if !ok { 113 | githubUsername = user.GitHub 114 | } 115 | studentGithubMap[studentName] = githubUsername 116 | } 117 | } 118 | return studentGithubMap 119 | } 120 | -------------------------------------------------------------------------------- /object/student_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | ) 21 | 22 | func TestAddStudents(t *testing.T) { 23 | InitConfig() 24 | 25 | startDate := getDateFromString("2022-01-13") 26 | 27 | for i := 0; i < 24; i++ { 28 | for j := 0; j < 60; j++ { 29 | student := &Student{ 30 | Owner: "admin", 31 | Name: fmt.Sprintf("%02d-%02d", i, j), 32 | Program: ProgramName, 33 | CreatedTime: fmt.Sprintf("%sT00:00:00+08:00", startDate), 34 | OrgRepositories: []*OrgRepositories{}, 35 | Mentor: "", 36 | } 37 | 38 | AddStudent(student) 39 | fmt.Printf("%v\n", student) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /object/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "context" 19 | "log" 20 | 21 | "github.com/chromedp/chromedp" 22 | ) 23 | 24 | func getContext() (context.Context, context.CancelFunc) { 25 | options := append( 26 | chromedp.DefaultExecAllocatorOptions[:], 27 | //chromedp.ExecPath("C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"), 28 | //chromedp.ExecPath("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"), 29 | chromedp.ExecPath("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"), 30 | chromedp.Flag("headless", true), 31 | chromedp.Flag("disable-web-security", true), 32 | chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"), 33 | //chromedp.WindowSize(400, 711), 34 | //chromedp.WindowSize(1920, 1080), 35 | ) 36 | allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...) 37 | 38 | ctx, cancel2 := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf)) 39 | //ctx, cancel2 := chromedp.NewContext(allocCtx, chromedp.WithDebugf(log.Printf)) 40 | 41 | // create a timeout 42 | //ctx, cancel = context.WithTimeout(ctx, 15*time.Second) 43 | //defer cancel() 44 | 45 | cancelWait := func() { 46 | cancel() 47 | cancel2() 48 | } 49 | 50 | return ctx, cancelWait 51 | } 52 | -------------------------------------------------------------------------------- /proxy/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "net/http" 21 | "time" 22 | 23 | "github.com/astaxie/beego" 24 | "golang.org/x/net/proxy" 25 | ) 26 | 27 | var DefaultHttpClient *http.Client 28 | var ProxyHttpClient *http.Client 29 | 30 | func InitHttpClient() { 31 | // not use proxy 32 | DefaultHttpClient = http.DefaultClient 33 | 34 | // use proxy 35 | ProxyHttpClient = getProxyHttpClient() 36 | } 37 | 38 | func isAddressOpen(address string) bool { 39 | timeout := time.Millisecond * 100 40 | conn, err := net.DialTimeout("tcp", address, timeout) 41 | if err != nil { 42 | // cannot connect to address, proxy is not active 43 | return false 44 | } 45 | 46 | if conn != nil { 47 | defer conn.Close() 48 | fmt.Printf("Socks5 proxy enabled: %s\n", address) 49 | return true 50 | } 51 | 52 | return false 53 | } 54 | 55 | func getProxyHttpClient() *http.Client { 56 | httpProxy := beego.AppConfig.String("httpProxy") 57 | if httpProxy == "" { 58 | return &http.Client{} 59 | } 60 | 61 | if !isAddressOpen(httpProxy) { 62 | return &http.Client{} 63 | } 64 | 65 | // https://stackoverflow.com/questions/33585587/creating-a-go-socks5-client 66 | dialer, err := proxy.SOCKS5("tcp", httpProxy, nil, proxy.Direct) 67 | if err != nil { 68 | panic(err) 69 | } 70 | 71 | tr := &http.Transport{Dial: dialer.Dial} 72 | return &http.Client{ 73 | Transport: tr, 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /routers/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package routers 16 | 17 | import ( 18 | "net/http" 19 | "strings" 20 | 21 | "github.com/astaxie/beego/context" 22 | "github.com/casbin/casbin-oa/util" 23 | ) 24 | 25 | func Static(ctx *context.Context) { 26 | urlPath := ctx.Request.URL.Path 27 | if strings.HasPrefix(urlPath, "/api/") { 28 | return 29 | } 30 | 31 | path := "web/build" 32 | if urlPath == "/" { 33 | path += "/index.html" 34 | } else { 35 | path += urlPath 36 | } 37 | 38 | if util.FileExist(path) { 39 | http.ServeFile(ctx.ResponseWriter, ctx.Request, path) 40 | } else { 41 | http.ServeFile(ctx.ResponseWriter, ctx.Request, "web/build/index.html") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package routers 16 | 17 | import ( 18 | "github.com/astaxie/beego" 19 | 20 | "github.com/casbin/casbin-oa/controllers" 21 | ) 22 | 23 | func init() { 24 | initAPI() 25 | } 26 | 27 | func initAPI() { 28 | ns := 29 | beego.NewNamespace("/api", 30 | beego.NSInclude( 31 | &controllers.ApiController{}, 32 | ), 33 | ) 34 | beego.AddNamespace(ns) 35 | 36 | beego.Router("/api/signin", &controllers.ApiController{}, "POST:Signin") 37 | beego.Router("/api/signout", &controllers.ApiController{}, "POST:Signout") 38 | beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount") 39 | beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers") 40 | 41 | beego.Router("/api/get-programs", &controllers.ApiController{}, "GET:GetPrograms") 42 | beego.Router("/api/get-program", &controllers.ApiController{}, "GET:GetProgram") 43 | beego.Router("/api/update-program", &controllers.ApiController{}, "POST:UpdateProgram") 44 | beego.Router("/api/add-program", &controllers.ApiController{}, "POST:AddProgram") 45 | beego.Router("/api/delete-program", &controllers.ApiController{}, "POST:DeleteProgram") 46 | 47 | beego.Router("/api/get-students", &controllers.ApiController{}, "GET:GetStudents") 48 | beego.Router("/api/get-filtered-students", &controllers.ApiController{}, "GET:GetFilteredStudents") 49 | beego.Router("/api/get-student", &controllers.ApiController{}, "GET:GetStudent") 50 | beego.Router("/api/update-student", &controllers.ApiController{}, "POST:UpdateStudent") 51 | beego.Router("/api/add-student", &controllers.ApiController{}, "POST:AddStudent") 52 | beego.Router("/api/delete-student", &controllers.ApiController{}, "POST:DeleteStudent") 53 | 54 | beego.Router("/api/get-rounds", &controllers.ApiController{}, "GET:GetRounds") 55 | beego.Router("/api/get-filtered-rounds", &controllers.ApiController{}, "GET:GetFilteredRounds") 56 | beego.Router("/api/get-round", &controllers.ApiController{}, "GET:GetRound") 57 | beego.Router("/api/update-round", &controllers.ApiController{}, "POST:UpdateRound") 58 | beego.Router("/api/add-round", &controllers.ApiController{}, "POST:AddRound") 59 | beego.Router("/api/delete-round", &controllers.ApiController{}, "POST:DeleteRound") 60 | 61 | beego.Router("/api/get-reports", &controllers.ApiController{}, "GET:GetReports") 62 | beego.Router("/api/get-filtered-reports", &controllers.ApiController{}, "GET:GetFilteredReports") 63 | beego.Router("/api/get-report", &controllers.ApiController{}, "GET:GetReport") 64 | beego.Router("/api/update-report", &controllers.ApiController{}, "POST:UpdateReport") 65 | beego.Router("/api/add-report", &controllers.ApiController{}, "POST:AddReport") 66 | beego.Router("/api/delete-report", &controllers.ApiController{}, "POST:DeleteReport") 67 | beego.Router("/api/auto-update-report", &controllers.ApiController{}, "POST:AutoUpdateReport") 68 | 69 | beego.Router("/api/get-repositories", &controllers.ApiController{}, "Get:GetRepositoryByOrg") 70 | beego.Router("/api/get-project-columns", &controllers.ApiController{}, "Get:GetProjectColumns") 71 | beego.Router("/api/get-github-user", &controllers.ApiController{}, "Get:GetGithubUserByUsername") 72 | 73 | beego.Router("/api/get-issue", &controllers.ApiController{}, "GET:GetIssue") 74 | beego.Router("/api/get-filtered-issue", &controllers.ApiController{}, "Get:GetIssueByName") 75 | beego.Router("/api/update-issue", &controllers.ApiController{}, "POST:UpdateIssue") 76 | beego.Router("/api/add-issue", &controllers.ApiController{}, "POST:AddIssue") 77 | beego.Router("/api/delete-issue", &controllers.ApiController{}, "POST:DeleteIssue") 78 | 79 | beego.Router("/api/webhook", &controllers.ApiController{}, "Post:WebhookOpen") 80 | beego.Router("/api/is-mainland-ip", &controllers.ApiController{}, "GET:IsMainlandIp") 81 | 82 | beego.Router("/api/get-machines", &controllers.ApiController{}, "GET:GetMachines") 83 | beego.Router("/api/get-machine", &controllers.ApiController{}, "GET:GetMachine") 84 | beego.Router("/api/update-machine", &controllers.ApiController{}, "POST:UpdateMachine") 85 | beego.Router("/api/add-machine", &controllers.ApiController{}, "POST:AddMachine") 86 | beego.Router("/api/delete-machine", &controllers.ApiController{}, "POST:DeleteMachine") 87 | } 88 | -------------------------------------------------------------------------------- /ssh/ssh.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ssh 16 | 17 | import ( 18 | "net" 19 | 20 | "github.com/casbin/casbin-oa/util" 21 | "github.com/melbahja/goph" 22 | "golang.org/x/crypto/ssh" 23 | ) 24 | 25 | func RunCommand(ip string, port int, username string, password string, command string) string { 26 | client, err := goph.NewConn(&goph.Config{ 27 | User: username, 28 | Addr: ip, 29 | Port: uint(port), 30 | Auth: goph.Password(password), 31 | Timeout: goph.DefaultTimeout, 32 | Callback: func(host string, remote net.Addr, key ssh.PublicKey) error { 33 | return nil 34 | }, 35 | }) 36 | if err != nil { 37 | panic(err) 38 | } 39 | defer client.Close() 40 | 41 | output, err := client.Run(command) 42 | if output == nil && err != nil { 43 | panic(err) 44 | } 45 | 46 | gbkOutput, err := util.GbkToUtf8(output) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | res := string(gbkOutput) 52 | return res 53 | } 54 | -------------------------------------------------------------------------------- /util/encoding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "bytes" 19 | "io/ioutil" 20 | 21 | "golang.org/x/text/encoding/simplifiedchinese" 22 | "golang.org/x/text/transform" 23 | ) 24 | 25 | // GbkToUtf8 http://mengqi.info/html/2015/201507071345-using-golang-to-convert-text-between-gbk-and-utf-8.html 26 | func GbkToUtf8(s []byte) ([]byte, error) { 27 | reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) 28 | d, e := ioutil.ReadAll(reader) 29 | if e != nil { 30 | return nil, e 31 | } 32 | return d, nil 33 | } 34 | 35 | func Utf8ToGbk(s []byte) ([]byte, error) { 36 | reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder()) 37 | d, e := ioutil.ReadAll(reader) 38 | if e != nil { 39 | return nil, e 40 | } 41 | return d, nil 42 | } 43 | -------------------------------------------------------------------------------- /util/githubAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "net/http" 21 | "strings" 22 | 23 | "github.com/astaxie/beego" 24 | "github.com/casbin/casbin-oa/proxy" 25 | "github.com/google/go-github/v38/github" 26 | "golang.org/x/oauth2" 27 | ) 28 | 29 | func GetClient() *github.Client { 30 | accessToken := beego.AppConfig.String("githubAccessToken") 31 | if len(accessToken) == 0 { 32 | return github.NewClient(proxy.ProxyHttpClient) 33 | } else { 34 | return github.NewClient(GetHttpClient()) 35 | } 36 | } 37 | 38 | func GetHttpClient() *http.Client { 39 | accessToken := beego.AppConfig.String("githubAccessToken") 40 | ts := oauth2.StaticTokenSource( 41 | &oauth2.Token{AccessToken: accessToken}, 42 | ) 43 | c := context.WithValue(context.Background(), oauth2.HTTPClient, proxy.ProxyHttpClient) 44 | return oauth2.NewClient(c, ts) 45 | } 46 | 47 | func SetIssueLabel(owner string, repo string, number int, label string) bool { 48 | client := GetClient() 49 | issueService := client.Issues 50 | _, response, err := issueService.AddLabelsToIssue(context.Background(), owner, repo, number, []string{label}) 51 | 52 | if err != nil { 53 | return false 54 | } 55 | 56 | if response.StatusCode == 200 { 57 | return true 58 | } 59 | return false 60 | } 61 | 62 | func SetIssueAssignee(owner string, repo string, number int, assignee string) bool { 63 | client := GetClient() 64 | issueService := client.Issues 65 | _, response, err := issueService.AddAssignees(context.Background(), owner, repo, number, []string{assignee}) 66 | 67 | if err != nil { 68 | return false 69 | } 70 | 71 | if response.StatusCode == 200 { 72 | return true 73 | } 74 | return false 75 | } 76 | 77 | func AddIssueToProjectCard(cardId int64, issueId int64) bool { 78 | client := GetClient() 79 | cardOption := github.ProjectCardOptions{ContentType: "Issue", ContentID: issueId} 80 | projects := client.Projects 81 | _, response, err := projects.CreateProjectCard(context.Background(), cardId, &cardOption) 82 | if err != nil { 83 | return false 84 | } 85 | 86 | if response.StatusCode == 200 { 87 | return true 88 | } 89 | return false 90 | } 91 | 92 | func Comment(commentStr string, org string, repo string, number int) bool { 93 | client := GetClient() 94 | issues := client.Issues 95 | 96 | comment := github.IssueComment{Body: &commentStr} 97 | _, response, err := issues.CreateComment(context.Background(), org, repo, number, &comment) 98 | if err != nil { 99 | panic(err) 100 | } 101 | 102 | return response.StatusCode == 201 103 | } 104 | 105 | func AtPeople(people []string, org string, repo string, number int) bool { 106 | var commentStr string 107 | for i := range people { 108 | commentStr = fmt.Sprintf("%s @%s", commentStr, people[i]) 109 | } 110 | 111 | return Comment(commentStr, org, repo, number) 112 | } 113 | 114 | func GetIssueLabel(title string, content string) string { 115 | title = strings.ToLower(title) 116 | content = strings.ToLower(content) 117 | 118 | bugWords := []string{"bug", "wrong", "error", "broken", "failed", "disable"} 119 | for i := range bugWords { 120 | if strings.Contains(title, bugWords[i]) { 121 | return "bug" 122 | } 123 | } 124 | 125 | enhancementWords := []string{"make", "implement", "support", "update", "add", "allow", "enable", "design", "use", "extract"} 126 | for i := range enhancementWords { 127 | if strings.Contains(title, enhancementWords[i]) { 128 | return "enhancement" 129 | } 130 | } 131 | 132 | questionWords := []string{"?", "what", "how", "why"} 133 | for i := range questionWords { 134 | if strings.Contains(title, questionWords[i]) || strings.Contains(content, questionWords[i]) { 135 | return "question" 136 | } 137 | } 138 | 139 | return "question" 140 | } 141 | 142 | func GetProjectColumns(projectId int64) []*github.ProjectColumn { 143 | client := GetClient() 144 | projects := client.Projects 145 | columns, _, err := projects.ListProjectColumns(context.Background(), projectId, nil) 146 | if err != nil { 147 | panic(err) 148 | } 149 | 150 | return columns 151 | } 152 | 153 | func GetUserByUsername(githubUsername string) *github.User { 154 | client := GetClient() 155 | users := client.Users 156 | 157 | user, response, err := users.Get(context.Background(), githubUsername) 158 | if err != nil { 159 | panic(err) 160 | } 161 | 162 | if response.StatusCode == 404 { 163 | return nil 164 | } 165 | 166 | return user 167 | } 168 | 169 | func RequestReviewers(owner string, repo string, number int, reviewerNames []string) bool { 170 | client := GetClient() 171 | pullRequests := client.PullRequests 172 | 173 | reviewers := github.ReviewersRequest{Reviewers: reviewerNames} 174 | _, response, err := pullRequests.RequestReviewers(context.Background(), owner, repo, number, reviewers) 175 | if err != nil { 176 | panic(err) 177 | } 178 | 179 | return response.StatusCode == 201 180 | } 181 | 182 | func GetOrgMembers(org string) map[string]bool { 183 | client := GetClient() 184 | 185 | curPage := 1 186 | membersMap := make(map[string]bool) 187 | options := github.ListMembersOptions{ListOptions: github.ListOptions{Page: curPage, PerPage: 100}} 188 | lists, _, err := client.Organizations.ListMembers(context.Background(), org, &options) 189 | 190 | for { 191 | if err != nil { 192 | panic(err) 193 | } 194 | if len(lists) != 0 { 195 | for i := range lists { 196 | membersMap[lists[i].GetLogin()] = true 197 | } 198 | curPage++ 199 | options = github.ListMembersOptions{ListOptions: github.ListOptions{Page: curPage, PerPage: 100}} 200 | lists, _, err = client.Organizations.ListMembers(context.Background(), org, &options) 201 | } else { 202 | break 203 | } 204 | } 205 | 206 | return membersMap 207 | } 208 | -------------------------------------------------------------------------------- /util/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import "encoding/json" 18 | 19 | func StructToJson(v interface{}) string { 20 | //data, err := json.MarshalIndent(v, "", " ") 21 | data, err := json.Marshal(v) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | return string(data) 27 | } 28 | 29 | func JsonToStruct(data string, v interface{}) error { 30 | return json.Unmarshal([]byte(data), v) 31 | } 32 | -------------------------------------------------------------------------------- /util/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "github.com/astaxie/beego/context" 19 | "github.com/astaxie/beego/logs" 20 | ) 21 | 22 | func LogInfo(ctx *context.Context, f string, v ...interface{}) { 23 | logs.Info(f, v...) 24 | } 25 | -------------------------------------------------------------------------------- /util/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "io/ioutil" 19 | "os" 20 | ) 21 | 22 | func FileExist(path string) bool { 23 | if _, err := os.Stat(path); os.IsNotExist(err) { 24 | return false 25 | } 26 | return true 27 | } 28 | 29 | func ListFiles(path string) []string { 30 | res := []string{} 31 | 32 | files, err := ioutil.ReadDir(path) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | for _, f := range files { 38 | if !f.IsDir() { 39 | res = append(res, f.Name()) 40 | } 41 | } 42 | 43 | return res 44 | } 45 | -------------------------------------------------------------------------------- /util/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "errors" 19 | "io/ioutil" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | func ParseInt(s string) int { 25 | i, err := strconv.Atoi(s) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | return i 31 | } 32 | 33 | func GetOwnerAndNameFromId(id string) (string, string) { 34 | tokens := strings.Split(id, "/") 35 | if len(tokens) != 2 { 36 | panic(errors.New("GetOwnerAndNameFromId() error, wrong token count for ID: " + id)) 37 | } 38 | 39 | return tokens[0], tokens[1] 40 | } 41 | 42 | func ReadStringFromPath(path string) string { 43 | data, err := ioutil.ReadFile(path) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | return string(data) 49 | } 50 | 51 | func WriteStringToPath(s string, path string) { 52 | err := ioutil.WriteFile(path, []byte(s), 0644) 53 | if err != nil { 54 | panic(err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /util/time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import "time" 18 | 19 | func GetCurrentTime() string { 20 | timestamp := time.Now().Unix() 21 | tm := time.Unix(timestamp, 0) 22 | return tm.Format(time.RFC3339) 23 | } 24 | 25 | func GetCurrentTimeFormatted() string { 26 | timestamp := time.Now().Unix() 27 | tm := time.Unix(timestamp, 0) 28 | return tm.Format("2006-01-02 15:04:05") 29 | } 30 | -------------------------------------------------------------------------------- /util/webhookAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "io" 21 | 22 | "github.com/google/go-github/v38/github" 23 | ) 24 | 25 | func GetOrgWebhooks(org string) []*github.Hook { 26 | client := GetClient() 27 | organizations := client.Organizations 28 | hooks, response, err := organizations.ListHooks(context.Background(), org, nil) 29 | 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | if response.StatusCode == 404 { 35 | return nil 36 | } 37 | 38 | return hooks 39 | } 40 | 41 | func GetWebhookDelivers(org string, webhook int64) []*github.HookDelivery { 42 | client := GetClient() 43 | organizations := client.Organizations 44 | 45 | option := github.ListCursorOptions{PerPage: 100} 46 | 47 | var deliveries []*github.HookDelivery 48 | var response *github.Response 49 | var err error 50 | times := 0 51 | for { 52 | deliveries, response, err = organizations.ListHookDeliveries(context.Background(), org, webhook, &option) 53 | if err != nil { 54 | times += 1 55 | fmt.Printf("GetWebhookDelivers() error: %s, org = %s, times = %d\n", err.Error(), org, times) 56 | if times >= 5 { 57 | panic(err) 58 | } 59 | } else { 60 | break 61 | } 62 | } 63 | 64 | if response.StatusCode == 404 { 65 | return nil 66 | } 67 | 68 | return deliveries 69 | } 70 | 71 | func RedeliverWebhook(org string, webhook int64, delivery int64) { 72 | url := fmt.Sprintf("https://api.github.com/orgs/%v/hooks/%v/deliveries/%v/attempts", org, webhook, delivery) 73 | httpClient := GetHttpClient() 74 | var buf io.ReadWriter 75 | httpClient.Post(url, "application/json", buf) 76 | } 77 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /web/craco.config.js: -------------------------------------------------------------------------------- 1 | const CracoLessPlugin = require('craco-less'); 2 | 3 | module.exports = { 4 | plugins: [ 5 | { 6 | plugin: CracoLessPlugin, 7 | options: { 8 | lessLoaderOptions: { 9 | lessOptions: { 10 | modifyVars: { '@primary-color': 'rgb(45,120,213)' }, 11 | javascriptEnabled: true, 12 | }, 13 | }, 14 | }, 15 | }, 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "4.6.2", 7 | "@craco/craco": "^6.3.0", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.3.2", 10 | "@testing-library/user-event": "^7.1.2", 11 | "antd": "4.15.5", 12 | "casdoor-js-sdk": "^0.0.3", 13 | "codemirror": "^5.57.0", 14 | "copy-to-clipboard": "^3.3.1", 15 | "craco-less": "^1.20.0", 16 | "file-saver": "^2.0.5", 17 | "moment": "^2.27.0", 18 | "react": "^17.0.2", 19 | "react-codemirror2": "^7.2.1", 20 | "react-csv": "^2.0.3", 21 | "react-device-detect": "^1.12.1", 22 | "react-dom": "^17.0.2", 23 | "react-github-corner": "^2.5.0", 24 | "react-markdown": "^4.3.1", 25 | "react-router-dom": "^5.2.0", 26 | "react-scripts": "4.0.3" 27 | }, 28 | "scripts": { 29 | "start": "set PORT=9000 && craco --openssl-legacy-provider start", 30 | "build": "craco --openssl-legacy-provider build", 31 | "test": "craco test", 32 | "eject": "craco eject" 33 | }, 34 | "eslintConfig": { 35 | "extends": "react-app" 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /web/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casbin/casbin-oa/7a6a67b29004dd5a338e5cdf3c93996a956b1557/web/public/favicon.png -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Casbin OA 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /web/src/App.less: -------------------------------------------------------------------------------- 1 | @import '~antd/dist/antd.less'; 2 | 3 | .App { 4 | text-align: center; 5 | } 6 | 7 | .App-logo { 8 | height: 40vmin; 9 | pointer-events: none; 10 | } 11 | 12 | .App-header { 13 | background-color: #282c34; 14 | min-height: 100vh; 15 | display: flex; 16 | flex-direction: column; 17 | align-items: center; 18 | justify-content: center; 19 | font-size: calc(10px + 2vmin); 20 | color: white; 21 | } 22 | 23 | .App-link { 24 | color: #61dafb; 25 | } 26 | 27 | #parent-area { 28 | position: relative; 29 | min-height: 100vh; 30 | } 31 | 32 | #content-wrap { 33 | padding-bottom: 70px; /* Footer height */ 34 | } 35 | 36 | #footer { 37 | position: absolute; 38 | bottom: 0; 39 | width: 100%; 40 | height: 70px; /* Footer height */ 41 | } 42 | 43 | .rightDropDown { 44 | &:hover { 45 | background-color: #f5f5f5; 46 | color: rgba(0, 0, 0, 0.85); 47 | } 48 | 49 | color: rgba(0, 0, 0, 0.85); 50 | } 51 | -------------------------------------------------------------------------------- /web/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /web/src/AuthCallback.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Button, Result, Spin} from "antd"; 17 | import {withRouter} from "react-router-dom"; 18 | import * as Setting from "./Setting"; 19 | 20 | class AuthCallback extends React.Component { 21 | constructor(props) { 22 | super(props); 23 | this.state = { 24 | classes: props, 25 | msg: null, 26 | }; 27 | } 28 | 29 | componentWillMount() { 30 | this.login(); 31 | } 32 | 33 | getFromLink() { 34 | const from = sessionStorage.getItem("from"); 35 | if (from === null) { 36 | return "/"; 37 | } 38 | return from; 39 | } 40 | 41 | login() { 42 | Setting.signin().then((res) => { 43 | if (res.status === "ok") { 44 | Setting.showMessage("success", `Logged in successfully`) 45 | 46 | const link = this.getFromLink(); 47 | Setting.goToLink(link); 48 | } else { 49 | this.setState({ 50 | msg: res.msg, 51 | }); 52 | } 53 | }); 54 | } 55 | 56 | render() { 57 | return ( 58 |
59 | { 60 | (this.state.msg === null) ? ( 61 | 62 | ) : ( 63 |
64 | 70 | Details 71 | , 72 | , 73 | ]} 74 | /> 75 |
76 | ) 77 | } 78 |
79 | ); 80 | } 81 | } 82 | 83 | export default withRouter(AuthCallback); 84 | -------------------------------------------------------------------------------- /web/src/Conf.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | export const ShowGithubCorner = false; 16 | export const GithubRepo = "https://github.com/casbin/casbin-oa"; 17 | 18 | export const DefaultProgramName = "talent2023"; 19 | 20 | export const AuthConfig = { 21 | // serverUrl: "https://door.casbin.com", 22 | serverUrl: "http://localhost:7001", 23 | clientId: "0ba528121ea87b3eb54d", 24 | appName: "app-casbin-oa", 25 | organizationName: "casbin", 26 | redirectPath: "/callback", 27 | }; 28 | 29 | export const Assignees = ["hsluoyz"] 30 | export const defaultOrgs = ["casbin", "casdoor" ,"casnode", "casbin-lua", "casbin-net", "casbin-rs", "casbin-ex", "casbin-ruby", "casbin-cpp", "casbin-js", "casbin4d", "node-casbin", "scala-casbin", "dart-casbin", "pycasbin", "jcasbin", "php-casbin", "SwiftCasbin", "k8s-authz"] 31 | 32 | export const CasnodeEndpoint = "https://forum.casbin.com"; 33 | -------------------------------------------------------------------------------- /web/src/CustomGithubCorner.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import * as Conf from "./Conf"; 17 | import GithubCorner from "react-github-corner"; 18 | 19 | class CustomGithubCorner extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | classes: props, 24 | }; 25 | } 26 | 27 | render() { 28 | if (!Conf.ShowGithubCorner) { 29 | return null; 30 | } 31 | 32 | return ( 33 | 34 | ); 35 | } 36 | } 37 | 38 | export default CustomGithubCorner; 39 | -------------------------------------------------------------------------------- /web/src/IssueListPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Link} from "react-router-dom"; 17 | import {Button, Col, Popconfirm, Row, Table} from "antd"; 18 | import * as Setting from "./Setting"; 19 | import * as issueBackend from "./backend/issueBackend"; 20 | 21 | class IssueListPage extends React.Component { 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | classes: props, 26 | issues: null, 27 | }; 28 | } 29 | 30 | componentWillMount() { 31 | this.getIssues(); 32 | } 33 | 34 | getIssues() { 35 | issueBackend.getIssues().then(issues => { 36 | this.setState({ 37 | issues: issues, 38 | }); 39 | }); 40 | } 41 | 42 | 43 | addNewIssue() { 44 | const newIssue = this.newIssue() 45 | issueBackend.addIssue(newIssue).then(res => { 46 | Setting.showMessage("success", "issue added successfully"); 47 | this.setState({ 48 | issues: Setting.prependRow(this.state.issues, newIssue), 49 | }) 50 | }).catch(error => { 51 | Setting.showMessage("error", `issue failed to add: ${error}`) 52 | }) 53 | } 54 | 55 | newIssue() { 56 | return { 57 | name: `issue_${this.state.issues.length}`, 58 | org: `casbin`, 59 | repo: 'All', 60 | assignee: 'hsluoyz', 61 | project_name: '', 62 | project_id: -1, 63 | at_people: [], 64 | } 65 | } 66 | 67 | deleteIssue(index) { 68 | issueBackend.deleteIssue(this.state.issues[index]) 69 | .then((res) => { 70 | Setting.showMessage("success", `issue deleted successfully`); 71 | this.setState({ 72 | issues: Setting.deleteRow(this.state.issues, index), 73 | }); 74 | }) 75 | .catch(error => { 76 | Setting.showMessage("error", `issue failed to delete: ${error}`); 77 | }); 78 | } 79 | 80 | renderTable(issues) { 81 | const columns = [ 82 | { 83 | title: 'Name', 84 | dataIndex: 'name', 85 | key: 'name', 86 | width: '150px', 87 | sorter: (a, b) => a.name.localeCompare(b.name), 88 | render: (text, record, index) => { 89 | return ( 90 | {text} 91 | ) 92 | } 93 | }, 94 | { 95 | title: 'Organization', 96 | dataIndex: 'org', 97 | key: 'org', 98 | width: '120px', 99 | sorter: (a, b) => a.org.localeCompare(b.org), 100 | render: (text, record, index) => { 101 | return ( 102 | {text} 103 | ) 104 | } 105 | }, 106 | { 107 | title: 'Repository', 108 | dataIndex: 'repo', 109 | key: 'repo', 110 | width: '200px', 111 | sorter: (a, b) => a.repo.localeCompare(b.repo), 112 | render: (text, record, index) => { 113 | if (text !== "All") { 114 | return ( 115 | {text} 116 | ) 117 | } else { 118 | return "All" 119 | } 120 | } 121 | }, 122 | { 123 | title: 'Assignee', 124 | dataIndex: 'assignee', 125 | key: 'assignee', 126 | width: '120px', 127 | sorter: (a, b) => a.assignee.localeCompare(b.assignee), 128 | }, 129 | { 130 | title: 'At people', 131 | dataIndex: 'at_people', 132 | key: 'at_people', 133 | sorter: (a, b) => a.at_people.localeCompare(b.at_people), 134 | 135 | render: (text, record, index) => { 136 | let at_people = "" 137 | for (let i = 0; i < text.length - 1; i++) { 138 | at_people += `${text[i]} , ` 139 | } 140 | let lastPeople = text.length > 0 ? text[text.length - 1] : "" 141 | at_people += lastPeople 142 | return at_people; 143 | } 144 | }, 145 | { 146 | title: 'Project', 147 | dataIndex: 'project_name', 148 | key: 'project_name', 149 | width: '250px', 150 | sorter: (a, b) => a.project_name.localeCompare(b.project_name), 151 | }, 152 | { 153 | title: 'Action', 154 | dataIndex: '', 155 | key: 'op', 156 | width: '160px', 157 | render: (text, record, index) => { 158 | return ( 159 |
160 | 162 | this.deleteIssue(index)} 165 | disabled={!Setting.isAdminUser(this.props.account)} 166 | > 167 | 169 | 170 |
171 | ) 172 | } 173 | }, 174 | ]; 175 | 176 | return ( 177 |
178 | ( 180 |
181 | Issues     182 | 184 |
185 | )} 186 | loading={issues === null} 187 | /> 188 | 189 | ); 190 | } 191 | 192 | render() { 193 | return ( 194 |
195 | 196 |
197 | 198 | 199 | { 200 | this.renderTable(this.state.issues) 201 | } 202 | 203 | 204 | 205 | 206 | 207 | ); 208 | } 209 | } 210 | 211 | export default IssueListPage 212 | -------------------------------------------------------------------------------- /web/src/ProgramEditPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Button, Card, Col, DatePicker, Input, Row, Switch} from 'antd'; 17 | import {LinkOutlined} from "@ant-design/icons"; 18 | import * as ProgramBackend from "./backend/ProgramBackend"; 19 | import * as Setting from "./Setting"; 20 | import moment from "moment"; 21 | 22 | class ProgramEditPage extends React.Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | classes: props, 27 | programName: props.match.params.programName, 28 | program: null, 29 | tasks: [], 30 | resources: [], 31 | }; 32 | } 33 | 34 | componentWillMount() { 35 | this.getProgram(); 36 | } 37 | 38 | getProgram() { 39 | ProgramBackend.getProgram("admin", this.state.programName) 40 | .then((program) => { 41 | this.setState({ 42 | program: program, 43 | }); 44 | }); 45 | } 46 | 47 | parseProgramField(key, value) { 48 | // if ([].includes(key)) { 49 | // value = Setting.myParseInt(value); 50 | // } 51 | return value; 52 | } 53 | 54 | updateProgramField(key, value) { 55 | value = this.parseProgramField(key, value); 56 | 57 | let program = this.state.program; 58 | program[key] = value; 59 | this.setState({ 60 | program: program, 61 | }); 62 | } 63 | 64 | renderProgram() { 65 | return ( 66 | 68 | Edit Program     69 | 70 | 71 | } style={{marginLeft: '5px'}} type="inner"> 72 | 73 | 74 | Name: 75 | 76 | 77 | { 78 | this.updateProgramField('name', e.target.value); 79 | }} /> 80 | 81 | 82 | 83 | 84 | Title: 85 | 86 | 87 | { 88 | this.updateProgramField('title', e.target.value); 89 | }} /> 90 | 91 | 92 | 93 | 94 | Link: 95 | 96 | 97 | } value={this.state.program.url} onChange={e => { 98 | this.updateProgramField('url', e.target.value); 99 | }} /> 100 | 101 | 102 | 103 | 104 | Start date: 105 | 106 | 107 | { 108 | this.updateProgramField('startDate', timeString); 109 | }} /> 110 | 111 | 112 | 113 | 114 | End date: 115 | 116 | 117 | { 118 | this.updateProgramField('endDate', timeString); 119 | }} /> 120 | 121 | 122 | 123 | 124 | Provider 2: 125 | 126 | 127 | { 128 | this.updateProgramField('provider2', e.target.value); 129 | }} /> 130 | 131 | 132 | 133 | 134 | Can apply: 135 | 136 | 137 | { 138 | this.updateProgramField('canApply', checked); 139 | }} /> 140 | 141 | 142 | 143 | ) 144 | } 145 | 146 | submitProgramEdit() { 147 | let program = Setting.deepCopy(this.state.program); 148 | ProgramBackend.updateProgram(this.state.program.owner, this.state.programName, program) 149 | .then((res) => { 150 | if (res) { 151 | Setting.showMessage("success", `Successfully saved`); 152 | this.setState({ 153 | programName: this.state.program.name, 154 | }); 155 | this.props.history.push(`/programs/${this.state.program.name}`); 156 | } else { 157 | Setting.showMessage("error", `failed to save: server side failure`); 158 | this.updateProgramField('name', this.state.programName); 159 | } 160 | }) 161 | .catch(error => { 162 | Setting.showMessage("error", `failed to save: ${error}`); 163 | }); 164 | } 165 | 166 | render() { 167 | return ( 168 |
169 | 170 |
171 | 172 | 173 | { 174 | this.state.program !== null ? this.renderProgram() : null 175 | } 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | ); 189 | } 190 | } 191 | 192 | export default ProgramEditPage; 193 | -------------------------------------------------------------------------------- /web/src/ReportEditPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Button, Card, Col, Input, Row} from 'antd'; 17 | import * as ReportBackend from "./backend/ReportBackend"; 18 | import * as Setting from "./Setting"; 19 | 20 | import {Controlled as CodeMirror} from 'react-codemirror2' 21 | import "codemirror/lib/codemirror.css" 22 | require("codemirror/mode/markdown/markdown"); 23 | 24 | class ReportEditPage extends React.Component { 25 | constructor(props) { 26 | super(props); 27 | this.state = { 28 | classes: props, 29 | reportName: props.match.params.reportName, 30 | report: null, 31 | tasks: [], 32 | resources: [], 33 | }; 34 | } 35 | 36 | componentWillMount() { 37 | this.getReport(); 38 | } 39 | 40 | getReport() { 41 | ReportBackend.getReport("admin", this.state.reportName) 42 | .then((report) => { 43 | this.setState({ 44 | report: report, 45 | }); 46 | }); 47 | } 48 | 49 | parseReportField(key, value) { 50 | if (["score"].includes(key)) { 51 | value = Setting.myParseInt(value); 52 | } 53 | return value; 54 | } 55 | 56 | updateReportField(key, value) { 57 | value = this.parseReportField(key, value); 58 | 59 | let report = this.state.report; 60 | report[key] = value; 61 | this.setState({ 62 | report: report, 63 | }); 64 | } 65 | 66 | renderReport() { 67 | return ( 68 | 70 | Edit Report     71 | 72 | 73 | } style={{marginLeft: '5px'}} type="inner"> 74 | 75 | 76 | Name: 77 | 78 | 79 | { 80 | this.updateReportField('name', e.target.value); 81 | }} /> 82 | 83 | 84 | 85 | 86 | Program: 87 | 88 | 89 | { 90 | this.updateReportField('program', e.target.value); 91 | }} /> 92 | 93 | 94 | 95 | 96 | Round: 97 | 98 | 99 | { 100 | this.updateReportField('round', e.target.value); 101 | }} /> 102 | 103 | 104 | 105 | 106 | Student: 107 | 108 | 109 | { 110 | this.updateReportField('student', e.target.value); 111 | }} /> 112 | 113 | 114 | 115 | 116 | Mentor: 117 | 118 | 119 | { 120 | this.updateReportField('mentor', e.target.value); 121 | }} /> 122 | 123 | 124 | 125 | 126 | Text: 127 | 128 | 129 | { 133 | this.updateReportField('text', value); 134 | }} 135 | /> 136 | 137 | 138 | 139 | 140 | Score: 141 | 142 | 143 | { 144 | this.updateReportField('score', e.target.value); 145 | }} /> 146 | 147 | 148 | 149 | ) 150 | } 151 | 152 | submitReportEdit() { 153 | let report = Setting.deepCopy(this.state.report); 154 | ReportBackend.updateReport(this.state.report.owner, this.state.reportName, report) 155 | .then((res) => { 156 | if (res) { 157 | Setting.showMessage("success", `Successfully saved`); 158 | this.setState({ 159 | reportName: this.state.report.name, 160 | }); 161 | this.props.history.push(`/reports/${this.state.report.name}`); 162 | } else { 163 | Setting.showMessage("error", `failed to save: server side failure`); 164 | this.updateReportField('name', this.state.reportName); 165 | } 166 | }) 167 | .catch(error => { 168 | Setting.showMessage("error", `failed to save: ${error}`); 169 | }); 170 | } 171 | 172 | render() { 173 | return ( 174 |
175 | 176 |
177 | 178 | 179 | { 180 | this.state.report !== null ? this.renderReport() : null 181 | } 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | ); 195 | } 196 | } 197 | 198 | export default ReportEditPage; 199 | -------------------------------------------------------------------------------- /web/src/RoundEditPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Button, Card, Col, DatePicker, Select, Input, Row} from 'antd'; 17 | import * as RoundBackend from "./backend/RoundBackend"; 18 | import * as ProgramBackend from "./backend/ProgramBackend" 19 | import * as Setting from "./Setting"; 20 | import moment from "moment"; 21 | 22 | const { Option } = Select; 23 | 24 | class RoundEditPage extends React.Component { 25 | constructor(props) { 26 | super(props); 27 | this.state = { 28 | classes: props, 29 | roundName: props.match.params.roundName, 30 | round: null, 31 | tasks: [], 32 | resources: [], 33 | programs: [], 34 | }; 35 | } 36 | 37 | componentWillMount() { 38 | this.getRound(); 39 | this.getPrograms(); 40 | } 41 | 42 | getPrograms() { 43 | ProgramBackend.getPrograms("admin") 44 | .then(res => { 45 | this.setState({ 46 | programs: res, 47 | }); 48 | }); 49 | } 50 | 51 | getRound() { 52 | RoundBackend.getRound("admin", this.state.roundName) 53 | .then((round) => { 54 | this.setState({ 55 | round: round, 56 | }); 57 | }); 58 | } 59 | 60 | parseRoundField(key, value) { 61 | // if ([].includes(key)) { 62 | // value = Setting.myParseInt(value); 63 | // } 64 | return value; 65 | } 66 | 67 | updateRoundField(key, value) { 68 | value = this.parseRoundField(key, value); 69 | 70 | let round = this.state.round; 71 | round[key] = value; 72 | this.setState({ 73 | round: round, 74 | }); 75 | } 76 | 77 | renderRound() { 78 | return ( 79 | 81 | Edit Round     82 | 83 | 84 | } style={{marginLeft: '5px'}} type="inner"> 85 | 86 | 87 | Name: 88 | 89 | 90 | { 91 | this.updateRoundField('name', e.target.value); 92 | }} /> 93 | 94 | 95 | 96 | 97 | Title: 98 | 99 | 100 | { 101 | this.updateRoundField('title', e.target.value); 102 | }} /> 103 | 104 | 105 | 106 | 107 | Program: 108 | 109 | 110 | 115 | 116 | 117 | 118 | 119 | Start date: 120 | 121 | 122 | { 123 | this.updateRoundField('startDate', timeString); 124 | }} /> 125 | 126 | 127 | 128 | 129 | End date: 130 | 131 | 132 | { 133 | this.updateRoundField('endDate', timeString); 134 | }} /> 135 | 136 | 137 | 138 | ) 139 | } 140 | 141 | submitRoundEdit() { 142 | let round = Setting.deepCopy(this.state.round); 143 | RoundBackend.updateRound(this.state.round.owner, this.state.roundName, round) 144 | .then((res) => { 145 | if (res) { 146 | Setting.showMessage("success", `Successfully saved`); 147 | this.setState({ 148 | roundName: this.state.round.name, 149 | }); 150 | this.props.history.push(`/rounds`); 151 | } else { 152 | Setting.showMessage("error", `failed to save: server side failure`); 153 | this.updateRoundField('name', this.state.roundName); 154 | } 155 | }) 156 | .catch(error => { 157 | Setting.showMessage("error", `failed to save: ${error}`); 158 | }); 159 | } 160 | 161 | render() { 162 | return ( 163 |
164 | 165 |
166 | 167 | 168 | { 169 | this.state.round !== null ? this.renderRound() : null 170 | } 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | ); 184 | } 185 | } 186 | 187 | export default RoundEditPage; 188 | -------------------------------------------------------------------------------- /web/src/RoundListPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Link} from "react-router-dom"; 17 | import {Button, Col, Popconfirm, Row, Table} from 'antd'; 18 | import moment from "moment"; 19 | import * as Setting from "./Setting"; 20 | import * as RoundBackend from "./backend/RoundBackend"; 21 | 22 | class RoundListPage extends React.Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | classes: props, 27 | rounds: null, 28 | }; 29 | } 30 | 31 | componentWillMount() { 32 | this.getRounds(); 33 | } 34 | 35 | getRounds() { 36 | RoundBackend.getRounds("admin") 37 | .then((res) => { 38 | this.setState({ 39 | rounds: res, 40 | }); 41 | }); 42 | } 43 | 44 | newRound() { 45 | return { 46 | owner: "admin", // this.props.account.name, 47 | name: `round_${this.state.rounds.length}`, 48 | createdTime: moment().format(), 49 | title: `New Round - ${this.state.rounds.length}`, 50 | startDate: "2020-01-23", 51 | endDate: "2020-01-23", 52 | } 53 | } 54 | 55 | addRound() { 56 | const newRound = this.newRound(); 57 | RoundBackend.addRound(newRound) 58 | .then((res) => { 59 | Setting.showMessage("success", `Round added successfully`); 60 | this.setState({ 61 | rounds: Setting.prependRow(this.state.rounds, newRound), 62 | }); 63 | }) 64 | .catch(error => { 65 | Setting.showMessage("error", `Round failed to add: ${error}`); 66 | }); 67 | } 68 | 69 | deleteRound(i) { 70 | RoundBackend.deleteRound(this.state.rounds[i]) 71 | .then((res) => { 72 | Setting.showMessage("success", `Round deleted successfully`); 73 | this.setState({ 74 | rounds: Setting.deleteRow(this.state.rounds, i), 75 | }); 76 | }) 77 | .catch(error => { 78 | Setting.showMessage("error", `Round failed to delete: ${error}`); 79 | }); 80 | } 81 | 82 | renderTable(rounds) { 83 | const columns = [ 84 | { 85 | title: 'Name', 86 | dataIndex: 'name', 87 | key: 'name', 88 | width: '120px', 89 | sorter: (a, b) => a.name.localeCompare(b.name), 90 | render: (text, record, index) => { 91 | return ( 92 | {text} 93 | ) 94 | } 95 | }, 96 | { 97 | title: 'Title', 98 | dataIndex: 'title', 99 | key: 'title', 100 | // width: '80px', 101 | sorter: (a, b) => a.title.localeCompare(b.title), 102 | }, 103 | { 104 | title: 'Created time', 105 | dataIndex: 'createdTime', 106 | key: 'createdTime', 107 | width: '160px', 108 | sorter: (a, b) => a.createdTime.localeCompare(b.createdTime), 109 | render: (text, record, index) => { 110 | return Setting.getFormattedDate(text); 111 | } 112 | }, 113 | { 114 | title: 'Program', 115 | dataIndex: 'program', 116 | key: 'program', 117 | width: '120px', 118 | sorter: (a, b) => a.program.localeCompare(b.program), 119 | render: (text, record, index) => { 120 | return ( 121 | {text} 122 | ) 123 | } 124 | }, 125 | { 126 | title: 'Start date', 127 | dataIndex: 'startDate', 128 | key: 'startDate', 129 | width: '120px', 130 | sorter: (a, b) => a.startDate.localeCompare(b.startDate), 131 | }, 132 | { 133 | title: 'End date', 134 | dataIndex: 'endDate', 135 | key: 'endDate', 136 | width: '120px', 137 | sorter: (a, b) => a.endDate.localeCompare(b.endDate), 138 | }, 139 | { 140 | title: 'Action', 141 | dataIndex: '', 142 | key: 'op', 143 | width: '160px', 144 | render: (text, record, index) => { 145 | return ( 146 |
147 | 148 | this.deleteRound(index)} 151 | disabled={!Setting.isAdminUser(this.props.account)} 152 | > 153 | 154 | 155 |
156 | ) 157 | } 158 | }, 159 | ]; 160 | 161 | return ( 162 |
163 |
( 165 |
166 | Rounds     167 | 168 |
169 | )} 170 | loading={rounds === null} 171 | /> 172 | 173 | ); 174 | } 175 | 176 | render() { 177 | return ( 178 |
179 | 180 |
181 | 182 | 183 | { 184 | this.renderTable(this.state.rounds) 185 | } 186 | 187 | 188 | 189 | 190 | 191 | ); 192 | } 193 | } 194 | 195 | export default RoundListPage; 196 | -------------------------------------------------------------------------------- /web/src/Setting.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import {message} from "antd"; 16 | import {isMobile as isMobileDevice} from "react-device-detect"; 17 | import Sdk from "casdoor-js-sdk"; 18 | 19 | export let ServerUrl = ''; 20 | export let CasdoorSdk; 21 | 22 | export function initServerUrl() { 23 | const hostname = window.location.hostname; 24 | if (hostname === 'localhost') { 25 | ServerUrl = `http://${hostname}:10000`; 26 | } 27 | } 28 | 29 | export function initCasdoorSdk(config) { 30 | CasdoorSdk = new Sdk(config); 31 | } 32 | 33 | export function getSignupUrl() { 34 | return CasdoorSdk.getSignupUrl(); 35 | } 36 | 37 | export function getSigninUrl() { 38 | return CasdoorSdk.getSigninUrl(); 39 | } 40 | 41 | export function getUserProfileUrl(userName, account) { 42 | return CasdoorSdk.getUserProfileUrl(userName, account); 43 | } 44 | 45 | export function getMyProfileUrl(account) { 46 | return CasdoorSdk.getMyProfileUrl(account); 47 | } 48 | 49 | export function signin() { 50 | return CasdoorSdk.signin(ServerUrl); 51 | } 52 | 53 | export function parseJson(s) { 54 | if (s === "") { 55 | return null; 56 | } else { 57 | return JSON.parse(s); 58 | } 59 | } 60 | 61 | export function myParseInt(i) { 62 | const res = parseInt(i); 63 | return isNaN(res) ? 0 : res; 64 | } 65 | 66 | export function openLink(link) { 67 | // this.props.history.push(link); 68 | const w = window.open('about:blank'); 69 | w.location.href = link; 70 | } 71 | 72 | export function goToLink(link) { 73 | window.location.href = link; 74 | } 75 | 76 | export function showMessage(type, text) { 77 | if (type === "success") { 78 | message.success(text); 79 | } else if (type === "error") { 80 | message.error(text); 81 | } else if (type === "warning") { 82 | message.warn(text) 83 | } 84 | } 85 | 86 | export function isAdminUser(account) { 87 | return account?.isAdmin; 88 | } 89 | 90 | export function deepCopy(obj) { 91 | return Object.assign({}, obj); 92 | } 93 | 94 | export function addRow(array, row) { 95 | return [...array, row]; 96 | } 97 | 98 | export function prependRow(array, row) { 99 | return [row, ...array]; 100 | } 101 | 102 | export function insertRow(array, i, row) { 103 | return [].concat(array.slice(0, i), row, array.slice(i)); 104 | } 105 | 106 | export function deleteRow(array, i) { 107 | // return array = array.slice(0, i).concat(array.slice(i + 1)); 108 | return [...array.slice(0, i), ...array.slice(i + 1)]; 109 | } 110 | 111 | export function swapRow(array, i, j) { 112 | return [...array.slice(0, i), array[j], ...array.slice(i + 1, j), array[i], ...array.slice(j + 1)]; 113 | } 114 | 115 | export function isMobile() { 116 | // return getIsMobileView(); 117 | return isMobileDevice; 118 | } 119 | 120 | export function getFormattedDate(date) { 121 | if (date === undefined || date === null) { 122 | return null; 123 | } 124 | 125 | date = date.replace('T', ' '); 126 | date = date.replace('+08:00', ' '); 127 | return date; 128 | } 129 | 130 | export function getFormattedDateShort(date) { 131 | return date.slice(0, 10); 132 | } 133 | 134 | export function getShortName(s) { 135 | return s.split('/').slice(-1)[0]; 136 | } 137 | 138 | export function getShortText(s, maxLength=35) { 139 | if (s.length > maxLength) { 140 | return `${s.slice(0, maxLength)}...`; 141 | } else { 142 | return s; 143 | } 144 | } 145 | 146 | export function toCsv(s) { 147 | if (s === undefined) { 148 | return ""; 149 | } 150 | 151 | if (typeof s === "string") { 152 | return s.replace(/"/g, '""'); 153 | } else { 154 | return s; 155 | } 156 | } 157 | 158 | export function getPercentage(f) { 159 | if (f === undefined) { 160 | return 0.0; 161 | } 162 | 163 | return (100 * f).toFixed(1); 164 | } 165 | 166 | function getRandomInt(s) { 167 | let hash = 0; 168 | if (s.length !== 0) { 169 | for (let i = 0; i < s.length; i ++) { 170 | let char = s.charCodeAt(i); 171 | hash = ((hash << 5) - hash) + char; 172 | hash = hash & hash; 173 | } 174 | } 175 | 176 | return hash; 177 | } 178 | 179 | export function getAvatarColor(s) { 180 | const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae']; 181 | let random = getRandomInt(s); 182 | if (random < 0) { 183 | random = -random; 184 | } 185 | return colorList[random % 4]; 186 | } 187 | 188 | export function isChineseStr(s) { 189 | if (s === undefined || s === null || s === "") { 190 | return false; 191 | } 192 | 193 | // https://www.cnblogs.com/weihanli/p/validrealnameandidcardno.html 194 | const re =/^[\u4E00-\u9FA5]{2,4}$/u; 195 | return re.test(s); 196 | } 197 | -------------------------------------------------------------------------------- /web/src/SigninPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from 'react'; 16 | import * as Setting from "./Setting"; 17 | 18 | class SigninPage extends React.Component { 19 | componentDidMount(){ 20 | window.location.replace(Setting.getSigninUrl()); 21 | } 22 | 23 | render() { 24 | return ""; 25 | } 26 | } 27 | 28 | export default SigninPage; 29 | -------------------------------------------------------------------------------- /web/src/StudentListPage.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from "react"; 16 | import {Link} from "react-router-dom"; 17 | import {Button, Col, Popconfirm, Row, Table} from 'antd'; 18 | import moment from "moment"; 19 | import * as Setting from "./Setting"; 20 | import * as StudentBackend from "./backend/StudentBackend"; 21 | 22 | class StudentListPage extends React.Component { 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | classes: props, 27 | students: null, 28 | }; 29 | } 30 | 31 | componentWillMount() { 32 | this.getStudents(); 33 | } 34 | 35 | getStudents() { 36 | StudentBackend.getStudents("admin") 37 | .then((res) => { 38 | this.setState({ 39 | students: res, 40 | }); 41 | }); 42 | } 43 | 44 | newStudent() { 45 | return { 46 | owner: "admin", // this.props.account.name, 47 | name: `student_${this.state.students.length}`, 48 | createdTime: moment().format(), 49 | program: "summer2020", 50 | mentor: "alice", 51 | } 52 | } 53 | 54 | addStudent() { 55 | const newStudent = this.newStudent(); 56 | StudentBackend.addStudent(newStudent) 57 | .then((res) => { 58 | Setting.showMessage("success", `Student added successfully`); 59 | this.setState({ 60 | students: Setting.prependRow(this.state.students, newStudent), 61 | }); 62 | }) 63 | .catch(error => { 64 | Setting.showMessage("error", `Student failed to add: ${error}`); 65 | }); 66 | } 67 | 68 | deleteStudent(i) { 69 | StudentBackend.deleteStudent(this.state.students[i]) 70 | .then((res) => { 71 | Setting.showMessage("success", `Student deleted successfully`); 72 | this.setState({ 73 | students: Setting.deleteRow(this.state.students, i), 74 | }); 75 | }) 76 | .catch(error => { 77 | Setting.showMessage("error", `Student failed to delete: ${error}`); 78 | }); 79 | } 80 | 81 | renderTable(students) { 82 | const columns = [ 83 | { 84 | title: 'Name', 85 | dataIndex: 'name', 86 | key: 'name', 87 | width: '120px', 88 | sorter: (a, b) => a.name.localeCompare(b.name), 89 | render: (text, record, index) => { 90 | return ( 91 | {text} 92 | ) 93 | } 94 | }, 95 | { 96 | title: 'Created time', 97 | dataIndex: 'createdTime', 98 | key: 'createdTime', 99 | width: '160px', 100 | sorter: (a, b) => a.createdTime.localeCompare(b.createdTime), 101 | render: (text, record, index) => { 102 | return Setting.getFormattedDate(text); 103 | } 104 | }, 105 | { 106 | title: 'Program', 107 | dataIndex: 'program', 108 | key: 'program', 109 | width: '120px', 110 | sorter: (a, b) => a.program.localeCompare(b.program), 111 | render: (text, record, index) => { 112 | return ( 113 | {text} 114 | ) 115 | } 116 | }, 117 | { 118 | title: 'Repositories', 119 | dataIndex: 'org_repositories', 120 | key: 'createdTime', 121 | // width: '160px', 122 | sorter: (a, b) => a.createdTime.localeCompare(b.createdTime), 123 | render: (text, record, index) => { 124 | let repositories = '' 125 | if (text !== null && text !== undefined){ 126 | text.forEach(item => { 127 | repositories += item.organization+'[' +item.repositories+']\n'; 128 | }) 129 | return repositories 130 | } 131 | else 132 | return "" 133 | } 134 | }, 135 | { 136 | title: 'Mentor', 137 | dataIndex: 'mentor', 138 | key: 'mentor', 139 | width: '120px', 140 | sorter: (a, b) => a.mentor.localeCompare(b.mentor), 141 | render: (text, record, index) => { 142 | return ( 143 | {text} 144 | ) 145 | } 146 | }, 147 | { 148 | title: 'Action', 149 | dataIndex: '', 150 | key: 'op', 151 | width: '160px', 152 | render: (text, record, index) => { 153 | return ( 154 |
155 | 156 | this.deleteStudent(index)} 159 | disabled={!Setting.isAdminUser(this.props.account)} 160 | > 161 | 162 | 163 |
164 | ) 165 | } 166 | }, 167 | ]; 168 | 169 | return ( 170 |
171 |
( 173 |
174 | Students     175 | 176 |
177 | )} 178 | loading={students === null} 179 | /> 180 | 181 | ); 182 | } 183 | 184 | render() { 185 | return ( 186 |
187 | 188 |
189 | 190 | 191 | { 192 | this.renderTable(this.state.students) 193 | } 194 | 195 | 196 | 197 | 198 | 199 | ); 200 | } 201 | } 202 | 203 | export default StudentListPage; 204 | -------------------------------------------------------------------------------- /web/src/backend/AccountBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getAccount() { 18 | return fetch(`${Setting.ServerUrl}/api/get-account`, { 19 | method: 'GET', 20 | credentials: 'include' 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getUsers(owner) { 25 | return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function signout() { 32 | return fetch(`${Setting.ServerUrl}/api/signout`, { 33 | method: 'POST', 34 | credentials: "include", 35 | }).then(res => res.json()); 36 | } 37 | -------------------------------------------------------------------------------- /web/src/backend/MachineBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getMachines(owner) { 18 | return fetch(`${Setting.ServerUrl}/api/get-machines?owner=${owner}`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getMachine(owner, name) { 25 | return fetch(`${Setting.ServerUrl}/api/get-machine?id=${owner}/${encodeURIComponent(name)}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function updateMachine(owner, name, machine) { 32 | let newMachine = Setting.deepCopy(machine); 33 | return fetch(`${Setting.ServerUrl}/api/update-machine?id=${owner}/${encodeURIComponent(name)}`, { 34 | method: 'POST', 35 | credentials: 'include', 36 | body: JSON.stringify(newMachine), 37 | }).then(res => res.json()); 38 | } 39 | 40 | export function addMachine(machine) { 41 | let newMachine = Setting.deepCopy(machine); 42 | return fetch(`${Setting.ServerUrl}/api/add-machine`, { 43 | method: 'POST', 44 | credentials: 'include', 45 | body: JSON.stringify(newMachine), 46 | }).then(res => res.json()); 47 | } 48 | 49 | export function deleteMachine(machine) { 50 | let newMachine = Setting.deepCopy(machine); 51 | return fetch(`${Setting.ServerUrl}/api/delete-machine`, { 52 | method: 'POST', 53 | credentials: 'include', 54 | body: JSON.stringify(newMachine), 55 | }).then(res => res.json()); 56 | } 57 | -------------------------------------------------------------------------------- /web/src/backend/ProgramBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getPrograms(owner) { 18 | return fetch(`${Setting.ServerUrl}/api/get-programs?owner=${owner}`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getProgram(owner, name) { 25 | return fetch(`${Setting.ServerUrl}/api/get-program?id=${owner}/${encodeURIComponent(name)}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function updateProgram(owner, name, program) { 32 | let newProgram = Setting.deepCopy(program); 33 | return fetch(`${Setting.ServerUrl}/api/update-program?id=${owner}/${encodeURIComponent(name)}`, { 34 | method: 'POST', 35 | credentials: 'include', 36 | body: JSON.stringify(newProgram), 37 | }).then(res => res.json()); 38 | } 39 | 40 | export function addProgram(program) { 41 | let newProgram = Setting.deepCopy(program); 42 | return fetch(`${Setting.ServerUrl}/api/add-program`, { 43 | method: 'POST', 44 | credentials: 'include', 45 | body: JSON.stringify(newProgram), 46 | }).then(res => res.json()); 47 | } 48 | 49 | export function deleteProgram(program) { 50 | let newProgram = Setting.deepCopy(program); 51 | return fetch(`${Setting.ServerUrl}/api/delete-program`, { 52 | method: 'POST', 53 | credentials: 'include', 54 | body: JSON.stringify(newProgram), 55 | }).then(res => res.json()); 56 | } 57 | -------------------------------------------------------------------------------- /web/src/backend/ReportBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getReports(owner) { 18 | return fetch(`${Setting.ServerUrl}/api/get-reports?owner=${owner}`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getFilteredReports(owner, program) { 25 | return fetch(`${Setting.ServerUrl}/api/get-filtered-reports?owner=${owner}&program=${program}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function getReport(owner, name) { 32 | return fetch(`${Setting.ServerUrl}/api/get-report?id=${owner}/${encodeURIComponent(name)}`, { 33 | method: "GET", 34 | credentials: "include" 35 | }).then(res => res.json()); 36 | } 37 | 38 | export function updateReport(owner, name, report) { 39 | let newReport = Setting.deepCopy(report); 40 | return fetch(`${Setting.ServerUrl}/api/update-report?id=${owner}/${encodeURIComponent(name)}`, { 41 | method: 'POST', 42 | credentials: 'include', 43 | body: JSON.stringify(newReport), 44 | }).then(res => res.json()); 45 | } 46 | 47 | export function addReport(report) { 48 | let newReport = Setting.deepCopy(report); 49 | return fetch(`${Setting.ServerUrl}/api/add-report`, { 50 | method: 'POST', 51 | credentials: 'include', 52 | body: JSON.stringify(newReport), 53 | }).then(res => res.json()); 54 | } 55 | 56 | export function deleteReport(report) { 57 | let newReport = Setting.deepCopy(report); 58 | return fetch(`${Setting.ServerUrl}/api/delete-report`, { 59 | method: 'POST', 60 | credentials: 'include', 61 | body: JSON.stringify(newReport), 62 | }).then(res => res.json()); 63 | } 64 | 65 | export function autoUpdateReport(owner, name, student, githubUsername, curRound){ 66 | return fetch(`${Setting.ServerUrl}/api/auto-update-report?id=${owner}/${encodeURIComponent(name)}&startDate=${curRound.startDate}&endDate=${curRound.endDate}&author=${githubUsername}`, { 67 | method: 'Post', 68 | credentials: 'include', 69 | body: JSON.stringify(student) 70 | }).then(res => res.json()); 71 | 72 | } 73 | 74 | export function getRepositoriesByOrg(org){ 75 | if (org === ""){ 76 | return false 77 | } 78 | return fetch(`${Setting.ServerUrl}/api/get-repositories?org=${org}`,{ 79 | method: "GET", 80 | credentials: "include" 81 | }).then(res => res.json()) 82 | 83 | } 84 | -------------------------------------------------------------------------------- /web/src/backend/RoundBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getRounds(owner) { 18 | return fetch(`${Setting.ServerUrl}/api/get-rounds?owner=${owner}`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getFilteredRounds(owner, program) { 25 | return fetch(`${Setting.ServerUrl}/api/get-filtered-rounds?owner=${owner}&program=${program}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function getRound(owner, name) { 32 | return fetch(`${Setting.ServerUrl}/api/get-round?id=${owner}/${encodeURIComponent(name)}`, { 33 | method: "GET", 34 | credentials: "include" 35 | }).then(res => res.json()); 36 | } 37 | 38 | export function updateRound(owner, name, round) { 39 | let newRound = Setting.deepCopy(round); 40 | return fetch(`${Setting.ServerUrl}/api/update-round?id=${owner}/${encodeURIComponent(name)}`, { 41 | method: 'POST', 42 | credentials: 'include', 43 | body: JSON.stringify(newRound), 44 | }).then(res => res.json()); 45 | } 46 | 47 | export function addRound(round) { 48 | let newRound = Setting.deepCopy(round); 49 | return fetch(`${Setting.ServerUrl}/api/add-round`, { 50 | method: 'POST', 51 | credentials: 'include', 52 | body: JSON.stringify(newRound), 53 | }).then(res => res.json()); 54 | } 55 | 56 | export function deleteRound(round) { 57 | let newRound = Setting.deepCopy(round); 58 | return fetch(`${Setting.ServerUrl}/api/delete-round`, { 59 | method: 'POST', 60 | credentials: 'include', 61 | body: JSON.stringify(newRound), 62 | }).then(res => res.json()); 63 | } 64 | -------------------------------------------------------------------------------- /web/src/backend/StudentBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getStudents(owner) { 18 | return fetch(`${Setting.ServerUrl}/api/get-students?owner=${owner}`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getFilteredStudents(owner, program) { 25 | return fetch(`${Setting.ServerUrl}/api/get-filtered-students?owner=${owner}&program=${program}`, { 26 | method: "GET", 27 | credentials: "include" 28 | }).then(res => res.json()); 29 | } 30 | 31 | export function getStudent(owner, name, program) { 32 | return fetch(`${Setting.ServerUrl}/api/get-student?owner=${owner}&name=${name}&program=${program}`, { 33 | method: "GET", 34 | credentials: "include" 35 | }).then(res => res.json()); 36 | } 37 | 38 | export function updateStudent(owner, name, program, student) { 39 | let newStudent = Setting.deepCopy(student); 40 | return fetch(`${Setting.ServerUrl}/api/update-student?owner=${owner}&name=${name}&program=${program}`, { 41 | method: 'POST', 42 | credentials: 'include', 43 | body: JSON.stringify(newStudent), 44 | }).then(res => res.json()); 45 | } 46 | 47 | export function addStudent(student) { 48 | let newStudent = Setting.deepCopy(student); 49 | return fetch(`${Setting.ServerUrl}/api/add-student`, { 50 | method: 'POST', 51 | credentials: 'include', 52 | body: JSON.stringify(newStudent), 53 | }).then(res => res.json()); 54 | } 55 | 56 | export function deleteStudent(student) { 57 | let newStudent = Setting.deepCopy(student); 58 | return fetch(`${Setting.ServerUrl}/api/delete-student`, { 59 | method: 'POST', 60 | credentials: 'include', 61 | body: JSON.stringify(newStudent), 62 | }).then(res => res.json()); 63 | } 64 | -------------------------------------------------------------------------------- /web/src/backend/issueBackend.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as Setting from "../Setting"; 16 | 17 | export function getIssues() { 18 | return fetch(`${Setting.ServerUrl}/api/get-issue`, { 19 | method: "GET", 20 | credentials: "include" 21 | }).then(res => res.json()); 22 | } 23 | 24 | export function getIssue(name) { 25 | return fetch(`${Setting.ServerUrl}/api/get-filtered-issue?name=${name}`, { 26 | method: "Get", 27 | credentials: "include" 28 | }).then(res => res.json()) 29 | } 30 | 31 | export function addIssue(issueWebhook) { 32 | let newIssueWebhook = Setting.deepCopy(issueWebhook); 33 | return fetch(`${Setting.ServerUrl}/api/add-issue`, { 34 | method: 'POST', 35 | credentials: 'include', 36 | body: JSON.stringify(newIssueWebhook), 37 | }).then(res => res.json()); 38 | } 39 | 40 | export function updateIssue(name, issueWebhook) { 41 | let newIssueWebhook = Setting.deepCopy(issueWebhook); 42 | return fetch(`${Setting.ServerUrl}/api/update-issue?name=${name}`, { 43 | method: 'POST', 44 | credentials: 'include', 45 | body: JSON.stringify(newIssueWebhook), 46 | }).then(res => res.json()); 47 | } 48 | 49 | export function deleteIssue(issueWebhook) { 50 | let newIssueWebhook = Setting.deepCopy(issueWebhook); 51 | return fetch(`${Setting.ServerUrl}/api/delete-issue`, { 52 | method: 'POST', 53 | credentials: 'include', 54 | body: JSON.stringify(newIssueWebhook), 55 | }).then(res => res.json()); 56 | } 57 | 58 | export function getProjectColumns() { 59 | return fetch(`${Setting.ServerUrl}/api/get-project-columns`,{ 60 | method: 'Get', 61 | credentials: 'include', 62 | }).then(res => res.json()); 63 | } 64 | 65 | export function getAvatarByUsername(username){ 66 | return fetch(`${Setting.ServerUrl}/api/get-github-user?username=${username}`,{ 67 | method: 'Get', 68 | credentials: `include` 69 | }).then(res => res.json()); 70 | } -------------------------------------------------------------------------------- /web/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | .logo { 16 | background: url("https://cdn.casbin.org/img/casbin_logo_1024x256.png"); 17 | background-size: 108px, 33px; 18 | width: 108px; 19 | height: 27px; 20 | /*background: rgba(0, 0, 0, 0.2);*/ 21 | margin: 17px 10px 16px 20px; 22 | float: left; 23 | } 24 | 25 | .ant-table.ant-table-middle .ant-table-title, .ant-table.ant-table-middle .ant-table-footer, .ant-table.ant-table-middle thead > tr > th, .ant-table.ant-table-middle tbody > tr > td { 26 | padding: 1px 8px !important; 27 | } 28 | 29 | .ant-list-sm .ant-list-item { 30 | padding: 2px !important; 31 | } 32 | 33 | .alert-row { 34 | font-weight: bolder; 35 | background: #ffccc7; 36 | } 37 | 38 | .self-row { 39 | font-weight: bolder; 40 | background: #d9f7be; 41 | } 42 | -------------------------------------------------------------------------------- /web/src/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The casbin Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import React from 'react'; 16 | import ReactDOM from 'react-dom'; 17 | import './index.css'; 18 | import App from './App'; 19 | import * as serviceWorker from './serviceWorker'; 20 | import { BrowserRouter } from 'react-router-dom' 21 | 22 | ReactDOM.render( 23 | 24 | 25 | , 26 | document.getElementById('root') 27 | ); 28 | 29 | // If you want your app to work offline and load faster, you can change 30 | // unregister() to register() below. Note this comes with some pitfalls. 31 | // Learn more about service workers: https://bit.ly/CRA-PWA 32 | serviceWorker.unregister(); 33 | -------------------------------------------------------------------------------- /web/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /web/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | --------------------------------------------------------------------------------