Spaces:
Running
Running
add permission
Browse files- Dockerfile +10 -6
- app.py +57 -18
Dockerfile
CHANGED
@@ -8,6 +8,11 @@ RUN apt-get update && \
|
|
8 |
build-essential \
|
9 |
&& rm -rf /var/lib/apt/lists/*
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
# Copy requirements first to leverage Docker cache
|
12 |
COPY requirements.txt .
|
13 |
RUN pip install --no-cache-dir -r requirements.txt
|
@@ -16,12 +21,8 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|
16 |
COPY . .
|
17 |
|
18 |
# Create upload directory with proper permissions
|
19 |
-
RUN mkdir -p static/uploads && \
|
20 |
-
chmod -R 777 static
|
21 |
-
|
22 |
-
# Create metadata directory with proper permissions
|
23 |
-
RUN mkdir -p static/metadata && \
|
24 |
-
chmod -R 777 static/metadata
|
25 |
|
26 |
# Set environment variables for Hugging Face
|
27 |
ENV PYTHONUNBUFFERED=1
|
@@ -37,5 +38,8 @@ ENV ENV=production
|
|
37 |
# Expose port for Hugging Face Spaces (uses port 7860)
|
38 |
EXPOSE 7860
|
39 |
|
|
|
|
|
|
|
40 |
# Run the application
|
41 |
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
8 |
build-essential \
|
9 |
&& rm -rf /var/lib/apt/lists/*
|
10 |
|
11 |
+
# Create a non-root user to run the application
|
12 |
+
RUN useradd -m appuser && \
|
13 |
+
mkdir -p /home/appuser/app /home/appuser/.cache && \
|
14 |
+
chown -R appuser:appuser /home/appuser
|
15 |
+
|
16 |
# Copy requirements first to leverage Docker cache
|
17 |
COPY requirements.txt .
|
18 |
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
21 |
COPY . .
|
22 |
|
23 |
# Create upload directory with proper permissions
|
24 |
+
RUN mkdir -p static/uploads static/metadata && \
|
25 |
+
chmod -R 777 static
|
|
|
|
|
|
|
|
|
26 |
|
27 |
# Set environment variables for Hugging Face
|
28 |
ENV PYTHONUNBUFFERED=1
|
|
|
38 |
# Expose port for Hugging Face Spaces (uses port 7860)
|
39 |
EXPOSE 7860
|
40 |
|
41 |
+
# Switch to the non-root user
|
42 |
+
# USER appuser
|
43 |
+
|
44 |
# Run the application
|
45 |
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
CHANGED
@@ -19,6 +19,7 @@ import io
|
|
19 |
from huggingface_hub import HfApi, HfFolder, create_repo
|
20 |
from huggingface_hub.utils import RepositoryNotFoundError
|
21 |
from huggingface_hub.hf_api import RepoFile
|
|
|
22 |
|
23 |
# Create FastAPI app
|
24 |
app = FastAPI(title="Image Uploader")
|
@@ -63,6 +64,13 @@ DATASET_REPO = os.environ.get("HF_DATASET_REPO", "image-uploader-data")
|
|
63 |
IMAGES_PATH = "images"
|
64 |
METADATA_PATH = "metadata"
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
# Initialize HfApi
|
67 |
hf_api = HfApi(token=HF_TOKEN)
|
68 |
|
@@ -71,22 +79,38 @@ def ensure_repo_exists():
|
|
71 |
try:
|
72 |
# Check if repo exists
|
73 |
hf_api.repo_info(repo_id=f"{HF_USERNAME}/{DATASET_REPO}", repo_type="dataset")
|
|
|
74 |
except RepositoryNotFoundError:
|
75 |
# Create repo if it doesn't exist
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
# Initialize repository if in production
|
88 |
if os.environ.get("ENV", "development") == "production":
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
|
91 |
def get_file_extension(filename: str) -> str:
|
92 |
"""Get the file extension from a filename."""
|
@@ -114,14 +138,17 @@ def verify_auth(request: Request):
|
|
114 |
def get_image_metadata():
|
115 |
"""Get all image metadata including hashtags."""
|
116 |
# In production, get metadata from Hugging Face
|
117 |
-
if os.environ.get("ENV", "development") == "production" and HF_USERNAME:
|
118 |
try:
|
|
|
119 |
metadata_file = hf_api.hf_hub_download(
|
120 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
121 |
filename=f"{METADATA_PATH}/image_metadata.json",
|
122 |
repo_type="dataset",
|
123 |
-
token=HF_TOKEN
|
|
|
124 |
)
|
|
|
125 |
with open(metadata_file, "r") as f:
|
126 |
return json.load(f)
|
127 |
except Exception as e:
|
@@ -139,6 +166,7 @@ def save_image_metadata(metadata):
|
|
139 |
# In production, save to Hugging Face
|
140 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
141 |
try:
|
|
|
142 |
metadata_str = json.dumps(metadata)
|
143 |
hf_api.upload_file(
|
144 |
path_or_fileobj=io.BytesIO(metadata_str.encode()),
|
@@ -147,12 +175,16 @@ def save_image_metadata(metadata):
|
|
147 |
repo_type="dataset",
|
148 |
token=HF_TOKEN
|
149 |
)
|
|
|
150 |
except Exception as e:
|
151 |
print(f"Error saving metadata to Hugging Face: {e}")
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
|
|
|
|
|
|
156 |
|
157 |
def add_hashtags_to_image(filename, hashtags, original_filename=None):
|
158 |
"""Add hashtags to an image."""
|
@@ -182,6 +214,7 @@ def upload_to_hf(file_content, filename):
|
|
182 |
"""Upload a file to Hugging Face Dataset Repository."""
|
183 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
184 |
try:
|
|
|
185 |
hf_api.upload_file(
|
186 |
path_or_fileobj=io.BytesIO(file_content),
|
187 |
path_in_repo=f"{IMAGES_PATH}/{filename}",
|
@@ -189,6 +222,7 @@ def upload_to_hf(file_content, filename):
|
|
189 |
repo_type="dataset",
|
190 |
token=HF_TOKEN
|
191 |
)
|
|
|
192 |
return True
|
193 |
except Exception as e:
|
194 |
print(f"Error uploading to Hugging Face: {e}")
|
@@ -199,12 +233,14 @@ def delete_from_hf(filename):
|
|
199 |
"""Delete a file from Hugging Face Dataset Repository."""
|
200 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
201 |
try:
|
|
|
202 |
hf_api.delete_file(
|
203 |
path_in_repo=f"{IMAGES_PATH}/{filename}",
|
204 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
205 |
repo_type="dataset",
|
206 |
token=HF_TOKEN
|
207 |
)
|
|
|
208 |
return True
|
209 |
except Exception as e:
|
210 |
print(f"Error deleting from Hugging Face: {e}")
|
@@ -221,6 +257,7 @@ def list_hf_images():
|
|
221 |
"""List all images in the Hugging Face repo."""
|
222 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
223 |
try:
|
|
|
224 |
files = hf_api.list_repo_files(
|
225 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
226 |
repo_type="dataset",
|
@@ -228,7 +265,9 @@ def list_hf_images():
|
|
228 |
)
|
229 |
# Filter only image files in the images directory
|
230 |
image_files = [f for f in files if f.startswith(f"{IMAGES_PATH}/")]
|
231 |
-
|
|
|
|
|
232 |
except Exception as e:
|
233 |
print(f"Error listing files from Hugging Face: {e}")
|
234 |
return []
|
|
|
19 |
from huggingface_hub import HfApi, HfFolder, create_repo
|
20 |
from huggingface_hub.utils import RepositoryNotFoundError
|
21 |
from huggingface_hub.hf_api import RepoFile
|
22 |
+
import tempfile
|
23 |
|
24 |
# Create FastAPI app
|
25 |
app = FastAPI(title="Image Uploader")
|
|
|
64 |
IMAGES_PATH = "images"
|
65 |
METADATA_PATH = "metadata"
|
66 |
|
67 |
+
# Set HF cache directory to a writable location
|
68 |
+
# This is necessary for Hugging Face Spaces which has permission issues with the default cache location
|
69 |
+
os.environ["HF_HOME"] = os.path.join(tempfile.gettempdir(), "huggingface")
|
70 |
+
os.environ["HUGGINGFACE_HUB_CACHE"] = os.path.join(tempfile.gettempdir(), "huggingface", "hub")
|
71 |
+
os.makedirs(os.environ["HF_HOME"], exist_ok=True)
|
72 |
+
os.makedirs(os.environ["HUGGINGFACE_HUB_CACHE"], exist_ok=True)
|
73 |
+
|
74 |
# Initialize HfApi
|
75 |
hf_api = HfApi(token=HF_TOKEN)
|
76 |
|
|
|
79 |
try:
|
80 |
# Check if repo exists
|
81 |
hf_api.repo_info(repo_id=f"{HF_USERNAME}/{DATASET_REPO}", repo_type="dataset")
|
82 |
+
print(f"Repository {HF_USERNAME}/{DATASET_REPO} exists")
|
83 |
except RepositoryNotFoundError:
|
84 |
# Create repo if it doesn't exist
|
85 |
+
try:
|
86 |
+
print(f"Creating repository {HF_USERNAME}/{DATASET_REPO}")
|
87 |
+
create_repo(f"{HF_USERNAME}/{DATASET_REPO}", repo_type="dataset", token=HF_TOKEN)
|
88 |
+
# Initialize metadata
|
89 |
+
metadata = json.dumps({})
|
90 |
+
hf_api.upload_file(
|
91 |
+
path_or_fileobj=io.BytesIO(metadata.encode()),
|
92 |
+
path_in_repo=f"{METADATA_PATH}/image_metadata.json",
|
93 |
+
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
94 |
+
repo_type="dataset",
|
95 |
+
token=HF_TOKEN
|
96 |
+
)
|
97 |
+
print(f"Repository created and initialized")
|
98 |
+
except Exception as e:
|
99 |
+
print(f"Error creating repository: {e}")
|
100 |
+
except Exception as e:
|
101 |
+
print(f"Error checking repository: {e}")
|
102 |
|
103 |
# Initialize repository if in production
|
104 |
if os.environ.get("ENV", "development") == "production":
|
105 |
+
print("Running in production mode, checking Hugging Face repository...")
|
106 |
+
if HF_USERNAME and HF_TOKEN:
|
107 |
+
print(f"Using Hugging Face credentials for user: {HF_USERNAME}")
|
108 |
+
try:
|
109 |
+
ensure_repo_exists()
|
110 |
+
except Exception as e:
|
111 |
+
print(f"Error ensuring repository exists: {e}")
|
112 |
+
else:
|
113 |
+
print("Warning: HF_USERNAME or HF_TOKEN not set. Running without Hugging Face integration.")
|
114 |
|
115 |
def get_file_extension(filename: str) -> str:
|
116 |
"""Get the file extension from a filename."""
|
|
|
138 |
def get_image_metadata():
|
139 |
"""Get all image metadata including hashtags."""
|
140 |
# In production, get metadata from Hugging Face
|
141 |
+
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
142 |
try:
|
143 |
+
print(f"Fetching metadata from Hugging Face repository {HF_USERNAME}/{DATASET_REPO}")
|
144 |
metadata_file = hf_api.hf_hub_download(
|
145 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
146 |
filename=f"{METADATA_PATH}/image_metadata.json",
|
147 |
repo_type="dataset",
|
148 |
+
token=HF_TOKEN,
|
149 |
+
local_dir=os.path.join(tempfile.gettempdir(), "hf_downloads")
|
150 |
)
|
151 |
+
print(f"Metadata downloaded to {metadata_file}")
|
152 |
with open(metadata_file, "r") as f:
|
153 |
return json.load(f)
|
154 |
except Exception as e:
|
|
|
166 |
# In production, save to Hugging Face
|
167 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
168 |
try:
|
169 |
+
print(f"Saving metadata to Hugging Face repository {HF_USERNAME}/{DATASET_REPO}")
|
170 |
metadata_str = json.dumps(metadata)
|
171 |
hf_api.upload_file(
|
172 |
path_or_fileobj=io.BytesIO(metadata_str.encode()),
|
|
|
175 |
repo_type="dataset",
|
176 |
token=HF_TOKEN
|
177 |
)
|
178 |
+
print(f"Metadata saved successfully")
|
179 |
except Exception as e:
|
180 |
print(f"Error saving metadata to Hugging Face: {e}")
|
181 |
+
# Still save locally as fallback
|
182 |
+
with open(METADATA_FILE, "w") as f:
|
183 |
+
json.dump(metadata, f)
|
184 |
+
else:
|
185 |
+
# Local development or fallback
|
186 |
+
with open(METADATA_FILE, "w") as f:
|
187 |
+
json.dump(metadata, f)
|
188 |
|
189 |
def add_hashtags_to_image(filename, hashtags, original_filename=None):
|
190 |
"""Add hashtags to an image."""
|
|
|
214 |
"""Upload a file to Hugging Face Dataset Repository."""
|
215 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
216 |
try:
|
217 |
+
print(f"Uploading file {filename} to Hugging Face repository {HF_USERNAME}/{DATASET_REPO}")
|
218 |
hf_api.upload_file(
|
219 |
path_or_fileobj=io.BytesIO(file_content),
|
220 |
path_in_repo=f"{IMAGES_PATH}/{filename}",
|
|
|
222 |
repo_type="dataset",
|
223 |
token=HF_TOKEN
|
224 |
)
|
225 |
+
print(f"File {filename} uploaded successfully")
|
226 |
return True
|
227 |
except Exception as e:
|
228 |
print(f"Error uploading to Hugging Face: {e}")
|
|
|
233 |
"""Delete a file from Hugging Face Dataset Repository."""
|
234 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
235 |
try:
|
236 |
+
print(f"Deleting file {filename} from Hugging Face repository {HF_USERNAME}/{DATASET_REPO}")
|
237 |
hf_api.delete_file(
|
238 |
path_in_repo=f"{IMAGES_PATH}/{filename}",
|
239 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
240 |
repo_type="dataset",
|
241 |
token=HF_TOKEN
|
242 |
)
|
243 |
+
print(f"File {filename} deleted successfully")
|
244 |
return True
|
245 |
except Exception as e:
|
246 |
print(f"Error deleting from Hugging Face: {e}")
|
|
|
257 |
"""List all images in the Hugging Face repo."""
|
258 |
if os.environ.get("ENV", "development") == "production" and HF_USERNAME and HF_TOKEN:
|
259 |
try:
|
260 |
+
print(f"Listing files from Hugging Face repository {HF_USERNAME}/{DATASET_REPO}")
|
261 |
files = hf_api.list_repo_files(
|
262 |
repo_id=f"{HF_USERNAME}/{DATASET_REPO}",
|
263 |
repo_type="dataset",
|
|
|
265 |
)
|
266 |
# Filter only image files in the images directory
|
267 |
image_files = [f for f in files if f.startswith(f"{IMAGES_PATH}/")]
|
268 |
+
image_basenames = [os.path.basename(f) for f in image_files]
|
269 |
+
print(f"Found {len(image_basenames)} images")
|
270 |
+
return image_basenames
|
271 |
except Exception as e:
|
272 |
print(f"Error listing files from Hugging Face: {e}")
|
273 |
return []
|