Compare commits

...

3 Commits

9 changed files with 127 additions and 22 deletions

View File

@ -8,7 +8,7 @@
<img alt="docker pull casbin/casdoor" src="https://img.shields.io/docker/pulls/casbin/casdoor.svg"> <img alt="docker pull casbin/casdoor" src="https://img.shields.io/docker/pulls/casbin/casdoor.svg">
</a> </a>
<a href="https://github.com/casdoor/casdoor/actions/workflows/build.yml"> <a href="https://github.com/casdoor/casdoor/actions/workflows/build.yml">
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casbin/jcasbin/workflows/build/badge.svg?style=flat-square"> <img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/releases/latest"> <a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg">
@ -42,65 +42,48 @@
</a> </a>
</p> </p>
## Online demo ## Online demo
- International: https://door.casdoor.org (read-only) - International: https://door.casdoor.org (read-only)
- Asian mirror: https://door.casdoor.com (read-only) - Asian mirror: https://door.casdoor.com (read-only)
- Asian mirror: https://demo.casdoor.com (read-write, will restore for every 5 minutes) - Asian mirror: https://demo.casdoor.com (read-write, will restore for every 5 minutes)
## Documentation ## Documentation
- International: https://casdoor.org - International: https://casdoor.org
- Asian mirror: https://docs.casdoor.cn - Asian mirror: https://docs.casdoor.cn
## Install ## Install
- By source code: https://casdoor.org/docs/basic/server-installation - By source code: https://casdoor.org/docs/basic/server-installation
- By Docker: https://casdoor.org/docs/basic/try-with-docker - By Docker: https://casdoor.org/docs/basic/try-with-docker
## How to connect to Casdoor? ## How to connect to Casdoor?
https://casdoor.org/docs/how-to-connect/overview https://casdoor.org/docs/how-to-connect/overview
## Casdoor Public API ## Casdoor Public API
- Docs: https://casdoor.org/docs/basic/public-api - Docs: https://casdoor.org/docs/basic/public-api
- Swagger: https://door.casdoor.com/swagger - Swagger: https://door.casdoor.com/swagger
## Integrations ## Integrations
https://casdoor.org/docs/integration/apisix https://casdoor.org/docs/integration/apisix
## How to contact? ## How to contact?
- Gitter: https://gitter.im/casbin/casdoor - Gitter: https://gitter.im/casbin/casdoor
- Forum: https://forum.casbin.com - Forum: https://forum.casbin.com
- Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e - Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e
## Contribute ## Contribute
For casdoor, if you have any questions, you can give Issues, or you can also directly start Pull Requests(but we recommend giving issues first to communicate with the community). For casdoor, if you have any questions, you can give Issues, or you can also directly start Pull Requests(but we recommend giving issues first to communicate with the community).
### I18n translation ### I18n translation
If you are contributing to casdoor, please note that we use [Crowdin](https://crowdin.com/project/casdoor-site) as translating platform and i18next as translating tool. When you add some words using i18next in the ```web/``` directory, please remember to add what you have added to the ```web/src/locales/en/data.json``` file. If you are contributing to casdoor, please note that we use [Crowdin](https://crowdin.com/project/casdoor-site) as translating platform and i18next as translating tool. When you add some words using i18next in the `web/` directory, please remember to add what you have added to the `web/src/locales/en/data.json` file.
## License ## License

70
deployment/deploy.go Normal file
View File

@ -0,0 +1,70 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package deployment
import (
"fmt"
"os"
"strings"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/storage"
"github.com/casdoor/casdoor/util"
"github.com/casdoor/oss"
)
func deployStaticFiles(provider *object.Provider) {
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
if storageProvider == nil {
panic(fmt.Sprintf("the provider type: %s is not supported", provider.Type))
}
uploadFolder(storageProvider, "js")
uploadFolder(storageProvider, "css")
updateHtml(provider.Domain)
}
func uploadFolder(storageProvider oss.StorageInterface, folder string) {
path := fmt.Sprintf("../web/build/static/%s/", folder)
filenames := util.ListFiles(path)
for _, filename := range filenames {
if !strings.HasSuffix(filename, folder) {
continue
}
file, err := os.Open(path + filename)
if err != nil {
panic(err)
}
objectKey := fmt.Sprintf("static/%s/%s", folder, filename)
_, err = storageProvider.Put(objectKey, file)
if err != nil {
panic(err)
}
fmt.Printf("Uploaded [%s] to [%s]\n", path, objectKey)
}
}
func updateHtml(domainPath string) {
htmlPath := "../web/build/index.html"
html := util.ReadStringFromPath(htmlPath)
html = strings.Replace(html, "\"/static/", fmt.Sprintf("\"%s", domainPath), -1)
util.WriteStringToPath(html, htmlPath)
fmt.Printf("Updated HTML to [%s]\n", html)
}

29
deployment/deploy_test.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !skipCi
// +build !skipCi
package deployment
import (
"testing"
"github.com/casdoor/casdoor/object"
)
func TestDeployStaticFiles(t *testing.T) {
provider := object.GetProvider("admin/provider_storage_aliyun_oss")
deployStaticFiles(provider)
}

View File

@ -16,6 +16,7 @@ package util
import ( import (
"fmt" "fmt"
"io/ioutil"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
@ -43,6 +44,23 @@ func EnsureFileFolderExists(path string) {
} }
} }
func ListFiles(path string) []string {
res := []string{}
files, err := ioutil.ReadDir(path)
if err != nil {
panic(err)
}
for _, f := range files {
if !f.IsDir() {
res = append(res, f.Name())
}
}
return res
}
func RemoveExt(filename string) string { func RemoveExt(filename string) string {
return filename[:len(filename)-len(filepath.Ext(filename))] return filename[:len(filename)-len(filepath.Ext(filename))]
} }

View File

@ -89,6 +89,7 @@ export const ResetModal = (props) => {
textBefore={i18next.t("code:Code You Received")} textBefore={i18next.t("code:Code You Received")}
onChange={setCode} onChange={setCode}
onButtonClickArgs={[dest, destType, Setting.getApplicationName(application)]} onButtonClickArgs={[dest, destType, Setting.getApplicationName(application)]}
application={application}
/> />
</Row> </Row>
</Col> </Col>

View File

@ -353,11 +353,13 @@ class ForgetPage extends React.Component {
<CountDownInput <CountDownInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.state.application), this.state.name]} onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.state.application), this.state.name]}
application={application}
/> />
) : ( ) : (
<CountDownInput <CountDownInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.state.application), this.state.name]} onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.state.application), this.state.name]}
application={application}
/> />
)} )}
</Form.Item> </Form.Item>

View File

@ -625,6 +625,7 @@ class LoginPage extends React.Component {
<CountDownInput <CountDownInput
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone} disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
application={application}
/> />
</Form.Item> </Form.Item>
) : ( ) : (

View File

@ -360,6 +360,7 @@ class SignupPage extends React.Component {
<CountDownInput <CountDownInput
disabled={!this.state.validEmail} disabled={!this.state.validEmail}
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
application={application}
/> />
</Form.Item> </Form.Item>
} }
@ -412,6 +413,7 @@ class SignupPage extends React.Component {
<CountDownInput <CountDownInput
disabled={!this.state.validPhone} disabled={!this.state.validPhone}
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
application={application}
/> />
</Form.Item> </Form.Item>
</React.Fragment> </React.Fragment>

View File

@ -17,13 +17,12 @@ import React from "react";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import {SafetyOutlined} from "@ant-design/icons"; import {SafetyOutlined} from "@ant-design/icons";
import {authConfig} from "../auth/Auth";
import {CaptchaWidget} from "./CaptchaWidget"; import {CaptchaWidget} from "./CaptchaWidget";
const {Search} = Input; const {Search} = Input;
export const CountDownInput = (props) => { export const CountDownInput = (props) => {
const {disabled, textBefore, onChange, onButtonClickArgs} = props; const {disabled, textBefore, onChange, onButtonClickArgs, application} = props;
const [visible, setVisible] = React.useState(false); const [visible, setVisible] = React.useState(false);
const [key, setKey] = React.useState(""); const [key, setKey] = React.useState("");
const [captchaImg, setCaptchaImg] = React.useState(""); const [captchaImg, setCaptchaImg] = React.useState("");
@ -69,7 +68,7 @@ export const CountDownInput = (props) => {
}; };
const loadCaptcha = () => { const loadCaptcha = () => {
UserBackend.getCaptcha("admin", authConfig.appName, false).then(res => { UserBackend.getCaptcha(application.owner, application.name, false).then(res => {
if (res.type === "none") { if (res.type === "none") {
UserBackend.sendCode("none", "", "", ...onButtonClickArgs).then(res => { UserBackend.sendCode("none", "", "", ...onButtonClickArgs).then(res => {
if (res) { if (res) {