Files
winget.pro/winget/views.py
T
2023-10-09 17:21:03 +02:00

134 lines
5.3 KiB
Python

from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_POST
from .models import Package
from .util import load_tenant, return_jsonresponse, parse_jsonrequest
@require_GET
@load_tenant
def index(*_):
# The sole motivation for this view is that we want to be able to
# reverse('winget:index') in instructions for setting up the winget source.
return HttpResponse("Please log in at /admin for instructions.")
@require_GET
@load_tenant
@return_jsonresponse
def information(*_):
return {
'SourceIdentifier': 'api.winget.pro',
'ServerSupportedVersions': ['1.4.0', '1.5.0']
}
@require_POST
@csrf_exempt
@load_tenant
@parse_jsonrequest
@return_jsonresponse
def manifestSearch(_, data, tenant):
db_query = Q(tenant=tenant)
if 'Query' in data:
keyword = data['Query']['KeyWord']
db_query &= Q(name__icontains=keyword)
inclusions_query = Q()
for inclusion in data.get('Inclusions', []):
field = inclusion['PackageMatchField']
if field == 'PackageName':
keyword = inclusion['RequestMatch']['KeyWord']
inclusions_query |= Q(name__icontains=keyword)
elif field == 'ProductCode':
keyword = inclusion['RequestMatch']['KeyWord']
# We don't have a ProductCode. Use the identifier instead.
inclusions_query |= Q(identifier__icontains=keyword)
elif field == 'PackageFamilyName':
keyword = inclusion['RequestMatch']['KeyWord']
# We don't have family name. Use the name instead.
inclusions_query |= Q(name__icontains=keyword)
db_query &= inclusions_query
for filter_ in data.get('Filters', []):
field = filter_['PackageMatchField']
keyword = filter_['RequestMatch']['KeyWord']
if field == 'PackageIdentifier':
db_query &= Q(identifier__icontains=keyword)
return [
{
'PackageIdentifier': package.identifier,
'PackageName': package.name,
'Publisher': package.publisher,
'Versions': [
{'PackageVersion': version.version}
for version in package.version_set.all()
]
}
for package in Package.objects.filter(db_query)
if package.version_set.exists()
]
@require_GET
@load_tenant
def packageManifests(request, tenant, identifier):
try:
package = Package.objects.get(tenant=tenant, identifier=identifier)
except ObjectDoesNotExist:
# This is a peculiarity / inconsistency of the winget client. The API
# design docs say that packageManifests should return 404 when a package
# does not exist. But winget doesn't gracefully handle this case.
# Instead, it expects HTTP 204.
# See: https://github.com/microsoft/winget-cli-restsource/issues/170
return HttpResponse(status=204)
return _packageManifests(request, package)
@return_jsonresponse
def _packageManifests(request, package):
result = {
'PackageIdentifier': package.identifier
}
for version in package.version_set.all():
installers = []
for installer in version.installer_set.all():
for scope in installer.scopes:
installer_json = {
'Architecture': installer.architecture,
'InstallerType': installer.type,
'InstallerUrl':
request.build_absolute_uri(installer.file.url),
'InstallerSha256': installer.sha256,
'Scope': scope
}
if installer.nested_installer:
# NestedInstallerFiles needs to be a list even though at
# least winget 1.6.2771 does not support more than one.
installer_json['NestedInstallerFiles'] = [{
'RelativeFilePath': installer.nested_installer
}]
if installer.nested_installer_type:
installer_json['NestedInstallerType'] = \
installer.nested_installer_type
switches = {
'Silent': installer.silent_switch,
'SilentWithProgress': installer.silent_progress_switch,
'Interactive': installer.interactive_switch,
'InstallLocation': installer.install_location_switch,
'Log': installer.log_switch,
'Upgrade': installer.upgrade_switch,
'Custom': installer.custom_switch
}
nonempty_switches = {k: v for k, v in switches.items() if v}
if nonempty_switches:
installer_json['InstallerSwitches'] = nonempty_switches
installers.append(installer_json)
version_json = {
'PackageVersion': version.version,
'DefaultLocale': {
'PackageLocale': 'en-us',
'Publisher': package.publisher,
'PackageName': package.name,
'ShortDescription': package.description
},
'Installers': installers
}
result.setdefault('Versions', []).append(version_json)
return result