diff --git a/.gitignore b/.gitignore index ce1ee4c..f504977 100644 --- a/.gitignore +++ b/.gitignore @@ -11,14 +11,6 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Dependency directories (remove the comment below to include it) -# vendor/ - -*.ini -!package/configs/ini/example.ini -!package/configs/ini/test.ini - -artifacts/ dist.zip # Ignore generated files @@ -27,3 +19,5 @@ dist.zip bin/ __debug_bin** + +override.toml \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 36534fa..f1e48b3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,6 @@ "ms-azuretools.vscode-docker", "tamasfe.even-better-toml", "pkief.material-icon-theme", - "bierner.markdown-preview-github-styles", + "davidanson.vscode-markdownlint" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index f2eb9ea..f4f7017 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,7 @@ { "material-icon-theme.folders.associations": { - "application": "core", - "mapper": "database", - "migrate_db": "import", - "service": "components", - "business": "hook", - "postman": "api", - ".github/workflows": "github", - "test-collection": "resource" + "problem_packages": "packages", + "modules/agent": "client" }, "go.testFlags": [ "-v", diff --git a/Dockerfile b/Dockerfile index 872f0df..64982c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,27 @@ FROM golang:latest as build -COPY . /oj-lab-platform-build +WORKDIR /workdir + +COPY . /workdir -WORKDIR /oj-lab-platform-build RUN apt update && apt install -y make zip curl RUN make build -RUN ./scripts/update-frontend-dist.sh /oj-lab-platform-build/frontend_dist +RUN make get-front FROM ubuntu:latest -WORKDIR /workdir +WORKDIR /oj-lab-platform + +COPY --from=build /workdir/bin/web_server /usr/local/bin/web_server +COPY --from=build /workdir/frontend/dist ./frontend_dist -COPY --from=build /oj-lab-platform-build/bin/web_server /usr/local/bin/web_server -COPY --from=build /oj-lab-platform-build/frontend_dist /workdir/frontend_dist +COPY config.toml ./config.toml -COPY workdirs/docker/config.toml /workdir/config.toml +ENV OJ_LAB_SERVICE_ENV='production' +ENV DATABASE_DSN='user=postgres password=postgres host=host.docker.internal port=5432 dbname=oj_lab sslmode=disable TimeZone=Asia/Shanghai' +ENV REDIS_HOSTS='["host.docker.internal:6379"]' +ENV MINIO_ENDPOINT='http://host.docker.internal:9000' -ENV OJ_LAB_SERVICE_ENV=production -ENV OJ_LAB_WORKDIR=/workdir EXPOSE 8080 CMD [ "web_server" ] \ No newline at end of file diff --git a/Makefile b/Makefile index c41f1db..d6b2d96 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,33 @@ OS := $(shell uname -s) -DEV_WORKDIR := workdirs/development -DB_DOCKER_COMPOSE_FILE := $(DEV_WORKDIR)/docker-compose.yml -JUDGER_DOCKER_COMPOSE_FILE := $(DEV_WORKDIR)/judger/docker-compose.yml -FRONTEND_DIST_DIR := $(DEV_WORKDIR)/frontend_dist +FRONTEND_DIST_DIR := frontend/dist +FRONTEND_DIST_URL := https://github.com/oj-lab/oj-lab-front/releases/download/v0.0.3/dist.zip +ICPC_PROBLEM_PACKAGES_DIR := problem_packages/icpc +ICPC_PROBLEM_PACKAGES_URL := https://github.com/oj-lab/problem-packages/releases/download/v0.0.1/icpc_problem.zip .PHONY: help help: @echo "Usage: make [target]" @echo "" @echo "Targets:" - @echo " build - Build the application, swagger document will be generated" - @echo " run - Run the application" - @echo " clean - Clean the build" - @echo " check - Run go vet" - @echo " test - Run tests, database will be setup" - @echo " gen-swagger - Generate swagger document" - @echo " setup-dependencies - Setup the dependencies docker image" - @echo " unset-dependencies - Unset the dependencies docker image" - @echo " get-front - Get the frontend files" + @echo " build - Build the application, swagger document will be generated" + @echo " run - Run the application" + @echo " clean - Clean the build" + @echo " check - Run go vet" + @echo " test - Run tests, database will be setup" + @echo " gen-swagger - Generate swagger document" + @echo " setup-dependencies - Setup the dependencies docker image" + @echo " unset-dependencies - Unset the dependencies docker image" + @echo " get-front - Get the frontend files" + @echo " update-front - Update the frontend files" + @echo " get-problem-packages - Get the problem packages" + @echo " update-problem-packages - Update the problem packages" .PHONY: build build: gen-swagger gen-proto @echo "Building on $(OS)" go mod tidy - go build -o bin/init_db cmd/init_db/main.go - go build -o bin/web_server cmd/web_server/main.go - go build -o bin/schedule cmd/schedule/main.go - go build -o bin/problem_loader cmd/problem_loader/main.go + go build -o bin/ ./cmd/... .PHONY: run run: build @@ -56,23 +56,35 @@ gen-proto: install-proto .PHONY: unset-dependencies unset-dependencies: - docker compose -f $(JUDGER_DOCKER_COMPOSE_FILE) -p oj-lab-judger stop - docker compose -f $(JUDGER_DOCKER_COMPOSE_FILE) -p oj-lab-judger rm -f - docker compose -f $(DB_DOCKER_COMPOSE_FILE) -p oj-lab-dbs stop - docker compose -f $(DB_DOCKER_COMPOSE_FILE) -p oj-lab-dbs rm -f + docker compose stop + docker compose rm -f .PHONY: setup-dependencies -setup-dependencies: unset-dependencies build - docker compose -f $(DB_DOCKER_COMPOSE_FILE) -p oj-lab-dbs up -d +setup-dependencies: unset-dependencies build get-front get-problem-packages + docker compose up -d postgres redis minio clickhouse adminer @echo "Wait 10 seconds for db setup" sleep 10s - ./bin/init_db - ./bin/problem_loader - docker compose -f $(JUDGER_DOCKER_COMPOSE_FILE) -p oj-lab-judger up -d + ./bin/init .PHONY: get-front get-front: - ./scripts/update-frontend-dist.sh $(FRONTEND_DIST_DIR) + ./scripts/download_and_unzip.sh $(FRONTEND_DIST_DIR) $(FRONTEND_DIST_URL) \ + OVERRIDE=false + +.PHONY: update-front +update-front: + ./scripts/download_and_unzip.sh $(FRONTEND_DIST_DIR) $(FRONTEND_DIST_URL) \ + OVERRIDE=true + +.PHONY: get-problem-packages +get-problem-packages: + ./scripts/download_and_unzip.sh $(ICPC_PROBLEM_PACKAGES_DIR) $(ICPC_PROBLEM_PACKAGES_URL) \ + OVERRIDE=false + +.PHONY: update-problem-packages +update-problem-packages: + ./scripts/download_and_unzip.sh $(ICPC_PROBLEM_PACKAGES_DIR) $(ICPC_PROBLEM_PACKAGES_URL) \ + OVERRIDE=true .PHONY: check check: gen-proto install-cilint @@ -101,7 +113,7 @@ install-cilint: .PHONY: install-proto install-proto: @# Referencing https://grpc.io/docs/protoc-installation/ - @./scripts/install-protoc.sh + @./scripts/install_protoc.sh @# Track https://grpc.io/docs/languages/go/quickstart/ for update go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 \ No newline at end of file diff --git a/README.md b/README.md index 84e83d0..7c1547a 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,11 @@ OJ Lab Platform depends on several foundational services, including: -- PostgreSQL (or other SQL database in the future) -- Redis -- MinIO -- [Judger](https://github.com/oj-lab/judger) +- PostgreSQL (or other SQL database in the future) for data storage +- Redis for caching & session management +- MinIO (or other S3 like storage) for file storage +- ClickHouse for analytics (currently not developed) +- [Judger](https://github.com/oj-lab/judger) for judging This project provides a Makefile to help you quickly set up dependencies & other optional choices. Run `make setup-dependencies` to start these services and load the initial data. @@ -24,31 +25,18 @@ Run `make setup-dependencies` to start these services and load the initial data. Launch the programs with VSCode launch configurations is the most recommended way. It will automatically set the environment and run the program in debug mode. -### Optional +### Run Judger -There is also some optional approach you may want to use. +There is a `judger` service in the project's `docker-compose.yml`. +It won't start from the `make setup-dependencies` command by default +(since it takes time to let MinIO & PostgreSQL start up). -#### Serve frontend - -Use `make get-front` to get the frontend dist codes. -> In development config by default, it points to the postion where the frontend codes are located, -> so you will automatically get the frontend view when you start the program. - -#### Generate Swagger docs - -Use `make gen-swagger` to generate swagger docs. - -#### Set environment variables - -The following environment variables are available to modify the behavior of the program: - -- OJ_LAB_SERVICE_ENV: The environment of the service, default to `development` -- OJ_LAB_WORKDIR: Directly set the path of the workdir, application will automatically locate the workdir if not set -(it will be set to `workdirs/` in this project by default) +Run `docker-compose up -d judger` to start the judger service. #### Manage DB data -Along with the `make setup-dependencies`, we provide `adminer` to access PostgreSQL & MinIO with its web interface. +Along with the `make setup-dependencies`, +we provide `adminer` to access PostgreSQL & MinIO with its web interface. > Remember to set the type of the database to `PostgreSQL` when login to adminer. diff --git a/cmd/init_db/main.go b/cmd/init/db.go similarity index 81% rename from cmd/init_db/main.go rename to cmd/init/db.go index a7522e2..562e5d3 100644 --- a/cmd/init_db/main.go +++ b/cmd/init/db.go @@ -6,12 +6,11 @@ import ( judge_model "github.com/oj-lab/oj-lab-platform/models/judge" problem_model "github.com/oj-lab/oj-lab-platform/models/problem" user_model "github.com/oj-lab/oj-lab-platform/models/user" - casbin_agent "github.com/oj-lab/oj-lab-platform/modules/agent/casbin" gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) -func main() { +func initDB() { db := gorm_agent.GetDefaultDB() err := db.AutoMigrate( &user_model.User{}, @@ -58,10 +57,5 @@ func main() { if err != nil { panic("failed to create anonymous user") } - log.AppLogger().Info("migrate tables ans users success") - - err = casbin_agent.LoadDefaultCasbinPolicies() - if err != nil { - panic(fmt.Sprintf("failed to load default casbin policies: %v", err)) - } + log_module.AppLogger().Info("migrate tables ans users success") } diff --git a/cmd/init/main.go b/cmd/init/main.go new file mode 100644 index 0000000..6b6337c --- /dev/null +++ b/cmd/init/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "context" + "fmt" + + casbin_agent "github.com/oj-lab/oj-lab-platform/modules/agent/casbin" +) + +func main() { + ctx := context.Background() + initDB() + loadProblemPackages(ctx) + err := casbin_agent.LoadDefaultCasbinPolicies() + if err != nil { + panic(fmt.Sprintf("failed to load default casbin policies: %v", err)) + } +} diff --git a/cmd/problem_loader/main.go b/cmd/init/problem_package.go similarity index 62% rename from cmd/problem_loader/main.go rename to cmd/init/problem_package.go index f9a5e7f..8db48ba 100644 --- a/cmd/problem_loader/main.go +++ b/cmd/init/problem_package.go @@ -13,14 +13,12 @@ import ( problem_model "github.com/oj-lab/oj-lab-platform/models/problem" gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm" minio_agent "github.com/oj-lab/oj-lab-platform/modules/agent/minio" - "github.com/oj-lab/oj-lab-platform/modules/config" - "github.com/oj-lab/oj-lab-platform/modules/log" - yaml "gopkg.in/yaml.v2" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" + "gopkg.in/yaml.v2" ) -var ctx = context.Background() - -func main() { +func loadProblemPackages(ctx context.Context) { db := gorm_agent.GetDefaultDB() minioClient := minio_agent.GetMinioClient() @@ -31,13 +29,13 @@ func main() { // parse problem.md as description. // 2. insert object into minio storage. var ( - packagePath string = path.Join(config.Workdir, "problem_packages") + packagePath string = path.Join(config_module.ProjectRoot(), "problem_packages/icpc") title string slug string ) err := filepath.Walk(packagePath, func(path string, info fs.FileInfo, err error) error { if err != nil { - log.AppLogger().WithError(err).Error("Walk package path failed") + log_module.AppLogger().WithError(err).Error("Walk package path failed") return err } if info == nil { @@ -47,31 +45,35 @@ func main() { return nil } relativePath := strings.Replace(path, packagePath, "", 1) - log.AppLogger().WithField("relativePath", relativePath).Debug("Read file from package") + log_module.AppLogger().WithField("relativePath", relativePath).Debug("Read file from package") if filepath.Base(relativePath) == "problem.yaml" { resultMap := make(map[string]interface{}) yamlFile, err := os.ReadFile(path) if err != nil { - log.AppLogger().WithError(err).Error("Read problem.yaml failed") + log_module.AppLogger().WithError(err).Error("Read problem.yaml failed") } err = yaml.Unmarshal(yamlFile, &resultMap) if err != nil { - log.AppLogger().WithError(err).Error("Unmarshal problem.yaml failed") + log_module.AppLogger().WithError(err).Error("Unmarshal problem.yaml failed") + } + if resultMap["name"] == nil { + log_module.AppLogger().Error("Problem name is nil") + return nil } title = resultMap["name"].(string) if title == "" { - log.AppLogger().Error("Problem title is empty") + log_module.AppLogger().Error("Problem title is empty") } slug = strings.Split(relativePath, "/")[1] - log.AppLogger().WithField("title", title).WithField("slug", slug).Debug("Read problem.yaml") + log_module.AppLogger().WithField("title", title).WithField("slug", slug).Debug("Read problem.yaml") } if filepath.Base(relativePath) == "problem.md" { content, err := os.ReadFile(path) if err != nil { - log.AppLogger().WithError(err).Error("Read problem.md failed") + log_module.AppLogger().WithError(err).Error("Read problem.md failed") } description := string(content) - log.AppLogger().WithField("description", description).Debug("Read problem.md") + log_module.AppLogger().WithField("description", description).Debug("Read problem.md") err = problem_model.CreateProblem(db, problem_model.Problem{ Slug: slug, Title: title, @@ -90,7 +92,7 @@ func main() { path, minio.PutObjectOptions{}) if err != nil { - log.AppLogger().WithError(err).Error("Put object to minio failed") + log_module.AppLogger().WithError(err).Error("Put object to minio failed") } return err }) @@ -98,5 +100,5 @@ func main() { panic(err) } - log.AppLogger().Info("Problem loaded") + log_module.AppLogger().Info("Problem loaded") } diff --git a/cmd/rpc_server/main.go b/cmd/rpc_server/main.go index d10a864..124859a 100644 --- a/cmd/rpc_server/main.go +++ b/cmd/rpc_server/main.go @@ -9,7 +9,7 @@ import ( "net" "github.com/oj-lab/oj-lab-platform/cmd/rpc_server/impls" - "github.com/oj-lab/oj-lab-platform/modules/config" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" "github.com/oj-lab/oj-lab-platform/proto" "google.golang.org/grpc" "google.golang.org/grpc/reflection" @@ -20,7 +20,7 @@ const ( ) var ( - port = config.AppConfig.GetInt(portProp) + port = config_module.AppConfig().GetInt(portProp) ) func main() { diff --git a/cmd/web_server/handler/event.go b/cmd/web_server/handler/event.go index 5e9a0cf..bd0cf74 100644 --- a/cmd/web_server/handler/event.go +++ b/cmd/web_server/handler/event.go @@ -6,7 +6,7 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) func SetupEventRouter(baseRoute *gin.RouterGroup) { @@ -34,7 +34,7 @@ func Stream(ginCtx *gin.Context) { ginCtx.Stream(func(w io.Writer) bool { // With event type message := fmt.Sprintf("event: %s\ndata: %s\n\n", "eventType", time.Now().String()) - log.AppLogger().Infof("Send message:\n%s", message) + log_module.AppLogger().Infof("Send message:\n%s", message) fmt.Fprint(w, message) time.Sleep(1 * time.Second) counter++ diff --git a/cmd/web_server/main.go b/cmd/web_server/main.go index 34df985..8b987aa 100644 --- a/cmd/web_server/main.go +++ b/cmd/web_server/main.go @@ -9,8 +9,8 @@ import ( "github.com/oj-lab/oj-lab-platform/cmd/web_server/middleware" "github.com/gin-gonic/gin" - "github.com/oj-lab/oj-lab-platform/modules/config" - "github.com/oj-lab/oj-lab-platform/modules/log" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) const ( @@ -30,11 +30,11 @@ var ( ) func init() { - serviceForceConsoleColor = config.AppConfig.GetBool(serviceForceConsoleColorProp) - servicePort = config.AppConfig.GetString(servicePortProp) - serviceMode = config.AppConfig.GetString(serviceModeProp) - swaggerOn = config.AppConfig.GetBool(swaggerOnProp) - frontendDist = config.AppConfig.GetString(frontendDistProp) + serviceForceConsoleColor = config_module.AppConfig().GetBool(serviceForceConsoleColorProp) + servicePort = config_module.AppConfig().GetString(servicePortProp) + serviceMode = config_module.AppConfig().GetString(serviceModeProp) + swaggerOn = config_module.AppConfig().GetBool(swaggerOnProp) + frontendDist = config_module.AppConfig().GetString(frontendDistProp) } func GetProjectDir() string { @@ -56,16 +56,16 @@ func main() { if frontendDist != "" { // If dist folder is not empty, serve frontend if _, err := os.Stat(frontendDist); os.IsNotExist(err) { - log.AppLogger().Warn("Frontend dist is set but folder not found") + log_module.AppLogger().Warn("Frontend dist is set but folder not found") } else { - log.AppLogger().Info("Serving frontend...") + log_module.AppLogger().Info("Serving frontend...") r.LoadHTMLFiles(frontendDist + "/index.html") handler.SetupFrontendRoute(baseRouter, frontendDist) } } if swaggerOn { - log.AppLogger().Info("Serving swagger Doc...") + log_module.AppLogger().Info("Serving swagger Doc...") handler.SetupSwaggoRouter(baseRouter) } diff --git a/cmd/web_server/middleware/casbin.go b/cmd/web_server/middleware/casbin.go index b27ef22..1348f60 100644 --- a/cmd/web_server/middleware/casbin.go +++ b/cmd/web_server/middleware/casbin.go @@ -4,7 +4,7 @@ import ( "github.com/gin-gonic/gin" "github.com/oj-lab/oj-lab-platform/modules" casbin_agent "github.com/oj-lab/oj-lab-platform/modules/agent/casbin" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) func BuildCasbinEnforceHandlerWithDomain(domain string) gin.HandlerFunc { @@ -20,7 +20,7 @@ func BuildCasbinEnforceHandlerWithDomain(domain string) gin.HandlerFunc { allow, err := enforcer.Enforce(ls.Key.Account, "_", domain, path, method) if err != nil { - log.AppLogger().Errorf("Failed to enforce: %v", err) + log_module.AppLogger().Errorf("Failed to enforce: %v", err) modules.NewInternalError("Failed to enforce").AppendToGin(ginCtx) return } diff --git a/cmd/web_server/middleware/error.go b/cmd/web_server/middleware/error.go index cb010db..9b2c2f1 100644 --- a/cmd/web_server/middleware/error.go +++ b/cmd/web_server/middleware/error.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" "github.com/oj-lab/oj-lab-platform/modules" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) func GetServiceError(ginErr gin.Error) *modules.SeviceError { @@ -23,7 +23,7 @@ func HandleError(ginCtx *gin.Context) { errCount := len(ginCtx.Errors) if errCount > 0 { - log.AppLogger().Errorf("Last error from GIN middleware: %+v", ginCtx.Errors[errCount-1].Err) + log_module.AppLogger().Errorf("Last error from GIN middleware: %+v", ginCtx.Errors[errCount-1].Err) err := GetServiceError(*ginCtx.Errors[errCount-1]) ginCtx.JSON(err.Code, gin.H{ "code": err.Code, diff --git a/workdirs/development/config.toml b/config.toml similarity index 88% rename from workdirs/development/config.toml rename to config.toml index 8105af9..59f7211 100644 --- a/workdirs/development/config.toml +++ b/config.toml @@ -15,10 +15,9 @@ duration = "24h" force_console_color = true auth_on = true port = ":8080" -cookie.age = "24h" mode = "debug" swagger_on = true -frontend_dist = "workdirs/development/frontend_dist" +frontend_dist = "frontend/dist" [rpc-server] port = 50051 diff --git a/workdirs/development/docker-compose.yml b/docker-compose.yml similarity index 80% rename from workdirs/development/docker-compose.yml rename to docker-compose.yml index 790b422..ee89a0e 100644 --- a/workdirs/development/docker-compose.yml +++ b/docker-compose.yml @@ -48,4 +48,14 @@ services: - "9100:9000" environment: - CLICKHOUSE_USER=clickhouse - - CLICKHOUSE_PASSWORD=clickhouse \ No newline at end of file + - CLICKHOUSE_PASSWORD=clickhouse + + judger: + image: ghcr.io/oj-lab/judger:main + pull_policy: always + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - ENABLE_RCLONE=true + ports: + - 8000:8000 \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..3e22129 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1 @@ +/dist \ No newline at end of file diff --git a/modules/agent/casbin/enforcer.go b/modules/agent/casbin/enforcer.go index c9a3f42..11658c9 100644 --- a/modules/agent/casbin/enforcer.go +++ b/modules/agent/casbin/enforcer.go @@ -11,7 +11,7 @@ import ( rediswatcher "github.com/casbin/redis-watcher/v2" gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm" redis_agent "github.com/oj-lab/oj-lab-platform/modules/agent/redis" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) var casbinEnforcer *casbin.SyncedCachedEnforcer @@ -54,10 +54,10 @@ func GetDefaultCasbinEnforcer() *casbin.SyncedCachedEnforcer { if err != nil { panic(err) } - log.AppLogger().Info("Casbin enforcer watcher initialized") + log_module.AppLogger().Info("Casbin enforcer watcher initialized") } casbinEnforcer.AddFunction("keyMatchGin", keyMatchGinFunc) - log.AppLogger().Info("Casbin enforcer initialized") + log_module.AppLogger().Info("Casbin enforcer initialized") } return casbinEnforcer diff --git a/modules/agent/gorm/database.go b/modules/agent/gorm/database.go index 95099b2..b1ca0eb 100644 --- a/modules/agent/gorm/database.go +++ b/modules/agent/gorm/database.go @@ -1,7 +1,7 @@ package gorm_agent import ( - "github.com/oj-lab/oj-lab-platform/modules/config" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" "gorm.io/driver/postgres" "gorm.io/gorm" ) @@ -13,7 +13,7 @@ var db *gorm.DB var dsn string func init() { - dsn = config.AppConfig.GetString(dsnProp) + dsn = config_module.AppConfig().GetString(dsnProp) if dsn == "" { panic("database dsn is not set") } diff --git a/modules/agent/minio/client.go b/modules/agent/minio/client.go index 0a99e56..a84e4be 100644 --- a/modules/agent/minio/client.go +++ b/modules/agent/minio/client.go @@ -5,8 +5,8 @@ import ( "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/oj-lab/oj-lab-platform/modules/config" - "github.com/oj-lab/oj-lab-platform/modules/log" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) const ( @@ -29,12 +29,12 @@ var ( ) func init() { - endpoint = config.AppConfig.GetString(minioEndpointProp) - accessKeyID = config.AppConfig.GetString(minioAccessKeyProp) - secretAccessKey = config.AppConfig.GetString(minioSecretAccessKeyProp) - useSSL = config.AppConfig.GetBool(minioUseSSLProp) - region = config.AppConfig.GetString(minioRegionProp) - bucketName = config.AppConfig.GetString(minioBucketNameProp) + endpoint = config_module.AppConfig().GetString(minioEndpointProp) + accessKeyID = config_module.AppConfig().GetString(minioAccessKeyProp) + secretAccessKey = config_module.AppConfig().GetString(minioSecretAccessKeyProp) + useSSL = config_module.AppConfig().GetBool(minioUseSSLProp) + region = config_module.AppConfig().GetString(minioRegionProp) + bucketName = config_module.AppConfig().GetString(minioBucketNameProp) } func GetBucketName() string { @@ -56,16 +56,16 @@ func GetMinioClient() *minio.Client { exists, err := minioClient.BucketExists(ctx, bucketName) if err == nil && exists { - log.AppLogger().WithField("bucket", bucketName).Info("Bucket already exists") + log_module.AppLogger().WithField("bucket", bucketName).Info("Bucket already exists") return minioClient } err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}) if err != nil { - log.AppLogger().WithError(err). + log_module.AppLogger().WithError(err). WithField("bucket", bucketName).Error("Failed to create bucket") } else { - log.AppLogger().WithField("bucket", bucketName).Info("Successfully created bucket") + log_module.AppLogger().WithField("bucket", bucketName).Info("Successfully created bucket") } } diff --git a/modules/agent/redis/client.go b/modules/agent/redis/client.go index 3ee9579..dabaefd 100644 --- a/modules/agent/redis/client.go +++ b/modules/agent/redis/client.go @@ -1,7 +1,7 @@ package redis_agent import ( - "github.com/oj-lab/oj-lab-platform/modules/config" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" "github.com/redis/go-redis/v9" ) @@ -14,7 +14,7 @@ var ( ) func init() { - RedisHosts = config.AppConfig.GetStringSlice(redisHostsProp) + RedisHosts = config_module.AppConfig().GetStringSlice(redisHostsProp) } type RedisClientInterface interface { diff --git a/modules/auth/jwt.go b/modules/auth/jwt.go index 084294e..6a938dc 100644 --- a/modules/auth/jwt.go +++ b/modules/auth/jwt.go @@ -5,15 +5,15 @@ import ( "time" "github.com/golang-jwt/jwt/v4" - "github.com/oj-lab/oj-lab-platform/modules/config" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" ) var jwtSecret string var jwtDuration time.Duration func init() { - jwtSecret = config.AppConfig.GetString("jwt.secret") - jwtDuration = config.AppConfig.GetDuration("jwt.duration") + jwtSecret = config_module.AppConfig().GetString("jwt.secret") + jwtDuration = config_module.AppConfig().GetDuration("jwt.duration") } type AuthToken struct { diff --git a/modules/auth/redis.go b/modules/auth/redis.go index a901d00..fda0eb4 100644 --- a/modules/auth/redis.go +++ b/modules/auth/redis.go @@ -6,7 +6,7 @@ import ( "time" redis_agent "github.com/oj-lab/oj-lab-platform/modules/agent/redis" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" "github.com/redis/go-redis/v9" ) @@ -67,7 +67,7 @@ func UpdateLoginSessionByAccount(ctx context.Context, account string, data Login // TODO: KeepTTL only works in redis v6+ err = redisClient.Set(ctx, redisKey, val, redis.KeepTTL).Err() if err != nil { - log.AppLogger().Errorf("failed to update login session: %v", err) + log_module.AppLogger().Errorf("failed to update login session: %v", err) } } diff --git a/modules/config/config.go b/modules/config/config.go index 27a12a3..cb03873 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -1,4 +1,4 @@ -package config +package config_module import ( "fmt" @@ -10,12 +10,10 @@ import ( ) const serviceEnvEnvKey = "OJ_LAB_SERVICE_ENV" -const workdirEnvKey = "OJ_LAB_WORKDIR" const defaultConfigName = "config" const defaultOverrideConfigName = "override" const defaultProjectRootName = "oj-lab-platform" -const defaultProjectWorkdirFolder = "workdirs" type ServiceEnv string @@ -25,8 +23,8 @@ const ( ) var serviceEnv ServiceEnv -var Workdir string -var AppConfig *viper.Viper +var projectRoot string +var appConfig *viper.Viper func (se ServiceEnv) isValid() bool { if se == serviceEnvDev || se == serviceEnvPrd { @@ -49,7 +47,7 @@ func loadServiceEnv() { } func loadConfig() error { - viper.AddConfigPath(Workdir) + viper.AddConfigPath(projectRoot) viper.SetConfigName(defaultConfigName) err := viper.ReadInConfig() @@ -66,16 +64,15 @@ func loadConfig() error { viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - AppConfig = viper.GetViper() + appConfig = viper.GetViper() return nil } -func loadWorkdir() { - Workdir = os.Getenv(workdirEnvKey) - if Workdir != "" { - return - } +func AppConfig() *viper.Viper { + return appConfig +} +func loadProjectRoot() { // Try to locate project root, then find the workdir wd, err := os.Getwd() if err != nil { @@ -90,16 +87,20 @@ func loadWorkdir() { if wd == "/" { panic("Cannot find projectRoot") } - Workdir = path.Join(wd, defaultProjectWorkdirFolder, string(serviceEnv)) + projectRoot = wd +} + +func ProjectRoot() string { + return projectRoot } func init() { loadServiceEnv() - loadWorkdir() - if _, err := os.Stat(Workdir); err != nil { - panic(fmt.Sprintf("Set workdir %s with error: %v", Workdir, err)) + loadProjectRoot() + if _, err := os.Stat(projectRoot); err != nil { + panic(fmt.Sprintf("Project root not found: %v", projectRoot)) } - println("Workdir:", Workdir) + println("Project root:", projectRoot) if err := loadConfig(); err != nil { panic(fmt.Sprintf("Load config with error: %v", err)) } diff --git a/modules/log/log.go b/modules/log/log.go index b84183c..ee9df3f 100644 --- a/modules/log/log.go +++ b/modules/log/log.go @@ -1,10 +1,10 @@ -package log +package log_module import ( "os" "runtime" - "github.com/oj-lab/oj-lab-platform/modules/config" + config_module "github.com/oj-lab/oj-lab-platform/modules/config" "github.com/sirupsen/logrus" ) @@ -25,7 +25,7 @@ func setupLog() { logrus.SetOutput(os.Stdout) logrus.SetLevel(logrus.DebugLevel) - lvl := config.AppConfig.GetString(logLevelProp) + lvl := config_module.AppConfig().GetString(logLevelProp) logLevel, err := logrus.ParseLevel(lvl) if err == nil { println("log level:", lvl) diff --git a/workdirs/development/override.example.toml b/override.example.toml similarity index 100% rename from workdirs/development/override.example.toml rename to override.example.toml diff --git a/problem_packages/.gitignore b/problem_packages/.gitignore new file mode 100644 index 0000000..295e381 --- /dev/null +++ b/problem_packages/.gitignore @@ -0,0 +1 @@ +icpc/ \ No newline at end of file diff --git a/scripts/download_and_unzip.sh b/scripts/download_and_unzip.sh new file mode 100755 index 0000000..1dc374a --- /dev/null +++ b/scripts/download_and_unzip.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +TARGET_PATH=$1 +DOWNLOAD_URL=$2 +OVERRIDE=$3 + +# Check if a parameter was provided +if [ -z "$TARGET_PATH" ] +then + echo "Please provide the TARGET_PATH." + exit 1 +fi + +if [ -z "$DOWNLOAD_URL" ] +then + echo "Please provide the DOWNLOAD_URL." + exit 1 +fi + +# Check dist exists +if [ -d "$TARGET_PATH" ]; then + if [ "$OVERRIDE" != "OVERRIDE=true" ]; then + echo "$TARGET_PATH already exists. Use OVERRIDE=true to override." >&2 + exit 0 + fi + echo "\"$TARGET_PATH\" already exists, cleaning..." >&2 + rm -rf "$TARGET_PATH" +else + mkdir -p "$TARGET_PATH" +fi + +# Check zip installed +if ! [ -x "$(command -v zip)" ]; then + echo 'Error: zip is not installed.' >&2 + exit 1 +fi + +curl -o /tmp/download.zip -L "$DOWNLOAD_URL" +unzip -q -o /tmp/download.zip -d "$TARGET_PATH" +echo "Unzipped to \"$TARGET_PATH\"" +rm /tmp/download.zip +echo "Removed \"/tmp/download.zip\"" \ No newline at end of file diff --git a/scripts/install-protoc.sh b/scripts/install_protoc.sh similarity index 100% rename from scripts/install-protoc.sh rename to scripts/install_protoc.sh diff --git a/scripts/update-frontend-dist.sh b/scripts/update-frontend-dist.sh deleted file mode 100755 index 70fee60..0000000 --- a/scripts/update-frontend-dist.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -FRONTEND_DIST_PATH=$1 - -# Check if a parameter was provided -if [ -z "$FRONTEND_DIST_PATH" ] -then - echo "No argument supplied. Please provide the path to FRONTEND_DIST_PATH." - exit 1 -fi - -# Check zip installed -if ! [ -x "$(command -v zip)" ]; then - echo 'Error: zip is not installed.' >&2 - exit 1 -fi - -# Check dist exists -if [ -d "$FRONTEND_DIST_PATH" ]; then - echo "Info: $FRONTEND_DIST_PATH already exists, cleaning..." >&2 - rm -rf "$FRONTEND_DIST_PATH" -else - mkdir -p "$FRONTEND_DIST_PATH" -fi - -curl -o dist.zip -L https://github.com/oj-lab/oj-lab-front/releases/download/v0.0.3/dist.zip -unzip -o dist.zip -d "$FRONTEND_DIST_PATH" -rm dist.zip \ No newline at end of file diff --git a/services/user/user.go b/services/user/user.go index 2818745..e767662 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -6,7 +6,7 @@ import ( user_model "github.com/oj-lab/oj-lab-platform/models/user" gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm" "github.com/oj-lab/oj-lab-platform/modules/auth" - "github.com/oj-lab/oj-lab-platform/modules/log" + log_module "github.com/oj-lab/oj-lab-platform/modules/log" ) func GetUser(ctx context.Context, account string) (*user_model.User, error) { @@ -44,7 +44,7 @@ func CheckUserExist(ctx context.Context, account string) (bool, error) { } if count > 1 { - log.AppLogger(). + log_module.AppLogger(). WithField("account", account). WithField("count", count). Warn("user account is not unique") diff --git a/workdirs/development/.gitignore b/workdirs/development/.gitignore deleted file mode 100644 index 2e98ce9..0000000 --- a/workdirs/development/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -frontend_dist/ -override.* -!override.example* \ No newline at end of file diff --git a/workdirs/development/judger/docker-compose.yml b/workdirs/development/judger/docker-compose.yml deleted file mode 100644 index ca084ce..0000000 --- a/workdirs/development/judger/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - judger: - image: ghcr.io/oj-lab/judger:main - pull_policy: always - extra_hosts: - - "host.docker.internal:host-gateway" - environment: - - ENABLE_RCLONE=true - ports: - - 8000:8000 \ No newline at end of file diff --git a/workdirs/development/problem_packages/hello-world/.timelimit b/workdirs/development/problem_packages/hello-world/.timelimit deleted file mode 100644 index d00491f..0000000 --- a/workdirs/development/problem_packages/hello-world/.timelimit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/workdirs/development/problem_packages/hello-world/data/secret/0.ans b/workdirs/development/problem_packages/hello-world/data/secret/0.ans deleted file mode 100644 index 2a1588e..0000000 --- a/workdirs/development/problem_packages/hello-world/data/secret/0.ans +++ /dev/null @@ -1 +0,0 @@ -Hello! world! diff --git a/workdirs/development/problem_packages/hello-world/data/secret/0.in b/workdirs/development/problem_packages/hello-world/data/secret/0.in deleted file mode 100644 index c944ebc..0000000 --- a/workdirs/development/problem_packages/hello-world/data/secret/0.in +++ /dev/null @@ -1 +0,0 @@ -world! \ No newline at end of file diff --git a/workdirs/development/problem_packages/hello-world/data/secret/1.ans b/workdirs/development/problem_packages/hello-world/data/secret/1.ans deleted file mode 100644 index fb35721..0000000 --- a/workdirs/development/problem_packages/hello-world/data/secret/1.ans +++ /dev/null @@ -1 +0,0 @@ -Hello! oj-lab! diff --git a/workdirs/development/problem_packages/hello-world/data/secret/1.in b/workdirs/development/problem_packages/hello-world/data/secret/1.in deleted file mode 100644 index 893a717..0000000 --- a/workdirs/development/problem_packages/hello-world/data/secret/1.in +++ /dev/null @@ -1 +0,0 @@ -oj-lab! \ No newline at end of file diff --git a/workdirs/development/problem_packages/hello-world/output_validators/interactor-a-plus-b b/workdirs/development/problem_packages/hello-world/output_validators/interactor-a-plus-b deleted file mode 100755 index c237a18..0000000 Binary files a/workdirs/development/problem_packages/hello-world/output_validators/interactor-a-plus-b and /dev/null differ diff --git a/workdirs/development/problem_packages/hello-world/output_validators/lcmp b/workdirs/development/problem_packages/hello-world/output_validators/lcmp deleted file mode 100755 index 786b861..0000000 Binary files a/workdirs/development/problem_packages/hello-world/output_validators/lcmp and /dev/null differ diff --git a/workdirs/development/problem_packages/hello-world/output_validators/ncmp b/workdirs/development/problem_packages/hello-world/output_validators/ncmp deleted file mode 100755 index 8711c86..0000000 Binary files a/workdirs/development/problem_packages/hello-world/output_validators/ncmp and /dev/null differ diff --git a/workdirs/development/problem_packages/hello-world/problem.yaml b/workdirs/development/problem_packages/hello-world/problem.yaml deleted file mode 100644 index 89fe447..0000000 --- a/workdirs/development/problem_packages/hello-world/problem.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: Hello world - -limits: - time_multiplier: 5 - time_safety_margin: 2 - memory: 2048 - output: 8 - code: 128 - compilation_time: 60 - compilation_memory: 2048 - validation_time: 60 - validation_memory: 2048 - validation_output: 8 diff --git a/workdirs/development/problem_packages/hello-world/problem_statement/problem.md b/workdirs/development/problem_packages/hello-world/problem_statement/problem.md deleted file mode 100644 index 6a1ff09..0000000 --- a/workdirs/development/problem_packages/hello-world/problem_statement/problem.md +++ /dev/null @@ -1 +0,0 @@ -Write a program that prints "Hello World!". diff --git a/workdirs/docker/config.toml b/workdirs/docker/config.toml deleted file mode 100644 index 32be525..0000000 --- a/workdirs/docker/config.toml +++ /dev/null @@ -1,33 +0,0 @@ -[log] -level = "debug" - -[database] -dsn = "user=postgres password=development host=host.docker.internal port=5432 dbname=oj_lab sslmode=disable TimeZone=Asia/Shanghai" - -[redis] -host = ["host.docker.internal:6379"] - -[jwt] -secret = "example_secret" -duration = "24h" - -[service] -auth_on = true -port = ":8080" -cookie.age = "24h" -mode = "debug" -swagger_on = true -frontend_dist = "frontend_dist" - -[rpc-server] -port = 50051 - -[judger] -host = "http://host.docker.internal:8000" - -[minio] -endpoint = "host.docker.internal:9000" -accessKeyID = "minio-root-user" -secretAccessKey = "minio-root-password" -useSSL = false -bucketName = "oj-lab-problem-package"