diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 96154c46..7e1af40b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -64,8 +64,11 @@ def new end def create + macondo_project_id = (params.dig(:project, :macondo_project_id) || params[:macondo_project_id]).presence + @project = current_user.projects.build(project_params) @project.status = :draft + @project.devlog_mode = "website" if macondo_project_id if @project.tier == Project::BUILD_REVIEW_TIER @project.build_review = true end @@ -73,8 +76,7 @@ def create if @project.save audit!("project.created", target: @project, metadata: { tier: @project.tier, build_review: @project.build_review }) - macondo_project_id = params.dig(:project, :macondo_project_id).presence - ImportMacondoDataJob.perform_later(@project.id, macondo_project_id) if macondo_project_id + ImportMacondoDataJob.perform_now(@project.id, macondo_project_id) if macondo_project_id redirect_to @project, notice: @project.build_review? ? "Build review created as draft." : "Project created as draft." else fallback_tier = @project.build_review? ? Project::BUILD_REVIEW_TIER : @project.tier diff --git a/app/javascript/pages/Projects/Form.tsx b/app/javascript/pages/Projects/Form.tsx index ecd35fc9..3a57021a 100644 --- a/app/javascript/pages/Projects/Form.tsx +++ b/app/javascript/pages/Projects/Form.tsx @@ -148,10 +148,12 @@ export default function ProjectsForm({ form.setData({ ...form.data, name: data.name || form.data.name, + subtitle: data.description || form.data.subtitle, repo_link: data.repo_link || form.data.repo_link, hackatime_projects: Array.isArray(data.hackatime_projects) ? data.hackatime_projects : form.data.hackatime_projects, + devlog_mode: 'website', macondo_project_id: data.macondo_project_id || '', }) diff --git a/app/jobs/import_macondo_data_job.rb b/app/jobs/import_macondo_data_job.rb index 9a35608f..141c0c4c 100644 --- a/app/jobs/import_macondo_data_job.rb +++ b/app/jobs/import_macondo_data_job.rb @@ -1,34 +1,53 @@ -require "open-uri" - class ImportMacondoDataJob < ApplicationJob queue_as :default + TRUSTED_CDN_HOST = "cdn.hackclub.com" + def perform(project_id, macondo_project_id) project = Project.find_by(id: project_id) - return unless project + unless project + Rails.logger.warn("ImportMacondoDataJob: project #{project_id} not found, aborting") + return + end data = MacondoService.get_project(macondo_project_id) - return unless data - return unless MacondoService.owned_by?(data, project.user) - return if MacondoService.shipped?(data) + unless data + Rails.logger.warn("ImportMacondoDataJob: Macondo project #{macondo_project_id} fetch failed, aborting") + return + end - attach_cover_image(project, data["thumbnail_url"]) if data["thumbnail_url"].present? + unless MacondoService.owned_by?(data, project.user) + Rails.logger.warn("ImportMacondoDataJob: ownership check failed for Macondo project #{macondo_project_id} (owner=#{data["owner"].inspect}), aborting") + return + end + + if MacondoService.shipped?(data) + Rails.logger.warn("ImportMacondoDataJob: Macondo project #{macondo_project_id} already shipped, aborting") + return + end + + Rails.logger.info("ImportMacondoDataJob: importing cover image and journals for project #{project_id} from Macondo project #{macondo_project_id}") + set_cover_image(project, data["thumbnail_url"]) if data["thumbnail_url"].present? import_journals(project, data["journals"].presence || MacondoService.get_journals(macondo_project_id)) end private + def set_cover_image(project, url) + uri = URI.parse(url) + unless uri.is_a?(URI::HTTPS) && uri.host == TRUSTED_CDN_HOST + Rails.logger.error("ImportMacondoDataJob cover image skipped: untrusted host (#{url.inspect})") + return + end - def attach_cover_image(project, url) - io = URI.open(url) - filename = File.basename(URI.parse(url).path.presence || "cover.png") - project.cover_image.attach(io: io, filename: filename) + project.update!(cover_image_url: url) + Rails.logger.info("ImportMacondoDataJob: cover image set for project #{project.id}") rescue StandardError => e - Rails.logger.error("ImportMacondoDataJob cover image failed: #{e.message}") + Rails.logger.error("ImportMacondoDataJob cover image failed (#{url.inspect}): #{e.class}: #{e.message}") end def import_journals(project, journals) - Array(journals).each do |j| - title = j["short_brief"].presence || "Imported entry" + Array(journals).each_with_index do |j, i| + title = j["short_brief"].presence || "Imported entry #{j["id"] || i + 1}" next if project.devlogs.exists?(title: title) project.devlogs.create!(