Skip to content

Commit

Permalink
Merge pull request #36 from henilp105/main
Browse files Browse the repository at this point in the history
feat: extract homepage and repository and description from fpm.toml
  • Loading branch information
henilp105 authored Jun 25, 2023
2 parents 97209f2 + de439df commit 4705f7b
Show file tree
Hide file tree
Showing 35 changed files with 1,497 additions and 767 deletions.
55 changes: 47 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
# registry
Registry for Fortran package manager
# Registry for Fortran package manager

## Python Flask app with Nginx and Mongo database
Currently for the testing phase :
1. backend APIs are hosted at: http://registry-apis.vercel.app/
2. frontend is hosted at: https://registry-frontend.vercel.app/
3. Documentation for the APIs are available at: https://registry-apis.vercel.app/apidocs/

Project structure:
**Please note: the current registry is a playground: its database will be fully deleted once its functionality is established. Please do not use it for production yet! more information will follow then.**

The fpm release [0.8.2](https://fortran-lang.discourse.group/t/fpm-version-0-8-2-released-centralized-registry-playground/5792) introduces fpm support for uploading packages to the fpm-registry server directly from the command-line interface, via

```
fpm publish --token <token-here>
```

fpm will now interact with a web interface that will help to manage the namespaces & packages.


## fpm - registry : Python Flask app with Nginx and Mongo database

backend Project structure:
```
.
├── compose.yaml
Expand All @@ -20,7 +35,7 @@ Project structure:
```

## Deploy with docker compose
## Instructions for Deploy with docker compose

```
$ sudo chmod 666 /var/run/docker.sock (for root access)
Expand All @@ -38,9 +53,9 @@ Hello world, Mongo Flask
set MONGO_URI=MONGO_DB_ATLAS_URL (in .env file in flask directory)
The MONGO_URI must be set in the environment (or, alternatively, in the .env file in the flask directory) to the URL value of the MongoDB to use. For example,If deploying to production, MONGO_URI should be set to mongo container address.
set the following env variables in the .env file in the flask folder:
- SALT=MYSALT
- MONGO_URI
- MONGO_DB_NAME
- SALT
- MONGO_URI
- MONGO_DB_NAME
- SUDO_PASSWORD
- MONGO_USER_NAME
- MONGO_PASSWORD
Expand All @@ -53,3 +68,27 @@ Stop and remove the containers
```
$ docker compose down
```

## Steps to setup mongodump for registry Archives functionality
fpm - registry archives automatically created at weekly intervals by the mongodump command and stored in a tar archives format in the static directory of flask , to support caching and direct rendering of archives without manually fetching the mongodb for each archive request. to reduce the resource used by mongodb , we will only be installing the `mongodb-org-tools` only. the steps to setup mongodump on a Ubuntu linux 22.04 are:

1. Import the public key used by the package management system.

```
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
```

2. Create a list file for MongoDB.

```
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
```

3. Reload local package database and install the tools:

```
sudo apt-get update
sudo apt install mongodb-org-tools
```

for more details, please refer: [mongodb tools installation docs](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/).
160 changes: 122 additions & 38 deletions flask/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
env_var["host"] = host
env_var["salt"] = salt
env_var["sudo_password"] = sudo_password
smtp = smtplib.SMTP('smtp.gmail.com', 587)
smtp = smtplib.SMTP("smtp.gmail.com", 587)
smtp.starttls()
smtp.login(fortran_email, fortran_password)

Expand All @@ -47,14 +47,22 @@ def login():
password = request.form.get("password")
password += salt
hashed_password = hashlib.sha256(password.encode()).hexdigest()
query = {"$and": [{"$or": [{"email": user_identifier}, {"username": user_identifier}]}, {"password": hashed_password}]}

query = {
"$and": [
{"$or": [{"email": user_identifier}, {"username": user_identifier}]},
{"password": hashed_password},
]
}

# search for the user both by user name or email
user = db.users.find_one(query)

if not user:
return jsonify({"message": "Invalid email or password", "code": 401}), 401

if not user["isverified"]:
return jsonify({"message": "Please verify your email", "code": 401}), 401

uuid = generate_uuid() if user["loggedCount"] == 0 else user["uuid"]

user["loggedCount"] += 1
Expand Down Expand Up @@ -124,24 +132,23 @@ def signup():
"createdAt": datetime.utcnow(),
"uuid": uuid,
"loggedCount": 1,
"isverified": False,
"newemail":'',
}

if hashed_password == sudo_hashed_password:
user["roles"] = ["admin"]
forgot_password(email)
else:
user["roles"] = ["user"]

if not registry_user:
if hashed_password == sudo_hashed_password:
user["roles"] = ["admin"]
forgot_password(email)
else:
user["roles"] = ["user"]
db.users.insert_one(user)

send_verify_email(email)
return (
jsonify(
{
"message": "Signup successful",
"uuid": uuid,
"message": "Signup successful, Please verify your email",
"code": 200,
"username": user["username"],
}
),
200,
Expand Down Expand Up @@ -196,40 +203,30 @@ def reset_password():

if not uuid:
return jsonify({"message": "Unauthorized", "code": 401}), 401

if not oldpassword:
return jsonify({"message": "Please enter old password", "code": 400}), 400


if not password:
return jsonify({"message": "Please enter new password", "code": 400}), 400

user = db.users.find_one({"uuid": uuid})
salt = env_var["salt"]

if not user:
return jsonify({"message": "User not found", "code": 404}), 404

if not oldpassword:
password += salt
hashed_password = hashlib.sha256(password.encode()).hexdigest()
db.users.update_one(
{"uuid": uuid},
{"$set": {"password": hashed_password}},
)
return jsonify({"message": "Password reset successful", "code": 200}), 200

oldpassword += salt
hashed_password = hashlib.sha256(oldpassword.encode()).hexdigest()
if hashed_password != user["password"]:
return jsonify({"message": "Invalid password", "code": 401}), 401
if oldpassword:
oldpassword += salt
hashed_password = hashlib.sha256(oldpassword.encode()).hexdigest()
if hashed_password != user["password"]:
return jsonify({"message": "Invalid old password", "code": 401}), 401

password += salt
hashed_password = hashlib.sha256(password.encode()).hexdigest()
db.users.update_one(
{"uuid": uuid},
{"$set": {"password": hashed_password}},
)
{"uuid": uuid},
{"$set": {"password": hashed_password}},
)
return jsonify({"message": "Password reset successful", "code": 200}), 200




@app.route("/auth/forgot-password", methods=["POST"])
@swag_from("documentation/forgot_password.yaml", methods=["POST"])
Expand All @@ -244,6 +241,9 @@ def forgot_password(*email):
if not user:
return jsonify({"message": "User not found", "code": 404}), 404

if not user["isverified"]:
return jsonify({"message": "Please verify your email", "code": 401}), 401

uuid = generate_uuid()
db.users.update_one({"email": email}, {"$set": {"uuid": uuid, "loggedCount": 1}})

Expand All @@ -257,12 +257,96 @@ def forgot_password(*email):
Thank you,
The Fortran-lang Team"""

message = f'Subject: Password reset link\nTo: {email}\n{message}'
message = f"Subject: Password reset link\nTo: {email}\n{message}"

# sending the mail
smtp.sendmail(to_addrs=email, msg=message, from_addr=fortran_email)

return (
jsonify({"message": "Password reset link sent to your email", "code": 200}),
200,
)


def send_verify_email(email):
query = {"$and": [{"$or": [{"email": email}, {"newemail": email}]}]}

user = db.users.find_one(query)

if not user:
return jsonify({"message": "User not found", "code": 404}), 404

uuid = user["uuid"]

message = f"""\n
Dear {user['username']},
We received a request to verify your email. To verify your email, please copy paste the link below in a new browser window:
{env_var['host']}/account/verify/{uuid}
Thank you,
The Fortran-lang Team"""

message = f"Subject: Verify email \nTo: {email}\n{message}"

# sending the mail
smtp.sendmail(to_addrs=email, msg=message, from_addr=fortran_email)

return (
jsonify({"message": "verification link sent to your email", "code": 200}),
200,
)


@app.route("/auth/verify-email", methods=["POST"])
def verify_email():
uuid = request.form.get("uuid")

if not uuid:
return jsonify({"message": "Unauthorized", "code": 401}), 401

user = db.users.find_one({"uuid": uuid})

if not user:
return jsonify({"message": "User not found", "code": 404}), 404

if user["newemail"] != "":
db.users.update_one(
{"uuid": uuid}, {"$set": {"email": user["newemail"], "newemail": ""}}
)

if not user['isverified']:
db.users.update_one({"uuid": uuid}, {"$set": {"isverified": True}})

return jsonify({"message": "Successfully Verified Email", "code": 200}), 200


@app.route("/auth/change-email", methods=["POST"])
def change_email():
uuid = request.form.get("uuid")
new_email = request.form.get("newemail")

if not uuid:
return jsonify({"message": "Unauthorized", "code": 401}), 401

user = db.users.find_one({"uuid": uuid})

if not user:
return jsonify({"message": "User not found", "code": 404}), 404

if not new_email:
return jsonify({"message": "Please enter new email", "code": 400}), 400

used_email = db.users.find_one({"email": new_email})

if used_email:
return jsonify({"message": "Email already in use", "code": 400}), 400

db.users.update_one(
{"uuid": uuid},
{"$set": {"newemail": new_email}},
)
send_verify_email(new_email)

return jsonify({"message": "Please verify your new email.", "code": 200}), 200
49 changes: 19 additions & 30 deletions flask/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
from dotenv import load_dotenv
from gridfs import GridFS
from app import app
from flask import send_file
from flask import jsonify
import subprocess

load_dotenv()
database_name = os.environ['MONGO_DB_NAME']
database_name = os.environ["MONGO_DB_NAME"]
try:
mongo_uri = os.environ['MONGO_URI']
mongo_username = os.environ['MONGO_USER_NAME']
mongo_password = os.environ['MONGO_PASSWORD']
mongo_uri = os.environ["MONGO_URI"]
mongo_username = os.environ["MONGO_USER_NAME"]
mongo_password = os.environ["MONGO_PASSWORD"]
client = MongoClient(mongo_uri)
except KeyError as err:
print("Add MONGO_URI to .env file")
Expand All @@ -21,35 +21,24 @@
file_storage = GridFS(db, collection="tarballs")


@app.route("/registry/clone", methods=["GET"])
@app.route("/registry/archives", methods=["GET"])
def clone():
filename = "registry.tar.gz"
static_path = os.path.join(os.getcwd(), "static")
file_path = os.path.join(static_path, filename)
folder_path = "static"
file_list = os.listdir(folder_path)

# Check if the file exists and was modified less than 1 week ago
if os.path.exists(file_path):
mod_time = datetime.fromtimestamp(os.path.getmtime(file_path))
if datetime.now() - mod_time < timedelta(days=7):
return send_file(file_path, as_attachment=True)

generate_latest_tarball()
return send_file(file_path, as_attachment=True)
# Check if the folder exists and was modified more than 1 week ago
if os.path.exists(folder_path):
mod_time = datetime.fromtimestamp(os.path.getmtime(folder_path))
if datetime.now() - mod_time > timedelta(days=7):
generate_latest_tarball()

return jsonify(
{"message": "Successfully Fetched Archives", "archives": file_list, "code": 200}
)

def generate_latest_tarball():
backup_dir = "static"
if not os.path.exists(backup_dir):
os.mkdir(backup_dir)

def generate_latest_tarball():
# Execute the mongodump command
command = f"mongodump --host {mongo_uri} --authenticationDatabase admin --username {mongo_username} --password {mongo_password} --db {database_name} --out {backup_dir}"
subprocess.call(command, shell=True)

# Create a tar archive of the backup directory
archive_name = "registry.tar.gz"
archive_path = os.path.join(backup_dir, archive_name)

# Create the archive file
command = f"tar -czvf {archive_path} static/"
archive_date = datetime.datetime.now().strftime("%Y-%m-%d")
command = f"mongodump --uri={mongo_uri}--archive=static/registry-{archive_date}.tar.gz --db={database_name} --gzip --excludeCollection=users"
subprocess.call(command, shell=True)
Loading

0 comments on commit 4705f7b

Please sign in to comment.